Merge mozilla-inbound to mozilla-central r=merge a=merge
authorANDREEA PAVEL <apavel@mozilla.com>
Thu, 02 Nov 2017 11:40:57 +0200
changeset 440555 cb05c80b965520825841dfa003177790faff635b
parent 440532 dfa093fe1e158e6ed8ec2ef377192af94fc86832 (current diff)
parent 440554 9ef586a0f767e3e83519fc3935692b86694a97ab (diff)
child 440556 b5a3b8ef6902998507fc881b6d628b055457fe31
push id8114
push userjlorenzo@mozilla.com
push dateThu, 02 Nov 2017 16:33:21 +0000
treeherdermozilla-beta@73e0d89a540f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge, merge
milestone58.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-inbound to mozilla-central r=merge a=merge
dom/interfaces/html/nsIDOMHTMLTextAreaElement.idl
layout/painting/nsDisplayList.cpp
layout/painting/nsDisplayList.h
netwerk/wifi/DBusHelpers.h
--- a/accessible/html/HTMLFormControlAccessible.cpp
+++ b/accessible/html/HTMLFormControlAccessible.cpp
@@ -10,17 +10,17 @@
 #include "nsEventShell.h"
 #include "nsTextEquivUtils.h"
 #include "Relation.h"
 #include "Role.h"
 #include "States.h"
 
 #include "nsContentList.h"
 #include "mozilla/dom/HTMLInputElement.h"
-#include "nsIDOMHTMLTextAreaElement.h"
+#include "mozilla/dom/HTMLTextAreaElement.h"
 #include "nsIEditor.h"
 #include "nsIFormControl.h"
 #include "nsIPersistentProperties2.h"
 #include "nsISelectionController.h"
 #include "nsIServiceManager.h"
 #include "nsITextControlElement.h"
 #include "nsITextControlFrame.h"
 #include "nsNameSpaceManager.h"
@@ -340,17 +340,17 @@ HTMLTextFieldAccessible::NativeName(nsSt
 
 void
 HTMLTextFieldAccessible::Value(nsString& aValue)
 {
   aValue.Truncate();
   if (NativeState() & states::PROTECTED)    // Don't return password text!
     return;
 
-  nsCOMPtr<nsIDOMHTMLTextAreaElement> textArea(do_QueryInterface(mContent));
+  HTMLTextAreaElement* textArea = HTMLTextAreaElement::FromContent(mContent);
   if (textArea) {
     textArea->GetValue(aValue);
     return;
   }
 
   HTMLInputElement* input = HTMLInputElement::FromContent(mContent);
   if (input) {
     // Pass NonSystem as the caller type, to be safe.  We don't expect to have a
--- a/browser/components/sessionstore/test/browser_248970_b_perwindowpb.js
+++ b/browser/components/sessionstore/test/browser_248970_b_perwindowpb.js
@@ -60,17 +60,17 @@ function test() {
 
   function compareFormValue(aTab, aQuery, aValue) {
     let node = getElementByXPath(aTab, aQuery);
     if (!node)
       return false;
     if (node instanceof Ci.nsIDOMHTMLInputElement)
       return aValue == (node.type == "checkbox" || node.type == "radio" ?
                        node.checked : node.value);
-    if (node instanceof Ci.nsIDOMHTMLTextAreaElement)
+    if (ChromeUtils.getClassName(node) === "HTMLTextAreaElement")
       return aValue == node.value;
     if (!node.multiple)
       return aValue == node.selectedIndex;
     return Array.every(node.options, (aOpt, aIx) =>
             (aValue.indexOf(aIx) > -1) == aOpt.selected);
   }
 
   /**
--- a/docshell/base/nsDocShellTreeOwner.cpp
+++ b/docshell/base/nsDocShellTreeOwner.cpp
@@ -35,17 +35,16 @@
 #include "Link.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/SVGTitleElement.h"
 #include "nsIDOMEvent.h"
 #include "nsIDOMFileList.h"
 #include "nsIDOMMouseEvent.h"
 #include "nsIFormControl.h"
 #include "nsIDOMHTMLInputElement.h"
-#include "nsIDOMHTMLTextAreaElement.h"
 #include "nsIDOMHTMLHtmlElement.h"
 #include "nsIDOMHTMLDocument.h"
 #include "nsIImageLoadingContent.h"
 #include "nsIWebNavigation.h"
 #include "nsIDOMHTMLElement.h"
 #include "nsIPresShell.h"
 #include "nsIStringBundle.h"
 #include "nsPIDOMWindow.h"
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -167,16 +167,17 @@
 #include "nsFrameLoader.h"
 #include "nsEscape.h"
 #include "nsObjectLoadingContent.h"
 #include "nsHtml5TreeOpExecutor.h"
 #include "mozilla/dom/HTMLLinkElement.h"
 #include "mozilla/dom/HTMLMediaElement.h"
 #include "mozilla/dom/HTMLIFrameElement.h"
 #include "mozilla/dom/HTMLImageElement.h"
+#include "mozilla/dom/HTMLTextAreaElement.h"
 #include "mozilla/dom/MediaSource.h"
 #include "mozilla/dom/FlyWebService.h"
 
 #include "mozAutoDocUpdate.h"
 #include "nsGlobalWindow.h"
 #include "mozilla/Encoding.h"
 #include "nsDOMNavigationTiming.h"
 
@@ -225,17 +226,16 @@
 #include "mozilla/dom/TabChild.h"
 #include "mozilla/dom/WebComponentsBinding.h"
 #include "mozilla/dom/CustomElementRegistryBinding.h"
 #include "mozilla/dom/CustomElementRegistry.h"
 #include "mozilla/dom/TimeoutManager.h"
 #include "mozilla/ExtensionPolicyService.h"
 #include "nsFrame.h"
 #include "nsDOMCaretPosition.h"
-#include "nsIDOMHTMLTextAreaElement.h"
 #include "nsViewportInfo.h"
 #include "mozilla/StaticPtr.h"
 #include "nsITextControlElement.h"
 #include "nsIDOMNSEditableElement.h"
 #include "nsIEditor.h"
 #include "nsIDOMCSSStyleRule.h"
 #include "mozilla/css/StyleRule.h"
 #include "nsIHttpChannelInternal.h"
@@ -10946,17 +10946,17 @@ nsIDocument::CaretPositionFromPoint(floa
 
   nsCOMPtr<nsIContent> node = offsets.content;
   uint32_t offset = offsets.offset;
   nsCOMPtr<nsIContent> anonNode = node;
   bool nodeIsAnonymous = node && node->IsInNativeAnonymousSubtree();
   if (nodeIsAnonymous) {
     node = ptFrame->GetContent();
     nsIContent* nonanon = node->FindFirstNonChromeOnlyAccessContent();
-    nsCOMPtr<nsIDOMHTMLTextAreaElement> textArea = do_QueryInterface(nonanon);
+    HTMLTextAreaElement* textArea = HTMLTextAreaElement::FromContent(nonanon);
     nsITextControlFrame* textFrame = do_QueryFrame(nonanon->GetPrimaryFrame());
     nsNumberControlFrame* numberFrame = do_QueryFrame(nonanon->GetPrimaryFrame());
     if (textFrame || numberFrame) {
       // If the anonymous content node has a child, then we need to make sure
       // that we get the appropriate child, as otherwise the offset may not be
       // correct when we construct a range for it.
       nsCOMPtr<nsIContent> firstChild = anonNode->GetFirstChild();
       if (firstChild) {
--- a/dom/base/nsWindowRoot.cpp
+++ b/dom/base/nsWindowRoot.cpp
@@ -14,22 +14,22 @@
 #include "nsPresContext.h"
 #include "nsLayoutCID.h"
 #include "nsContentCID.h"
 #include "nsString.h"
 #include "nsGlobalWindow.h"
 #include "nsFocusManager.h"
 #include "nsIContent.h"
 #include "nsIDOMHTMLInputElement.h"
-#include "nsIDOMHTMLTextAreaElement.h"
 #include "nsIControllers.h"
 #include "nsIController.h"
 #include "xpcpublic.h"
 #include "nsCycleCollectionParticipant.h"
 #include "mozilla/dom/TabParent.h"
+#include "mozilla/dom/HTMLTextAreaElement.h"
 
 #ifdef MOZ_XUL
 #include "nsXULElement.h"
 #endif
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
@@ -229,18 +229,18 @@ nsWindowRoot::GetControllers(bool aForVi
     if (xulElement) {
       ErrorResult rv;
       *aResult = xulElement->GetControllers(rv);
       NS_IF_ADDREF(*aResult);
       return rv.StealNSResult();
     }
 #endif
 
-    nsCOMPtr<nsIDOMHTMLTextAreaElement> htmlTextArea =
-      do_QueryInterface(focusedContent);
+    HTMLTextAreaElement* htmlTextArea =
+      HTMLTextAreaElement::FromContent(focusedContent);
     if (htmlTextArea)
       return htmlTextArea->GetControllers(aResult);
 
     nsCOMPtr<nsIDOMHTMLInputElement> htmlInputElement =
       do_QueryInterface(focusedContent);
     if (htmlInputElement)
       return htmlInputElement->GetControllers(aResult);
 
new file mode 100644
--- /dev/null
+++ b/dom/canvas/CacheMap.cpp
@@ -0,0 +1,43 @@
+/* -*- Mode: C++; tab-width: 13; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set ts=13 sts=4 et sw=4 tw=90: */
+/* 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 "CacheMap.h"
+
+namespace mozilla {
+
+void
+CacheMapInvalidator::InvalidateCaches() const
+{
+    while (mCacheEntries.size()) {
+        const auto& entry = *(mCacheEntries.begin());
+        entry->Invalidate();
+        MOZ_ASSERT(mCacheEntries.find(entry) == mCacheEntries.end());
+    }
+}
+
+namespace detail {
+
+CacheMapUntypedEntry::CacheMapUntypedEntry(std::vector<const CacheMapInvalidator*>&& invalidators)
+    : mInvalidators(Move(invalidators))
+{
+    for (const auto& cur : mInvalidators) {
+        // Don't assert that we insert, since there may be dupes in `invalidators`.
+        // (and it's not worth removing the dupes)
+        (void)cur->mCacheEntries.insert(this);
+    }
+}
+
+CacheMapUntypedEntry::~CacheMapUntypedEntry()
+{
+    for (const auto& cur : mInvalidators) {
+        // There might be dupes, so erase might return >1.
+        (void)cur->mCacheEntries.erase(this);
+    }
+}
+
+} // namespace detail
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/canvas/CacheMap.h
@@ -0,0 +1,128 @@
+/* -*- Mode: C++; tab-width: 13; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set ts=13 sts=4 et sw=4 tw=90: */
+/* 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 MOZILLA_CACHE_MAP_H_
+#define MOZILLA_CACHE_MAP_H_
+
+#include "mozilla/UniquePtr.h"
+#include <map>
+#include <unordered_set>
+#include <vector>
+
+namespace mozilla {
+
+namespace detail {
+struct CacheMapUntypedEntry;
+}
+
+class CacheMapInvalidator
+{
+    friend struct detail::CacheMapUntypedEntry;
+
+    mutable std::unordered_set<const detail::CacheMapUntypedEntry*> mCacheEntries;
+
+public:
+    ~CacheMapInvalidator() {
+        InvalidateCaches();
+    }
+
+    void InvalidateCaches() const;
+};
+
+namespace detail {
+
+struct CacheMapUntypedEntry
+{
+    template<typename, typename> friend class CacheMap;
+
+    const std::vector<const CacheMapInvalidator*> mInvalidators;
+
+    CacheMapUntypedEntry(std::vector<const CacheMapInvalidator*>&& invalidators);
+    ~CacheMapUntypedEntry();
+
+public:
+    virtual void Invalidate() const = 0;
+};
+
+template<typename T>
+struct DerefLess final {
+    bool operator ()(const T* const a, const T* const b) const {
+        return *a < *b;
+    }
+};
+
+} // namespace detail
+
+
+template<typename KeyT, typename ValueT>
+class CacheMap final
+{
+    struct Entry final : public detail::CacheMapUntypedEntry {
+        CacheMap& mParent;
+        const KeyT mKey;
+        const ValueT mValue;
+
+        Entry(std::vector<const CacheMapInvalidator*>&& invalidators, CacheMap& parent,
+              KeyT&& key, ValueT&& value)
+            : detail::CacheMapUntypedEntry(Move(invalidators))
+            , mParent(parent)
+            , mKey(Move(key))
+            , mValue(Move(value))
+        { }
+
+        void Invalidate() const override {
+            const auto erased = mParent.mMap.erase(&mKey);
+            MOZ_ALWAYS_TRUE( erased == 1 );
+        }
+
+        bool operator <(const Entry& x) const {
+            return mKey < x.mKey;
+        }
+    };
+
+    typedef std::map<const KeyT*, UniquePtr<const Entry>, detail::DerefLess<KeyT>> MapT;
+    MapT mMap;
+
+public:
+    const ValueT* Insert(KeyT&& key, ValueT&& value,
+                         std::vector<const CacheMapInvalidator*>&& invalidators)
+    {
+        UniquePtr<const Entry> entry( new Entry(Move(invalidators), *this, Move(key),
+                                                Move(value)) );
+
+        typename MapT::value_type insertable{
+            &entry->mKey,
+            nullptr
+        };
+        insertable.second = Move(entry);
+
+        const auto res = mMap.insert(Move(insertable));
+        const auto& didInsert = res.second;
+        MOZ_ALWAYS_TRUE( didInsert );
+
+        const auto& itr = res.first;
+        return &itr->second->mValue;
+    }
+
+    const ValueT* Find(const KeyT& key) const {
+        const auto itr = mMap.find(&key);
+        if (itr == mMap.end())
+            return nullptr;
+
+        return &itr->second->mValue;
+    }
+
+    void Invalidate() {
+        while (mMap.size()) {
+            const auto& itr = mMap.begin();
+            itr->second->Invalidate();
+        }
+    }
+};
+
+} // namespace mozilla
+
+#endif // MOZILLA_CACHE_MAP_H_
--- a/dom/canvas/WebGLBuffer.cpp
+++ b/dom/canvas/WebGLBuffer.cpp
@@ -54,17 +54,20 @@ WebGLBuffer::SetContentAfterBind(GLenum 
     }
 }
 
 void
 WebGLBuffer::Delete()
 {
     mContext->MakeContextCurrent();
     mContext->gl->fDeleteBuffers(1, &mGLName);
+
     mByteLength = 0;
+    mFetchInvalidator.InvalidateCaches();
+
     mIndexCache = nullptr;
     mIndexRanges.clear();
     LinkedListElement<WebGLBuffer>::remove(); // remove from mContext->mBuffers
 }
 
 ////////////////////////////////////////
 
 static bool
@@ -133,18 +136,16 @@ WebGLBuffer::BufferData(GLenum target, s
     }
 
     const auto& gl = mContext->gl;
     gl->MakeCurrent();
     const ScopedLazyBind lazyBind(gl, target, this);
 
     const bool sizeChanges = (size != ByteLength());
     if (sizeChanges) {
-        mContext->InvalidateBufferFetching();
-
         gl::GLContext::LocalErrorScope errorScope(*gl);
         gl->fBufferData(target, size, uploadData, usage);
         const auto error = errorScope.GetError();
 
         if (error) {
             MOZ_ASSERT(error == LOCAL_GL_OUT_OF_MEMORY);
             mContext->ErrorOutOfMemory("%s: Error from driver: 0x%04x", funcName, error);
             return;
@@ -152,16 +153,17 @@ WebGLBuffer::BufferData(GLenum target, s
     } else {
         gl->fBufferData(target, size, uploadData, usage);
     }
 
     mContext->OnDataAllocCall();
 
     mUsage = usage;
     mByteLength = size;
+    mFetchInvalidator.InvalidateCaches();
     mIndexCache = Move(newIndexCache);
 
     if (mIndexCache) {
         if (!mIndexRanges.empty()) {
             mContext->GeneratePerfWarning("[%p] Invalidating %u ranges.", this,
                                           uint32_t(mIndexRanges.size()));
             mIndexRanges.clear();
         }
@@ -229,28 +231,28 @@ IndexByteSizeByType(GLenum type)
     case LOCAL_GL_UNSIGNED_SHORT: return 2;
     case LOCAL_GL_UNSIGNED_INT:   return 4;
     default:
         MOZ_CRASH();
     }
 }
 
 void
-WebGLBuffer::InvalidateCacheRange(size_t byteOffset, size_t byteLength) const
+WebGLBuffer::InvalidateCacheRange(uint64_t byteOffset, uint64_t byteLength) const
 {
     MOZ_ASSERT(mIndexCache);
 
     std::vector<IndexRange> invalids;
-    const size_t updateBegin = byteOffset;
-    const size_t updateEnd = updateBegin + byteLength;
+    const uint64_t updateBegin = byteOffset;
+    const uint64_t updateEnd = updateBegin + byteLength;
     for (const auto& cur : mIndexRanges) {
         const auto& range = cur.first;
         const auto& indexByteSize = IndexByteSizeByType(range.type);
-        const size_t rangeBegin = range.first * indexByteSize;
-        const size_t rangeEnd = rangeBegin + range.count*indexByteSize;
+        const auto rangeBegin = range.byteOffset * indexByteSize;
+        const auto rangeEnd = rangeBegin + uint64_t(range.indexCount) * indexByteSize;
         if (rangeBegin >= updateEnd || rangeEnd <= updateBegin)
             continue;
         invalids.push_back(range);
     }
 
     if (!invalids.empty()) {
         mContext->GeneratePerfWarning("[%p] Invalidating %u/%u ranges.", this,
                                       uint32_t(invalids.size()),
@@ -268,89 +270,96 @@ WebGLBuffer::SizeOfIncludingThis(mozilla
     size_t size = mallocSizeOf(this);
     if (mIndexCache) {
         size += mByteLength;
     }
     return size;
 }
 
 template<typename T>
-static size_t
-MaxForRange(const void* data, size_t first, size_t count, const uint32_t ignoredVal)
+static Maybe<uint32_t>
+MaxForRange(const void* const start, const uint32_t count,
+            const Maybe<uint32_t>& untypedIgnoredVal)
 {
-    const T ignoredTVal(ignoredVal);
-    T ret = 0;
+    const Maybe<T> ignoredVal = (untypedIgnoredVal ? Some(T(untypedIgnoredVal.value()))
+                                                   : Nothing());
+    Maybe<uint32_t> maxVal;
 
-    auto itr = (const T*)data + first;
+    auto itr = (const T*)start;
     const auto end = itr + count;
 
     for (; itr != end; ++itr) {
         const auto& val = *itr;
-        if (val <= ret)
+        if (ignoredVal && val == ignoredVal.value())
             continue;
 
-        if (val == ignoredTVal)
+        if (maxVal && val <= maxVal.value())
             continue;
 
-        ret = val;
+        maxVal = Some(val);
     }
 
-    return size_t(ret);
+    return maxVal;
 }
 
-const uint32_t kMaxIndexRanges = 256;
+static const uint32_t kMaxIndexRanges = 256;
 
-bool
-WebGLBuffer::ValidateIndexedFetch(GLenum type, uint32_t numFetchable, size_t first,
-                                  size_t count) const
+Maybe<uint32_t>
+WebGLBuffer::GetIndexedFetchMaxVert(const GLenum type, const uint64_t byteOffset,
+                                    const uint32_t indexCount) const
 {
     if (!mIndexCache)
-        return true;
+        return Nothing();
 
-    if (!count)
-        return true;
-
-    const IndexRange range = { type, first, count };
-    auto res = mIndexRanges.insert({ range, size_t(0) });
+    const IndexRange range = { type, byteOffset, indexCount };
+    auto res = mIndexRanges.insert({ range, Nothing() });
     if (mIndexRanges.size() > kMaxIndexRanges) {
         mContext->GeneratePerfWarning("[%p] Clearing mIndexRanges after exceeding %u.",
                                       this, kMaxIndexRanges);
         mIndexRanges.clear();
-        res = mIndexRanges.insert({ range, size_t(0) });
+        res = mIndexRanges.insert({ range, Nothing() });
     }
 
     const auto& itr = res.first;
     const auto& didInsert = res.second;
 
     auto& maxFetchIndex = itr->second;
     if (didInsert) {
         const auto& data = mIndexCache.get();
-        const uint32_t ignoreVal = (mContext->IsWebGL2() ? UINT32_MAX : 0);
+
+        const auto start = (const uint8_t*)data + byteOffset;
+
+        Maybe<uint32_t> ignoredVal;
+        if (mContext->IsWebGL2()) {
+            ignoredVal = Some(UINT32_MAX);
+        }
 
         switch (type) {
         case LOCAL_GL_UNSIGNED_BYTE:
-            maxFetchIndex = MaxForRange<uint8_t>(data, first, count, ignoreVal);
+            maxFetchIndex = MaxForRange<uint8_t>(start, indexCount, ignoredVal);
             break;
         case LOCAL_GL_UNSIGNED_SHORT:
-            maxFetchIndex = MaxForRange<uint16_t>(data, first, count, ignoreVal);
+            maxFetchIndex = MaxForRange<uint16_t>(start, indexCount, ignoredVal);
             break;
         case LOCAL_GL_UNSIGNED_INT:
-            maxFetchIndex = MaxForRange<uint32_t>(data, first, count, ignoreVal);
+            maxFetchIndex = MaxForRange<uint32_t>(start, indexCount, ignoredVal);
             break;
         default:
             MOZ_CRASH();
         }
-
-        mContext->GeneratePerfWarning("[%p] New range #%u: (0x%04x, %u, %u): %u", this,
-                                      uint32_t(mIndexRanges.size()), type,
-                                      uint32_t(first), uint32_t(count),
-                                      uint32_t(maxFetchIndex));
+        const auto displayMaxVertIndex = maxFetchIndex ? int64_t(maxFetchIndex.value())
+                                                       : -1;
+        mContext->GeneratePerfWarning("[%p] New range #%u: (0x%04x, %" PRIu64 ", %u):"
+                                      " %" PRIi64,
+                                      this, uint32_t(mIndexRanges.size()), range.type,
+                                      range.byteOffset, range.indexCount,
+                                      displayMaxVertIndex);
     }
 
-    return maxFetchIndex < numFetchable;
+    return maxFetchIndex;
 }
 
 ////
 
 bool
 WebGLBuffer::ValidateCanBindToTarget(const char* funcName, GLenum target)
 {
     /* https://www.khronos.org/registry/webgl/specs/latest/2.0/#5.1
--- a/dom/canvas/WebGLBuffer.h
+++ b/dom/canvas/WebGLBuffer.h
@@ -3,16 +3,17 @@
  * 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 WEBGL_BUFFER_H_
 #define WEBGL_BUFFER_H_
 
 #include <map>
 
+#include "CacheMap.h"
 #include "GLDefs.h"
 #include "mozilla/LinkedList.h"
 #include "nsWrapperCache.h"
 #include "WebGLObjectModel.h"
 #include "WebGLTypes.h"
 
 namespace mozilla {
 
@@ -39,17 +40,18 @@ public:
 
     void Delete();
 
     size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
 
     GLenum Usage() const { return mUsage; }
     size_t ByteLength() const { return mByteLength; }
 
-    bool ValidateIndexedFetch(GLenum type, uint32_t max_allowed, size_t first, size_t count) const;
+    Maybe<uint32_t> GetIndexedFetchMaxVert(GLenum type, uint64_t byteOffset,
+                                           uint32_t indexCount) const;
     bool ValidateRange(const char* funcName, size_t byteOffset, size_t byteLen) const;
 
     WebGLContext* GetParentObject() const {
         return mContext;
     }
 
     virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto) override;
 
@@ -62,16 +64,17 @@ public:
 
     static void AddBindCount(GLenum target, WebGLBuffer* buffer, int8_t addVal) {
         if (!buffer)
             return;
 
         if (target == LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER) {
             MOZ_ASSERT_IF(addVal < 0, buffer->mTFBindCount >= size_t(-addVal));
             buffer->mTFBindCount += addVal;
+            buffer->mFetchInvalidator.InvalidateCaches();
         } else {
             MOZ_ASSERT_IF(addVal < 0, buffer->mNonTFBindCount >= size_t(-addVal));
             buffer->mNonTFBindCount += addVal;
         }
     }
 
     static void SetSlot(GLenum target, WebGLBuffer* newBuffer,
                         WebGLRefPtr<WebGLBuffer>* const out_slot)
@@ -90,39 +93,42 @@ public:
     const GLenum mGLName;
 
     NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLBuffer)
     NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WebGLBuffer)
 
 protected:
     ~WebGLBuffer();
 
-    void InvalidateCacheRange(size_t offset, size_t length) const;
+    void InvalidateCacheRange(uint64_t byteOffset, uint64_t byteLength) const;
 
     Kind mContent;
     GLenum mUsage;
     size_t mByteLength;
     size_t mTFBindCount;
     size_t mNonTFBindCount;
 
     struct IndexRange final {
         GLenum type;
-        size_t first;
-        size_t count;
+        uint64_t byteOffset;
+        uint32_t indexCount;
 
         bool operator<(const IndexRange& x) const {
             if (type != x.type)
                 return type < x.type;
 
-            if (first != x.first)
-                return first < x.first;
+            if (byteOffset != x.byteOffset)
+                return byteOffset < x.byteOffset;
 
-            return count < x.count;
+            return indexCount < x.indexCount;
         }
     };
 
     UniqueBuffer mIndexCache;
-    mutable std::map<IndexRange, size_t> mIndexRanges;
+    mutable std::map<IndexRange, Maybe<uint32_t>> mIndexRanges;
+
+public:
+    CacheMapInvalidator mFetchInvalidator;
 };
 
 } // namespace mozilla
 
 #endif // WEBGL_BUFFER_H_
--- a/dom/canvas/WebGLContext.cpp
+++ b/dom/canvas/WebGLContext.cpp
@@ -107,20 +107,16 @@ WebGLContextOptions::WebGLContextOptions
 }
 
 WebGLContext::WebGLContext()
     : WebGLContextUnchecked(nullptr)
     , mMaxPerfWarnings(gfxPrefs::WebGLMaxPerfWarnings())
     , mNumPerfWarnings(0)
     , mMaxAcceptableFBStatusInvals(gfxPrefs::WebGLMaxAcceptableFBStatusInvals())
     , mDataAllocGLCallCount(0)
-    , mBufferFetchingIsVerified(false)
-    , mBufferFetchingHasPerVertex(false)
-    , mMaxFetchedVertices(0)
-    , mMaxFetchedInstances(0)
     , mBypassShaderValidation(false)
     , mEmptyTFO(0)
     , mContextLossHandler(this)
     , mNeedsFakeNoAlpha(false)
     , mNeedsFakeNoDepth(false)
     , mNeedsFakeNoStencil(false)
     , mNeedsEmulatedLoneDepthStencil(false)
     , mAllowFBInvalidation(gfxPrefs::WebGLFBInvalidation())
@@ -179,18 +175,16 @@ WebGLContext::WebGLContext()
     mMaxWarnings = gfxPrefs::WebGLMaxWarningsPerContext();
     if (mMaxWarnings < -1) {
         GenerateWarning("webgl.max-warnings-per-context size is too large (seems like a negative value wrapped)");
         mMaxWarnings = 0;
     }
 
     mLastUseIndex = 0;
 
-    InvalidateBufferFetching();
-
     mDisableFragHighP = false;
 
     mDrawCallsSinceLastFlush = 0;
 }
 
 WebGLContext::~WebGLContext()
 {
     RemovePostRefreshObserver();
@@ -2298,16 +2292,37 @@ Intersect(const int32_t srcSize, const i
     }
 
     *out_intRead0 = intRead0;
     *out_intWrite0 = intWrite0;
     *out_intSize = intSize;
     return true;
 }
 
+// --
+
+uint64_t
+AvailGroups(const uint64_t totalAvailItems, const uint64_t firstItemOffset,
+            const uint32_t groupSize, const uint32_t groupStride)
+{
+    MOZ_ASSERT(groupSize && groupStride);
+    MOZ_ASSERT(groupSize <= groupStride);
+
+    if (totalAvailItems <= firstItemOffset)
+        return 0;
+    const size_t availItems = totalAvailItems - firstItemOffset;
+
+    size_t availGroups     = availItems / groupStride;
+    const size_t tailItems = availItems % groupStride;
+    if (tailItems >= groupSize) {
+        availGroups += 1;
+    }
+    return availGroups;
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 
 CheckedUint32
 WebGLContext::GetUnpackSize(bool isFunc3D, uint32_t width, uint32_t height,
                             uint32_t depth, uint8_t bytesPerPixel)
 {
     if (!width || !height || !depth)
         return 0;
@@ -2413,32 +2428,34 @@ WebGLContext::ValidateArrayBufferView(co
         elemCount = elemCountOverride;
     }
 
     *out_bytes = bytes + (elemOffset * elemSize);
     *out_byteLen = elemCount * elemSize;
     return true;
 }
 
-////////////////////////////////////////////////////////////////////////////////
-// XPCOM goop
+////
 
 void
 WebGLContext::UpdateMaxDrawBuffers()
 {
     gl->MakeCurrent();
     mGLMaxColorAttachments = gl->GetIntAs<uint32_t>(LOCAL_GL_MAX_COLOR_ATTACHMENTS);
     mGLMaxDrawBuffers = gl->GetIntAs<uint32_t>(LOCAL_GL_MAX_DRAW_BUFFERS);
 
     // WEBGL_draw_buffers:
     // "The value of the MAX_COLOR_ATTACHMENTS_WEBGL parameter must be greater than or
     //  equal to that of the MAX_DRAW_BUFFERS_WEBGL parameter."
     mGLMaxDrawBuffers = std::min(mGLMaxDrawBuffers, mGLMaxColorAttachments);
 }
 
+////////////////////////////////////////////////////////////////////////////////
+// XPCOM goop
+
 void
 ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& callback,
                             const std::vector<IndexedBufferBinding>& field,
                             const char* name, uint32_t flags)
 {
     for (const auto& cur : field) {
         ImplCycleCollectionTraverse(callback, cur.mBufferBinding, name, flags);
     }
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -29,16 +29,17 @@
 #include "ScopedGLHelpers.h"
 #include "TexUnpackBlob.h"
 
 #ifdef XP_MACOSX
 #include "ForceDiscreteGPUHelperCGL.h"
 #endif
 
 // Local
+#include "CacheMap.h"
 #include "WebGLContextLossHandler.h"
 #include "WebGLContextUnchecked.h"
 #include "WebGLFormats.h"
 #include "WebGLObjectModel.h"
 #include "WebGLStrongTypes.h"
 #include "WebGLTexture.h"
 
 // Generated
@@ -288,16 +289,17 @@ class WebGLContext
     friend class WebGLExtensionCompressedTextureS3TC;
     friend class WebGLExtensionCompressedTextureS3TC_SRGB;
     friend class WebGLExtensionDepthTexture;
     friend class WebGLExtensionDisjointTimerQuery;
     friend class WebGLExtensionDrawBuffers;
     friend class WebGLExtensionLoseContext;
     friend class WebGLExtensionVertexArray;
     friend class WebGLMemoryTracker;
+    friend struct webgl::LinkedProgramInfo;
     friend struct webgl::UniformBlockInfo;
 
     enum {
         UNPACK_FLIP_Y_WEBGL = 0x9240,
         UNPACK_PREMULTIPLY_ALPHA_WEBGL = 0x9241,
         // We throw InvalidOperation in TexImage if we fail to use GPU fast-path
         // for texture copy when it is set to true, only for debug purpose.
         UNPACK_REQUIRE_FASTPATH = 0x10001,
@@ -1385,59 +1387,41 @@ public:
         const bool isFuncInt = false;
         VertexAttribAnyPointer(funcName, isFuncInt, index, size, type, normalized, stride,
                                byteOffset);
     }
 
     void VertexAttribDivisor(GLuint index, GLuint divisor);
 
 private:
-    // Cache the max number of vertices and instances that can be read from
-    // bound VBOs (result of ValidateBuffers).
-    bool mBufferFetchingIsVerified;
-    bool mBufferFetchingHasPerVertex;
-    uint32_t mMaxFetchedVertices;
-    uint32_t mMaxFetchedInstances;
-    bool mBufferFetch_IsAttrib0Active;
-
-    bool DrawArrays_check(const char* funcName, GLenum mode, GLint first,
-                          GLsizei vertCount, GLsizei instanceCount);
-    bool DrawElements_check(const char* funcName, GLenum mode, GLsizei vertCount,
-                            GLenum type, WebGLintptr byteOffset,
-                            GLsizei instanceCount);
-    bool DrawInstanced_check(const char* info);
+    bool DrawArrays_check(const char* funcName, GLint first, GLsizei vertCount,
+                          GLsizei instanceCount, Maybe<uint32_t>* out_lastVert);
+    bool DrawElements_check(const char* funcName, GLsizei indexCount, GLenum type,
+                            WebGLintptr byteOffset, GLsizei instanceCount,
+                            Maybe<uint32_t>* out_lastVert);
     void Draw_cleanup(const char* funcName);
 
     void VertexAttrib1fv_base(GLuint index, uint32_t arrayLength,
                               const GLfloat* ptr);
     void VertexAttrib2fv_base(GLuint index, uint32_t arrayLength,
                               const GLfloat* ptr);
     void VertexAttrib3fv_base(GLuint index, uint32_t arrayLength,
                               const GLfloat* ptr);
     void VertexAttrib4fv_base(GLuint index, uint32_t arrayLength,
                               const GLfloat* ptr);
 
-    bool ValidateBufferFetching(const char* info);
     bool BindArrayAttribToLocation0(WebGLProgram* prog);
 
 // -----------------------------------------------------------------------------
 // PROTECTED
 protected:
     WebGLVertexAttrib0Status WhatDoesVertexAttrib0Need() const;
     bool DoFakeVertexAttrib0(const char* funcName, GLuint vertexCount);
     void UndoFakeVertexAttrib0();
 
-    inline void InvalidateBufferFetching()
-    {
-        mBufferFetchingIsVerified = false;
-        mBufferFetchingHasPerVertex = false;
-        mMaxFetchedVertices = 0;
-        mMaxFetchedInstances = 0;
-    }
-
     CheckedUint32 mGeneration;
 
     WebGLContextOptions mOptions;
 
     bool mInvalidated;
     bool mCapturedFrameInvalidated;
     bool mResetLayer;
     bool mOptionsFrozen;
@@ -1922,16 +1906,17 @@ protected:
     GLuint mEmptyTFO;
 
     // Generic Vertex Attributes
     // Though CURRENT_VERTEX_ATTRIB is listed under "Vertex Shader State" in the spec
     // state tables, this isn't vertex shader /object/ state. This array is merely state
     // useful to vertex shaders, but is global state.
     UniquePtr<GLenum[]> mGenericVertexAttribTypes;
     uint8_t mGenericVertexAttrib0Data[sizeof(float) * 4];
+    CacheMapInvalidator mGenericVertexAttribTypeInvalidator;
 
     GLuint mFakeVertexAttrib0BufferObject;
     size_t mFakeVertexAttrib0BufferObjectSize;
     bool mFakeVertexAttrib0DataDefined;
     uint8_t mFakeVertexAttrib0Data[sizeof(float) * 4];
 
     JSObject* GetVertexAttribFloat32Array(JSContext* cx, GLuint index);
     JSObject* GetVertexAttribInt32Array(JSContext* cx, GLuint index);
@@ -2184,16 +2169,20 @@ private:
 };
 
 ////
 
 bool
 Intersect(int32_t srcSize, int32_t read0, int32_t readSize, int32_t* out_intRead0,
           int32_t* out_intWrite0, int32_t* out_intSize);
 
+uint64_t
+AvailGroups(uint64_t totalAvailItems, uint64_t firstItemOffset, uint32_t groupSize,
+            uint32_t groupStride);
+
 ////
 
 void
 ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& callback,
                             const std::vector<IndexedBufferBinding>& field,
                             const char* name, uint32_t flags = 0);
 
 void
--- a/dom/canvas/WebGLContextBuffers.cpp
+++ b/dom/canvas/WebGLContextBuffers.cpp
@@ -496,18 +496,16 @@ WebGLContext::DeleteBuffer(WebGLBuffer* 
         for (auto& binding : mIndexedUniformBufferBindings) {
             fnClearIfBuffer(0, binding.mBufferBinding);
         }
     }
 
     ////
 
     buffer->RequestDelete();
-
-    InvalidateBufferFetching();
 }
 
 bool
 WebGLContext::IsBuffer(WebGLBuffer* buffer)
 {
     if (!ValidateIsObject("isBuffer", buffer))
         return false;
 
--- a/dom/canvas/WebGLContextDraw.cpp
+++ b/dom/canvas/WebGLContextDraw.cpp
@@ -1,15 +1,16 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* 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 "WebGLContext.h"
 
+#include "GeckoProfiler.h"
 #include "GLContext.h"
 #include "mozilla/CheckedInt.h"
 #include "mozilla/UniquePtrExtensions.h"
 #include "nsPrintfCString.h"
 #include "WebGLBuffer.h"
 #include "WebGLContextUtils.h"
 #include "WebGLFramebuffer.h"
 #include "WebGLProgram.h"
@@ -225,140 +226,62 @@ WebGLContext::BindFakeBlack(uint32_t tex
     gl->fActiveTexture(LOCAL_GL_TEXTURE0 + texUnit);
     gl->fBindTexture(target.get(), fakeBlackTex->mGLName);
     gl->fActiveTexture(LOCAL_GL_TEXTURE0 + mActiveTexture);
     return true;
 }
 
 ////////////////////////////////////////
 
-bool
-WebGLContext::DrawInstanced_check(const char* info)
-{
-    MOZ_ASSERT(IsWebGL2() ||
-               IsExtensionEnabled(WebGLExtensionID::ANGLE_instanced_arrays));
-    if (!mBufferFetchingHasPerVertex) {
-        /* http://www.khronos.org/registry/gles/extensions/ANGLE/ANGLE_instanced_arrays.txt
-         *  If all of the enabled vertex attribute arrays that are bound to active
-         *  generic attributes in the program have a non-zero divisor, the draw
-         *  call should return INVALID_OPERATION.
-         *
-         * NB: This also appears to apply to NV_instanced_arrays, though the
-         * INVALID_OPERATION emission is not explicitly stated.
-         * ARB_instanced_arrays does not have this restriction.
-         */
-        ErrorInvalidOperation("%s: at least one vertex attribute divisor should be 0", info);
-        return false;
-    }
-
-    return true;
-}
-
-bool
-WebGLContext::DrawArrays_check(const char* funcName, GLenum mode, GLint first,
-                               GLsizei vertCount, GLsizei instanceCount)
-{
-    if (!ValidateDrawModeEnum(mode, funcName))
-        return false;
-
-    if (!ValidateNonNegative(funcName, "first", first) ||
-        !ValidateNonNegative(funcName, "vertCount", vertCount) ||
-        !ValidateNonNegative(funcName, "instanceCount", instanceCount))
-    {
-        return false;
-    }
-
-    if (!ValidateStencilParamsForDrawCall())
-        return false;
-
-    if (IsWebGL2() && !gl->IsSupported(gl::GLFeature::prim_restart_fixed)) {
-        MOZ_ASSERT(gl->IsSupported(gl::GLFeature::prim_restart));
-        if (mPrimRestartTypeBytes != 0) {
-            mPrimRestartTypeBytes = 0;
-
-            // OSX appears to have severe perf issues with leaving this enabled.
-            gl->fDisable(LOCAL_GL_PRIMITIVE_RESTART);
-        }
-    }
-
-    if (!vertCount || !instanceCount)
-        return false; // No error, just early out.
-
-    if (!ValidateBufferFetching(funcName))
-        return false;
-
-    const auto checked_firstPlusCount = CheckedInt<GLsizei>(first) + vertCount;
-    if (!checked_firstPlusCount.isValid()) {
-        ErrorInvalidOperation("%s: overflow in first+vertCount", funcName);
-        return false;
-    }
-
-    if (uint32_t(checked_firstPlusCount.value()) > mMaxFetchedVertices) {
-        ErrorInvalidOperation("%s: Bound vertex attribute buffers do not have sufficient"
-                              " size for given first and count.",
-                              funcName);
-        return false;
-    }
-
-    return true;
-}
-
-////////////////////////////////////////
-
 template<typename T>
 static bool
 DoSetsIntersect(const std::set<T>& a, const std::set<T>& b)
 {
     std::vector<T> intersection;
     std::set_intersection(a.begin(), a.end(), b.begin(), b.end(),
                           std::back_inserter(intersection));
     return bool(intersection.size());
 }
 
 class ScopedDrawHelper final
 {
     WebGLContext* const mWebGL;
     bool mDidFake;
 
 public:
-    ScopedDrawHelper(WebGLContext* webgl, const char* funcName, uint32_t firstVertex,
-                     uint32_t vertCount, uint32_t instanceCount, bool* const out_error)
+    ScopedDrawHelper(WebGLContext* const webgl, const char* const funcName,
+                     const GLenum mode, const Maybe<uint32_t>& lastRequiredVertex,
+                     const uint32_t instanceCount, bool* const out_error)
         : mWebGL(webgl)
         , mDidFake(false)
     {
-        if (instanceCount > mWebGL->mMaxFetchedInstances) {
-            mWebGL->ErrorInvalidOperation("%s: Bound instance attribute buffers do not"
-                                          " have sufficient size for given"
-                                          " `instanceCount`.",
-                                          funcName);
+        MOZ_ASSERT(mWebGL->gl->IsCurrent());
+
+        if (!mWebGL->ValidateDrawModeEnum(mode, funcName)) {
             *out_error = true;
             return;
         }
 
-        MOZ_ASSERT(mWebGL->gl->IsCurrent());
+        if (!mWebGL->ValidateStencilParamsForDrawCall()) {
+            *out_error = true;
+            return;
+        }
+
+        ////
 
         if (mWebGL->mBoundDrawFramebuffer) {
             if (!mWebGL->mBoundDrawFramebuffer->ValidateAndInitAttachments(funcName)) {
                 *out_error = true;
                 return;
             }
         } else {
             mWebGL->ClearBackbufferIfNeeded();
         }
 
         ////
-
-        const size_t requiredVerts = firstVertex + vertCount;
-        if (!mWebGL->DoFakeVertexAttrib0(funcName, requiredVerts)) {
-            *out_error = true;
-            return;
-        }
-        mDidFake = true;
-
-        ////
         // Check UBO sizes.
 
         const auto& linkInfo = mWebGL->mActiveProgramLinkInfo;
 
         for (const auto& cur : linkInfo->uniformBlocks) {
             const auto& dataSize = cur->mDataSize;
             const auto& binding = cur->mBinding;
             if (!binding) {
@@ -414,49 +337,49 @@ public:
                     *out_error = true;
                     return;
                 }
             }
         }
 
         ////
 
-        for (const auto& progAttrib : linkInfo->attribs) {
-            const auto& loc = progAttrib.mLoc;
-            if (loc == -1)
-                continue;
-
-            const auto& attribData = mWebGL->mBoundVertexArray->mAttribs[loc];
-
-            GLenum attribDataBaseType;
-            if (attribData.mEnabled) {
-                attribDataBaseType = attribData.BaseType();
+        const auto& fetchLimits = linkInfo->GetDrawFetchLimits(funcName);
+        if (!fetchLimits) {
+            *out_error = true;
+            return;
+        }
 
-                if (attribData.mBuf->IsBoundForTF()) {
-                    mWebGL->ErrorInvalidOperation("%s: Vertex attrib %u's buffer is bound"
-                                                  " or in use for transform feedback.",
-                                                  funcName, loc);
-                    *out_error = true;
-                    return;
-                }
-            } else {
-                attribDataBaseType = mWebGL->mGenericVertexAttribTypes[loc];
-            }
-
-            if (attribDataBaseType != progAttrib.mBaseType) {
-                nsCString progType, dataType;
-                WebGLContext::EnumName(progAttrib.mBaseType, &progType);
-                WebGLContext::EnumName(attribDataBaseType, &dataType);
-                mWebGL->ErrorInvalidOperation("%s: Vertex attrib %u requires data of type"
-                                              " %s, but is being supplied with type %s.",
-                                              funcName, loc, progType.BeginReading(),
-                                              dataType.BeginReading());
+        if (lastRequiredVertex && instanceCount) {
+            if (lastRequiredVertex.value() >= fetchLimits->maxVerts) {
+                mWebGL->ErrorInvalidOperation("%s: Vertex fetch requires vertex #%u, but"
+                                              " attribs only supply %" PRIu64 ".",
+                                              funcName, lastRequiredVertex.value(),
+                                              fetchLimits->maxVerts);
                 *out_error = true;
                 return;
             }
+            if (instanceCount > fetchLimits->maxInstances) {
+                mWebGL->ErrorInvalidOperation("%s: Instance fetch requires %u, but"
+                                              " attribs only supply %" PRIu64 ".",
+                                              funcName, instanceCount,
+                                              fetchLimits->maxInstances);
+                *out_error = true;
+                return;
+            }
+        }
+
+        ////
+
+        if (lastRequiredVertex) {
+            if (!mWebGL->DoFakeVertexAttrib0(funcName, lastRequiredVertex.value())) {
+                *out_error = true;
+                return;
+            }
+            mDidFake = true;
         }
 
         ////
 
         mWebGL->RunContextLossTimer();
     }
 
     ~ScopedDrawHelper() {
@@ -543,214 +466,223 @@ public:
             return;
 
         mTFO->mActive_VertPosition += mUsedVerts;
     }
 };
 
 ////////////////////////////////////////
 
+bool
+WebGLContext::DrawArrays_check(const char* const funcName, const GLint first,
+                               const GLsizei vertCount, const GLsizei instanceCount,
+                               Maybe<uint32_t>* const out_lastVert)
+{
+    if (!ValidateNonNegative(funcName, "first", first) ||
+        !ValidateNonNegative(funcName, "vertCount", vertCount) ||
+        !ValidateNonNegative(funcName, "instanceCount", instanceCount))
+    {
+        return false;
+    }
+
+    if (IsWebGL2() && !gl->IsSupported(gl::GLFeature::prim_restart_fixed)) {
+        MOZ_ASSERT(gl->IsSupported(gl::GLFeature::prim_restart));
+        if (mPrimRestartTypeBytes != 0) {
+            mPrimRestartTypeBytes = 0;
+
+            // OSX appears to have severe perf issues with leaving this enabled.
+            gl->fDisable(LOCAL_GL_PRIMITIVE_RESTART);
+        }
+    }
+
+    if (!vertCount) {
+        *out_lastVert = Nothing();
+    } else {
+        const auto lastVert_checked = CheckedInt<uint32_t>(first) + vertCount - 1;
+        if (!lastVert_checked.isValid()) {
+            ErrorOutOfMemory("%s: `first+vertCount` out of range.", funcName);
+            return false;
+        }
+        *out_lastVert = Some(lastVert_checked.value());
+    }
+    return true;
+}
+
 void
 WebGLContext::DrawArrays(GLenum mode, GLint first, GLsizei vertCount)
 {
+    AUTO_PROFILER_LABEL("WebGLContext::DrawArrays", GRAPHICS);
     const char funcName[] = "drawArrays";
     if (IsContextLost())
         return;
 
     MakeContextCurrent();
 
     bool error = false;
     ScopedResolveTexturesForDraw scopedResolve(this, funcName, &error);
     if (error)
         return;
 
     const GLsizei instanceCount = 1;
-    if (!DrawArrays_check(funcName, mode, first, vertCount, instanceCount))
+    Maybe<uint32_t> lastVert;
+    if (!DrawArrays_check(funcName, first, vertCount, instanceCount, &lastVert))
         return;
 
-    const ScopedDrawHelper scopedHelper(this, funcName, first, vertCount, instanceCount, &error);
+    const ScopedDrawHelper scopedHelper(this, funcName, mode, lastVert, instanceCount,
+                                        &error);
     if (error)
         return;
 
     const ScopedDrawWithTransformFeedback scopedTF(this, funcName, mode, vertCount,
                                                    instanceCount, &error);
     if (error)
         return;
 
     {
         ScopedDrawCallWrapper wrapper(*this);
-        gl->fDrawArrays(mode, first, vertCount);
+        if (vertCount) {
+            AUTO_PROFILER_LABEL("glDrawArrays", GRAPHICS);
+            gl->fDrawArrays(mode, first, vertCount);
+        }
     }
 
     Draw_cleanup(funcName);
     scopedTF.Advance();
 }
 
 void
 WebGLContext::DrawArraysInstanced(GLenum mode, GLint first, GLsizei vertCount,
                                   GLsizei instanceCount)
 {
+    AUTO_PROFILER_LABEL("WebGLContext::DrawArraysInstanced", GRAPHICS);
     const char funcName[] = "drawArraysInstanced";
     if (IsContextLost())
         return;
 
     MakeContextCurrent();
 
     bool error = false;
     ScopedResolveTexturesForDraw scopedResolve(this, funcName, &error);
     if (error)
         return;
 
-    if (!DrawArrays_check(funcName, mode, first, vertCount, instanceCount))
+    Maybe<uint32_t> lastVert;
+    if (!DrawArrays_check(funcName, first, vertCount, instanceCount, &lastVert))
         return;
 
-    if (!DrawInstanced_check(funcName))
-        return;
-
-    const ScopedDrawHelper scopedHelper(this, funcName, first, vertCount, instanceCount, &error);
+    const ScopedDrawHelper scopedHelper(this, funcName, mode, lastVert, instanceCount,
+                                        &error);
     if (error)
         return;
 
     const ScopedDrawWithTransformFeedback scopedTF(this, funcName, mode, vertCount,
                                                    instanceCount, &error);
     if (error)
         return;
 
     {
         ScopedDrawCallWrapper wrapper(*this);
-        gl->fDrawArraysInstanced(mode, first, vertCount, instanceCount);
+        if (vertCount && instanceCount) {
+            AUTO_PROFILER_LABEL("glDrawArraysInstanced", GRAPHICS);
+            gl->fDrawArraysInstanced(mode, first, vertCount, instanceCount);
+        }
     }
 
     Draw_cleanup(funcName);
     scopedTF.Advance();
 }
 
 ////////////////////////////////////////
 
 bool
-WebGLContext::DrawElements_check(const char* funcName, GLenum mode, GLsizei vertCount,
-                                 GLenum type, WebGLintptr byteOffset,
-                                 GLsizei instanceCount)
+WebGLContext::DrawElements_check(const char* const funcName, const GLsizei rawIndexCount,
+                                 const GLenum type, const WebGLintptr byteOffset,
+                                 const GLsizei instanceCount,
+                                 Maybe<uint32_t>* const out_lastVert)
 {
-    if (!ValidateDrawModeEnum(mode, funcName))
-        return false;
-
     if (mBoundTransformFeedback &&
         mBoundTransformFeedback->mIsActive &&
         !mBoundTransformFeedback->mIsPaused)
     {
         ErrorInvalidOperation("%s: DrawElements* functions are incompatible with"
                               " transform feedback.",
                               funcName);
         return false;
     }
 
-    if (!ValidateNonNegative(funcName, "vertCount", vertCount) ||
+    if (!ValidateNonNegative(funcName, "vertCount", rawIndexCount) ||
         !ValidateNonNegative(funcName, "byteOffset", byteOffset) ||
         !ValidateNonNegative(funcName, "instanceCount", instanceCount))
     {
         return false;
     }
-
-    if (!ValidateStencilParamsForDrawCall())
-        return false;
+    const auto indexCount = uint32_t(rawIndexCount);
 
-    if (!vertCount || !instanceCount)
-        return false; // No error, just early out.
-
-    uint8_t bytesPerElem = 0;
+    uint8_t bytesPerIndex = 0;
     switch (type) {
     case LOCAL_GL_UNSIGNED_BYTE:
-        bytesPerElem = 1;
+        bytesPerIndex = 1;
         break;
 
     case LOCAL_GL_UNSIGNED_SHORT:
-        bytesPerElem = 2;
+        bytesPerIndex = 2;
         break;
 
     case LOCAL_GL_UNSIGNED_INT:
         if (IsWebGL2() || IsExtensionEnabled(WebGLExtensionID::OES_element_index_uint)) {
-            bytesPerElem = 4;
+            bytesPerIndex = 4;
         }
         break;
     }
-
-    if (!bytesPerElem) {
+    if (!bytesPerIndex) {
         ErrorInvalidEnum("%s: Invalid `type`: 0x%04x", funcName, type);
         return false;
     }
-
-    if (byteOffset % bytesPerElem != 0) {
+    if (byteOffset % bytesPerIndex != 0) {
         ErrorInvalidOperation("%s: `byteOffset` must be a multiple of the size of `type`",
                               funcName);
         return false;
     }
 
     ////
 
     if (IsWebGL2() && !gl->IsSupported(gl::GLFeature::prim_restart_fixed)) {
         MOZ_ASSERT(gl->IsSupported(gl::GLFeature::prim_restart));
-        if (mPrimRestartTypeBytes != bytesPerElem) {
-            mPrimRestartTypeBytes = bytesPerElem;
+        if (mPrimRestartTypeBytes != bytesPerIndex) {
+            mPrimRestartTypeBytes = bytesPerIndex;
 
             const uint32_t ones = UINT32_MAX >> (32 - 8*mPrimRestartTypeBytes);
             gl->fEnable(LOCAL_GL_PRIMITIVE_RESTART);
             gl->fPrimitiveRestartIndex(ones);
         }
     }
 
     ////
-
-    const GLsizei first = byteOffset / bytesPerElem;
-    const CheckedUint32 checked_byteCount = bytesPerElem * CheckedUint32(vertCount);
+    // Index fetching
 
-    if (!checked_byteCount.isValid()) {
-        ErrorInvalidValue("%s: Overflow in byteCount.", funcName);
-        return false;
+    if (!indexCount || !instanceCount) {
+        *out_lastVert = Nothing();
+        return true;
     }
 
-    if (!mBoundVertexArray->mElementArrayBuffer) {
-        ErrorInvalidOperation("%s: Must have element array buffer binding.", funcName);
-        return false;
-    }
+    const auto& indexBuffer = mBoundVertexArray->mElementArrayBuffer;
 
-    WebGLBuffer& elemArrayBuffer = *mBoundVertexArray->mElementArrayBuffer;
-
-    if (!elemArrayBuffer.ByteLength()) {
-        ErrorInvalidOperation("%s: Bound element array buffer doesn't have any data.",
-                              funcName);
+    size_t availBytes = 0;
+    if (indexBuffer) {
+        MOZ_ASSERT(!indexBuffer->IsBoundForTF(), "This should be impossible.");
+        availBytes = indexBuffer->ByteLength();
+    }
+    const auto availIndices = AvailGroups(availBytes, byteOffset, bytesPerIndex,
+                                          bytesPerIndex);
+    if (indexCount > availIndices) {
+        ErrorInvalidOperation("%s: Index buffer too small.", funcName);
         return false;
     }
 
-    CheckedInt<GLsizei> checked_neededByteCount = checked_byteCount.toChecked<GLsizei>() + byteOffset;
-
-    if (!checked_neededByteCount.isValid()) {
-        ErrorInvalidOperation("%s: Overflow in byteOffset+byteCount.", funcName);
-        return false;
-    }
-
-    if (uint32_t(checked_neededByteCount.value()) > elemArrayBuffer.ByteLength()) {
-        ErrorInvalidOperation("%s: Bound element array buffer is too small for given"
-                              " count and offset.",
-                              funcName);
-        return false;
-    }
-
-    if (!ValidateBufferFetching(funcName))
-        return false;
-
-    if (!mMaxFetchedVertices ||
-        !elemArrayBuffer.ValidateIndexedFetch(type, mMaxFetchedVertices, first, vertCount))
-    {
-        ErrorInvalidOperation("%s: bound vertex attribute buffers do not have sufficient "
-                              "size for given indices from the bound element array",
-                              funcName);
-        return false;
-    }
-
+    *out_lastVert = indexBuffer->GetIndexedFetchMaxVert(type, byteOffset, indexCount);
     return true;
 }
 
 static void
 HandleDrawElementsErrors(WebGLContext* webgl, const char* funcName,
                          gl::GLContext::LocalErrorScope& errorScope)
 {
     const auto err = errorScope.GetError();
@@ -765,101 +697,114 @@ HandleDrawElementsErrors(WebGLContext* w
         webgl->ErrorImplementationBug("%s: Unexpected driver error during indexed draw"
                                       " call. Please file a bug.",
                                       funcName);
         return;
     }
 }
 
 void
-WebGLContext::DrawElements(GLenum mode, GLsizei vertCount, GLenum type,
+WebGLContext::DrawElements(GLenum mode, GLsizei indexCount, GLenum type,
                            WebGLintptr byteOffset, const char* funcName)
 {
+    AUTO_PROFILER_LABEL("WebGLContext::DrawElements", GRAPHICS);
     if (!funcName) {
         funcName = "drawElements";
     }
-
     if (IsContextLost())
         return;
 
     MakeContextCurrent();
 
     bool error = false;
     ScopedResolveTexturesForDraw scopedResolve(this, funcName, &error);
     if (error)
         return;
 
     const GLsizei instanceCount = 1;
-    if (!DrawElements_check(funcName, mode, vertCount, type, byteOffset, instanceCount))
+    Maybe<uint32_t> lastVert;
+    if (!DrawElements_check(funcName, indexCount, type, byteOffset, instanceCount,
+                            &lastVert))
+    {
         return;
+    }
 
-    const ScopedDrawHelper scopedHelper(this, funcName, 0, mMaxFetchedVertices, instanceCount,
+    const ScopedDrawHelper scopedHelper(this, funcName, mode, lastVert, instanceCount,
                                         &error);
     if (error)
         return;
 
     {
         ScopedDrawCallWrapper wrapper(*this);
         {
             UniquePtr<gl::GLContext::LocalErrorScope> errorScope;
 
             if (gl->IsANGLE()) {
                 errorScope.reset(new gl::GLContext::LocalErrorScope(*gl));
             }
 
-            gl->fDrawElements(mode, vertCount, type,
-                              reinterpret_cast<GLvoid*>(byteOffset));
+            if (lastVert) {
+                AUTO_PROFILER_LABEL("glDrawElements", GRAPHICS);
+                gl->fDrawElements(mode, indexCount, type,
+                                  reinterpret_cast<GLvoid*>(byteOffset));
+            }
 
             if (errorScope) {
                 HandleDrawElementsErrors(this, funcName, *errorScope);
             }
         }
     }
 
     Draw_cleanup(funcName);
 }
 
 void
-WebGLContext::DrawElementsInstanced(GLenum mode, GLsizei vertCount, GLenum type,
+WebGLContext::DrawElementsInstanced(GLenum mode, GLsizei indexCount, GLenum type,
                                     WebGLintptr byteOffset, GLsizei instanceCount)
 {
+    AUTO_PROFILER_LABEL("WebGLContext::DrawElementsInstanced", GRAPHICS);
     const char funcName[] = "drawElementsInstanced";
     if (IsContextLost())
         return;
 
     MakeContextCurrent();
 
     bool error = false;
     ScopedResolveTexturesForDraw scopedResolve(this, funcName, &error);
     if (error)
         return;
 
-    if (!DrawElements_check(funcName, mode, vertCount, type, byteOffset, instanceCount))
+    Maybe<uint32_t> lastVert;
+    if (!DrawElements_check(funcName, indexCount, type, byteOffset, instanceCount,
+                            &lastVert))
+    {
         return;
+    }
 
-    if (!DrawInstanced_check(funcName))
-        return;
-
-    const ScopedDrawHelper scopedHelper(this, funcName, 0, mMaxFetchedVertices, instanceCount,
+    const ScopedDrawHelper scopedHelper(this, funcName, mode, lastVert, instanceCount,
                                         &error);
     if (error)
         return;
 
     {
         ScopedDrawCallWrapper wrapper(*this);
         {
             UniquePtr<gl::GLContext::LocalErrorScope> errorScope;
 
             if (gl->IsANGLE()) {
                 errorScope.reset(new gl::GLContext::LocalErrorScope(*gl));
             }
 
-            gl->fDrawElementsInstanced(mode, vertCount, type,
-                                       reinterpret_cast<GLvoid*>(byteOffset),
-                                       instanceCount);
+            if (lastVert && instanceCount) {
+                AUTO_PROFILER_LABEL("glDrawElementsInstanced", GRAPHICS);
+                gl->fDrawElementsInstanced(mode, indexCount, type,
+                                           reinterpret_cast<GLvoid*>(byteOffset),
+                                           instanceCount);
+            }
+
             if (errorScope) {
                 HandleDrawElementsErrors(this, funcName, *errorScope);
             }
         }
     }
 
     Draw_cleanup(funcName);
 }
@@ -905,156 +850,47 @@ WebGLContext::Draw_cleanup(const char* f
             GenerateWarning("%s: Drawing to a destination rect smaller than the viewport"
                             " rect. (This warning will only be given once)",
                             funcName);
             mAlreadyWarnedAboutViewportLargerThanDest = true;
         }
     }
 }
 
-/*
- * Verify that state is consistent for drawing, and compute max number of elements (maxAllowedCount)
- * that will be legal to be read from bound VBOs.
- */
-
-bool
-WebGLContext::ValidateBufferFetching(const char* info)
-{
-    MOZ_ASSERT(mCurrentProgram);
-    // Note that mCurrentProgram->IsLinked() is NOT GUARANTEED.
-    MOZ_ASSERT(mActiveProgramLinkInfo);
-
-#ifdef DEBUG
-    GLint currentProgram = 0;
-    MakeContextCurrent();
-    gl->fGetIntegerv(LOCAL_GL_CURRENT_PROGRAM, &currentProgram);
-    MOZ_ASSERT(GLuint(currentProgram) == mCurrentProgram->mGLName,
-               "WebGL: current program doesn't agree with GL state");
-#endif
-
-    if (mBufferFetchingIsVerified)
-        return true;
-
-    bool hasPerVertex = false;
-    uint32_t maxVertices = UINT32_MAX;
-    uint32_t maxInstances = UINT32_MAX;
-    const uint32_t attribCount = mBoundVertexArray->mAttribs.Length();
-
-    uint32_t i = 0;
-    for (const auto& vd : mBoundVertexArray->mAttribs) {
-        // If the attrib array isn't enabled, there's nothing to check;
-        // it's a static value.
-        if (!vd.mEnabled)
-            continue;
-
-        if (!vd.mBuf) {
-            ErrorInvalidOperation("%s: no VBO bound to enabled vertex attrib index %du!",
-                                  info, i);
-            return false;
-        }
-
-        ++i;
-    }
-
-    mBufferFetch_IsAttrib0Active = false;
-
-    for (const auto& attrib : mActiveProgramLinkInfo->attribs) {
-        if (attrib.mLoc == -1)
-            continue;
-
-        const uint32_t attribLoc(attrib.mLoc);
-        if (attribLoc >= attribCount)
-            continue;
-
-        if (attribLoc == 0) {
-            mBufferFetch_IsAttrib0Active = true;
-        }
-
-        const auto& vd = mBoundVertexArray->mAttribs[attribLoc];
-        if (!vd.mEnabled)
-            continue;
-
-        const auto& bufByteLen = vd.mBuf->ByteLength();
-        if (vd.ByteOffset() > bufByteLen) {
-            maxVertices = 0;
-            maxInstances = 0;
-            break;
-        }
-
-        size_t availBytes = bufByteLen - vd.ByteOffset();
-        if (vd.BytesPerVertex() > availBytes) {
-            maxVertices = 0;
-            maxInstances = 0;
-            break;
-        }
-        availBytes -= vd.BytesPerVertex(); // Snip off the tail.
-        const size_t vertCapacity = availBytes / vd.ExplicitStride() + 1; // Add +1 for the snipped tail.
-
-        if (vd.mDivisor == 0) {
-            if (vertCapacity < maxVertices) {
-                maxVertices = vertCapacity;
-            }
-            hasPerVertex = true;
-        } else {
-            const auto curMaxInstances = CheckedInt<size_t>(vertCapacity) * vd.mDivisor;
-            // If this isn't valid, it's because we overflowed, which means we can support
-            // *too much*. Don't update maxInstances in this case.
-            if (curMaxInstances.isValid() &&
-                curMaxInstances.value() < maxInstances)
-            {
-                maxInstances = curMaxInstances.value();
-            }
-        }
-    }
-
-    mBufferFetchingIsVerified = true;
-    mBufferFetchingHasPerVertex = hasPerVertex;
-    mMaxFetchedVertices = maxVertices;
-    mMaxFetchedInstances = maxInstances;
-
-    return true;
-}
-
 WebGLVertexAttrib0Status
 WebGLContext::WhatDoesVertexAttrib0Need() const
 {
     MOZ_ASSERT(mCurrentProgram);
     MOZ_ASSERT(mActiveProgramLinkInfo);
 
-    const auto& isAttribArray0Enabled = mBoundVertexArray->mAttribs[0].mEnabled;
-
     bool legacyAttrib0 = gl->IsCompatibilityProfile();
 #ifdef XP_MACOSX
     if (gl->WorkAroundDriverBugs()) {
         // Failures in conformance/attribs/gl-disabled-vertex-attrib.
         // Even in Core profiles on NV. Sigh.
         legacyAttrib0 |= (gl->Vendor() == gl::GLVendor::NVIDIA);
     }
 #endif
 
     if (!legacyAttrib0)
         return WebGLVertexAttrib0Status::Default;
 
-    if (isAttribArray0Enabled && mBufferFetch_IsAttrib0Active)
-        return WebGLVertexAttrib0Status::Default;
+    if (!mActiveProgramLinkInfo->attrib0Active) {
+        // Ensure that the legacy code has enough buffer.
+        return WebGLVertexAttrib0Status::EmulatedUninitializedArray;
+    }
 
-    if (mBufferFetch_IsAttrib0Active)
-        return WebGLVertexAttrib0Status::EmulatedInitializedArray;
-
-    // Ensure that the legacy code has enough buffer.
-    return WebGLVertexAttrib0Status::EmulatedUninitializedArray;
+    const auto& isAttribArray0Enabled = mBoundVertexArray->mAttribs[0].mEnabled;
+    return isAttribArray0Enabled ? WebGLVertexAttrib0Status::Default
+                                 : WebGLVertexAttrib0Status::EmulatedInitializedArray;
 }
 
 bool
-WebGLContext::DoFakeVertexAttrib0(const char* funcName, GLuint vertexCount)
+WebGLContext::DoFakeVertexAttrib0(const char* const funcName, const uint32_t lastVert)
 {
-    if (!vertexCount) {
-        vertexCount = 1;
-    }
-
     const auto whatDoesAttrib0Need = WhatDoesVertexAttrib0Need();
     if (MOZ_LIKELY(whatDoesAttrib0Need == WebGLVertexAttrib0Status::Default))
         return true;
 
     if (!mAlreadyWarnedAboutFakeVertexAttrib0) {
         GenerateWarning("Drawing without vertex attrib 0 array enabled forces the browser "
                         "to do expensive emulation work when running on desktop OpenGL "
                         "platforms, for example on Mac. It is preferable to always draw "
@@ -1088,20 +924,22 @@ WebGLContext::DoFakeVertexAttrib0(const 
 
     default:
         MOZ_CRASH();
     }
 
     ////
 
     const auto bytesPerVert = sizeof(mFakeVertexAttrib0Data);
-    const auto checked_dataSize = CheckedUint32(vertexCount) * bytesPerVert;
+    const auto checked_dataSize = (CheckedUint32(lastVert)+1) * bytesPerVert;
     if (!checked_dataSize.isValid()) {
-        ErrorOutOfMemory("Integer overflow trying to construct a fake vertex attrib 0 array for a draw-operation "
-                         "with %d vertices. Try reducing the number of vertices.", vertexCount);
+        ErrorOutOfMemory("Integer overflow trying to construct a fake vertex attrib 0"
+                         " array for a draw-operation with %" PRIu64 " vertices. Try"
+                         " reducing the number of vertices.",
+                         uint64_t(lastVert) + 1);
         return false;
     }
     const auto dataSize = checked_dataSize.value();
 
     if (mFakeVertexAttrib0BufferObjectSize < dataSize) {
         gl->fBufferData(LOCAL_GL_ARRAY_BUFFER, dataSize, nullptr, LOCAL_GL_DYNAMIC_DRAW);
         mFakeVertexAttrib0BufferObjectSize = dataSize;
         mFakeVertexAttrib0DataDefined = false;
--- a/dom/canvas/WebGLContextValidate.cpp
+++ b/dom/canvas/WebGLContextValidate.cpp
@@ -748,16 +748,17 @@ WebGLContext::InitAndValidateGL(FailureR
     mPixelStore_PackSkipRows = 0;
     mPixelStore_PackSkipPixels = 0;
     mPixelStore_PackAlignment = 4;
 
     mPrimRestartTypeBytes = 0;
 
     mGenericVertexAttribTypes.reset(new GLenum[mGLMaxVertexAttribs]);
     std::fill_n(mGenericVertexAttribTypes.get(), mGLMaxVertexAttribs, LOCAL_GL_FLOAT);
+    mGenericVertexAttribTypeInvalidator.InvalidateCaches();
 
     static const float kDefaultGenericVertexAttribData[4] = { 0, 0, 0, 1 };
     memcpy(mGenericVertexAttrib0Data, kDefaultGenericVertexAttribData,
            sizeof(mGenericVertexAttrib0Data));
 
     mFakeVertexAttrib0BufferObject = 0;
 
     mNeedsIndexValidation = !gl->IsSupported(gl::GLFeature::robust_buffer_access_behavior);
--- a/dom/canvas/WebGLContextVertexArray.cpp
+++ b/dom/canvas/WebGLContextVertexArray.cpp
@@ -16,18 +16,16 @@ void
 WebGLContext::BindVertexArray(WebGLVertexArray* array)
 {
     if (IsContextLost())
         return;
 
     if (array && !ValidateObject("bindVertexArrayObject", *array))
         return;
 
-    InvalidateBufferFetching();
-
     MakeContextCurrent();
 
     if (mBoundVertexArray) {
         mBoundVertexArray->AddBufferBindCounts(-1);
     }
 
     if (array == nullptr) {
         array = mDefaultVertexArray;
--- a/dom/canvas/WebGLContextVertices.cpp
+++ b/dom/canvas/WebGLContextVertices.cpp
@@ -77,16 +77,17 @@ WebGLContext::VertexAttrib4f(GLuint inde
     gl->MakeCurrent();
     if (index || !gl->IsCompatibilityProfile()) {
         gl->fVertexAttrib4f(index, x, y, z, w);
     }
 
     ////
 
     mGenericVertexAttribTypes[index] = LOCAL_GL_FLOAT;
+    mGenericVertexAttribTypeInvalidator.InvalidateCaches();
 
     if (!index) {
         const float data[4] = { x, y, z, w };
         memcpy(mGenericVertexAttrib0Data, data, sizeof(data));
     }
 }
 
 void
@@ -108,16 +109,17 @@ WebGL2Context::VertexAttribI4i(GLuint in
     gl->MakeCurrent();
     if (index || !gl->IsCompatibilityProfile()) {
         gl->fVertexAttribI4i(index, x, y, z, w);
     }
 
     ////
 
     mGenericVertexAttribTypes[index] = LOCAL_GL_INT;
+    mGenericVertexAttribTypeInvalidator.InvalidateCaches();
 
     if (!index) {
         const int32_t data[4] = { x, y, z, w };
         memcpy(mGenericVertexAttrib0Data, data, sizeof(data));
     }
 }
 
 void
@@ -139,16 +141,17 @@ WebGL2Context::VertexAttribI4ui(GLuint i
     gl->MakeCurrent();
     if (index || !gl->IsCompatibilityProfile()) {
         gl->fVertexAttribI4ui(index, x, y, z, w);
     }
 
     ////
 
     mGenericVertexAttribTypes[index] = LOCAL_GL_UNSIGNED_INT;
+    mGenericVertexAttribTypeInvalidator.InvalidateCaches();
 
     if (!index) {
         const uint32_t data[4] = { x, y, z, w };
         memcpy(mGenericVertexAttrib0Data, data, sizeof(data));
     }
 }
 
 ////////////////////////////////////////
@@ -158,42 +161,42 @@ WebGLContext::EnableVertexAttribArray(GL
 {
     if (IsContextLost())
         return;
 
     if (!ValidateAttribIndex(index, "enableVertexAttribArray"))
         return;
 
     MakeContextCurrent();
-    InvalidateBufferFetching();
 
     gl->fEnableVertexAttribArray(index);
 
     MOZ_ASSERT(mBoundVertexArray);
     mBoundVertexArray->mAttribs[index].mEnabled = true;
+    mBoundVertexArray->InvalidateCaches();
 }
 
 void
 WebGLContext::DisableVertexAttribArray(GLuint index)
 {
     if (IsContextLost())
         return;
 
     if (!ValidateAttribIndex(index, "disableVertexAttribArray"))
         return;
 
     MakeContextCurrent();
-    InvalidateBufferFetching();
 
     if (index || !gl->IsCompatibilityProfile()) {
         gl->fDisableVertexAttribArray(index);
     }
 
     MOZ_ASSERT(mBoundVertexArray);
     mBoundVertexArray->mAttribs[index].mEnabled = false;
+    mBoundVertexArray->InvalidateCaches();
 }
 
 JS::Value
 WebGLContext::GetVertexAttrib(JSContext* cx, GLuint index, GLenum pname,
                               ErrorResult& rv)
 {
     const char funcName[] = "getVertexAttrib";
     if (IsContextLost())
@@ -420,36 +423,32 @@ WebGLContext::VertexAttribAnyPointer(con
                                   reinterpret_cast<void*>(byteOffset));
     } else {
         gl->fVertexAttribPointer(index, size, type, normalized, stride,
                                  reinterpret_cast<void*>(byteOffset));
     }
 
     WebGLVertexAttribData& vd = mBoundVertexArray->mAttribs[index];
     vd.VertexAttribPointer(isFuncInt, buffer, size, type, normalized, stride, byteOffset);
-
-    InvalidateBufferFetching();
+    mBoundVertexArray->InvalidateCaches();
 }
 
 ////////////////////////////////////////
 
 void
 WebGLContext::VertexAttribDivisor(GLuint index, GLuint divisor)
 {
     if (IsContextLost())
         return;
 
     if (!ValidateAttribIndex(index, "vertexAttribDivisor"))
         return;
 
     MOZ_ASSERT(mBoundVertexArray);
-
-    WebGLVertexAttribData& vd = mBoundVertexArray->mAttribs[index];
-    vd.mDivisor = divisor;
-
-    InvalidateBufferFetching();
+    mBoundVertexArray->mAttribs[index].mDivisor = divisor;
+    mBoundVertexArray->InvalidateCaches();
 
     MakeContextCurrent();
 
     gl->fVertexAttribDivisor(index, divisor);
 }
 
 } // namespace mozilla
--- a/dom/canvas/WebGLProgram.cpp
+++ b/dom/canvas/WebGLProgram.cpp
@@ -7,21 +7,23 @@
 
 #include "GLContext.h"
 #include "mozilla/CheckedInt.h"
 #include "mozilla/dom/WebGL2RenderingContextBinding.h"
 #include "mozilla/dom/WebGLRenderingContextBinding.h"
 #include "mozilla/RefPtr.h"
 #include "nsPrintfCString.h"
 #include "WebGLActiveInfo.h"
+#include "WebGLBuffer.h"
 #include "WebGLContext.h"
 #include "WebGLShader.h"
 #include "WebGLTransformFeedback.h"
 #include "WebGLUniformLocation.h"
 #include "WebGLValidateStrings.h"
+#include "WebGLVertexArray.h"
 
 namespace mozilla {
 
 /* If `name`: "foo[3]"
  * Then returns true, with
  *     `out_baseName`: "foo"
  *     `out_isArray`: true
  *     `out_index`: 3
@@ -259,16 +261,20 @@ QueryProgramInfo(WebGLProgram* prog, gl:
         const bool isArray = false;
         const RefPtr<WebGLActiveInfo> activeInfo = new WebGLActiveInfo(webgl, elemCount,
                                                                        elemType, isArray,
                                                                        userName,
                                                                        mappedName);
         const GLenum baseType = AttribBaseType(elemType);
         const webgl::AttribInfo attrib = {activeInfo, loc, baseType};
         info->attribs.push_back(attrib);
+
+        if (loc == 0) {
+            info->attrib0Active = true;
+        }
     }
 
     // Uniforms (can be basically anything)
 
     const bool needsCheckForArrays = gl->WorkAroundDriverBugs();
 
     GLuint numActiveUniforms = 0;
     gl->fGetProgramiv(prog->mGLName, LOCAL_GL_ACTIVE_UNIFORMS,
@@ -438,28 +444,124 @@ QueryProgramInfo(WebGLProgram* prog, gl:
     return info.forget();
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 
 webgl::LinkedProgramInfo::LinkedProgramInfo(WebGLProgram* prog)
     : prog(prog)
     , transformFeedbackBufferMode(prog->mNextLink_TransformFeedbackBufferMode)
+    , attrib0Active(false)
 { }
 
 webgl::LinkedProgramInfo::~LinkedProgramInfo()
 {
     for (auto& cur : uniforms) {
         delete cur;
     }
     for (auto& cur : uniformBlocks) {
         delete cur;
     }
 }
 
+const webgl::CachedDrawFetchLimits*
+webgl::LinkedProgramInfo::GetDrawFetchLimits(const char* const funcName) const
+{
+    const auto& webgl = prog->mContext;
+    const auto& vao = webgl->mBoundVertexArray;
+
+    const auto found = mDrawFetchCache.Find(vao);
+    if (found)
+        return found;
+
+    std::vector<const CacheMapInvalidator*> cacheDeps;
+    cacheDeps.push_back(vao.get());
+    cacheDeps.push_back(&webgl->mGenericVertexAttribTypeInvalidator);
+
+    {
+        // We have to ensure that every enabled attrib array (not just the active ones)
+        // has a non-null buffer.
+        uint32_t i = 0;
+        for (const auto& cur : vao->mAttribs) {
+            if (cur.mEnabled && !cur.mBuf) {
+                webgl->ErrorInvalidOperation("%s: Vertex attrib array %u is enabled but"
+                                             " has no buffer bound.",
+                                             funcName, i);
+                return nullptr;
+            }
+        }
+    }
+
+    bool hasActiveAttrib = false;
+    bool hasActiveDivisor0 = false;
+    webgl::CachedDrawFetchLimits fetchLimits = { UINT64_MAX, UINT64_MAX };
+
+    for (const auto& progAttrib : this->attribs) {
+        const auto& loc = progAttrib.mLoc;
+        if (loc == -1)
+            continue;
+        hasActiveAttrib |= true;
+
+        const auto& attribData = vao->mAttribs[loc];
+        hasActiveDivisor0 |= (attribData.mDivisor == 0);
+
+        GLenum attribDataBaseType;
+        if (attribData.mEnabled) {
+            MOZ_ASSERT(attribData.mBuf);
+            if (attribData.mBuf->IsBoundForTF()) {
+                webgl->ErrorInvalidOperation("%s: Vertex attrib %u's buffer is bound for"
+                                             " transform feedback.",
+                                              funcName, loc);
+                return nullptr;
+            }
+            cacheDeps.push_back(&attribData.mBuf->mFetchInvalidator);
+
+            attribDataBaseType = attribData.BaseType();
+
+            const size_t availBytes = attribData.mBuf->ByteLength();
+            const auto availElems = AvailGroups(availBytes, attribData.ByteOffset(),
+                                                attribData.BytesPerVertex(),
+                                                attribData.ExplicitStride());
+            if (attribData.mDivisor) {
+                const auto availInstances = CheckedInt<uint64_t>(availElems) * attribData.mDivisor;
+                if (availInstances.isValid()) {
+                    fetchLimits.maxInstances = std::min(fetchLimits.maxInstances,
+                                                        availInstances.value());
+                } // If not valid, it overflowed too large, so we're super safe.
+            } else {
+                fetchLimits.maxVerts = std::min(fetchLimits.maxVerts, availElems);
+            }
+        } else {
+            attribDataBaseType = webgl->mGenericVertexAttribTypes[loc];
+        }
+
+        if (attribDataBaseType != progAttrib.mBaseType) {
+            nsCString progType, dataType;
+            WebGLContext::EnumName(progAttrib.mBaseType, &progType);
+            WebGLContext::EnumName(attribDataBaseType, &dataType);
+            webgl->ErrorInvalidOperation("%s: Vertex attrib %u requires data of type %s,"
+                                         " but is being supplied with type %s.",
+                                         funcName, loc, progType.BeginReading(),
+                                         dataType.BeginReading());
+            return nullptr;
+        }
+    }
+
+    if (hasActiveAttrib && !hasActiveDivisor0) {
+        webgl->ErrorInvalidOperation("%s: One active vertex attrib (if any are active)"
+                                     " must have a divisor of 0.",
+                                     funcName);
+        return nullptr;
+    }
+
+    // --
+
+    return mDrawFetchCache.Insert(vao.get(), Move(fetchLimits), Move(cacheDeps));
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // WebGLProgram
 
 static GLuint
 CreateProgram(gl::GLContext* gl)
 {
     gl->MakeCurrent();
     return gl->fCreateProgram();
@@ -1064,17 +1166,16 @@ WebGLProgram::LinkProgram()
     if (mNumActiveTFOs) {
         mContext->ErrorInvalidOperation("%s: Program is in-use by one or more active"
                                         " transform feedback objects.",
                                         funcName);
         return;
     }
 
     mContext->MakeContextCurrent();
-    mContext->InvalidateBufferFetching(); // we do it early in this function
     // as some of the validation changes program state
 
     mLinkLog.Truncate();
     mMostRecentLinkInfo = nullptr;
 
     if (!ValidateForLink()) {
         mContext->GenerateWarning("%s: %s", funcName, mLinkLog.BeginReading());
         return;
@@ -1360,18 +1461,16 @@ WebGLProgram::UseProgram() const
     {
         mContext->ErrorInvalidOperation("%s: Transform feedback active and not paused.",
                                         funcName);
         return false;
     }
 
     mContext->MakeContextCurrent();
 
-    mContext->InvalidateBufferFetching();
-
     mContext->gl->fUseProgram(mGLName);
     return true;
 }
 
 void
 WebGLProgram::ValidateProgram() const
 {
     mContext->MakeContextCurrent();
--- a/dom/canvas/WebGLProgram.h
+++ b/dom/canvas/WebGLProgram.h
@@ -12,16 +12,17 @@
 #include <vector>
 
 #include "mozilla/LinkedList.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/WeakPtr.h"
 #include "nsString.h"
 #include "nsWrapperCache.h"
 
+#include "CacheMap.h"
 #include "WebGLContext.h"
 #include "WebGLObjectModel.h"
 
 namespace mozilla {
 class ErrorResult;
 class WebGLActiveInfo;
 class WebGLProgram;
 class WebGLShader;
@@ -70,16 +71,21 @@ struct UniformBlockInfo final
                      const nsACString& mappedName, uint32_t dataSize)
         : mUserName(userName)
         , mMappedName(mappedName)
         , mDataSize(dataSize)
         , mBinding(&webgl->mIndexedUniformBufferBindings[0])
     { }
 };
 
+struct CachedDrawFetchLimits final {
+    uint64_t maxVerts;
+    uint64_t maxInstances;
+};
+
 struct LinkedProgramInfo final
     : public RefCounted<LinkedProgramInfo>
     , public SupportsWeakPtr<LinkedProgramInfo>
 {
     friend class WebGLProgram;
 
     MOZ_DECLARE_REFCOUNTED_TYPENAME(LinkedProgramInfo)
     MOZ_DECLARE_WEAKREFERENCE_TYPENAME(LinkedProgramInfo)
@@ -94,21 +100,32 @@ struct LinkedProgramInfo final
     std::vector<UniformBlockInfo*> uniformBlocks; // Owns its contents.
     std::vector<RefPtr<WebGLActiveInfo>> transformFeedbackVaryings;
 
     // Needed for draw call validation.
     std::vector<UniformInfo*> uniformSamplers;
 
     mutable std::vector<size_t> componentsPerTFVert;
 
+    bool attrib0Active;
+
     //////
 
     // The maps for the frag data names to the translated names.
     std::map<nsCString, const nsCString> fragDataMap;
 
+    //////
+
+    mutable CacheMap<const WebGLVertexArray*,
+                     CachedDrawFetchLimits> mDrawFetchCache;
+
+    const CachedDrawFetchLimits* GetDrawFetchLimits(const char* funcName) const;
+
+    //////
+
     explicit LinkedProgramInfo(WebGLProgram* prog);
     ~LinkedProgramInfo();
 
     bool FindAttrib(const nsCString& userName, const AttribInfo** const out_info) const;
     bool FindUniform(const nsCString& userName, nsCString* const out_mappedName,
                      size_t* const out_arrayIndex, UniformInfo** const out_info) const;
     bool MapFragDataName(const nsCString& userName,
                          nsCString* const out_mappedName) const;
--- a/dom/canvas/WebGLVertexArray.h
+++ b/dom/canvas/WebGLVertexArray.h
@@ -5,28 +5,33 @@
 
 #ifndef WEBGL_VERTEX_ARRAY_H_
 #define WEBGL_VERTEX_ARRAY_H_
 
 #include "nsTArray.h"
 #include "mozilla/LinkedList.h"
 #include "nsWrapperCache.h"
 
+#include "CacheMap.h"
 #include "WebGLObjectModel.h"
 #include "WebGLStrongTypes.h"
 #include "WebGLVertexAttribData.h"
 
 namespace mozilla {
 
 class WebGLVertexArrayFake;
+namespace webgl {
+struct LinkedProgramInfo;
+}
 
 class WebGLVertexArray
     : public nsWrapperCache
     , public WebGLRefCountedObject<WebGLVertexArray>
     , public LinkedListElement<WebGLVertexArray>
+    , public CacheMapInvalidator
 {
 public:
     static WebGLVertexArray* Create(WebGLContext* webgl);
 
     void BindVertexArray() {
         // Bind to dummy value to signal that this vertex array has ever been
         // bound.
         BindVertexArrayImpl();
@@ -61,13 +66,14 @@ protected:
     GLuint mGLName;
     nsTArray<WebGLVertexAttribData> mAttribs;
     WebGLRefPtr<WebGLBuffer> mElementArrayBuffer;
 
     friend class ScopedDrawHelper;
     friend class WebGLContext;
     friend class WebGLVertexArrayFake;
     friend class WebGL2Context;
+    friend struct webgl::LinkedProgramInfo;
 };
 
 } // namespace mozilla
 
 #endif // WEBGL_VERTEX_ARRAY_H_
--- a/dom/canvas/moz.build
+++ b/dom/canvas/moz.build
@@ -89,16 +89,17 @@ UNIFIED_SOURCES += [
 ]
 
 SOURCES += [
     'ImageUtils.cpp',
 ]
 
 # WebGL Sources
 UNIFIED_SOURCES += [
+    'CacheMap.cpp',
     'TexUnpackBlob.cpp',
     'WebGL1Context.cpp',
     'WebGL2Context.cpp',
     'WebGL2ContextBuffers.cpp',
     'WebGL2ContextFramebuffers.cpp',
     'WebGL2ContextMRTs.cpp',
     'WebGL2ContextPrograms.cpp',
     'WebGL2ContextQueries.cpp',
--- a/dom/canvas/test/webgl-mochitest/test_no_arr_points.html
+++ b/dom/canvas/test/webgl-mochitest/test_no_arr_points.html
@@ -111,27 +111,18 @@ void main(void) {
     ok(!isScreenBlack(), '[' + info + '] drawArrays should color pixels.');
 
     gl.clear(gl.COLOR_BUFFER_BIT);
     gl.drawArrays(gl.POINTS, hugeFirst, 1);
     ok(!isScreenBlack(), '[' + info + '] drawArrays[huge first] should color pixels.');
 
     checkGLError(ok, info);
 
-    var elemTestFunc = todo; // We fail on most implementations.
-    var checkGLTestFunc = todo;
-    if (DriverInfo.getDriver() == DriverInfo.DRIVER.ANGLE ||
-        (DriverInfo.getOS() == DriverInfo.OS.MAC &&
-         DriverInfo.getDriver() == DriverInfo.DRIVER.INTEL) ||
-        DriverInfo.getOS() == DriverInfo.OS.ANDROID)
-    {
-      // These seem to work fine.
-      elemTestFunc = ok;
-      checkGLTestFunc = ok;
-    }
+    var elemTestFunc = ok;
+    var checkGLTestFunc = ok;
     if (DriverInfo.getDriver() == DriverInfo.DRIVER.ANDROID_X86_EMULATOR) {
       // ...but the Android 4.2 x86 emulator environment is different
       elemTestFunc = todo;
       checkGLTestFunc = ok;
     }
 
     // Now for drawElements:
     gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, gl.createBuffer());
--- a/dom/html/HTMLTextAreaElement.cpp
+++ b/dom/html/HTMLTextAreaElement.cpp
@@ -81,17 +81,16 @@ HTMLTextAreaElement::HTMLTextAreaElement
 NS_IMPL_CYCLE_COLLECTION_INHERITED(HTMLTextAreaElement,
                                    nsGenericHTMLFormElementWithState,
                                    mValidity,
                                    mControllers,
                                    mState)
 
 NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED(HTMLTextAreaElement,
                                              nsGenericHTMLFormElementWithState,
-                                             nsIDOMHTMLTextAreaElement,
                                              nsITextControlElement,
                                              nsIDOMNSEditableElement,
                                              nsIMutationObserver,
                                              nsIConstraintValidation)
 
 // nsIDOMHTMLTextAreaElement
 
 nsresult
@@ -116,44 +115,37 @@ HTMLTextAreaElement::Clone(mozilla::dom:
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   it->mLastValueChangeWasInteractive = mLastValueChangeWasInteractive;
   it.forget(aResult);
   return NS_OK;
 }
 
-NS_IMETHODIMP
-HTMLTextAreaElement::GetForm(nsIDOMHTMLFormElement** aForm)
-{
-  return nsGenericHTMLFormElementWithState::GetForm(aForm);
-}
-
-
 // nsIContent
 
-NS_IMETHODIMP
+void
 HTMLTextAreaElement::Select()
 {
   // XXX Bug?  We have to give the input focus before contents can be
   // selected
 
   FocusTristate state = FocusState();
   if (state == eUnfocusable) {
-    return NS_OK;
+    return;
   }
 
   nsIFocusManager* fm = nsFocusManager::GetFocusManager();
 
   RefPtr<nsPresContext> presContext = GetPresContext(eForComposedDoc);
   if (state == eInactiveWindow) {
     if (fm)
       fm->SetFocus(this, nsIFocusManager::FLAG_NOSCROLL);
     SelectAll(presContext);
-    return NS_OK;
+    return;
   }
 
   nsEventStatus status = nsEventStatus_eIgnore;
   WidgetGUIEvent event(true, eFormSelect, nullptr);
   // XXXbz HTMLInputElement guards against this reentering; shouldn't we?
   EventDispatcher::Dispatch(static_cast<nsIContent*>(this), presContext,
                             &event, nullptr, &status);
 
@@ -167,18 +159,16 @@ HTMLTextAreaElement::Select()
       nsCOMPtr<nsIDOMElement> focusedElement;
       fm->GetFocusedElement(getter_AddRefs(focusedElement));
       if (SameCOMIdentity(static_cast<nsIDOMNode*>(this), focusedElement)) {
         // Now Select all the text!
         SelectAll(presContext);
       }
     }
   }
-
-  return NS_OK;
 }
 
 NS_IMETHODIMP
 HTMLTextAreaElement::SelectAll(nsPresContext* aPresContext)
 {
   nsIFormControlFrame* formControlFrame = GetFormControlFrame(true);
 
   if (formControlFrame) {
@@ -198,53 +188,38 @@ HTMLTextAreaElement::IsHTMLFocusable(boo
     return true;
   }
 
   // disabled textareas are not focusable
   *aIsFocusable = !IsDisabled();
   return false;
 }
 
-NS_IMPL_BOOL_ATTR(HTMLTextAreaElement, Autofocus, autofocus)
-NS_IMPL_UINT_ATTR_NON_ZERO_DEFAULT_VALUE(HTMLTextAreaElement, Cols, cols, DEFAULT_COLS)
-NS_IMPL_BOOL_ATTR(HTMLTextAreaElement, Disabled, disabled)
-NS_IMPL_NON_NEGATIVE_INT_ATTR(HTMLTextAreaElement, MaxLength, maxlength)
-NS_IMPL_NON_NEGATIVE_INT_ATTR(HTMLTextAreaElement, MinLength, minlength)
-NS_IMPL_STRING_ATTR(HTMLTextAreaElement, Name, name)
-NS_IMPL_BOOL_ATTR(HTMLTextAreaElement, ReadOnly, readonly)
-NS_IMPL_BOOL_ATTR(HTMLTextAreaElement, Required, required)
-NS_IMPL_UINT_ATTR_NON_ZERO_DEFAULT_VALUE(HTMLTextAreaElement, Rows, rows, DEFAULT_ROWS_TEXTAREA)
-NS_IMPL_STRING_ATTR(HTMLTextAreaElement, Wrap, wrap)
-NS_IMPL_STRING_ATTR(HTMLTextAreaElement, Placeholder, placeholder)
-
 int32_t
 HTMLTextAreaElement::TabIndexDefault()
 {
   return 0;
 }
 
-NS_IMETHODIMP
+void
 HTMLTextAreaElement::GetType(nsAString& aType)
 {
   aType.AssignLiteral("textarea");
-
-  return NS_OK;
 }
 
-NS_IMETHODIMP
+void
 HTMLTextAreaElement::GetValue(nsAString& aValue)
 {
   nsAutoString value;
   GetValueInternal(value, true);
 
   // Normalize CRLF and CR to LF
   nsContentUtils::PlatformToDOMLineBreaks(value);
 
   aValue = value;
-  return NS_OK;
 }
 
 void
 HTMLTextAreaElement::GetValueInternal(nsAString& aValue, bool aIgnoreWrap) const
 {
   mState.GetValue(aValue, aIgnoreWrap);
 }
 
@@ -366,41 +341,42 @@ HTMLTextAreaElement::SetValueInternal(co
 
   if (!mState.SetValue(aValue, aFlags)) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
   return NS_OK;
 }
 
-NS_IMETHODIMP
-HTMLTextAreaElement::SetValue(const nsAString& aValue)
+void
+HTMLTextAreaElement::SetValue(const nsAString& aValue, ErrorResult& aError)
 {
   // If the value has been set by a script, we basically want to keep the
   // current change event state. If the element is ready to fire a change
   // event, we should keep it that way. Otherwise, we should make sure the
   // element will not fire any event because of the script interaction.
   //
   // NOTE: this is currently quite expensive work (too much string
   // manipulation). We should probably optimize that.
   nsAutoString currentValue;
   GetValueInternal(currentValue, true);
 
   nsresult rv =
     SetValueInternal(aValue,
       nsTextEditorState::eSetValue_ByContent |
       nsTextEditorState::eSetValue_Notify |
       nsTextEditorState::eSetValue_MoveCursorToEndIfValueChanged);
-  NS_ENSURE_SUCCESS(rv, rv);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    aError.Throw(rv);
+    return;
+  }
 
   if (mFocusedValue.Equals(currentValue)) {
     GetValueInternal(mFocusedValue, true);
   }
-
-  return NS_OK;
 }
 
 NS_IMETHODIMP
 HTMLTextAreaElement::SetUserInput(const nsAString& aValue)
 {
   return SetValueInternal(aValue,
     nsTextEditorState::eSetValue_BySetUserInput |
     nsTextEditorState::eSetValue_Notify|
@@ -419,31 +395,22 @@ HTMLTextAreaElement::SetValueChanged(boo
 
   if (mValueChanged != previousValue) {
     UpdateState(true);
   }
 
   return NS_OK;
 }
 
-NS_IMETHODIMP
-HTMLTextAreaElement::GetDefaultValue(nsAString& aDefaultValue)
+void
+HTMLTextAreaElement::GetDefaultValue(nsAString& aDefaultValue, ErrorResult& aError)
 {
   if (!nsContentUtils::GetNodeTextContent(this, false, aDefaultValue, fallible)) {
-    return NS_ERROR_OUT_OF_MEMORY;
+    aError.Throw(NS_ERROR_OUT_OF_MEMORY);
   }
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-HTMLTextAreaElement::SetDefaultValue(const nsAString& aDefaultValue)
-{
-  ErrorResult error;
-  SetDefaultValue(aDefaultValue, error);
-  return error.StealNSResult();
 }
 
 void
 HTMLTextAreaElement::SetDefaultValue(const nsAString& aDefaultValue, ErrorResult& aError)
 {
   nsresult rv = nsContentUtils::SetNodeTextContent(this, aDefaultValue, true);
   if (NS_SUCCEEDED(rv) && !mValueChanged) {
     Reset();
@@ -697,17 +664,17 @@ HTMLTextAreaElement::GetControllers(Erro
     }
 
     mControllers->AppendController(controller);
   }
 
   return mControllers;
 }
 
-NS_IMETHODIMP
+nsresult
 HTMLTextAreaElement::GetControllers(nsIControllers** aResult)
 {
   NS_ENSURE_ARG_POINTER(aResult);
 
   ErrorResult error;
   *aResult = GetControllers(error);
   NS_IF_ADDREF(*aResult);
 
@@ -717,25 +684,16 @@ HTMLTextAreaElement::GetControllers(nsIC
 uint32_t
 HTMLTextAreaElement::GetTextLength()
 {
   nsAutoString val;
   GetValue(val);
   return val.Length();
 }
 
-NS_IMETHODIMP
-HTMLTextAreaElement::GetTextLength(int32_t *aTextLength)
-{
-  NS_ENSURE_ARG_POINTER(aTextLength);
-  *aTextLength = GetTextLength();
-
-  return NS_OK;
-}
-
 Nullable<uint32_t>
 HTMLTextAreaElement::GetSelectionStart(ErrorResult& aError)
 {
   uint32_t selStart, selEnd;
   GetSelectionRange(&selStart, &selEnd, aError);
   return Nullable<uint32_t>(selStart);
 }
 
@@ -821,17 +779,18 @@ HTMLTextAreaElement::SetValueFromSetRang
                           nsTextEditorState::eSetValue_ByContent |
                           nsTextEditorState::eSetValue_Notify);
 }
 
 nsresult
 HTMLTextAreaElement::Reset()
 {
   nsAutoString resetVal;
-  GetDefaultValue(resetVal);
+  IgnoredErrorResult res;
+  GetDefaultValue(resetVal, res);
   SetValueChanged(false);
 
   nsresult rv = SetValueInternal(resetVal,
                                  nsTextEditorState::eSetValue_Internal);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
@@ -916,22 +875,24 @@ bool
 HTMLTextAreaElement::RestoreState(nsPresState* aState)
 {
   nsCOMPtr<nsISupportsString> state
     (do_QueryInterface(aState->GetStateProperty()));
 
   if (state) {
     nsAutoString data;
     state->GetData(data);
-    nsresult rv = SetValue(data);
-    NS_ENSURE_SUCCESS(rv, false);
+    ErrorResult rv;
+    SetValue(data, rv);
+    ENSURE_SUCCESS(rv, false);
   }
 
   if (aState->IsDisabledSet() && !aState->GetDisabled()) {
-    SetDisabled(false);
+    IgnoredErrorResult rv;
+    SetDisabled(false, rv);
   }
 
   return false;
 }
 
 EventStates
 HTMLTextAreaElement::IntrinsicState() const
 {
@@ -1115,17 +1076,19 @@ HTMLTextAreaElement::CopyInnerTo(Element
 {
   nsresult rv = nsGenericHTMLFormElementWithState::CopyInnerTo(aDest,
                                                                aPreallocateChildren);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (aDest->OwnerDoc()->IsStaticDocument()) {
     nsAutoString value;
     GetValueInternal(value, true);
-    return static_cast<HTMLTextAreaElement*>(aDest)->SetValue(value);
+    ErrorResult ret;
+    static_cast<HTMLTextAreaElement*>(aDest)->SetValue(value, ret);
+    return ret.StealNSResult();
   }
   return NS_OK;
 }
 
 bool
 HTMLTextAreaElement::IsMutable() const
 {
   return (!HasAttr(kNameSpaceID_None, nsGkAtoms::readonly) && !IsDisabled());
@@ -1152,49 +1115,45 @@ bool
 HTMLTextAreaElement::IsTooLong()
 {
   if (!mValueChanged ||
       !mLastValueChangeWasInteractive ||
       !HasAttr(kNameSpaceID_None, nsGkAtoms::maxlength)) {
     return false;
   }
 
-  int32_t maxLength = -1;
-  GetMaxLength(&maxLength);
+  int32_t maxLength = MaxLength();
 
   // Maxlength of -1 means parsing error.
   if (maxLength == -1) {
     return false;
   }
 
-  int32_t textLength = -1;
-  GetTextLength(&textLength);
+  int32_t textLength = GetTextLength();
 
   return textLength > maxLength;
 }
 
 bool
 HTMLTextAreaElement::IsTooShort()
 {
   if (!mValueChanged ||
       !mLastValueChangeWasInteractive ||
       !HasAttr(kNameSpaceID_None, nsGkAtoms::minlength)) {
     return false;
   }
 
-  int32_t minLength = -1;
-  GetMinLength(&minLength);
+  int32_t minLength = MinLength();
 
   // Minlength of -1 means parsing error.
   if (minLength == -1) {
     return false;
   }
 
-  int32_t textLength = -1;
-  GetTextLength(&textLength);
+  int32_t textLength = GetTextLength();
 
   return textLength && textLength < minLength;
 }
 
 bool
 HTMLTextAreaElement::IsValueMissing() const
 {
   if (!Required() || !IsMutable()) {
@@ -1236,45 +1195,39 @@ HTMLTextAreaElement::GetValidationMessag
 {
   nsresult rv = NS_OK;
 
   switch (aType)
   {
     case VALIDITY_STATE_TOO_LONG:
       {
         nsAutoString message;
-        int32_t maxLength = -1;
-        int32_t textLength = -1;
+        int32_t maxLength = MaxLength();
+        int32_t textLength = GetTextLength();
         nsAutoString strMaxLength;
         nsAutoString strTextLength;
 
-        GetMaxLength(&maxLength);
-        GetTextLength(&textLength);
-
         strMaxLength.AppendInt(maxLength);
         strTextLength.AppendInt(textLength);
 
         const char16_t* params[] = { strMaxLength.get(), strTextLength.get() };
         rv = nsContentUtils::FormatLocalizedString(nsContentUtils::eDOM_PROPERTIES,
                                                    "FormValidationTextTooLong",
                                                    params, message);
         aValidationMessage = message;
       }
       break;
     case VALIDITY_STATE_TOO_SHORT:
       {
         nsAutoString message;
-        int32_t minLength = -1;
-        int32_t textLength = -1;
+        int32_t minLength = MinLength();
+        int32_t textLength = GetTextLength();
         nsAutoString strMinLength;
         nsAutoString strTextLength;
 
-        GetMinLength(&minLength);
-        GetTextLength(&textLength);
-
         strMinLength.AppendInt(minLength);
         strTextLength.AppendInt(textLength);
 
         const char16_t* params[] = { strMinLength.get(), strTextLength.get() };
         rv = nsContentUtils::FormatLocalizedString(nsContentUtils::eDOM_PROPERTIES,
                                                    "FormValidationTextTooShort",
                                                    params, message);
         aValidationMessage = message;
@@ -1346,17 +1299,18 @@ HTMLTextAreaElement::GetRows()
   }
 
   return DEFAULT_ROWS_TEXTAREA;
 }
 
 NS_IMETHODIMP_(void)
 HTMLTextAreaElement::GetDefaultValueFromContent(nsAString& aValue)
 {
-  GetDefaultValue(aValue);
+  IgnoredErrorResult rv;
+  GetDefaultValue(aValue, rv);
 }
 
 NS_IMETHODIMP_(bool)
 HTMLTextAreaElement::ValueChanged() const
 {
   return mValueChanged;
 }
 
--- a/dom/html/HTMLTextAreaElement.h
+++ b/dom/html/HTMLTextAreaElement.h
@@ -3,17 +3,16 @@
 /* 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 mozilla_dom_HTMLTextAreaElement_h
 #define mozilla_dom_HTMLTextAreaElement_h
 
 #include "mozilla/Attributes.h"
-#include "nsIDOMHTMLTextAreaElement.h"
 #include "nsITextControlElement.h"
 #include "nsIControllers.h"
 #include "nsIDOMNSEditableElement.h"
 #include "nsCOMPtr.h"
 #include "nsGenericHTMLElement.h"
 #include "nsStubMutationObserver.h"
 #include "nsIConstraintValidation.h"
 #include "mozilla/dom/HTMLFormElement.h"
@@ -34,42 +33,40 @@ class EventChainPostVisitor;
 class EventChainPreVisitor;
 class EventStates;
 
 namespace dom {
 
 class HTMLFormSubmission;
 
 class HTMLTextAreaElement final : public nsGenericHTMLFormElementWithState,
-                                  public nsIDOMHTMLTextAreaElement,
                                   public nsITextControlElement,
                                   public nsIDOMNSEditableElement,
                                   public nsStubMutationObserver,
                                   public nsIConstraintValidation
 {
 public:
   using nsIConstraintValidation::GetValidationMessage;
 
   explicit HTMLTextAreaElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo,
                                FromParser aFromParser = NOT_FROM_PARSER);
 
   // nsISupports
   NS_DECL_ISUPPORTS_INHERITED
 
+  NS_IMPL_FROMCONTENT_HTML_WITH_TAG(HTMLTextAreaElement, textarea)
+
   virtual int32_t TabIndexDefault() override;
 
   // Element
   virtual bool IsInteractiveHTMLContent(bool aIgnoreTabindex) const override
   {
     return true;
   }
 
-  // nsIDOMHTMLTextAreaElement
-  NS_DECL_NSIDOMHTMLTEXTAREAELEMENT
-
   // nsIDOMNSEditableElement
   NS_IMETHOD GetEditor(nsIEditor** aEditor) override
   {
     nsCOMPtr<nsIEditor> editor = GetEditor();
     editor.forget(aEditor);
     return NS_OK;
   }
   NS_IMETHOD SetUserInput(const nsAString& aInput) override;
@@ -232,22 +229,28 @@ public:
   {
     int32_t maxLength = MaxLength();
     if (aMinLength < 0 || (maxLength >= 0 && aMinLength > maxLength)) {
       aError.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
     } else {
       SetHTMLIntAttr(nsGkAtoms::minlength, aMinLength, aError);
     }
   }
-  // XPCOM GetName is fine
+  void GetName(nsAString& aName)
+  {
+    GetHTMLAttr(nsGkAtoms::name, aName);
+  }
   void SetName(const nsAString& aName, ErrorResult& aError)
   {
     SetHTMLAttr(nsGkAtoms::name, aName, aError);
   }
-  // XPCOM GetPlaceholder is fine
+  void GetPlaceholder(nsAString& aPlaceholder)
+  {
+    GetHTMLAttr(nsGkAtoms::placeholder, aPlaceholder);
+  }
   void SetPlaceholder(const nsAString& aPlaceholder, ErrorResult& aError)
   {
     SetHTMLAttr(nsGkAtoms::placeholder, aPlaceholder, aError);
   }
   bool ReadOnly()
   {
     return GetBoolAttr(nsGkAtoms::readonly);
   }
@@ -274,40 +277,48 @@ public:
   {
     return GetIntAttr(nsGkAtoms::rows, DEFAULT_ROWS_TEXTAREA);
   }
   void SetRows(uint32_t aRows, ErrorResult& aError)
   {
     uint32_t rows = aRows ? aRows : DEFAULT_ROWS_TEXTAREA;
     SetUnsignedIntAttr(nsGkAtoms::rows, rows, DEFAULT_ROWS_TEXTAREA, aError);
   }
-  // XPCOM GetWrap is fine
+  void GetWrap(nsAString& aWrap)
+  {
+    GetHTMLAttr(nsGkAtoms::wrap, aWrap);
+  }
   void SetWrap(const nsAString& aWrap, ErrorResult& aError)
   {
     SetHTMLAttr(nsGkAtoms::wrap, aWrap, aError);
   }
-  // XPCOM GetType is fine
-  // XPCOM GetDefaultValue is fine
+  void GetType(nsAString& aType);
+  void GetDefaultValue(nsAString& aDefaultValue, ErrorResult& aError);
   void SetDefaultValue(const nsAString& aDefaultValue, ErrorResult& aError);
-  // XPCOM GetValue/SetValue are fine
+  void GetValue(nsAString& aValue);
+  void SetValue(const nsAString& aValue, ErrorResult& aError);
+
   uint32_t GetTextLength();
 
   // Override SetCustomValidity so we update our state properly when it's called
   // via bindings.
   void SetCustomValidity(const nsAString& aError);
 
-  // XPCOM Select is fine
+  void Select();
   Nullable<uint32_t> GetSelectionStart(ErrorResult& aError);
   void SetSelectionStart(const Nullable<uint32_t>& aSelectionStart, ErrorResult& aError);
   Nullable<uint32_t> GetSelectionEnd(ErrorResult& aError);
   void SetSelectionEnd(const Nullable<uint32_t>& aSelectionEnd, ErrorResult& aError);
   void GetSelectionDirection(nsAString& aDirection, ErrorResult& aError);
   void SetSelectionDirection(const nsAString& aDirection, ErrorResult& aError);
   void SetSelectionRange(uint32_t aSelectionStart, uint32_t aSelectionEnd, const Optional<nsAString>& aDirecton, ErrorResult& aError);
   nsIControllers* GetControllers(ErrorResult& aError);
+  // XPCOM adapter function widely used throughout code, leaving it as is.
+  nsresult GetControllers(nsIControllers** aResult);
+
   nsIEditor* GetEditor()
   {
     return mState.GetTextEditor();
   }
 
 protected:
   virtual ~HTMLTextAreaElement() {}
 
--- a/dom/html/nsTextEditorState.cpp
+++ b/dom/html/nsTextEditorState.cpp
@@ -14,17 +14,16 @@
 #include "nsLayoutCID.h"
 #include "nsITextControlFrame.h"
 #include "nsIDOMCharacterData.h"
 #include "nsIDOMDocument.h"
 #include "nsContentCreatorFunctions.h"
 #include "nsTextControlFrame.h"
 #include "nsIControllers.h"
 #include "nsIDOMHTMLInputElement.h"
-#include "nsIDOMHTMLTextAreaElement.h"
 #include "nsITransactionManager.h"
 #include "nsIControllerContext.h"
 #include "nsAttrValue.h"
 #include "nsAttrValueInlines.h"
 #include "nsGenericHTMLElement.h"
 #include "nsIDOMEventListener.h"
 #include "nsIEditorObserver.h"
 #include "nsIWidget.h"
@@ -38,16 +37,17 @@
 #include "nsContentUtils.h"
 #include "mozilla/Preferences.h"
 #include "nsTextNode.h"
 #include "nsIController.h"
 #include "mozilla/AutoRestore.h"
 #include "mozilla/TextEvents.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/HTMLInputElement.h"
+#include "mozilla/dom/HTMLTextAreaElement.h"
 #include "nsNumberControlFrame.h"
 #include "nsFrameSelection.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/layers/ScrollInputMethods.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
@@ -963,18 +963,18 @@ DoCommandCallback(Command aCommand, void
   nsTextControlFrame *frame = static_cast<nsTextControlFrame*>(aData);
   nsIContent *content = frame->GetContent();
 
   nsCOMPtr<nsIControllers> controllers;
   nsCOMPtr<nsIDOMHTMLInputElement> input = do_QueryInterface(content);
   if (input) {
     input->GetControllers(getter_AddRefs(controllers));
   } else {
-    nsCOMPtr<nsIDOMHTMLTextAreaElement> textArea =
-      do_QueryInterface(content);
+    HTMLTextAreaElement* textArea =
+      HTMLTextAreaElement::FromContent(content);
 
     if (textArea) {
       textArea->GetControllers(getter_AddRefs(controllers));
     }
   }
 
   if (!controllers) {
     NS_WARNING("Could not get controllers");
@@ -1507,18 +1507,19 @@ nsTextEditorState::PrepareEditor(const n
 
   if (!SuppressEventHandlers(presContext)) {
     nsCOMPtr<nsIControllers> controllers;
     nsCOMPtr<nsIDOMHTMLInputElement> inputElement =
       do_QueryInterface(mTextCtrlElement);
     if (inputElement) {
       rv = inputElement->GetControllers(getter_AddRefs(controllers));
     } else {
-      nsCOMPtr<nsIDOMHTMLTextAreaElement> textAreaElement =
-        do_QueryInterface(mTextCtrlElement);
+      nsCOMPtr<nsIContent> content = do_QueryInterface(mTextCtrlElement);
+      HTMLTextAreaElement* textAreaElement =
+        HTMLTextAreaElement::FromContentOrNull(content);
 
       if (!textAreaElement)
         return NS_ERROR_FAILURE;
 
       rv = textAreaElement->GetControllers(getter_AddRefs(controllers));
     }
 
     NS_ENSURE_SUCCESS(rv, rv);
@@ -2171,18 +2172,19 @@ nsTextEditorState::UnbindFromFrame(nsTex
   {
     nsCOMPtr<nsIControllers> controllers;
     nsCOMPtr<nsIDOMHTMLInputElement> inputElement =
       do_QueryInterface(mTextCtrlElement);
     if (inputElement)
       inputElement->GetControllers(getter_AddRefs(controllers));
     else
     {
-      nsCOMPtr<nsIDOMHTMLTextAreaElement> textAreaElement =
-        do_QueryInterface(mTextCtrlElement);
+      nsCOMPtr<nsIContent> content = do_QueryInterface(mTextCtrlElement);
+      HTMLTextAreaElement* textAreaElement =
+        HTMLTextAreaElement::FromContentOrNull(content);
       if (textAreaElement) {
         textAreaElement->GetControllers(getter_AddRefs(controllers));
       }
     }
 
     if (controllers)
     {
       uint32_t numControllers;
--- a/dom/interfaces/html/moz.build
+++ b/dom/interfaces/html/moz.build
@@ -16,17 +16,16 @@ XPIDL_SOURCES += [
     'nsIDOMHTMLHtmlElement.idl',
     'nsIDOMHTMLInputElement.idl',
     'nsIDOMHTMLMediaElement.idl',
     'nsIDOMHTMLOptionElement.idl',
     'nsIDOMHTMLOptionsCollection.idl',
     'nsIDOMHTMLScriptElement.idl',
     'nsIDOMHTMLSelectElement.idl',
     'nsIDOMHTMLSourceElement.idl',
-    'nsIDOMHTMLTextAreaElement.idl',
     'nsIDOMMozBrowserFrame.idl',
     'nsIDOMTimeRanges.idl',
     'nsIDOMValidityState.idl',
     'nsIMozBrowserFrame.idl',
 ]
 
 XPIDL_MODULE = 'dom_html'
 
deleted file mode 100644
--- a/dom/interfaces/html/nsIDOMHTMLTextAreaElement.idl
+++ /dev/null
@@ -1,54 +0,0 @@
-/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "nsIDOMHTMLElement.idl"
-
-interface nsIControllers;
-interface nsIDOMValidityState;
-
-/**
- * The nsIDOMHTMLTextAreaElement interface is the interface to a
- * [X]HTML textarea element.
- *
- * This interface is trying to follow the DOM Level 2 HTML specification:
- * http://www.w3.org/TR/DOM-Level-2-HTML/
- *
- * with changes from the work-in-progress WHATWG HTML specification:
- * http://www.whatwg.org/specs/web-apps/current-work/
- */
-
-[uuid(7a4aeb2e-fcf3-443e-b002-ca1c8ea322e9)]
-interface nsIDOMHTMLTextAreaElement : nsISupports
-{
-           attribute boolean               autofocus;
-           attribute unsigned long         cols;
-           attribute boolean               disabled;
-  readonly attribute nsIDOMHTMLFormElement form;
-           attribute long                  maxLength;
-           attribute long                  minLength;
-           attribute DOMString             name;
-           attribute DOMString             placeholder;
-           attribute boolean               readOnly;
-           attribute boolean               required;
-           attribute unsigned long         rows;
-  /**
-   * Reflects the wrap content attribute. Possible values are "soft", "hard" and
-   * "off". "soft" is the default.
-   */
-  [Null(Stringify)]
-           attribute DOMString             wrap;
-
-  readonly attribute DOMString             type;
-           attribute DOMString             defaultValue;
-           attribute DOMString             value;
-  readonly attribute long                  textLength;
-
-  void select();
-
-  // Mozilla extensions
-  // Please make sure to update the HTMLTextAreaElement Web IDL interface to
-  // mirror the list of Mozilla extensions here when changing it.
-  readonly attribute nsIControllers   controllers;
-};
--- a/dom/media/platforms/apple/AppleATDecoder.cpp
+++ b/dom/media/platforms/apple/AppleATDecoder.cpp
@@ -7,16 +7,17 @@
 #include "AppleATDecoder.h"
 #include "AppleUtils.h"
 #include "MP4Decoder.h"
 #include "mp4_demuxer/Adts.h"
 #include "MediaInfo.h"
 #include "mozilla/Logging.h"
 #include "mozilla/SyncRunnable.h"
 #include "mozilla/UniquePtr.h"
+#include "VideoUtils.h"
 
 #define LOG(...) MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
 #define FourCC2Str(n) ((char[5]){(char)(n >> 24), (char)(n >> 16), (char)(n >> 8), (char)(n), 0})
 
 namespace mozilla {
 
 AppleATDecoder::AppleATDecoder(const AudioInfo& aConfig,
                                TaskQueue* aTaskQueue)
--- a/dom/media/platforms/apple/AppleCMLinker.cpp
+++ b/dom/media/platforms/apple/AppleCMLinker.cpp
@@ -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/. */
 
 #include <dlfcn.h>
 
 #include "AppleCMLinker.h"
 #include "mozilla/ArrayUtils.h"
+#include "mozilla/Logging.h"
+#include "PlatformDecoderModule.h"
 #include "nsDebug.h"
 
 #define LOG(...) MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
 
 namespace mozilla {
 
 AppleCMLinker::LinkStatus
 AppleCMLinker::sLinkStatus = LinkStatus_INIT;
--- a/dom/u2f/U2F.cpp
+++ b/dom/u2f/U2F.cpp
@@ -177,23 +177,26 @@ template<typename T, typename C>
 static void
 ExecuteCallback(T& aResp, Maybe<nsMainThreadPtrHandle<C>>& aCb)
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (aCb.isNothing()) {
     return;
   }
 
+  // reset callback earlier to allow reentry from callback.
+  nsMainThreadPtrHandle<C> callback(aCb.ref());
+  aCb.reset();
+  MOZ_ASSERT(aCb.isNothing());
+  MOZ_ASSERT(!!callback);
+
   ErrorResult error;
-  aCb.ref()->Call(aResp, error);
+  callback->Call(aResp, error);
   NS_WARNING_ASSERTION(!error.Failed(), "dom::U2F::Promise callback failed");
   error.SuppressException(); // Useful exceptions already emitted
-
-  aCb.reset();
-  MOZ_ASSERT(aCb.isNothing());
 }
 
 U2F::U2F(nsPIDOMWindowInner* aParent)
   : mParent(aParent)
 {
 }
 
 U2F::~U2F()
@@ -308,28 +311,32 @@ U2F::Register(const nsAString& aAppId,
   p->Then(mEventTarget, "dom::U2F::Register::Promise::Resolve",
           [&localCb, &localReqHolder](nsString aResponse) {
               MOZ_LOG(gU2FLog, LogLevel::Debug,
                       ("dom::U2F::Register::Promise::Resolve, response was %s",
                         NS_ConvertUTF16toUTF8(aResponse).get()));
               RegisterResponse response;
               response.Init(aResponse);
 
+              // U2F could be reentered from microtask-checkpoint while calling
+              // ExecuteCallback(), so we should mark Complete() earlier.
+              localReqHolder.Complete();
               ExecuteCallback(response, localCb);
-              localReqHolder.Complete();
           },
           [&localCb, &localReqHolder](ErrorCode aErrorCode) {
               MOZ_LOG(gU2FLog, LogLevel::Debug,
                       ("dom::U2F::Register::Promise::Reject, response was %d",
                         static_cast<uint32_t>(aErrorCode)));
               RegisterResponse response;
               response.mErrorCode.Construct(static_cast<uint32_t>(aErrorCode));
 
+              // U2F could be reentered from microtask-checkpoint while calling
+              // ExecuteCallback(), so we should mark Complete() earlier.
+              localReqHolder.Complete();
               ExecuteCallback(response, localCb);
-              localReqHolder.Complete();
           })
   ->Track(mPromiseHolder);
 }
 
 void
 U2F::Sign(const nsAString& aAppId,
           const nsAString& aChallenge,
           const Sequence<RegisteredKey>& aRegisteredKeys,
@@ -385,28 +392,32 @@ U2F::Sign(const nsAString& aAppId,
   p->Then(mEventTarget, "dom::U2F::Sign::Promise::Resolve",
           [&localCb, &localReqHolder](nsString aResponse) {
               MOZ_LOG(gU2FLog, LogLevel::Debug,
                       ("dom::U2F::Sign::Promise::Resolve, response was %s",
                         NS_ConvertUTF16toUTF8(aResponse).get()));
               SignResponse response;
               response.Init(aResponse);
 
+              // U2F could be reentered from microtask-checkpoint while calling
+              // ExecuteCallback(), so we should mark Complete() earlier.
+              localReqHolder.Complete();
               ExecuteCallback(response, localCb);
-              localReqHolder.Complete();
           },
           [&localCb, &localReqHolder](ErrorCode aErrorCode) {
               MOZ_LOG(gU2FLog, LogLevel::Debug,
                       ("dom::U2F::Sign::Promise::Reject, response was %d",
                         static_cast<uint32_t>(aErrorCode)));
               SignResponse response;
               response.mErrorCode.Construct(static_cast<uint32_t>(aErrorCode));
 
+              // U2F could be reentered from microtask-checkpoint while calling
+              // ExecuteCallback(), so we should mark Complete() earlier.
+              localReqHolder.Complete();
               ExecuteCallback(response, localCb);
-              localReqHolder.Complete();
           })
   ->Track(mPromiseHolder);
 }
 
 void
 U2F::Cancel()
 {
   MOZ_ASSERT(NS_IsMainThread());
--- a/dom/webbrowserpersist/WebBrowserPersistLocalDocument.cpp
+++ b/dom/webbrowserpersist/WebBrowserPersistLocalDocument.cpp
@@ -7,16 +7,17 @@
 #include "WebBrowserPersistDocumentParent.h"
 
 #include "mozilla/dom/HTMLAnchorElement.h"
 #include "mozilla/dom/HTMLAreaElement.h"
 #include "mozilla/dom/HTMLInputElement.h"
 #include "mozilla/dom/HTMLLinkElement.h"
 #include "mozilla/dom/HTMLObjectElement.h"
 #include "mozilla/dom/HTMLSharedElement.h"
+#include "mozilla/dom/HTMLTextAreaElement.h"
 #include "mozilla/dom/TabParent.h"
 #include "nsComponentManagerUtils.h"
 #include "nsContentUtils.h"
 #include "nsContentCID.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsFrameLoader.h"
 #include "nsIComponentRegistrar.h"
 #include "nsIContent.h"
@@ -26,17 +27,16 @@
 #include "nsIDOMHTMLBaseElement.h"
 #include "nsIDOMHTMLCollection.h"
 #include "nsIDOMHTMLDocument.h"
 #include "nsIDOMHTMLInputElement.h"
 #include "nsIDOMHTMLMediaElement.h"
 #include "nsIDOMHTMLOptionElement.h"
 #include "nsIDOMHTMLScriptElement.h"
 #include "nsIDOMHTMLSourceElement.h"
-#include "nsIDOMHTMLTextAreaElement.h"
 #include "nsIDOMMozNamedAttrMap.h"
 #include "nsIDOMNode.h"
 #include "nsIDOMNodeFilter.h"
 #include "nsIDOMNodeList.h"
 #include "nsIDOMProcessingInstruction.h"
 #include "nsIDOMTreeWalker.h"
 #include "nsIDOMWindowUtils.h"
 #include "nsIDocShell.h"
@@ -1148,17 +1148,17 @@ PersistNodeFixup::FixupNode(nsIDOMNode *
                     break;
                 default:
                     break;
             }
         }
         return rv;
     }
 
-    nsCOMPtr<nsIDOMHTMLTextAreaElement> nodeAsTextArea = do_QueryInterface(aNodeIn);
+    dom::HTMLTextAreaElement* nodeAsTextArea = dom::HTMLTextAreaElement::FromContent(content);
     if (nodeAsTextArea) {
         rv = GetNodeToFixup(aNodeIn, aNodeOut);
         if (NS_SUCCEEDED(rv) && *aNodeOut) {
             // Tell the document encoder to serialize the text child we create below
             *aSerializeCloneKids = true;
 
             nsAutoString valueStr;
             nodeAsTextArea->GetValue(valueStr);
--- a/dom/webidl/HTMLTextAreaElement.webidl
+++ b/dom/webidl/HTMLTextAreaElement.webidl
@@ -41,19 +41,19 @@ interface HTMLTextAreaElement : HTMLElem
            attribute boolean required;
   [CEReactions, SetterThrows, Pure]
            attribute unsigned long rows;
   [CEReactions, SetterThrows, Pure]
            attribute DOMString wrap;
 
   [Constant]
   readonly attribute DOMString type;
-  [CEReactions, SetterThrows, Pure]
+  [CEReactions, Throws, Pure]
            attribute DOMString defaultValue;
-  [CEReactions, TreatNullAs=EmptyString] attribute DOMString value;
+  [CEReactions, SetterThrows, TreatNullAs=EmptyString] attribute DOMString value;
   readonly attribute unsigned long textLength;
 
   readonly attribute boolean willValidate;
   readonly attribute ValidityState validity;
   [Throws]
   readonly attribute DOMString validationMessage;
   boolean checkValidity();
   boolean reportValidity();
@@ -73,18 +73,17 @@ interface HTMLTextAreaElement : HTMLElem
   [Throws]
   void setRangeText(DOMString replacement, unsigned long start,
     unsigned long end, optional SelectionMode selectionMode = "preserve");
   [Throws]
   void setSelectionRange(unsigned long start, unsigned long end, optional DOMString direction);
 };
 
 partial interface HTMLTextAreaElement {
-  // Mirrored chrome-only Mozilla extensions to nsIDOMHTMLTextAreaElement.
-  // Please make sure to update this list of nsIDOMHTMLTextAreaElement changes.
+  // Chrome-only Mozilla extensions
 
   [Throws, ChromeOnly]
   readonly attribute XULControllers controllers;
 };
 
 partial interface HTMLTextAreaElement {
   // Mirrored chrome-only nsIDOMNSEditableElement methods.  Please make sure
   // to update this list if nsIDOMNSEditableElement changes.
--- a/dom/workers/ServiceWorkerManager.cpp
+++ b/dom/workers/ServiceWorkerManager.cpp
@@ -3358,26 +3358,34 @@ ServiceWorkerManager::SetSkipWaitingFlag
     registration->TryToActivateAsync();
   }
 }
 
 void
 ServiceWorkerManager::FireControllerChange(ServiceWorkerRegistrationInfo* aRegistration)
 {
   AssertIsOnMainThread();
+
+  AutoTArray<nsCOMPtr<nsIDocument>, 16> documents;
   for (auto iter = mControlledDocuments.Iter(); !iter.Done(); iter.Next()) {
     if (iter.UserData() != aRegistration) {
       continue;
     }
 
     nsCOMPtr<nsIDocument> doc = do_QueryInterface(iter.Key());
     if (NS_WARN_IF(!doc)) {
       continue;
     }
 
+    documents.AppendElement(doc);
+  }
+
+  // Fire event after iterating mControlledDocuments is done to prevent
+  // modification by reentering from the event handlers during iteration.
+  for (auto& doc : documents) {
     FireControllerChangeOnDocument(doc);
   }
 }
 
 already_AddRefed<ServiceWorkerRegistrationInfo>
 ServiceWorkerManager::GetRegistration(nsIPrincipal* aPrincipal,
                                       const nsACString& aScope) const
 {
--- a/dom/workers/WorkerHolderToken.cpp
+++ b/dom/workers/WorkerHolderToken.cpp
@@ -91,20 +91,23 @@ WorkerHolderToken::Notify(Status aStatus
   NS_ASSERT_OWNINGTHREAD(WorkerHolderToken);
 
   // When the service worker thread is stopped we will get Terminating,
   // but nothing higher than that.  We must shut things down at Terminating.
   if (aStatus < mShutdownStatus || mShuttingDown) {
     return true;
   }
 
-  mShuttingDown = true;
-
   // Start the asynchronous destruction of our actors.  These will call back
   // into RemoveActor() once the actor is destroyed.
-  for (uint32_t i = 0; i < mListenerList.Length(); ++i) {
-    mListenerList[i]->WorkerShuttingDown();
+  nsTObserverArray<Listener*>::ForwardIterator iter(mListenerList);
+  while (iter.HasMore()) {
+    iter.GetNext()->WorkerShuttingDown();
   }
 
+  // Set this after calling WorkerShuttingDown() on listener list in case
+  // one callback triggers another listener to be added.
+  mShuttingDown = true;
+
   return true;
 }
 
 END_WORKERS_NAMESPACE
--- a/dom/workers/WorkerHolderToken.h
+++ b/dom/workers/WorkerHolderToken.h
@@ -3,17 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_workers_WorkerHolderToken_h
 #define mozilla_dom_workers_WorkerHolderToken_h
 
 #include "nsISupportsImpl.h"
-#include "nsTArray.h"
+#include "nsTObserverArray.h"
 #include "WorkerHolder.h"
 
 BEGIN_WORKERS_NAMESPACE
 
 class WorkerPrivate;
 
 // This is a ref-counted WorkerHolder implementation.  If you wish
 // to be notified of worker shutdown beginning, then you can implement
@@ -62,17 +62,17 @@ private:
   WorkerHolderToken(Status aShutdownStatus, Behavior aBehavior);
 
   ~WorkerHolderToken();
 
   // WorkerHolder methods
   virtual bool
   Notify(workers::Status aStatus) override;
 
-  nsTArray<Listener*> mListenerList;
+  nsTObserverArray<Listener*> mListenerList;
   const Status mShutdownStatus;
   bool mShuttingDown;
 
 public:
   NS_INLINE_DECL_REFCOUNTING(WorkerHolderToken)
 };
 
 END_WORKERS_NAMESPACE
--- a/dom/xbl/nsXBLPrototypeHandler.cpp
+++ b/dom/xbl/nsXBLPrototypeHandler.cpp
@@ -18,17 +18,16 @@
 #include "nsIDOMKeyEvent.h"
 #include "nsIDOMMouseEvent.h"
 #include "nsNameSpaceManager.h"
 #include "nsIDocument.h"
 #include "nsIController.h"
 #include "nsIControllers.h"
 #include "nsXULElement.h"
 #include "nsIURI.h"
-#include "nsIDOMHTMLTextAreaElement.h"
 #include "nsIDOMHTMLInputElement.h"
 #include "nsFocusManager.h"
 #include "nsIFormControl.h"
 #include "nsIDOMEventListener.h"
 #include "nsPIDOMWindow.h"
 #include "nsPIWindowRoot.h"
 #include "nsIDOMWindow.h"
 #include "nsIServiceManager.h"
@@ -45,16 +44,17 @@
 #include "nsXBLSerialize.h"
 #include "nsJSUtils.h"
 #include "mozilla/BasicEvents.h"
 #include "mozilla/JSEventHandler.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/TextEvents.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/EventHandlerBinding.h"
+#include "mozilla/dom/HTMLTextAreaElement.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/layers/KeyboardMap.h"
 #include "xpcpublic.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::layers;
 
@@ -676,17 +676,17 @@ nsXBLPrototypeHandler::GetController(Eve
   RefPtr<nsXULElement> xulElement =
     nsXULElement::FromContentOrNull(targetContent);
   if (xulElement) {
     IgnoredErrorResult rv;
     controllers = xulElement->GetControllers(rv);
   }
 
   if (!controllers) {
-    nsCOMPtr<nsIDOMHTMLTextAreaElement> htmlTextArea(do_QueryInterface(aTarget));
+    HTMLTextAreaElement* htmlTextArea = HTMLTextAreaElement::FromContent(targetContent);
     if (htmlTextArea)
       htmlTextArea->GetControllers(getter_AddRefs(controllers));
   }
 
   if (!controllers) {
     nsCOMPtr<nsIDOMHTMLInputElement> htmlInputElement(do_QueryInterface(aTarget));
     if (htmlInputElement)
       htmlInputElement->GetControllers(getter_AddRefs(controllers));
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -2537,21 +2537,21 @@ Parser<FullParseHandler, char16_t>::stan
                                                        FunctionAsyncKind asyncKind,
                                                        Directives inheritedDirectives,
                                                        Directives* newDirectives)
 {
     MOZ_ASSERT(checkOptionsCalled);
 
     // Skip prelude.
     TokenKind tt;
-    if (!tokenStream.getToken(&tt))
+    if (!tokenStream.getToken(&tt, TokenStream::Operand))
         return null();
     if (asyncKind == AsyncFunction) {
         MOZ_ASSERT(tt == TOK_ASYNC);
-        if (!tokenStream.getToken(&tt))
+        if (!tokenStream.getToken(&tt, TokenStream::Operand))
             return null();
     }
     MOZ_ASSERT(tt == TOK_FUNCTION);
 
     if (!tokenStream.getToken(&tt))
         return null();
     if (generatorKind == StarGenerator) {
         MOZ_ASSERT(tt == TOK_MUL);
@@ -2851,27 +2851,22 @@ ParserBase::newFunction(HandleAtom atom,
 #ifdef DEBUG
         if (isGlobalSelfHostedBuiltin)
             fun->setExtendedSlot(HAS_SELFHOSTED_CANONICAL_NAME_SLOT, BooleanValue(false));
 #endif
     }
     return fun;
 }
 
-/*
- * WARNING: Do not call this function directly.
- * Call either matchOrInsertSemicolonAfterExpression or
- * matchOrInsertSemicolonAfterNonExpression instead, depending on context.
- */
 template <class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::matchOrInsertSemicolonHelper(TokenStream::Modifier modifier)
+Parser<ParseHandler, CharT>::matchOrInsertSemicolon()
 {
     TokenKind tt = TOK_EOF;
-    if (!tokenStream.peekTokenSameLine(&tt, modifier))
+    if (!tokenStream.peekTokenSameLine(&tt, TokenStream::Operand))
         return false;
     if (tt != TOK_EOF && tt != TOK_EOL && tt != TOK_SEMI && tt != TOK_RC) {
         /*
          * When current token is `await` and it's outside of async function,
          * it's possibly intended to be an await expression.
          *
          *   await f();
          *        ^
@@ -2886,40 +2881,22 @@ Parser<ParseHandler, CharT>::matchOrInse
             return false;
         }
         if (!yieldExpressionsSupported() && tokenStream.currentToken().type == TOK_YIELD) {
             error(JSMSG_YIELD_OUTSIDE_GENERATOR);
             return false;
         }
 
         /* Advance the scanner for proper error location reporting. */
-        tokenStream.consumeKnownToken(tt, modifier);
+        tokenStream.consumeKnownToken(tt, TokenStream::Operand);
         error(JSMSG_SEMI_BEFORE_STMNT);
         return false;
     }
     bool matched;
-    if (!tokenStream.matchToken(&matched, TOK_SEMI, modifier))
-        return false;
-    if (!matched && modifier == TokenStream::None)
-        tokenStream.addModifierException(TokenStream::OperandIsNone);
-    return true;
-}
-
-template <class ParseHandler, typename CharT>
-bool
-Parser<ParseHandler, CharT>::matchOrInsertSemicolonAfterExpression()
-{
-    return matchOrInsertSemicolonHelper(TokenStream::None);
-}
-
-template <class ParseHandler, typename CharT>
-bool
-Parser<ParseHandler, CharT>::matchOrInsertSemicolonAfterNonExpression()
-{
-    return matchOrInsertSemicolonHelper(TokenStream::Operand);
+    return tokenStream.matchToken(&matched, TOK_SEMI, TokenStream::Operand);
 }
 
 template <class ParseHandler, typename CharT>
 bool
 Parser<ParseHandler, CharT>::leaveInnerFunction(ParseContext* outerpc)
 {
     MOZ_ASSERT(pc != outerpc);
 
@@ -3266,17 +3243,17 @@ Parser<FullParseHandler, char16_t>::skip
     if (!tokenStream.advance(fun->lazyScript()->end()))
         return false;
 
 #if JS_HAS_EXPR_CLOSURES
     // Only expression closure can be Statement kind.
     // If we remove expression closure, we can remove isExprBody flag from
     // LazyScript and JSScript.
     if (kind == Statement && funbox->isExprBody()) {
-        if (!matchOrInsertSemicolonAfterExpression())
+        if (!matchOrInsertSemicolon())
             return false;
     }
 #endif
 
     // Append possible Annex B function box only upon successfully parsing.
     if (tryAnnexB && !pc->innermostScope()->addPossibleAnnexBFunctionBox(pc, funbox))
         return false;
 
@@ -3299,17 +3276,17 @@ Parser<ParseHandler, CharT>::addExprAndG
                                                             TokenKind* ttp)
 {
     Node pn = expr(InAllowed, yieldHandling, TripledotProhibited);
     if (!pn)
         return false;
     handler.addList(nodeList, pn);
 
     TokenKind tt;
-    if (!tokenStream.getToken(&tt))
+    if (!tokenStream.getToken(&tt, TokenStream::Operand))
         return false;
     if (tt != TOK_RC) {
         error(JSMSG_TEMPLSTR_UNTERM_EXPR);
         return false;
     }
 
     return tokenStream.getToken(ttp, TokenStream::TemplateTail);
 }
@@ -3811,17 +3788,17 @@ Parser<ParseHandler, CharT>::functionFor
         funbox->setEnd(tokenStream);
     } else {
 #if !JS_HAS_EXPR_CLOSURES
         MOZ_ASSERT(kind == Arrow);
 #endif
         if (tokenStream.hadError())
             return false;
         funbox->setEnd(tokenStream);
-        if (kind == Statement && !matchOrInsertSemicolonAfterExpression())
+        if (kind == Statement && !matchOrInsertSemicolon())
             return false;
     }
 
     if (IsMethodDefinitionKind(kind) && pc->superScopeNeedsHomeObject())
         funbox->setNeedsHomeObject();
 
     if (!finishFunction(isStandaloneFunction))
         return false;
@@ -4186,20 +4163,22 @@ Parser<ParseHandler, CharT>::statementLi
     return pn;
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 Parser<ParseHandler, CharT>::condition(InHandling inHandling, YieldHandling yieldHandling)
 {
     MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_COND);
+
     Node pn = exprInParens(inHandling, yieldHandling, TripledotProhibited);
     if (!pn)
         return null();
-    MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_COND);
+
+    MUST_MATCH_TOKEN_MOD(TOK_RP, TokenStream::Operand, JSMSG_PAREN_AFTER_COND);
 
     /* Check for (a = b) and warn about possible (a == b) mistype. */
     if (handler.isUnparenthesizedAssignment(pn)) {
         if (!extraWarning(JSMSG_EQUAL_AS_ASSIGN))
             return null();
     }
     return pn;
 }
@@ -4575,40 +4554,39 @@ Parser<ParseHandler, CharT>::arrayBindin
         return null();
 
     uint32_t begin = pos().begin;
     Node literal = handler.newArrayLiteral(begin);
     if (!literal)
         return null();
 
      uint32_t index = 0;
-     TokenStream::Modifier modifier = TokenStream::Operand;
      for (; ; index++) {
          if (index >= NativeObject::MAX_DENSE_ELEMENTS_COUNT) {
              error(JSMSG_ARRAY_INIT_TOO_BIG);
              return null();
          }
 
          TokenKind tt;
-         if (!tokenStream.getToken(&tt, TokenStream::Operand))
+         if (!tokenStream.getToken(&tt))
              return null();
 
          if (tt == TOK_RB) {
              tokenStream.ungetToken();
              break;
          }
 
          if (tt == TOK_COMMA) {
              if (!handler.addElision(literal, pos()))
                  return null();
          } else if (tt == TOK_TRIPLEDOT) {
              uint32_t begin = pos().begin;
 
              TokenKind tt;
-             if (!tokenStream.getToken(&tt, TokenStream::Operand))
+             if (!tokenStream.getToken(&tt))
                  return null();
 
              Node inner = bindingIdentifierOrPattern(kind, yieldHandling, tt);
              if (!inner)
                  return null();
 
              if (!handler.addSpreadElement(literal, begin, inner))
                  return null();
@@ -4630,28 +4608,27 @@ Parser<ParseHandler, CharT>::arrayBindin
              handler.addArrayElement(literal, element);
          }
 
          if (tt != TOK_COMMA) {
              // If we didn't already match TOK_COMMA in above case.
              bool matched;
              if (!tokenStream.matchToken(&matched, TOK_COMMA))
                  return null();
-             if (!matched) {
-                 modifier = TokenStream::None;
+             if (!matched)
                  break;
-             }
+
              if (tt == TOK_TRIPLEDOT) {
                  error(JSMSG_REST_WITH_COMMA);
                  return null();
              }
          }
      }
 
-     MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RB, modifier,
+     MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RB, TokenStream::None,
                                       reportMissingClosing(JSMSG_BRACKET_AFTER_LIST,
                                                            JSMSG_BRACKET_OPENED, begin));
 
     handler.setEndPosition(literal, pos().end);
     return literal;
 }
 
 template <class ParseHandler, typename CharT>
@@ -4761,40 +4738,25 @@ Parser<ParseHandler, CharT>::declaration
             *forInOrOfExpression = expressionAfterForInOrOf(*forHeadKind, yieldHandling);
             if (!*forInOrOfExpression)
                 return null();
 
             return pattern;
         }
     }
 
-    MUST_MATCH_TOKEN(TOK_ASSIGN, JSMSG_BAD_DESTRUCT_DECL);
+    MUST_MATCH_TOKEN_MOD(TOK_ASSIGN, TokenStream::Operand, JSMSG_BAD_DESTRUCT_DECL);
 
     Node init = assignExpr(forHeadKind ? InProhibited : InAllowed,
                            yieldHandling, TripledotProhibited);
     if (!init)
         return null();
 
     handler.checkAndSetIsDirectRHSAnonFunction(init);
 
-    if (forHeadKind) {
-        // For for(;;) declarations, consistency with |for (;| parsing requires
-        // that the ';' first be examined as Operand, even though absence of a
-        // binary operator (examined with modifier None) terminated |init|.
-        // For all other declarations, through ASI's infinite majesty, a next
-        // token on a new line would begin an expression.
-        // Similar to the case in initializerInNameDeclaration(), we need to
-        // peek at the next token when assignExpr() is a lazily parsed arrow
-        // function.
-        TokenKind ignored;
-        if (!tokenStream.peekToken(&ignored))
-            return null();
-        tokenStream.addModifierException(TokenStream::OperandIsNone);
-    }
-
     return handler.newAssignment(PNK_ASSIGN, pattern, init);
 }
 
 template <class ParseHandler, typename CharT>
 bool
 Parser<ParseHandler, CharT>::initializerInNameDeclaration(Node decl, Node binding,
                                                           Handle<PropertyName*> name,
                                                           DeclarationKind declKind,
@@ -4811,74 +4773,49 @@ Parser<ParseHandler, CharT>::initializer
 
     Node initializer = assignExpr(forHeadKind ? InProhibited : InAllowed,
                                   yieldHandling, TripledotProhibited);
     if (!initializer)
         return false;
 
     handler.checkAndSetIsDirectRHSAnonFunction(initializer);
 
-    if (forHeadKind) {
-        if (initialDeclaration) {
-            bool isForIn, isForOf;
-            if (!matchInOrOf(&isForIn, &isForOf))
-                return false;
-
-            // An initialized declaration can't appear in a for-of:
+    if (forHeadKind && initialDeclaration) {
+        bool isForIn, isForOf;
+        if (!matchInOrOf(&isForIn, &isForOf))
+            return false;
+
+        // An initialized declaration can't appear in a for-of:
+        //
+        //   for (var/let/const x = ... of ...); // BAD
+        if (isForOf) {
+            errorAt(initializerOffset, JSMSG_OF_AFTER_FOR_LOOP_DECL);
+            return false;
+        }
+
+        if (isForIn) {
+            // Lexical declarations in for-in loops can't be initialized:
             //
-            //   for (var/let/const x = ... of ...); // BAD
-            if (isForOf) {
-                errorAt(initializerOffset, JSMSG_OF_AFTER_FOR_LOOP_DECL);
+            //   for (let/const x = ... in ...); // BAD
+            if (DeclarationKindIsLexical(declKind)) {
+                errorAt(initializerOffset, JSMSG_IN_AFTER_LEXICAL_FOR_DECL);
                 return false;
             }
 
-            if (isForIn) {
-                // Lexical declarations in for-in loops can't be initialized:
-                //
-                //   for (let/const x = ... in ...); // BAD
-                if (DeclarationKindIsLexical(declKind)) {
-                    errorAt(initializerOffset, JSMSG_IN_AFTER_LEXICAL_FOR_DECL);
-                    return false;
-                }
-
-                // This leaves only initialized for-in |var| declarations.  ES6
-                // forbids these; later ES un-forbids in non-strict mode code.
-                *forHeadKind = PNK_FORIN;
-                if (!strictModeErrorAt(initializerOffset, JSMSG_INVALID_FOR_IN_DECL_WITH_INIT))
-                    return false;
-
-                *forInOrOfExpression = expressionAfterForInOrOf(PNK_FORIN, yieldHandling);
-                if (!*forInOrOfExpression)
-                    return false;
-            } else {
-                *forHeadKind = PNK_FORHEAD;
-            }
+            // This leaves only initialized for-in |var| declarations.  ES6
+            // forbids these; later ES un-forbids in non-strict mode code.
+            *forHeadKind = PNK_FORIN;
+            if (!strictModeErrorAt(initializerOffset, JSMSG_INVALID_FOR_IN_DECL_WITH_INIT))
+                return false;
+
+            *forInOrOfExpression = expressionAfterForInOrOf(PNK_FORIN, yieldHandling);
+            if (!*forInOrOfExpression)
+                return false;
         } else {
-            MOZ_ASSERT(*forHeadKind == PNK_FORHEAD);
-
-            // In the very rare case of Parser::assignExpr consuming an
-            // ArrowFunction with block body, when full-parsing with the arrow
-            // function being a skipped lazy inner function, we don't have
-            // lookahead for the next token.  Do a one-off peek here to be
-            // consistent with what Parser::matchForInOrOf does in the other
-            // arm of this |if|.
-            //
-            // If you think this all sounds pretty code-smelly, you're almost
-            // certainly correct.
-            TokenKind ignored;
-            if (!tokenStream.peekToken(&ignored))
-                return false;
-        }
-
-        if (*forHeadKind == PNK_FORHEAD) {
-            // Per Parser::forHeadStart, the semicolon in |for (;| is
-            // ultimately gotten as Operand.  But initializer expressions
-            // terminate with the absence of an operator gotten as None,
-            // so we need an exception.
-            tokenStream.addModifierException(TokenStream::OperandIsNone);
+            *forHeadKind = PNK_FORHEAD;
         }
     }
 
     return handler.finishInitializerAssignment(binding, initializer);
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
@@ -4915,18 +4852,16 @@ Parser<ParseHandler, CharT>::declaration
 
     if (matched) {
         if (!initializerInNameDeclaration(decl, binding, name, declKind, initialDeclaration,
                                           yieldHandling, forHeadKind, forInOrOfExpression))
         {
             return null();
         }
     } else {
-        tokenStream.addModifierException(TokenStream::NoneIsOperand);
-
         if (initialDeclaration && forHeadKind) {
             bool isForIn, isForOf;
             if (!matchInOrOf(&isForIn, &isForOf))
                 return null();
 
             if (isForIn) {
                 *forHeadKind = PNK_FORIN;
             } else if (isForOf) {
@@ -4985,17 +4920,17 @@ Parser<ParseHandler, CharT>::declaration
       default:
         MOZ_CRASH("Unknown declaration kind");
     }
 
     Node decl = handler.newDeclarationList(kind, pos());
     if (!decl)
         return null();
 
-    bool matched;
+    bool moreDeclarations;
     bool initialDeclaration = true;
     do {
         MOZ_ASSERT_IF(!initialDeclaration && forHeadKind,
                       *forHeadKind == PNK_FORHEAD);
 
         TokenKind tt;
         if (!tokenStream.getToken(&tt))
             return null();
@@ -5005,24 +4940,26 @@ Parser<ParseHandler, CharT>::declaration
                                             forHeadKind, forInOrOfExpression)
                        : declarationName(decl, declKind, tt, initialDeclaration, yieldHandling,
                                          forHeadKind, forInOrOfExpression);
         if (!binding)
             return null();
 
         handler.addList(decl, binding);
 
+        // If we have a for-in/of loop, the above call matches the entirety
+        // of the loop head (up to the closing parenthesis).
         if (forHeadKind && *forHeadKind != PNK_FORHEAD)
             break;
 
         initialDeclaration = false;
 
-        if (!tokenStream.matchToken(&matched, TOK_COMMA))
-            return null();
-    } while (matched);
+        if (!tokenStream.matchToken(&moreDeclarations, TOK_COMMA, TokenStream::Operand))
+            return null();
+    } while (moreDeclarations);
 
     return decl;
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 Parser<ParseHandler, CharT>::lexicalDeclaration(YieldHandling yieldHandling, DeclarationKind kind)
 {
@@ -5036,17 +4973,17 @@ Parser<ParseHandler, CharT>::lexicalDecl
      *
      * However, they cannot be parsed exactly as vars, as ES6
      * requires that uninitialized lets throw ReferenceError on use.
      *
      * See 8.1.1.1.6 and the note in 13.2.1.
      */
     Node decl = declarationList(yieldHandling,
                                 kind == DeclarationKind::Const ? PNK_CONST : PNK_LET);
-    if (!decl || !matchOrInsertSemicolonAfterExpression())
+    if (!decl || !matchOrInsertSemicolon())
         return null();
 
     return decl;
 }
 
 template <>
 bool
 Parser<FullParseHandler, char16_t>::namedImportsOrNamespaceImport(TokenKind tt, Node importSpecSet)
@@ -5252,17 +5189,17 @@ Parser<FullParseHandler, char16_t>::impo
 
         MUST_MATCH_TOKEN(TOK_STRING, JSMSG_MODULE_SPEC_AFTER_FROM);
     }
 
     Node moduleSpec = stringLiteral();
     if (!moduleSpec)
         return null();
 
-    if (!matchOrInsertSemicolonAfterNonExpression())
+    if (!matchOrInsertSemicolon())
         return null();
 
     ParseNode* node =
         handler.newImportDeclaration(importSpecSet, moduleSpec, TokenPos(begin, pos().end));
     if (!node || !pc->sc()->asModuleContext()->builder.processImport(node))
         return null();
 
     return node;
@@ -5413,17 +5350,17 @@ Parser<ParseHandler, CharT>::exportFrom(
         return null();
 
     MUST_MATCH_TOKEN(TOK_STRING, JSMSG_MODULE_SPEC_AFTER_FROM);
 
     Node moduleSpec = stringLiteral();
     if (!moduleSpec)
         return null();
 
-    if (!matchOrInsertSemicolonAfterNonExpression())
+    if (!matchOrInsertSemicolon())
         return null();
 
     Node node = handler.newExportFromDeclaration(begin, specList, moduleSpec);
     if (!node)
         return null();
 
     if (!processExportFrom(node))
         return null();
@@ -5552,26 +5489,26 @@ Parser<ParseHandler, CharT>::exportClaus
     //   export { x }
     //   from "foo"; // a single ExportDeclaration
     //
     // But if it doesn't, we might have an ASI opportunity in Operand context:
     //
     //   export { x }   // ExportDeclaration, terminated by ASI
     //   fro\u006D      // ExpressionStatement, the name "from"
     //
-    // In that case let matchOrInsertSemicolonAfterNonExpression sort out ASI
-    // or any necessary error.
+    // In that case let matchOrInsertSemicolon sort out ASI or any necessary
+    // error.
     bool matched;
     if (!tokenStream.matchToken(&matched, TOK_FROM, TokenStream::Operand))
         return null();
 
     if (matched)
         return exportFrom(begin, kid);
 
-    if (!matchOrInsertSemicolonAfterNonExpression())
+    if (!matchOrInsertSemicolon())
         return null();
 
     if (!checkLocalExportNames(kid))
         return null();
 
     Node node = handler.newExportDeclaration(kid, TokenPos(begin, pos().end));
     if (!node)
         return null();
@@ -5589,17 +5526,17 @@ Parser<ParseHandler, CharT>::exportVaria
     if (!abortIfSyntaxParser())
         return null();
 
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_VAR));
 
     Node kid = declarationList(YieldIsName, PNK_VAR);
     if (!kid)
         return null();
-    if (!matchOrInsertSemicolonAfterExpression())
+    if (!matchOrInsertSemicolon())
         return null();
     if (!checkExportedNamesForDeclaration(kid))
         return null();
 
     Node node = handler.newExportDeclaration(kid, TokenPos(begin, pos().end));
     if (!node)
         return null();
 
@@ -5749,17 +5686,17 @@ Parser<ParseHandler, CharT>::exportDefau
     if (!nameNode)
         return null();
     if (!noteDeclaredName(name, DeclarationKind::Const, pos()))
         return null();
 
     Node kid = assignExpr(InAllowed, YieldIsName, TripledotProhibited);
     if (!kid)
         return null();
-    if (!matchOrInsertSemicolonAfterExpression())
+    if (!matchOrInsertSemicolon())
         return null();
 
     Node node = handler.newExportDefaultDeclaration(kid, nameNode, TokenPos(begin, pos().end));
     if (!node)
         return null();
 
     if (!processExport(node))
         return null();
@@ -5881,17 +5818,17 @@ typename ParseHandler::Node
 Parser<ParseHandler, CharT>::expressionStatement(YieldHandling yieldHandling,
                                                  InvokedPrediction invoked)
 {
     tokenStream.ungetToken();
     Node pnexpr = expr(InAllowed, yieldHandling, TripledotProhibited,
                        /* possibleError = */ nullptr, invoked);
     if (!pnexpr)
         return null();
-    if (!matchOrInsertSemicolonAfterExpression())
+    if (!matchOrInsertSemicolon())
         return null();
     return handler.newExprStatement(pnexpr, pos().end);
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 Parser<ParseHandler, CharT>::consequentOrAlternative(YieldHandling yieldHandling)
 {
@@ -6045,32 +5982,32 @@ Parser<ParseHandler, CharT>::whileStatem
     return handler.newWhileStatement(begin, cond, body);
 }
 
 template <class ParseHandler, typename CharT>
 bool
 Parser<ParseHandler, CharT>::matchInOrOf(bool* isForInp, bool* isForOfp)
 {
     TokenKind tt;
-    if (!tokenStream.getToken(&tt))
+    if (!tokenStream.getToken(&tt, TokenStream::Operand))
         return false;
 
     *isForInp = tt == TOK_IN;
     *isForOfp = tt == TOK_OF;
     if (!*isForInp && !*isForOfp)
         tokenStream.ungetToken();
 
     MOZ_ASSERT_IF(*isForInp || *isForOfp, *isForInp != *isForOfp);
     return true;
 }
 
 template <class ParseHandler, typename CharT>
 bool
 Parser<ParseHandler, CharT>::forHeadStart(YieldHandling yieldHandling,
-                                   IteratorKind iterKind,
+                                          IteratorKind iterKind,
                                           ParseNodeKind* forHeadKind,
                                           Node* forInitialPart,
                                           Maybe<ParseContext::Scope>& forLoopLexicalScope,
                                           Node* forInOrOfExpression)
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LP));
 
     TokenKind tt;
@@ -6152,24 +6089,22 @@ Parser<ParseHandler, CharT>::forHeadStar
     if (!*forInitialPart)
         return false;
 
     bool isForIn, isForOf;
     if (!matchInOrOf(&isForIn, &isForOf))
         return false;
 
     // If we don't encounter 'in'/'of', we have a for(;;) loop.  We've handled
-    // the init expression; the caller handles the rest.  Allow the Operand
-    // modifier when regetting: Operand must be used to examine the ';' in
-    // |for (;|, and our caller handles this case and that.
+    // the init expression; the caller handles the rest.
     if (!isForIn && !isForOf) {
         if (!possibleError.checkForExpressionError())
             return false;
+
         *forHeadKind = PNK_FORHEAD;
-        tokenStream.addModifierException(TokenStream::OperandIsNone);
         return true;
     }
 
     MOZ_ASSERT(isForIn != isForOf);
 
     // In a for-of loop, 'let' that starts the loop head is a |let| keyword,
     // per the [lookahead ≠ let] restriction on the LeftHandSideExpression
     // variant of such loops.  Expressions that start with |let| can't be used
@@ -6321,44 +6256,39 @@ Parser<ParseHandler, CharT>::forStatemen
         // this semicolon with that modifier.
         MUST_MATCH_TOKEN_MOD(TOK_SEMI, TokenStream::Operand, JSMSG_SEMI_AFTER_FOR_INIT);
 
         TokenKind tt;
         if (!tokenStream.peekToken(&tt, TokenStream::Operand))
             return null();
 
         Node test;
-        TokenStream::Modifier mod;
         if (tt == TOK_SEMI) {
             test = null();
-            mod = TokenStream::Operand;
         } else {
             test = expr(InAllowed, yieldHandling, TripledotProhibited);
             if (!test)
                 return null();
-            mod = TokenStream::None;
-        }
-
-        MUST_MATCH_TOKEN_MOD(TOK_SEMI, mod, JSMSG_SEMI_AFTER_FOR_COND);
+        }
+
+        MUST_MATCH_TOKEN_MOD(TOK_SEMI, TokenStream::Operand, JSMSG_SEMI_AFTER_FOR_COND);
 
         if (!tokenStream.peekToken(&tt, TokenStream::Operand))
             return null();
 
         Node update;
         if (tt == TOK_RP) {
             update = null();
-            mod = TokenStream::Operand;
         } else {
             update = expr(InAllowed, yieldHandling, TripledotProhibited);
             if (!update)
                 return null();
-            mod = TokenStream::None;
-        }
-
-        MUST_MATCH_TOKEN_MOD(TOK_RP, mod, JSMSG_PAREN_AFTER_FOR_CTRL);
+        }
+
+        MUST_MATCH_TOKEN_MOD(TOK_RP, TokenStream::Operand, JSMSG_PAREN_AFTER_FOR_CTRL);
 
         TokenPos headPos(begin, pos().end);
         forHead = handler.newForHead(init, test, update, headPos);
         if (!forHead)
             return null();
     } else {
         MOZ_ASSERT(headKind == PNK_FORIN || headKind == PNK_FOROF);
 
@@ -6371,19 +6301,19 @@ Parser<ParseHandler, CharT>::forStatemen
         if (headKind == PNK_FORIN) {
             stmt.refineForKind(StatementKind::ForInLoop);
             iflags |= JSITER_ENUMERATE;
         } else {
             stmt.refineForKind(StatementKind::ForOfLoop);
         }
 
         // Parser::declaration consumed everything up to the closing ')'.  That
-        // token follows an {Assignment,}Expression, so the next token must be
-        // consumed as if an operator continued the expression, i.e. as None.
-        MUST_MATCH_TOKEN_MOD(TOK_RP, TokenStream::None, JSMSG_PAREN_AFTER_FOR_CTRL);
+        // token follows an {Assignment,}Expression and so must be interpreted
+        // as an operand to be consistent with normal expression tokenizing.
+        MUST_MATCH_TOKEN_MOD(TOK_RP, TokenStream::Operand, JSMSG_PAREN_AFTER_FOR_CTRL);
 
         TokenPos headPos(begin, pos().end);
         forHead = handler.newForInOrOfHead(headKind, target, iteratedExpr, headPos);
         if (!forHead)
             return null();
     }
 
     Node body = statement(yieldHandling);
@@ -6408,17 +6338,17 @@ Parser<ParseHandler, CharT>::switchState
     uint32_t begin = pos().begin;
 
     MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_SWITCH);
 
     Node discriminant = exprInParens(InAllowed, yieldHandling, TripledotProhibited);
     if (!discriminant)
         return null();
 
-    MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_SWITCH);
+    MUST_MATCH_TOKEN_MOD(TOK_RP, TokenStream::Operand, JSMSG_PAREN_AFTER_SWITCH);
     MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_SWITCH);
 
     ParseContext::Statement stmt(pc, StatementKind::Switch);
     ParseContext::Scope scope(this);
     if (!scope.init(pc))
         return null();
 
     Node caseList = handler.newStatementList(pos());
@@ -6451,17 +6381,17 @@ Parser<ParseHandler, CharT>::switchState
                 return null();
             break;
 
           default:
             error(JSMSG_BAD_SWITCH);
             return null();
         }
 
-        MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_AFTER_CASE);
+        MUST_MATCH_TOKEN_MOD(TOK_COLON, TokenStream::Operand, JSMSG_COLON_AFTER_CASE);
 
         Node body = handler.newStatementList(pos());
         if (!body)
             return null();
 
         bool afterReturn = false;
         bool warnedAboutStatementsAfterReturn = false;
         uint32_t statementBegin = 0;
@@ -6554,17 +6484,17 @@ Parser<ParseHandler, CharT>::continueSta
             if (foundTarget)
                 break;
         }
     } else if (!pc->findInnermostStatement(isLoop)) {
         error(JSMSG_BAD_CONTINUE);
         return null();
     }
 
-    if (!matchOrInsertSemicolonAfterNonExpression())
+    if (!matchOrInsertSemicolon())
         return null();
 
     return handler.newContinueStatement(label, TokenPos(begin, pos().end));
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 Parser<ParseHandler, CharT>::breakStatement(YieldHandling yieldHandling)
@@ -6594,17 +6524,17 @@ Parser<ParseHandler, CharT>::breakStatem
         };
 
         if (!pc->findInnermostStatement(isBreakTarget)) {
             errorAt(begin, JSMSG_TOUGH_BREAK);
             return null();
         }
     }
 
-    if (!matchOrInsertSemicolonAfterNonExpression())
+    if (!matchOrInsertSemicolon())
         return null();
 
     return handler.newBreakStatement(label, TokenPos(begin, pos().end));
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 Parser<ParseHandler, CharT>::returnStatement(YieldHandling yieldHandling)
@@ -6633,23 +6563,18 @@ Parser<ParseHandler, CharT>::returnState
       default: {
         exprNode = expr(InAllowed, yieldHandling, TripledotProhibited);
         if (!exprNode)
             return null();
         pc->funHasReturnExpr = true;
       }
     }
 
-    if (exprNode) {
-        if (!matchOrInsertSemicolonAfterExpression())
-            return null();
-    } else {
-        if (!matchOrInsertSemicolonAfterNonExpression())
-            return null();
-    }
+    if (!matchOrInsertSemicolon())
+        return null();
 
     return handler.newReturnStatement(exprNode, TokenPos(begin, pos().end));
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 Parser<ParseHandler, CharT>::yieldExpression(InHandling inHandling)
 {
@@ -6721,20 +6646,22 @@ Parser<ParseHandler, CharT>::withStateme
     // mode code, it doesn't even merit a warning in non-strict code.  See
     // https://bugzilla.mozilla.org/show_bug.cgi?id=514576#c1.
     if (pc->sc()->strict()) {
         if (!strictModeError(JSMSG_STRICT_CODE_WITH))
             return null();
     }
 
     MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_WITH);
+
     Node objectExpr = exprInParens(InAllowed, yieldHandling, TripledotProhibited);
     if (!objectExpr)
         return null();
-    MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_WITH);
+
+    MUST_MATCH_TOKEN_MOD(TOK_RP, TokenStream::Operand, JSMSG_PAREN_AFTER_WITH);
 
     Node innerBlock;
     {
         ParseContext::Statement stmt(pc, StatementKind::With);
         innerBlock = statement(yieldHandling);
         if (!innerBlock)
             return null();
     }
@@ -6828,17 +6755,17 @@ Parser<ParseHandler, CharT>::throwStatem
         error(JSMSG_LINE_BREAK_AFTER_THROW);
         return null();
     }
 
     Node throwExpr = expr(InAllowed, yieldHandling, TripledotProhibited);
     if (!throwExpr)
         return null();
 
-    if (!matchOrInsertSemicolonAfterExpression())
+    if (!matchOrInsertSemicolon())
         return null();
 
     return handler.newThrowStatement(throwExpr, TokenPos(begin, pos().end));
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 Parser<ParseHandler, CharT>::tryStatement(YieldHandling yieldHandling)
@@ -6954,25 +6881,25 @@ Parser<ParseHandler, CharT>::tryStatemen
             Node catchGuard = null();
 #if JS_HAS_CATCH_GUARD
             /*
              * We use 'catch (x if x === 5)' (not 'catch (x : x === 5)')
              * to avoid conflicting with the JS2/ECMAv4 type annotation
              * catchguard syntax.
              */
             bool matched;
-            if (!tokenStream.matchToken(&matched, TOK_IF))
+            if (!tokenStream.matchToken(&matched, TOK_IF, TokenStream::Operand))
                 return null();
             if (matched) {
                 catchGuard = expr(InAllowed, yieldHandling, TripledotProhibited);
                 if (!catchGuard)
                     return null();
             }
 #endif
-            MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_CATCH);
+            MUST_MATCH_TOKEN_MOD(TOK_RP, TokenStream::Operand, JSMSG_PAREN_AFTER_CATCH);
 
             MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_CATCH);
 
             Node catchBody = catchBlockStatement(yieldHandling, scope);
             if (!catchBody)
                 return null();
 
             if (!catchGuard)
@@ -7063,17 +6990,17 @@ Parser<ParseHandler, CharT>::catchBlockS
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 Parser<ParseHandler, CharT>::debuggerStatement()
 {
     TokenPos p;
     p.begin = pos().begin;
-    if (!matchOrInsertSemicolonAfterNonExpression())
+    if (!matchOrInsertSemicolon())
         return null();
     p.end = pos().end;
 
     pc->sc()->setBindingsAccessedDynamically();
     pc->sc()->setHasDebuggerStatement();
 
     return handler.newDebuggerStatement(p);
 }
@@ -7362,17 +7289,17 @@ Parser<ParseHandler, CharT>::nextTokenCo
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 Parser<ParseHandler, CharT>::variableStatement(YieldHandling yieldHandling)
 {
     Node vars = declarationList(yieldHandling, PNK_VAR);
     if (!vars)
         return null();
-    if (!matchOrInsertSemicolonAfterExpression())
+    if (!matchOrInsertSemicolon())
         return null();
     return vars;
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 Parser<ParseHandler, CharT>::statement(YieldHandling yieldHandling)
 {
@@ -7789,17 +7716,17 @@ Parser<ParseHandler, CharT>::expr(InHand
                                   InvokedPrediction invoked /* = PredictUninvoked */)
 {
     Node pn = assignExpr(inHandling, yieldHandling, tripledotHandling,
                          possibleError, invoked);
     if (!pn)
         return null();
 
     bool matched;
-    if (!tokenStream.matchToken(&matched, TOK_COMMA))
+    if (!tokenStream.matchToken(&matched, TOK_COMMA, TokenStream::Operand))
         return null();
     if (!matched)
         return pn;
 
     Node seq = handler.newCommaExpressionList(pn);
     if (!seq)
         return null();
     while (true) {
@@ -7819,17 +7746,16 @@ Parser<ParseHandler, CharT>::expr(InHand
                 if (!tokenStream.peekToken(&tt))
                     return null();
                 if (tt != TOK_ARROW) {
                     error(JSMSG_UNEXPECTED_TOKEN, "expression", TokenKindToDesc(TOK_RP));
                     return null();
                 }
 
                 tokenStream.ungetToken();  // put back right paren
-                tokenStream.addModifierException(TokenStream::NoneIsOperand);
                 break;
             }
         }
 
         // Additional calls to assignExpr should not reuse the possibleError
         // which had been passed into the function. Otherwise we would lose
         // information needed to determine whether or not we're dealing with
         // a non-recoverable situation.
@@ -7844,17 +7770,17 @@ Parser<ParseHandler, CharT>::expr(InHand
             if (!possibleErrorInner.checkForExpressionError())
                 return null();
         } else {
             possibleErrorInner.transferErrorsTo(possibleError);
         }
 
         handler.addList(seq, pn);
 
-        if (!tokenStream.matchToken(&matched, TOK_COMMA))
+        if (!tokenStream.matchToken(&matched, TOK_COMMA, TokenStream::Operand))
             return null();
         if (!matched)
             break;
     }
     return seq;
 }
 
 static ParseNodeKind
@@ -7973,17 +7899,21 @@ Parser<ParseHandler, CharT>::orExpr(InHa
             break;
 
         nodeStack[depth] = pn;
         kindStack[depth] = pnk;
         depth++;
         MOZ_ASSERT(depth <= PRECEDENCE_CLASSES);
     }
 
+    // When the next token is no longer a binary operator, it's potentially the
+    // start of an expression.  Add a modifier exception so that the next token
+    // modifier can be Operand.
     tokenStream.ungetToken();
+    tokenStream.addModifierException(TokenStream::OperandIsNone);
 
     MOZ_ASSERT(depth == 0);
     return pn;
 }
 
 template <class ParseHandler, typename CharT>
 MOZ_ALWAYS_INLINE typename ParseHandler::Node
 Parser<ParseHandler, CharT>::condExpr(InHandling inHandling, YieldHandling yieldHandling,
@@ -8148,23 +8078,16 @@ Parser<ParseHandler, CharT>::assignExpr(
         if (!tokenStream.peekTokenSameLine(&next))
             return null();
         MOZ_ASSERT(next == TOK_ARROW || next == TOK_EOL);
 
         if (next != TOK_ARROW) {
             error(JSMSG_LINE_BREAK_BEFORE_ARROW);
             return null();
         }
-        tokenStream.consumeKnownToken(TOK_ARROW);
-
-        bool isBlock = false;
-        if (!tokenStream.peekToken(&next, TokenStream::Operand))
-            return null();
-        if (next == TOK_LC)
-            isBlock = true;
 
         tokenStream.seek(start);
 
         if (!tokenStream.getToken(&next, TokenStream::Operand))
             return null();
         uint32_t toStringStart = pos().begin;
         tokenStream.ungetToken();
 
@@ -8185,63 +8108,31 @@ Parser<ParseHandler, CharT>::assignExpr(
             else
                 tokenStream.ungetToken();
         }
 
         Node pn = handler.newArrowFunction(pos());
         if (!pn)
             return null();
 
-        Node arrowFunc = functionDefinition(pn, toStringStart, inHandling, yieldHandling, nullptr,
-                                            Arrow, NotGenerator, asyncKind);
-        if (!arrowFunc)
-            return null();
-
-        if (isBlock) {
-            // This arrow function could be a non-trailing member of a comma
-            // expression or a semicolon terminating a full expression.  If so,
-            // the next token is that comma/semicolon, gotten with None:
-            //
-            //   a => {}, b; // as if (a => {}), b;
-            //   a => {};
-            //
-            // But if this arrow function ends a statement, ASI permits the
-            // next token to start an expression statement.  In that case the
-            // next token must be gotten as Operand:
-            //
-            //   a => {} // complete expression statement
-            //   /x/g;   // regular expression as a statement, *not* division
-            //
-            // Getting the second case right requires the first token-peek
-            // after the arrow function use Operand, and that peek must occur
-            // before Parser::expr() looks for a comma.  Do so here, then
-            // immediately add the modifier exception needed for the first
-            // case.
-            //
-            // Note that the second case occurs *only* if the arrow function
-            // has block body.  An arrow function not ending in such, ends in
-            // another AssignmentExpression that we can inductively assume was
-            // peeked consistently.
-            TokenKind ignored;
-            if (!tokenStream.peekToken(&ignored, TokenStream::Operand))
-                return null();
-            tokenStream.addModifierException(TokenStream::NoneIsOperand);
-        }
-        return arrowFunc;
+        return functionDefinition(pn, toStringStart, inHandling, yieldHandling, nullptr,
+                                  Arrow, NotGenerator, asyncKind);
       }
 
       default:
         MOZ_ASSERT(!tokenStream.isCurrentTokenAssignment());
         if (!possibleError) {
             if (!possibleErrorInner.checkForExpressionError())
                 return null();
         } else {
             possibleErrorInner.transferErrorsTo(possibleError);
         }
+
         tokenStream.ungetToken();
+        tokenStream.addModifierException(TokenStream::OperandIsNone);
         return lhs;
     }
 
     // Verify the left-hand side expression doesn't have a forbidden form.
     if (handler.isUnparenthesizedDestructuringPattern(lhs)) {
         if (kind != PNK_ASSIGN) {
             error(JSMSG_BAD_DESTRUCT_ASS);
             return null();
@@ -8531,17 +8422,17 @@ Parser<ParseHandler, CharT>::generatorCo
     Node body = handler.newStatementList(TokenPos(begin, pos().end));
     if (!body)
         return null();
 
     Node comp = comprehension(StarGenerator);
     if (!comp)
         return null();
 
-    MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_IN_PAREN);
+    MUST_MATCH_TOKEN_MOD(TOK_RP, TokenStream::Operand, JSMSG_PAREN_IN_PAREN);
 
     uint32_t end = pos().end;
     handler.setBeginPosition(comp, begin);
     handler.setEndPosition(comp, end);
     genFunbox->setEnd(tokenStream);
     handler.addStatementToList(body, comp);
     handler.setEndPosition(body, end);
     handler.setBeginPosition(genfn, begin);
@@ -8600,17 +8491,17 @@ Parser<ParseHandler, CharT>::comprehensi
         error(JSMSG_OF_AFTER_FOR_NAME);
         return null();
     }
 
     Node rhs = assignExpr(InAllowed, YieldIsKeyword, TripledotProhibited);
     if (!rhs)
         return null();
 
-    MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_OF_ITERABLE);
+    MUST_MATCH_TOKEN_MOD(TOK_RP, TokenStream::Operand, JSMSG_PAREN_AFTER_FOR_OF_ITERABLE);
 
     TokenPos headPos(begin, pos().end);
 
     ParseContext::Scope scope(this);
     if (!scope.init(pc))
         return null();
 
     {
@@ -8649,17 +8540,17 @@ Parser<ParseHandler, CharT>::comprehensi
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_IF));
 
     uint32_t begin = pos().begin;
 
     MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_COND);
     Node cond = assignExpr(InAllowed, YieldIsKeyword, TripledotProhibited);
     if (!cond)
         return null();
-    MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_COND);
+    MUST_MATCH_TOKEN_MOD(TOK_RP, TokenStream::Operand, JSMSG_PAREN_AFTER_COND);
 
     /* Check for (a = b) and warn about possible (a == b) mistype. */
     if (handler.isUnparenthesizedAssignment(cond)) {
         if (!extraWarning(JSMSG_EQUAL_AS_ASSIGN))
             return null();
     }
 
     Node then = comprehensionTail(comprehensionKind);
@@ -8730,17 +8621,17 @@ Parser<ParseHandler, CharT>::comprehensi
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 Parser<ParseHandler, CharT>::arrayComprehension(uint32_t begin)
 {
     Node inner = comprehension(NotGenerator);
     if (!inner)
         return null();
 
-    MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_AFTER_ARRAY_COMPREHENSION);
+    MUST_MATCH_TOKEN_MOD(TOK_RB, TokenStream::Operand, JSMSG_BRACKET_AFTER_ARRAY_COMPREHENSION);
 
     Node comp = handler.newList(PNK_ARRAYCOMP, inner);
     if (!comp)
         return null();
 
     handler.setBeginPosition(comp, begin);
     handler.setEndPosition(comp, pos().end);
 
@@ -8940,17 +8831,17 @@ Parser<ParseHandler, CharT>::memberExpr(
                 error(JSMSG_NAME_AFTER_DOT);
                 return null();
             }
         } else if (tt == TOK_LB) {
             Node propExpr = expr(InAllowed, yieldHandling, TripledotProhibited);
             if (!propExpr)
                 return null();
 
-            MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_IN_INDEX);
+            MUST_MATCH_TOKEN_MOD(TOK_RB, TokenStream::Operand, JSMSG_BRACKET_IN_INDEX);
 
             if (handler.isSuperBase(lhs) && !checkAndMarkSuperScope()) {
                 error(JSMSG_BAD_SUPERPROP, "member");
                 return null();
             }
             nextMember = handler.newPropertyByValue(lhs, propExpr, pos().end);
             if (!nextMember)
                 return null();
@@ -9446,35 +9337,36 @@ Parser<ParseHandler, CharT>::arrayInitia
         /*
          * Mark empty arrays as non-constant, since we cannot easily
          * determine their type.
          */
         handler.setListFlag(literal, PNX_NONCONST);
     } else {
         tokenStream.ungetToken();
 
-        uint32_t index = 0;
-        TokenStream::Modifier modifier = TokenStream::Operand;
-        for (; ; index++) {
+        for (uint32_t index = 0; ; index++) {
             if (index >= NativeObject::MAX_DENSE_ELEMENTS_COUNT) {
                 error(JSMSG_ARRAY_INIT_TOO_BIG);
                 return null();
             }
 
             TokenKind tt;
             if (!tokenStream.peekToken(&tt, TokenStream::Operand))
                 return null();
             if (tt == TOK_RB)
                 break;
 
             if (tt == TOK_COMMA) {
                 tokenStream.consumeKnownToken(TOK_COMMA, TokenStream::Operand);
                 if (!handler.addElision(literal, pos()))
                     return null();
-            } else if (tt == TOK_TRIPLEDOT) {
+                continue;
+            }
+
+            if (tt == TOK_TRIPLEDOT) {
                 tokenStream.consumeKnownToken(TOK_TRIPLEDOT, TokenStream::Operand);
                 uint32_t begin = pos().begin;
 
                 TokenPos innerPos;
                 if (!tokenStream.peekTokenPos(&innerPos, TokenStream::Operand))
                     return null();
 
                 PossibleError possibleErrorInner(*this);
@@ -9505,34 +9397,31 @@ Parser<ParseHandler, CharT>::arrayInitia
                 {
                     return null();
                 }
                 if (foldConstants && !FoldConstants(context, &element, this))
                     return null();
                 handler.addArrayElement(literal, element);
             }
 
-            if (tt != TOK_COMMA) {
-                /* If we didn't already match TOK_COMMA in above case. */
-                bool matched;
-                if (!tokenStream.matchToken(&matched, TOK_COMMA))
-                    return null();
-                if (!matched) {
-                    modifier = TokenStream::None;
-                    break;
-                }
-                if (tt == TOK_TRIPLEDOT && possibleError)
-                    possibleError->setPendingDestructuringErrorAt(pos(), JSMSG_REST_WITH_COMMA);
-            }
-        }
-
-        MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RB, modifier,
+            bool matched;
+            if (!tokenStream.matchToken(&matched, TOK_COMMA, TokenStream::Operand))
+                return null();
+            if (!matched)
+                break;
+
+            if (tt == TOK_TRIPLEDOT && possibleError)
+                possibleError->setPendingDestructuringErrorAt(pos(), JSMSG_REST_WITH_COMMA);
+        }
+
+        MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RB, TokenStream::Operand,
                                          reportMissingClosing(JSMSG_BRACKET_AFTER_LIST,
                                                               JSMSG_BRACKET_OPENED, begin));
     }
+
     handler.setEndPosition(literal, pos().end);
     return literal;
 }
 
 static JSAtom*
 DoubleToAtom(JSContext* cx, double value)
 {
     // This is safe because doubles can not be moved.
@@ -9733,16 +9622,18 @@ Parser<ParseHandler, CharT>::propertyNam
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 Parser<ParseHandler, CharT>::computedPropertyName(YieldHandling yieldHandling,
                                                   const Maybe<DeclarationKind>& maybeDecl,
                                                   Node literal)
 {
+    MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LB));
+
     uint32_t begin = pos().begin;
 
     if (maybeDecl) {
         if (*maybeDecl == DeclarationKind::FormalParameter)
             pc->functionBox()->hasParameterExprs = true;
     } else {
         handler.setListFlag(literal, PNX_NONCONST);
     }
@@ -10112,17 +10003,17 @@ Parser<ParseHandler, CharT>::primaryExpr
       case TOK_LP: {
         TokenKind next;
         if (!tokenStream.peekToken(&next, TokenStream::Operand))
             return null();
 
         if (next == TOK_RP) {
             // Not valid expression syntax, but this is valid in an arrow function
             // with no params: `() => body`.
-            tokenStream.consumeKnownToken(next, TokenStream::Operand);
+            tokenStream.consumeKnownToken(TOK_RP, TokenStream::Operand);
 
             if (!tokenStream.peekToken(&next))
                 return null();
             if (next != TOK_ARROW) {
                 error(JSMSG_UNEXPECTED_TOKEN, "expression", TokenKindToDesc(TOK_RP));
                 return null();
             }
 
@@ -10137,17 +10028,17 @@ Parser<ParseHandler, CharT>::primaryExpr
             tokenStream.consumeKnownToken(next, TokenStream::Operand);
             return generatorComprehension(begin);
         }
 
         // Pass |possibleError| to support destructuring in arrow parameters.
         Node expr = exprInParens(InAllowed, yieldHandling, TripledotAllowed, possibleError);
         if (!expr)
             return null();
-        MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_IN_PAREN);
+        MUST_MATCH_TOKEN_MOD(TOK_RP, TokenStream::Operand, JSMSG_PAREN_IN_PAREN);
         return handler.parenthesize(expr);
       }
 
       case TOK_TEMPLATE_HEAD:
         return templateLiteral(yieldHandling);
 
       case TOK_NO_SUBS_TEMPLATE:
         return noSubstitutionUntaggedTemplate();
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -834,16 +834,18 @@ class Parser final : public ParserBase, 
     PropertyName* importedBinding() {
         return bindingIdentifier(YieldIsName);
     }
 
     Node identifierReference(Handle<PropertyName*> name);
 
     bool matchLabel(YieldHandling yieldHandling, MutableHandle<PropertyName*> label);
 
+    // Indicate if the next token (tokenized as Operand) is |in| or |of|.  If
+    // so, consume it.
     bool matchInOrOf(bool* isForInp, bool* isForOfp);
 
     bool hasUsedFunctionSpecialName(HandlePropertyName name);
     bool declareFunctionArgumentsObject();
     bool declareFunctionThis();
     Node newInternalDotName(HandlePropertyName name);
     Node newThisName();
     Node newDotGeneratorName();
@@ -861,19 +863,17 @@ class Parser final : public ParserBase, 
                                      FunctionSyntaxKind kind,
                                      GeneratorKind generatorKind, FunctionAsyncKind asyncKind,
                                      bool tryAnnexB,
                                      Directives inheritedDirectives, Directives* newDirectives);
     bool finishFunctionScopes(bool isStandaloneFunction);
     bool finishFunction(bool isStandaloneFunction = false);
     bool leaveInnerFunction(ParseContext* outerpc);
 
-    bool matchOrInsertSemicolonHelper(TokenStream::Modifier modifier);
-    bool matchOrInsertSemicolonAfterExpression();
-    bool matchOrInsertSemicolonAfterNonExpression();
+    bool matchOrInsertSemicolon();
 
   public:
     enum FunctionCallBehavior {
         PermitAssignmentToFunctionCalls,
         ForbidAssignmentToFunctionCalls
     };
 
     bool isValidSimpleAssignmentTarget(Node node,
--- a/js/src/frontend/TokenStream.h
+++ b/js/src/frontend/TokenStream.h
@@ -376,18 +376,26 @@ class TokenStreamAnyChars: public ErrorR
     typedef Token::ModifierException ModifierException;
     static constexpr ModifierException NoException = Token::NoException;
     static constexpr ModifierException NoneIsOperand = Token::NoneIsOperand;
     static constexpr ModifierException OperandIsNone = Token::OperandIsNone;
 
     void addModifierException(ModifierException modifierException) {
 #ifdef DEBUG
         const Token& next = nextToken();
-        if (next.modifierException == NoneIsOperand)
-        {
+
+        // Permit adding the same exception multiple times.  This is important
+        // particularly for Parser::assignExpr's early fast-path cases and
+        // arrow function parsing: we want to add modifier exceptions in the
+        // fast paths, then potentially (but not necessarily) duplicate them
+        // after parsing all of an arrow function.
+        if (next.modifierException == modifierException)
+            return;
+
+        if (next.modifierException == NoneIsOperand) {
             // Token after yield expression without operand already has
             // NoneIsOperand exception.
             MOZ_ASSERT(modifierException == OperandIsNone);
             MOZ_ASSERT(next.type != TOK_DIV,
                        "next token requires contextual specifier to be parsed unambiguously");
 
             // Do not update modifierException.
             return;
@@ -880,17 +888,25 @@ class MOZ_STACK_CLASS TokenStream final 
         MOZ_ALWAYS_TRUE(matchToken(&matched, tt, modifier));
         MOZ_ALWAYS_TRUE(matched);
     }
 
     MOZ_MUST_USE bool nextTokenEndsExpr(bool* endsExpr) {
         TokenKind tt;
         if (!peekToken(&tt))
             return false;
+
         *endsExpr = isExprEnding[tt];
+        if (*endsExpr) {
+            // If the next token ends an overall Expression, we'll parse this
+            // Expression without ever invoking Parser::orExpr().  But we need
+            // that function's side effect of adding this modifier exception,
+            // so we have to do it manually here.
+            addModifierException(OperandIsNone);
+        }
         return true;
     }
 
     class MOZ_STACK_CLASS Position {
       public:
         // The Token fields may contain pointers to atoms, so for correct
         // rooting we must ensure collection of atoms is disabled while objects
         // of this class are live.  Do this by requiring a dummy AutoKeepAtoms
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/ArrowFunctions/arrow-not-as-end-of-statement.js
@@ -0,0 +1,67 @@
+/* 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/. */
+
+// Grab-bag of ArrowFunctions with block bodies appearing in contexts where the
+// subsequent token-examination must use the Operand modifier to avoid an
+// assertion.
+
+assertEq(`x ${a => {}} z`, "x a => {} z");
+
+for (; a => {}; )
+  break;
+
+for (; ; a => {})
+  break;
+
+switch (1)
+{
+  case a => {}:
+   break;
+}
+
+try
+{
+  // Catch guards are non-standard, so ignore a syntax error.
+  eval(`try
+  {
+  }
+  catch (x if a => {})
+  {
+  }`);
+}
+catch (e)
+{
+  assertEq(e instanceof SyntaxError, true,
+           "should only have thrown SyntaxError, instead got " + e);
+}
+
+assertEq(0[a => {}], undefined);
+
+class Y {};
+class X extends Y { constructor() { super[a => {}](); } };
+
+if (a => {})
+  assertEq(true, true);
+else
+  assertEq(true, false);
+
+switch (a => {})
+{
+}
+
+with (a => {});
+
+assertEq(typeof (a => {}), "function");
+
+for (var x in y => {})
+  continue;
+
+var z = { x: 0 ? 1 : async a => {} };
+assertEq(typeof z.x, "function");
+
+var q = 0 ? 1 : async () => {};
+assertEq(typeof q, "function");
+
+if (typeof reportCompare === "function")
+  reportCompare(true, true);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/ArrowFunctions/arrow-returning-arrow-with-block-body-followed-by-regexp.js
@@ -0,0 +1,15 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+function t()
+{
+  var x = y => z => {} // ASI occurs here
+  /Q/;
+  return 42;
+}
+
+assertEq(t(), 42);
+
+if (typeof reportCompare === "function")
+  reportCompare(true, true);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/extensions/too-many-arguments-constructing-bound-function.js
@@ -0,0 +1,54 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+var gTestfile = "too-many-arguments-constructing-bound-function.js";
+//-----------------------------------------------------------------------------
+var BUGNUMBER = 1303678;
+var summary =
+  "Don't assert trying to construct a bound function whose bound-target " +
+  "construct operation passes more than ARGS_LENGTH_MAX arguments";
+
+print(BUGNUMBER + ": " + summary);
+
+/**************
+ * BEGIN TEST *
+ **************/
+
+const ARGS_LENGTH_MAX = typeof getMaxArgs === "function"
+                      ? getMaxArgs()
+                      : 500000;
+
+const halfRoundedDown = Math.floor(ARGS_LENGTH_MAX / 2);
+const halfRoundedUp = Math.ceil(ARGS_LENGTH_MAX / 2);
+
+function boundTarget()
+{
+  return new Number(arguments.length);
+}
+
+var boundArgs = Array(halfRoundedDown).fill(0);
+var boundFunction = boundTarget.bind(null, ...boundArgs);
+
+var additionalArgs = Array(halfRoundedUp + 1).fill(0);
+
+try
+{
+  assertEq(new (boundFunction)(...additionalArgs).valueOf(),
+           ARGS_LENGTH_MAX + 1);
+  // If we reach here, that's fine -- it's okay if ARGS_LENGTH_MAX isn't
+  // precisely respected, because there's no specified exact limit.
+}
+catch (e)
+{
+  assertEq(e instanceof RangeError, true,
+           "SpiderMonkey throws RangeError for too many args");
+}
+
+/******************************************************************************/
+
+if (typeof reportCompare === "function")
+  reportCompare(true, true);
+
+print("Tests complete");
--- a/layout/forms/nsTextControlFrame.cpp
+++ b/layout/forms/nsTextControlFrame.cpp
@@ -11,17 +11,16 @@
 #include "nsFontMetrics.h"
 #include "nsTextControlFrame.h"
 #include "nsIPlaintextEditor.h"
 #include "nsCaret.h"
 #include "nsCSSPseudoElements.h"
 #include "nsGenericHTMLElement.h"
 #include "nsIEditor.h"
 #include "nsTextFragment.h"
-#include "nsIDOMHTMLTextAreaElement.h"
 #include "nsNameSpaceManager.h"
 #include "nsCheckboxRadioFrame.h" //for registering accesskeys
 
 #include "nsIContent.h"
 #include "nsPresContext.h"
 #include "nsGkAtoms.h"
 #include "nsLayoutUtils.h"
 #include "nsIDOMElement.h"
@@ -1157,32 +1156,30 @@ nsTextControlFrame::AttributeChanged(int
   }
 
   // Allow the base class to handle common attributes supported by all form
   // elements...
   return nsContainerFrame::AttributeChanged(aNameSpaceID, aAttribute, aModType);
 }
 
 
-nsresult
+void
 nsTextControlFrame::GetText(nsString& aText)
 {
-  nsresult rv = NS_OK;
   nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
   NS_ASSERTION(txtCtrl, "Content not a text control element");
   if (IsSingleLineTextControl()) {
     // There will be no line breaks so we can ignore the wrap property.
     txtCtrl->GetTextEditorValue(aText, true);
   } else {
-    nsCOMPtr<nsIDOMHTMLTextAreaElement> textArea = do_QueryInterface(mContent);
+    HTMLTextAreaElement* textArea = HTMLTextAreaElement::FromContent(mContent);
     if (textArea) {
-      rv = textArea->GetValue(aText);
+      textArea->GetValue(aText);
     }
   }
-  return rv;
 }
 
 
 ///END NSIFRAME OVERLOADS
 /////BEGIN PROTECTED METHODS
 
 bool
 nsTextControlFrame::GetMaxLength(int32_t* aSize)
--- a/layout/forms/nsTextControlFrame.h
+++ b/layout/forms/nsTextControlFrame.h
@@ -164,17 +164,17 @@ public:
 
 //==== OVERLOAD of nsIFrame
 
   /** handler for attribute changes to mContent */
   virtual nsresult AttributeChanged(int32_t         aNameSpaceID,
                                     nsAtom*        aAttribute,
                                     int32_t         aModType) override;
 
-  nsresult GetText(nsString& aText);
+  void GetText(nsString& aText);
 
   virtual nsresult PeekOffset(nsPeekOffsetStruct *aPos) override;
 
   NS_DECL_QUERYFRAME
 
 protected:
   /**
    * Launch the reflow on the child frames - see nsTextControlFrame::Reflow()
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -21,30 +21,30 @@
 #include "nsNameSpaceManager.h"
 #include "nsIDocumentInlines.h"
 #include "nsFontMetrics.h"
 #include "nsBoxLayoutState.h"
 #include "mozilla/dom/NodeInfo.h"
 #include "nsScrollbarFrame.h"
 #include "nsIScrollbarMediator.h"
 #include "nsITextControlFrame.h"
-#include "nsIDOMHTMLTextAreaElement.h"
 #include "nsNodeInfoManager.h"
 #include "nsContentCreatorFunctions.h"
 #include "nsPresState.h"
 #include "nsIHTMLDocument.h"
 #include "nsContentUtils.h"
 #include "nsLayoutUtils.h"
 #include "nsBidiPresUtils.h"
 #include "nsBidiUtils.h"
 #include "mozilla/ContentEvents.h"
 #include "mozilla/EventDispatcher.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/LookAndFeel.h"
 #include "mozilla/dom/Element.h"
+#include "mozilla/dom/HTMLTextAreaElement.h"
 #include <stdint.h>
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/Telemetry.h"
 #include "FrameLayerBuilder.h"
 #include "nsSMILKeySpline.h"
 #include "nsSubDocumentFrame.h"
 #include "nsSVGOuterSVGFrame.h"
 #include "nsIObjectLoadingContent.h"
@@ -4497,17 +4497,18 @@ ScrollFrameHelper::CreateAnonymousConten
     canHaveHorizontal = true;
     canHaveVertical = true;
   }
 
   // The anonymous <div> used by <inputs> never gets scrollbars.
   nsITextControlFrame* textFrame = do_QueryFrame(parent);
   if (textFrame) {
     // Make sure we are not a text area.
-    nsCOMPtr<nsIDOMHTMLTextAreaElement> textAreaElement(do_QueryInterface(parent->GetContent()));
+    HTMLTextAreaElement* textAreaElement =
+      HTMLTextAreaElement::FromContent(parent->GetContent());
     if (!textAreaElement) {
       mNeverHasVerticalScrollbar = mNeverHasHorizontalScrollbar = true;
       return NS_OK;
     }
   }
 
   nsNodeInfoManager *nodeInfoManager =
     presContext->Document()->NodeInfoManager();
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -2590,16 +2590,17 @@ already_AddRefed<LayerManager> nsDisplay
 uint32_t nsDisplayList::Count() const {
   return mLength;
 }
 
 nsDisplayItem* nsDisplayList::RemoveBottom() {
   nsDisplayItem* item = mSentinel.mAbove;
   if (!item)
     return nullptr;
+  MOZ_DIAGNOSTIC_ASSERT(item->mSentinel == 0xDEADBEEFDEADBEEF);
   mSentinel.mAbove = item->mAbove;
   if (item == mTop) {
     // must have been the only item
     mTop = &mSentinel;
   }
   item->mAbove = nullptr;
   mLength--;
   return item;
--- a/layout/painting/nsDisplayList.h
+++ b/layout/painting/nsDisplayList.h
@@ -1819,16 +1819,17 @@ class nsDisplayList;
  * highest in z-order.
  */
 class nsDisplayItemLink {
   // This is never instantiated directly, so no need to count constructors and
   // destructors.
 protected:
   nsDisplayItemLink() : mAbove(nullptr) {}
   nsDisplayItemLink(const nsDisplayItemLink&) : mAbove(nullptr) {}
+  uint64_t mSentinel = 0xDEADBEEFDEADBEEF;
   nsDisplayItem* mAbove;
 
   friend class nsDisplayList;
 };
 
 class nsDisplayWrapList;
 
 /**
--- a/layout/style/ServoStyleSet.cpp
+++ b/layout/style/ServoStyleSet.cpp
@@ -335,17 +335,16 @@ ServoStyleSet::EndUpdate()
   return NS_OK;
 }
 
 already_AddRefed<ServoStyleContext>
 ServoStyleSet::ResolveStyleFor(Element* aElement,
                                ServoStyleContext* aParentContext,
                                LazyComputeBehavior aMayCompute)
 {
-  RefPtr<ServoStyleContext> computedValues;
   if (aMayCompute == LazyComputeBehavior::Allow) {
     PreTraverseSync();
     return ResolveStyleLazilyInternal(
         aElement, CSSPseudoElementType::NotPseudo);
   }
 
   return ResolveServoStyle(aElement);
 }
--- a/mobile/android/base/java/org/mozilla/gecko/GeckoAccessibility.java
+++ b/mobile/android/base/java/org/mozilla/gecko/GeckoAccessibility.java
@@ -5,16 +5,17 @@
 
 package org.mozilla.gecko;
 
 import org.json.JSONException;
 import org.json.JSONObject;
 import org.mozilla.gecko.util.GeckoBundle;
 import org.mozilla.gecko.util.ThreadUtils;
 
+import android.annotation.TargetApi;
 import android.content.Context;
 import android.graphics.Rect;
 import android.os.Build;
 import android.os.Bundle;
 import android.util.Log;
 import android.view.View;
 import android.view.ViewParent;
 import android.view.accessibility.AccessibilityEvent;
@@ -231,16 +232,17 @@ public class GeckoAccessibility {
         view.setOnFocusChangeListener(new View.OnFocusChangeListener() {
             @Override
             public void onFocusChange(final View v, final boolean hasFocus) {
                 onLayerViewFocusChanged(hasFocus);
             }
         });
     }
 
+    @TargetApi(19)
     public static void setAccessibilityManagerListeners(final Context context) {
         AccessibilityManager accessibilityManager =
             (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE);
 
         accessibilityManager.addAccessibilityStateChangeListener(new AccessibilityManager.AccessibilityStateChangeListener() {
             @Override
             public void onAccessibilityStateChanged(boolean enabled) {
                 updateAccessibilitySettings(context);
--- a/mobile/android/modules/ActionBarHandler.jsm
+++ b/mobile/android/modules/ActionBarHandler.jsm
@@ -203,17 +203,17 @@ var ActionBarHandler = {
     let [element, win] = [Services.focus.focusedElement, Services.focus.focusedWindow];
     if (!element) {
       // No focused editable.
       return [null, win];
     }
 
     // Return focused editable text element and its window.
     if (((element instanceof Ci.nsIDOMHTMLInputElement) && element.mozIsTextField(false)) ||
-        (element instanceof Ci.nsIDOMHTMLTextAreaElement) ||
+        (ChromeUtils.getClassName(element) === "HTMLTextAreaElement") ||
         element.isContentEditable) {
       return [element, win];
     }
 
     // Focused element can't contain text.
     return [null, win];
   },
 
@@ -738,17 +738,17 @@ var ActionBarHandler = {
     if (!this._selectionID) {
       return "";
     }
 
     let selection = this._getSelection();
 
     // Textarea can contain LF, etc.
     if (this._targetElement &&
-        this._targetElement instanceof Ci.nsIDOMHTMLTextAreaElement) {
+        ChromeUtils.getClassName(this._targetElement) === "HTMLTextAreaElement") {
       let flags = Ci.nsIDocumentEncoder.OutputPreformatted |
         Ci.nsIDocumentEncoder.OutputRaw;
       return selection.QueryInterface(Ci.nsISelectionPrivate).
         toStringWithFormat("text/plain", flags, 0);
     }
 
     // Return explicitly selected text.
     return selection.toString();
--- a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/ContentContextMenuTest.java
+++ b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/ContentContextMenuTest.java
@@ -76,18 +76,17 @@ abstract class ContentContextMenuTest ex
     protected void verifyCopyOption(String copyOption, final String copiedText) {
         if (!mSolo.searchText(copyOption)) {
             openWebContentContextMenu(copyOption); // Open the context menu if it is not already
         }
         mSolo.clickOnText(copyOption);
         boolean correctText = waitForCondition(new Condition() {
             @Override
             public boolean isSatisfied() {
-                final String clipboardText =
-                        Clipboard.getText(getInstrumentation().getContext());
+                final String clipboardText = Clipboard.getText(getActivity());
                 mAsserter.dumpLog("Clipboard text = " + clipboardText + " , expected text = " + copiedText);
                 return clipboardText.contains(copiedText);
             }
         }, MAX_TEST_TIMEOUT);
         mAsserter.ok(correctText, "Checking if the text is correctly copied", "The text was correctly copied");
     }
 
 
--- a/mobile/android/tests/browser/robocop/testAccessibleCarets.js
+++ b/mobile/android/tests/browser/robocop/testAccessibleCarets.js
@@ -40,18 +40,21 @@ function do_promiseTabChangeEvent(tabId,
   });
 }
 
 /**
  * Selection methods vary if we have an input / textarea element,
  * or if we have basic content.
  */
 function isInputOrTextarea(element) {
+  // ChromeUtils isn't included in robocop tests, so we have to use a different
+  // way to test elements.
   return ((element instanceof Ci.nsIDOMHTMLInputElement) ||
-          (element instanceof Ci.nsIDOMHTMLTextAreaElement));
+          (element.localName === "textarea" &&
+           element.namespaceURI === "http://www.w3.org/1999/xhtml"));
 }
 
 /**
  * Return the selection controller based on element.
  */
 function elementSelection(element) {
   return (isInputOrTextarea(element)) ?
     element.editor.selection :
--- a/modules/libpref/Preferences.cpp
+++ b/modules/libpref/Preferences.cpp
@@ -521,43 +521,16 @@ SetPrefValue(const char* aPrefName,
     case dom::PrefValue::Tbool:
       return PREF_SetBoolPref(aPrefName, aValue.get_bool(), setDefault);
 
     default:
       MOZ_CRASH();
   }
 }
 
-static nsresult
-pref_SetPref(const dom::PrefSetting& aPref)
-{
-  const char* prefName = aPref.name().get();
-  const dom::MaybePrefValue& defaultValue = aPref.defaultValue();
-  const dom::MaybePrefValue& userValue = aPref.userValue();
-
-  nsresult rv;
-  if (defaultValue.type() == dom::MaybePrefValue::TPrefValue) {
-    rv = SetPrefValue(prefName, defaultValue.get_PrefValue(), DEFAULT_VALUE);
-    if (NS_FAILED(rv)) {
-      return rv;
-    }
-  }
-
-  if (userValue.type() == dom::MaybePrefValue::TPrefValue) {
-    rv = SetPrefValue(prefName, userValue.get_PrefValue(), USER_VALUE);
-  } else {
-    rv = PREF_ClearUserPref(prefName);
-  }
-
-  // NB: we should never try to clear a default value, that doesn't
-  // make sense
-
-  return rv;
-}
-
 static PrefSaveData
 pref_savePrefs()
 {
   PrefSaveData savedPrefs(gHashTable->EntryCount());
 
   for (auto iter = gHashTable->Iter(); !iter.Done(); iter.Next()) {
     auto pref = static_cast<PrefHashEntry*>(iter.Get());
 
@@ -688,106 +661,100 @@ PREF_HasUserPref(const char* aPrefName)
   return pref && pref->mPrefFlags.HasUserValue();
 }
 
 static nsresult
 PREF_GetCStringPref(const char* aPrefName,
                     nsACString& aValueOut,
                     bool aGetDefault)
 {
+  aValueOut.SetIsVoid(true);
+
   if (!gHashTable) {
     return NS_ERROR_NOT_INITIALIZED;
   }
 
+  PrefHashEntry* pref = pref_HashTableLookup(aPrefName);
+  if (!pref || !pref->mPrefFlags.IsTypeString()) {
+    return NS_ERROR_UNEXPECTED;
+  }
+
   const char* stringVal = nullptr;
-  PrefHashEntry* pref = pref_HashTableLookup(aPrefName);
-
-  if (pref && pref->mPrefFlags.IsTypeString()) {
-    if (aGetDefault || pref->mPrefFlags.IsLocked() ||
-        !pref->mPrefFlags.HasUserValue()) {
-      stringVal = pref->mDefaultPref.mStringVal;
-    } else {
-      stringVal = pref->mUserPref.mStringVal;
+  if (aGetDefault || pref->mPrefFlags.IsLocked() ||
+      !pref->mPrefFlags.HasUserValue()) {
+
+    // Do we have a default?
+    if (!pref->mPrefFlags.HasDefault()) {
+      return NS_ERROR_UNEXPECTED;
     }
+    stringVal = pref->mDefaultPref.mStringVal;
+  } else {
+    stringVal = pref->mUserPref.mStringVal;
   }
 
   if (!stringVal) {
-    aValueOut.SetIsVoid(true);
     return NS_ERROR_UNEXPECTED;
   }
 
   aValueOut = stringVal;
   return NS_OK;
 }
 
-// Get an int preference. This function takes a dotted notation of the
-// preference name (e.g. "browser.startup.homepage")
-//
-// It also takes a pointer to fill in with the return value and return an error
-// value. At the moment, this is simply an int but it may be converted to an
-// enum once the global error strategy is worked out.
-//
-// This function will perform conversion if the type doesn't match what was
-// requested. (If it is reasonably possible.)
 static nsresult
 PREF_GetIntPref(const char* aPrefName, int32_t* aValueOut, bool aGetDefault)
 {
   if (!gHashTable) {
     return NS_ERROR_NOT_INITIALIZED;
   }
 
-  nsresult rv = NS_ERROR_UNEXPECTED;
   PrefHashEntry* pref = pref_HashTableLookup(aPrefName);
-  if (pref && pref->mPrefFlags.IsTypeInt()) {
-    if (aGetDefault || pref->mPrefFlags.IsLocked() ||
-        !pref->mPrefFlags.HasUserValue()) {
-      int32_t tempInt = pref->mDefaultPref.mIntVal;
-
-      // Check to see if we even had a default.
-      if (!pref->mPrefFlags.HasDefault()) {
-        return NS_ERROR_UNEXPECTED;
-      }
-      *aValueOut = tempInt;
-    } else {
-      *aValueOut = pref->mUserPref.mIntVal;
+  if (!pref || !pref->mPrefFlags.IsTypeInt()) {
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  if (aGetDefault || pref->mPrefFlags.IsLocked() ||
+      !pref->mPrefFlags.HasUserValue()) {
+
+    // Do we have a default?
+    if (!pref->mPrefFlags.HasDefault()) {
+      return NS_ERROR_UNEXPECTED;
     }
-    rv = NS_OK;
-  }
-
-  return rv;
+    *aValueOut = pref->mDefaultPref.mIntVal;
+  } else {
+    *aValueOut = pref->mUserPref.mIntVal;
+  }
+
+  return NS_OK;
 }
 
-// Like PREF_GetIntPref(), but for booleans.
 static nsresult
 PREF_GetBoolPref(const char* aPrefName, bool* aValueOut, bool aGetDefault)
 {
   if (!gHashTable) {
     return NS_ERROR_NOT_INITIALIZED;
   }
 
-  nsresult rv = NS_ERROR_UNEXPECTED;
   PrefHashEntry* pref = pref_HashTableLookup(aPrefName);
-  //NS_ASSERTION(pref, aPrefName);
-  if (pref && pref->mPrefFlags.IsTypeBool()) {
-    if (aGetDefault || pref->mPrefFlags.IsLocked() ||
-        !pref->mPrefFlags.HasUserValue()) {
-      bool tempBool = pref->mDefaultPref.mBoolVal;
-
-      // Check to see if we even had a default.
-      if (pref->mPrefFlags.HasDefault()) {
-        *aValueOut = tempBool;
-        rv = NS_OK;
-      }
-    } else {
-      *aValueOut = pref->mUserPref.mBoolVal;
-      rv = NS_OK;
+  if (!pref || !pref->mPrefFlags.IsTypeBool()) {
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  if (aGetDefault || pref->mPrefFlags.IsLocked() ||
+      !pref->mPrefFlags.HasUserValue()) {
+
+    // Do we have a default?
+    if (!pref->mPrefFlags.HasDefault()) {
+      return NS_ERROR_UNEXPECTED;
     }
-  }
-
-  return rv;
+    *aValueOut = pref->mDefaultPref.mBoolVal;
+  } else {
+    *aValueOut = pref->mUserPref.mBoolVal;
+  }
+
+  return NS_OK;
 }
 
 // Clears the given pref (reverts it to its default value).
 static nsresult
 PREF_ClearUserPref(const char* aPrefName)
 {
   if (!gHashTable) {
     return NS_ERROR_NOT_INITIALIZED;
@@ -4123,17 +4090,36 @@ ReadExtensionPrefs(nsIFile* aFile)
   }
 
   return rv;
 }
 
 void
 Preferences::SetPreference(const PrefSetting& aPref)
 {
-  pref_SetPref(aPref);
+  const char* prefName = aPref.name().get();
+  const dom::MaybePrefValue& defaultValue = aPref.defaultValue();
+  const dom::MaybePrefValue& userValue = aPref.userValue();
+
+  if (defaultValue.type() == dom::MaybePrefValue::TPrefValue) {
+    nsresult rv =
+      SetPrefValue(prefName, defaultValue.get_PrefValue(), DEFAULT_VALUE);
+    if (NS_FAILED(rv)) {
+      return;
+    }
+  }
+
+  if (userValue.type() == dom::MaybePrefValue::TPrefValue) {
+    SetPrefValue(prefName, userValue.get_PrefValue(), USER_VALUE);
+  } else {
+    PREF_ClearUserPref(prefName);
+  }
+
+  // NB: we should never try to clear a default value, that doesn't
+  // make sense
 }
 
 void
 Preferences::GetPreference(PrefSetting* aPref)
 {
   PrefHashEntry* entry = pref_HashTableLookup(aPref->name().get());
   if (!entry) {
     return;
--- a/netwerk/wifi/moz.build
+++ b/netwerk/wifi/moz.build
@@ -12,21 +12,16 @@ XPIDL_SOURCES += [
 
 XPIDL_MODULE = 'necko_wifi'
 
 UNIFIED_SOURCES += [
     'nsWifiAccessPoint.cpp',
     'nsWifiMonitor.cpp',
 ]
 
-if CONFIG['MOZ_ENABLE_DBUS']:
-    EXPORTS.mozilla += [
-        'DBusHelpers.h',
-    ]
-
 if CONFIG['OS_ARCH'] == 'Darwin':
     UNIFIED_SOURCES += [
         'nsWifiScannerMac.cpp',
     ]
     SOURCES += [
         'osx_corewlan.mm',
     ]
     # osx_corewlan.mm has warnings about scanForNetworksWithParameters,
--- a/toolkit/components/extensions/webrequest/StreamFilterChild.cpp
+++ b/toolkit/components/extensions/webrequest/StreamFilterChild.cpp
@@ -129,16 +129,17 @@ StreamFilterChild::Disconnect(ErrorResul
 {
   switch (mState) {
   case State::Suspended:
   case State::TransferringData:
   case State::FinishedTransferringData:
     mState = State::Disconnecting;
     mNextState = State::Disconnected;
 
+    WriteBufferedData();
     SendDisconnect();
     break;
 
   case State::Suspending:
   case State::Resuming:
     switch (mNextState) {
     case State::Suspended:
     case State::Resuming:
@@ -472,16 +473,26 @@ StreamFilterChild::FlushBufferedData()
 {
   while (!mBufferedData.isEmpty() && CanFlushData()) {
     UniquePtr<BufferedData> data(mBufferedData.popFirst());
 
     EmitData(data->mData);
   }
 }
 
+void
+StreamFilterChild::WriteBufferedData()
+{
+  while (!mBufferedData.isEmpty()) {
+    UniquePtr<BufferedData> data(mBufferedData.popFirst());
+
+    SendWrite(data->mData);
+  }
+}
+
 IPCResult
 StreamFilterChild::RecvData(Data&& aData)
 {
   MOZ_ASSERT(!mReceivedOnStop);
 
   if (mStreamFilter) {
     Unused << mStreamFilter->CheckAlive();
   }
--- a/toolkit/components/extensions/webrequest/StreamFilterChild.h
+++ b/toolkit/components/extensions/webrequest/StreamFilterChild.h
@@ -125,16 +125,17 @@ private:
   bool
   CanFlushData()
   {
     return (mState == State::TransferringData ||
             mState == State::Resuming);
   }
 
   void FlushBufferedData();
+  void WriteBufferedData();
 
   virtual void ActorDestroy(ActorDestroyReason aWhy) override;
 
 
   State mState;
   State mNextState;
   bool mReceivedOnStop;
 
--- a/toolkit/modules/BrowserUtils.jsm
+++ b/toolkit/modules/BrowserUtils.jsm
@@ -495,17 +495,17 @@ this.BrowserUtils = {
     let collapsed = selection.isCollapsed;
 
     let url;
     let linkText;
 
     // try getting a selected text in text input.
     if (!selectionStr && focusedElement instanceof Ci.nsIDOMNSEditableElement) {
       // Don't get the selection for password fields. See bug 565717.
-      if (focusedElement instanceof Ci.nsIDOMHTMLTextAreaElement ||
+      if (ChromeUtils.getClassName(focusedElement) === "HTMLTextAreaElement" ||
           (focusedElement instanceof Ci.nsIDOMHTMLInputElement &&
            focusedElement.mozIsTextField(true))) {
         selectionStr = focusedElement.editor.selection.toString();
       }
     }
 
     if (selectionStr) {
       // Have some text, let's figure out if it looks like a URL that isn't
--- a/toolkit/modules/sessionstore/FormData.jsm
+++ b/toolkit/modules/sessionstore/FormData.jsm
@@ -202,17 +202,17 @@ var FormDataInternal = {
 
       // We do not want to collect credit card numbers.
       if (node instanceof Ci.nsIDOMHTMLInputElement &&
           isValidCCNumber(node.value)) {
         continue;
       }
 
       if (node instanceof Ci.nsIDOMHTMLInputElement ||
-          node instanceof Ci.nsIDOMHTMLTextAreaElement ||
+          ChromeUtils.getClassName(node) === "HTMLTextAreaElement" ||
           node instanceof Ci.nsIDOMXULTextBoxElement) {
         switch (node.type) {
           case "checkbox":
           case "radio":
             value = node.checked;
             hasDefaultValue = value == node.defaultChecked;
             break;
           case "file":
rename from netwerk/wifi/DBusHelpers.h
rename to uriloader/exthandler/DBusHelpers.h
--- a/uriloader/exthandler/moz.build
+++ b/uriloader/exthandler/moz.build
@@ -96,16 +96,19 @@ elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'wi
     UNIFIED_SOURCES += [
         'win/nsMIMEInfoWin.cpp',
     ]
 
 if CONFIG['MOZ_ENABLE_DBUS']:
     UNIFIED_SOURCES += [
         'nsDBusHandlerApp.cpp',
     ]
+    EXPORTS.mozilla += [
+        'DBusHelpers.h',
+    ]
 
 if CONFIG['MOZ_ENABLE_CONTENTACTION']:
     UNIFIED_SOURCES += [
         'nsContentHandlerApp.cpp',
     ]
 
 EXTRA_COMPONENTS += [
     'nsHandlerService-json.js',
--- a/xpcom/ds/ArenaAllocator.h
+++ b/xpcom/ds/ArenaAllocator.h
@@ -4,16 +4,17 @@
  * 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 mozilla_ArenaAllocator_h
 #define mozilla_ArenaAllocator_h
 
 #include <algorithm>
 #include <cstdint>
+#include <sstream>
 
 #include "mozilla/Assertions.h"
 #include "mozilla/fallible.h"
 #include "mozilla/Likely.h"
 #include "mozilla/MemoryChecking.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/OperatorNewExtensions.h"
 #include "mozilla/TemplateLib.h"
@@ -126,16 +127,22 @@ public:
 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
   bool DebugContains(void* aPtr)
   {
     for (auto arena = mHead.next; arena; arena = arena->next) {
       if (arena->DebugContains(aPtr)) {
         return true;
       }
     }
+    std::stringstream log;
+    log << "Failed to find pointer " << aPtr << " within arena blocks: ";
+    for (ArenaChunk* arena = mHead.next; arena; arena = arena->next) {
+      log << "(" << reinterpret_cast<uintptr_t>(arena + 1) << ", " << arena->header.offset << "), ";
+    }
+    MOZ_CRASH_UNSAFE_OOL(log.str().c_str());
     return false;
   }
 #endif
 
 private:
   struct ArenaHeader
   {
     /**
--- a/xpcom/reflect/xptinfo/ShimInterfaceInfo.cpp
+++ b/xpcom/reflect/xptinfo/ShimInterfaceInfo.cpp
@@ -53,17 +53,16 @@
 #include "nsIDOMHTMLHtmlElement.h"
 #include "nsIDOMHTMLInputElement.h"
 #include "nsIDOMHTMLMediaElement.h"
 #include "nsIDOMHTMLOptionElement.h"
 #include "nsIDOMHTMLOptionsCollection.h"
 #include "nsIDOMHTMLScriptElement.h"
 #include "nsIDOMHTMLSelectElement.h"
 #include "nsIDOMHTMLSourceElement.h"
-#include "nsIDOMHTMLTextAreaElement.h"
 #include "nsIDOMKeyEvent.h"
 #include "nsIDOMMediaList.h"
 #include "nsIDOMMouseEvent.h"
 #include "nsIDOMMouseScrollEvent.h"
 #include "nsIDOMMutationEvent.h"
 #include "nsIDOMMozNamedAttrMap.h"
 #include "nsIDOMNode.h"
 #include "nsIDOMNodeIterator.h"
@@ -158,17 +157,16 @@
 #include "mozilla/dom/HTMLInputElementBinding.h"
 #include "mozilla/dom/HTMLMediaElementBinding.h"
 #include "mozilla/dom/HTMLObjectElementBinding.h"
 #include "mozilla/dom/HTMLOptionElementBinding.h"
 #include "mozilla/dom/HTMLOptionsCollectionBinding.h"
 #include "mozilla/dom/HTMLScriptElementBinding.h"
 #include "mozilla/dom/HTMLSelectElementBinding.h"
 #include "mozilla/dom/HTMLSourceElementBinding.h"
-#include "mozilla/dom/HTMLTextAreaElementBinding.h"
 #include "mozilla/dom/KeyEventBinding.h"
 #include "mozilla/dom/ListBoxObjectBinding.h"
 #include "mozilla/dom/MediaListBinding.h"
 #include "mozilla/dom/MessageEventBinding.h"
 #include "mozilla/dom/MenuBoxObjectBinding.h"
 #include "mozilla/dom/MouseEventBinding.h"
 #include "mozilla/dom/MouseScrollEventBinding.h"
 #include "mozilla/dom/MutationEventBinding.h"
@@ -314,17 +312,16 @@ const ComponentsInterfaceShimEntry kComp
   DEFINE_SHIM(HTMLHtmlElement),
   DEFINE_SHIM(HTMLInputElement),
   DEFINE_SHIM(HTMLMediaElement),
   DEFINE_SHIM(HTMLOptionElement),
   DEFINE_SHIM(HTMLOptionsCollection),
   DEFINE_SHIM(HTMLScriptElement),
   DEFINE_SHIM(HTMLSelectElement),
   DEFINE_SHIM(HTMLSourceElement),
-  DEFINE_SHIM(HTMLTextAreaElement),
   DEFINE_SHIM(KeyEvent),
   DEFINE_SHIM_WITH_CUSTOM_INTERFACE(nsIListBoxObject, ListBoxObject),
   DEFINE_SHIM(MediaList),
   DEFINE_SHIM_WITH_CUSTOM_INTERFACE(nsIMenuBoxObject, MenuBoxObject),
   DEFINE_SHIM(MouseEvent),
   DEFINE_SHIM(MouseScrollEvent),
   DEFINE_SHIM(MutationEvent),
   DEFINE_SHIM_WITH_CUSTOM_INTERFACE(nsIDOMMozNamedAttrMap, NamedNodeMap),