Merge mozilla-central to autoland. a=merge CLOSED TREE
authorCiure Andrei <aciure@mozilla.com>
Thu, 18 Oct 2018 19:39:05 +0300
changeset 490316 a58112b87de672f400dc5d23ad8bac80d292a2fd
parent 490315 a6b4461eadf0622f4d4f6ea1ee8389d1c655b336 (current diff)
parent 490254 733484af9034cb0372ed1fd745ec3d321d48e8b9 (diff)
child 490317 084f6e60778f01d66bee26657e0e459f8fddcc3e
push id247
push userfmarier@mozilla.com
push dateSat, 27 Oct 2018 01:06:44 +0000
reviewersmerge
milestone64.0a1
Merge mozilla-central to autoland. a=merge CLOSED TREE
--- a/dom/file/MemoryBlobImpl.h
+++ b/dom/file/MemoryBlobImpl.h
@@ -120,16 +120,17 @@ public:
                            uint32_t aLength,
                            nsIInputStream** _retval);
 
     NS_DECL_THREADSAFE_ISUPPORTS
 
     // These are mandatory.
     NS_FORWARD_NSIINPUTSTREAM(mStream->)
     NS_FORWARD_NSISEEKABLESTREAM(mSeekableStream->)
+    NS_FORWARD_NSITELLABLESTREAM(mSeekableStream->)
     NS_FORWARD_NSICLONEABLEINPUTSTREAM(mCloneableInputStream->)
 
     // This is optional. We use a conditional QI to keep it from being called
     // if the underlying stream doesn't support it.
     NS_FORWARD_NSIIPCSERIALIZABLEINPUTSTREAM(mSerializableInputStream->)
 
   private:
     ~DataOwnerAdapter() {}
--- a/dom/media/MediaTimer.cpp
+++ b/dom/media/MediaTimer.cpp
@@ -16,29 +16,28 @@
 
 namespace mozilla {
 
 NS_IMPL_ADDREF(MediaTimer)
 NS_IMPL_RELEASE_WITH_DESTROY(MediaTimer, DispatchDestroy())
 
 MediaTimer::MediaTimer(bool aFuzzy)
   : mMonitor("MediaTimer Monitor")
-  , mTimer(NS_NewTimer())
   , mCreationTimeStamp(TimeStamp::Now())
   , mUpdateScheduled(false)
   , mFuzzy(aFuzzy)
 {
   TIMER_LOG("MediaTimer::MediaTimer");
 
   // Use the SharedThreadPool to create an nsIThreadPool with a maximum of one
   // thread, which is equivalent to an nsIThread for our purposes.
   RefPtr<SharedThreadPool> threadPool(
     SharedThreadPool::Get(NS_LITERAL_CSTRING("MediaTimer"), 1));
   mThread = threadPool.get();
-  mTimer->SetTarget(mThread);
+  mTimer = NS_NewTimer(mThread);
 }
 
 void
 MediaTimer::DispatchDestroy()
 {
   // Hold a strong reference to the thread so that it doesn't get deleted in
   // Destroy(), which may run completely before the stack if Dispatch() begins
   // to unwind.
--- a/dom/security/featurepolicy/FeaturePolicyUtils.cpp
+++ b/dom/security/featurepolicy/FeaturePolicyUtils.cpp
@@ -18,25 +18,25 @@ struct FeatureMap {
 };
 
 /*
  * IMPORTANT: Do not change this list without review from a DOM peer _AND_ a
  * DOM Security peer!
  */
 static FeatureMap sSupportedFeatures[] = {
   { "autoplay", FeaturePolicyUtils::FeaturePolicyValue::eAll },
-  { "camera", FeaturePolicyUtils::FeaturePolicyValue::eAll },
+  { "camera", FeaturePolicyUtils::FeaturePolicyValue::eSelf },
   { "encrypted-media", FeaturePolicyUtils::FeaturePolicyValue::eAll },
   { "fullscreen", FeaturePolicyUtils::FeaturePolicyValue::eAll },
   { "geolocation", FeaturePolicyUtils::FeaturePolicyValue::eAll },
-  { "microphone", FeaturePolicyUtils::FeaturePolicyValue::eAll },
-  { "midi", FeaturePolicyUtils::FeaturePolicyValue::eAll },
+  { "microphone", FeaturePolicyUtils::FeaturePolicyValue::eSelf },
+  { "midi", FeaturePolicyUtils::FeaturePolicyValue::eSelf },
   { "payment", FeaturePolicyUtils::FeaturePolicyValue::eAll },
   // TODO: not supported yet!!!
-  { "speaker", FeaturePolicyUtils::FeaturePolicyValue::eAll },
+  { "speaker", FeaturePolicyUtils::FeaturePolicyValue::eSelf },
   { "vr", FeaturePolicyUtils::FeaturePolicyValue::eAll },
 };
 
 /* static */ bool
 FeaturePolicyUtils::IsSupportedFeature(const nsAString& aFeatureName)
 {
   uint32_t numFeatures = (sizeof(sSupportedFeatures) / sizeof(sSupportedFeatures[0]));
   for (uint32_t i = 0; i < numFeatures; ++i) {
--- a/dom/security/featurepolicy/test/mochitest/test_parser.html
+++ b/dom/security/featurepolicy/test/mochitest/test_parser.html
@@ -13,34 +13,34 @@ SimpleTest.waitForExplicitFinish();
 
 function test_document() {
   info("Checking document.policy");
   ok("policy" in document, "We have document.policy");
 
   ok(!document.policy.allowsFeature("foobar"), "Random feature");
   ok(!document.policy.allowsFeature("foobar", "http://www.something.net"), "Random feature");
 
-  ok(document.policy.allowsFeature("camera"), "Camera is always enabled");
-  ok(document.policy.allowsFeature("camera", "http://foo.bar"), "Camera is always enabled");
+  ok(document.policy.allowsFeature("camera"), "Camera is allowed for self");
+  ok(document.policy.allowsFeature("camera", "http://foo.bar"), "Camera is always allowed");
   let allowed = document.policy.getAllowlistForFeature("camera");
   is(allowed.length, 1, "Only 1 entry in allowlist for camera");
   is(allowed[0], "*", "allowlist is *");
 
-  ok(document.policy.allowsFeature("geolocation"), "Geolocation is enabled for self");
-  ok(document.policy.allowsFeature("geolocation", location.origin), "Geolocation is enabled for self");
-  ok(!document.policy.allowsFeature("geolocation", "http://foo.bar"), "Geolocation is not enabled for any random URL");
+  ok(document.policy.allowsFeature("geolocation"), "Geolocation is allowed for self");
+  ok(document.policy.allowsFeature("geolocation", location.origin), "Geolocation is allowed for self");
+  ok(!document.policy.allowsFeature("geolocation", "http://foo.bar"), "Geolocation is not allowed for any random URL");
   allowed = document.policy.getAllowlistForFeature("geolocation");
   is(allowed.length, 1, "Only 1 entry in allowlist for geolocation");
   is(allowed[0], location.origin, "allowlist is self");
 
   ok(!document.policy.allowsFeature("microphone"), "Microphone is disabled for self");
   ok(!document.policy.allowsFeature("microphone", location.origin), "Microphone is disabled for self");
   ok(!document.policy.allowsFeature("microphone", "http://foo.bar"), "Microphone is disabled for foo.bar");
-  ok(document.policy.allowsFeature("microphone", "http://example.com"), "Microphone is enabled for example.com");
-  ok(document.policy.allowsFeature("microphone", "http://example.org"), "Microphone is enabled for example.org");
+  ok(document.policy.allowsFeature("microphone", "http://example.com"), "Microphone is allowed for example.com");
+  ok(document.policy.allowsFeature("microphone", "http://example.org"), "Microphone is allowed for example.org");
   allowed = document.policy.getAllowlistForFeature("microphone");
   is(allowed.length, 0, "No allowlist for microphone");
 
   ok(!document.policy.allowsFeature("vr"), "Vibrate is disabled for self");
   ok(!document.policy.allowsFeature("vr", location.origin), "Vibrate is disabled for self");
   ok(!document.policy.allowsFeature("vr", "http://foo.bar"), "Vibrate is disabled for foo.bar");
   allowed = document.policy.getAllowlistForFeature("vr");
   is(allowed.length, 0, "No allowlist for vr");
@@ -63,25 +63,25 @@ function test_document() {
 function test_iframe_without_allow() {
   info("Checking HTMLIFrameElement.policy");
   let ifr = document.getElementById("ifr");
   ok("policy" in ifr, "HTMLIFrameElement.policy exists");
 
   ok(!ifr.policy.allowsFeature("foobar"), "Random feature");
   ok(!ifr.policy.allowsFeature("foobar", "http://www.something.net"), "Random feature");
 
-  ok(ifr.policy.allowsFeature("camera"), "Camera is always allowed");
-  ok(ifr.policy.allowsFeature("camera", location.origin), "Camera is always allowed");
-  ok(ifr.policy.allowsFeature("camera", "http://foo.bar"), "Camera is always allowed");
+  ok(ifr.policy.allowsFeature("camera"), "Camera is allowed for self");
+  ok(ifr.policy.allowsFeature("camera", location.origin), "Camera is allowed for self");
+  ok(!ifr.policy.allowsFeature("camera", "http://foo.bar"), "Camera is not allowed for a random URL");
   let allowed = ifr.policy.getAllowlistForFeature("camera");
   is(allowed.length, 1, "Only 1 entry in allowlist for camera");
-  is(allowed[0], "*", "allowlist is '*'");
+  is(allowed[0], location.origin, "allowlist is 'self'");
 
-  ok(ifr.policy.allowsFeature("geolocation"), "Geolocation is enabled for all");
-  ok(ifr.policy.allowsFeature("geolocation", location.origin), "Geolocation is enabled for all");
+  ok(ifr.policy.allowsFeature("geolocation"), "Geolocation is allowed for all");
+  ok(ifr.policy.allowsFeature("geolocation", location.origin), "Geolocation is allowed for all");
   ok(ifr.policy.allowsFeature("geolocation", "http://foo.bar"), "Geolocation is allowed for any random URL");
   allowed = ifr.policy.getAllowlistForFeature("geolocation");
   is(allowed.length, 1, "Only 1 entry in allowlist for geolocation");
   is(allowed[0], "*", "allowlist is '*'");
 
   ok(!ifr.policy.allowsFeature("microphone"), "Microphone is disabled for self");
   ok(!ifr.policy.allowsFeature("microphone", location.origin), "Microphone is disabled for self");
   ok(!ifr.policy.allowsFeature("microphone", "http://foo.bar"), "Microphone is disabled for foo.bar");
@@ -111,23 +111,23 @@ function test_iframe_with_allow() {
   let ifr = document.getElementById("ifr");
   ok("policy" in ifr, "HTMLIFrameElement.policy exists");
 
   ifr.setAttribute("allow", "camera 'none'");
 
   ok(!ifr.policy.allowsFeature("foobar"), "Random feature");
   ok(!ifr.policy.allowsFeature("foobar", "http://www.something.net"), "Random feature");
 
-  ok(!ifr.policy.allowsFeature("camera"), "Camera is not enabled");
+  ok(!ifr.policy.allowsFeature("camera"), "Camera is not allowed");
   let allowed = ifr.policy.getAllowlistForFeature("camera");
   is(allowed.length, 0, "Camera has an empty allowlist");
 
-  ok(ifr.policy.allowsFeature("geolocation"), "Geolocation is enabled for all");
-  ok(ifr.policy.allowsFeature("geolocation", location.origin), "Geolocation is enabled for all");
-  ok(ifr.policy.allowsFeature("geolocation", "http://foo.bar"), "Geolocation is enabled for all");
+  ok(ifr.policy.allowsFeature("geolocation"), "Geolocation is allowed for all");
+  ok(ifr.policy.allowsFeature("geolocation", location.origin), "Geolocation is allowed for all");
+  ok(ifr.policy.allowsFeature("geolocation", "http://foo.bar"), "Geolocation is allowed for all");
   allowed = ifr.policy.getAllowlistForFeature("geolocation");
   is(allowed.length, 1, "Only 1 entry in allowlist for geolocation");
   is(allowed[0], "*", "allowlist is '*'");
 
   ok(!ifr.policy.allowsFeature("microphone"), "Microphone is disabled for self");
   ok(!ifr.policy.allowsFeature("microphone", location.origin), "Microphone is disabled for self");
   ok(!ifr.policy.allowsFeature("microphone", "http://foo.bar"), "Microphone is disabled for foo.bar");
   ok(!ifr.policy.allowsFeature("microphone", "http://example.com"), "Microphone is disabled for example.com");
@@ -152,34 +152,35 @@ function test_iframe_contentDocument() {
   let ifr = document.createElement("iframe");
   ifr.setAttribute("src", "empty.html");
   ifr.onload = function() {
     ok("policy" in ifr.contentDocument, "We have ifr.contentDocument.policy");
 
     ok(!ifr.contentDocument.policy.allowsFeature("foobar"), "Random feature");
     ok(!ifr.contentDocument.policy.allowsFeature("foobar", "http://www.something.net"), "Random feature");
 
-    ok(ifr.contentDocument.policy.allowsFeature("camera"), "Camera is always allowed");
-    ok(ifr.contentDocument.policy.allowsFeature("camera", "http://foo.bar"), "Camera is always allowed");
+    ok(ifr.contentDocument.policy.allowsFeature("camera"), "Camera is allowed for self");
+    ok(ifr.contentDocument.policy.allowsFeature("camera", location.origin), "Camera is allowed for self");
+    ok(!ifr.contentDocument.policy.allowsFeature("camera", "http://foo.bar"), "Camera is allowed for self");
     let allowed = ifr.contentDocument.policy.getAllowlistForFeature("camera");
     is(allowed.length, 1, "Only 1 entry in allowlist for camera");
-    is(allowed[0], "*", "allowlist is '*'");
+    is(allowed[0], location.origin, "allowlist is 'self'");
 
-    ok(ifr.contentDocument.policy.allowsFeature("geolocation"), "Geolocation is enabled for all");
-    ok(ifr.contentDocument.policy.allowsFeature("geolocation", location.origin), "Geolocation is enabled for all");
-    ok(ifr.contentDocument.policy.allowsFeature("geolocation", "http://foo.bar"), "Geolocation is enabled for any random URL");
+    ok(ifr.contentDocument.policy.allowsFeature("geolocation"), "Geolocation is allowed for all");
+    ok(ifr.contentDocument.policy.allowsFeature("geolocation", location.origin), "Geolocation is allowed for all");
+    ok(ifr.contentDocument.policy.allowsFeature("geolocation", "http://foo.bar"), "Geolocation is allowed for any random URL");
     allowed = ifr.contentDocument.policy.getAllowlistForFeature("geolocation");
     is(allowed.length, 1, "Only 1 entry in allowlist for geolocation");
     is(allowed[0], "*", "allowlist is '*'");
 
     ok(!ifr.contentDocument.policy.allowsFeature("microphone"), "Microphone is disabled for self");
     ok(!ifr.contentDocument.policy.allowsFeature("microphone", location.origin), "Microphone is disabled for self");
     ok(!ifr.contentDocument.policy.allowsFeature("microphone", "http://foo.bar"), "Microphone is disabled for foo.bar");
-    ok(!ifr.contentDocument.policy.allowsFeature("microphone", "http://example.com"), "Microphone is enabled for example.com");
-    ok(!ifr.contentDocument.policy.allowsFeature("microphone", "http://example.org"), "Microphone is enabled for example.org");
+    ok(!ifr.contentDocument.policy.allowsFeature("microphone", "http://example.com"), "Microphone is allowed for example.com");
+    ok(!ifr.contentDocument.policy.allowsFeature("microphone", "http://example.org"), "Microphone is allowed for example.org");
     allowed = ifr.contentDocument.policy.getAllowlistForFeature("microphone");
     is(allowed.length, 0, "No allowlist for microphone");
 
     ok(!ifr.contentDocument.policy.allowsFeature("vr"), "Vibrate is disabled for self");
     ok(!ifr.contentDocument.policy.allowsFeature("vr", location.origin), "Vibrate is disabled for self");
     ok(!ifr.contentDocument.policy.allowsFeature("vr", "http://foo.bar"), "Vibrate is disabled for foo.bar");
     allowed = ifr.contentDocument.policy.getAllowlistForFeature("vr");
     is(allowed.length, 0, "No allowlist for vr");
--- a/dom/xul/XULMenuElement.cpp
+++ b/dom/xul/XULMenuElement.cpp
@@ -20,45 +20,32 @@ namespace mozilla {
 namespace dom {
 
 JSObject*
 XULMenuElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return XULMenuElement_Binding::Wrap(aCx, this, aGivenProto);
 }
 
-nsIFrame*
-XULMenuElement::GetFrame()
-{
-  nsCOMPtr<nsIContent> kungFuDeathGrip = this; // keep a reference
-
-  nsCOMPtr<nsIDocument> doc = GetUncomposedDoc();
-  if (doc) {
-    doc->FlushPendingNotifications(FlushType::Frames);
-  }
-
-  return GetPrimaryFrame();
-}
-
 already_AddRefed<Element>
 XULMenuElement::GetActiveChild()
 {
-  nsMenuFrame* menu = do_QueryFrame(GetFrame());
+  nsMenuFrame* menu = do_QueryFrame(GetPrimaryFrame(FlushType::Frames));
   if (menu) {
     RefPtr<Element> el;
     menu->GetActiveChild(getter_AddRefs(el));
     return el.forget();
   }
   return nullptr;
 }
 
 void
 XULMenuElement::SetActiveChild(Element* arg)
 {
-  nsMenuFrame* menu = do_QueryFrame(GetFrame());
+  nsMenuFrame* menu = do_QueryFrame(GetPrimaryFrame(FlushType::Frames));
   if (menu) {
     menu->SetActiveChild(arg);
   }
 }
 
 bool
 XULMenuElement::HandleKeyPress(KeyboardEvent& keyEvent)
 {
@@ -70,17 +57,17 @@ XULMenuElement::HandleKeyPress(KeyboardE
   // if event has already been handled, bail
   if (keyEvent.DefaultPrevented()) {
     return false;
   }
 
   if (nsMenuBarListener::IsAccessKeyPressed(&keyEvent))
     return false;
 
-  nsMenuFrame* menu = do_QueryFrame(GetFrame());
+  nsMenuFrame* menu = do_QueryFrame(GetPrimaryFrame(FlushType::Frames));
   if (!menu) {
     return false;
   }
 
   nsMenuPopupFrame* popupFrame = menu->GetPopup();
   if (!popupFrame) {
     return false;
   }
@@ -99,17 +86,17 @@ XULMenuElement::HandleKeyPress(KeyboardE
     default:
       return pm->HandleShortcutNavigation(&keyEvent, popupFrame);
   }
 }
 
 bool
 XULMenuElement::OpenedWithKey()
 {
-  nsMenuFrame* menuframe = do_QueryFrame(GetFrame());
+  nsMenuFrame* menuframe = do_QueryFrame(GetPrimaryFrame(FlushType::Frames));
   if (!menuframe) {
     return false;
   }
 
   nsIFrame* frame = menuframe->GetParent();
   while (frame) {
     nsMenuBarFrame* menubar = do_QueryFrame(frame);
     if (menubar) {
--- a/dom/xul/XULPopupElement.cpp
+++ b/dom/xul/XULPopupElement.cpp
@@ -28,29 +28,16 @@ NS_NewXULPopupElement(already_AddRefed<m
 }
 
 JSObject*
 XULPopupElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return XULPopupElement_Binding::Wrap(aCx, this, aGivenProto);
 }
 
-nsIFrame*
-XULPopupElement::GetFrame(bool aFlushLayout)
-{
-  nsCOMPtr<nsIContent> kungFuDeathGrip = this; // keep a reference
-
-  nsCOMPtr<nsIDocument> doc = GetUncomposedDoc();
-  if (doc) {
-    doc->FlushPendingNotifications(aFlushLayout ? FlushType::Layout : FlushType::Frames);
-  }
-
-  return GetPrimaryFrame();
-}
-
 void
 XULPopupElement::OpenPopup(Element* aAnchorElement,
                            const StringOrOpenPopupOptions& aOptions,
                            int32_t aXPos, int32_t aYPos,
                            bool aIsContextMenu,
                            bool aAttributesOverride,
                            Event* aTriggerEvent)
 {
@@ -238,17 +225,17 @@ XULPopupElement::GetAnchorNode() const
 }
 
 already_AddRefed<DOMRect>
 XULPopupElement::GetOuterScreenRect()
 {
   RefPtr<DOMRect> rect = new DOMRect(ToSupports(this));
 
   // Return an empty rectangle if the popup is not open.
-  nsMenuPopupFrame *menuPopupFrame = do_QueryFrame(GetFrame(false));
+  nsMenuPopupFrame *menuPopupFrame = do_QueryFrame(GetPrimaryFrame(FlushType::Frames));
   if (!menuPopupFrame || !menuPopupFrame->IsOpen()) {
     return rect.forget();
   }
 
   nsView* view = menuPopupFrame->GetView();
   if (view) {
     nsIWidget* widget = view->GetWidget();
     if (widget) {
@@ -262,17 +249,17 @@ XULPopupElement::GetOuterScreenRect()
 }
 
 void
 XULPopupElement::GetAlignmentPosition(nsString& positionStr)
 {
   positionStr.Truncate();
 
   // This needs to flush layout.
-  nsMenuPopupFrame *menuPopupFrame = do_QueryFrame(GetFrame(true));
+  nsMenuPopupFrame *menuPopupFrame = do_QueryFrame(GetPrimaryFrame(FlushType::Layout));
   if (!menuPopupFrame)
     return;
 
   int8_t position = menuPopupFrame->GetAlignmentPosition();
   switch (position) {
     case POPUPPOSITION_AFTERSTART:
       positionStr.AssignLiteral("after_start");
       break;
@@ -310,33 +297,33 @@ XULPopupElement::GetAlignmentPosition(ns
       // Leave as an empty string.
       break;
   }
 }
 
 int32_t
 XULPopupElement::AlignmentOffset()
 {
-  nsMenuPopupFrame *menuPopupFrame = do_QueryFrame(GetFrame(false));
+  nsMenuPopupFrame *menuPopupFrame = do_QueryFrame(GetPrimaryFrame(FlushType::Frames));
   if (!menuPopupFrame)
     return 0;
 
   int32_t pp = mozilla::AppUnitsPerCSSPixel();
   // Note that the offset might be along either the X or Y axis, but for the
   // sake of simplicity we use a point with only the X axis set so we can
   // use ToNearestPixels().
   nsPoint appOffset(menuPopupFrame->GetAlignmentOffset(), 0);
   nsIntPoint popupOffset = appOffset.ToNearestPixels(pp);
   return popupOffset.x;
 }
 
 void
 XULPopupElement::SetConstraintRect(dom::DOMRectReadOnly& aRect)
 {
-  nsMenuPopupFrame *menuPopupFrame = do_QueryFrame(GetFrame(false));
+  nsMenuPopupFrame *menuPopupFrame = do_QueryFrame(GetPrimaryFrame(FlushType::Frames));
   if (menuPopupFrame) {
     menuPopupFrame->SetOverrideConstraintRect(
       LayoutDeviceIntRect::Truncate(aRect.Left(), aRect.Top(), aRect.Width(), aRect.Height()));
   }
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/xul/nsXULElement.cpp
+++ b/dom/xul/nsXULElement.cpp
@@ -523,34 +523,28 @@ nsXULElement::HasMenu()
 {
   nsMenuFrame* menu = do_QueryFrame(GetPrimaryFrame());
   return menu != nullptr;
 }
 
 void
 nsXULElement::OpenMenu(bool aOpenFlag)
 {
-  nsCOMPtr<nsIDocument> doc = GetUncomposedDoc();
-  if (doc) {
-    doc->FlushPendingNotifications(FlushType::Frames);
-  }
+  nsMenuFrame* menu = do_QueryFrame(GetPrimaryFrame(FlushType::Frames));
 
   nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
   if (pm) {
     if (aOpenFlag) {
       // Nothing will happen if this element isn't a menu.
       pm->ShowMenu(this, false, false);
     }
-    else {
-      nsMenuFrame* menu = do_QueryFrame(GetPrimaryFrame());
-      if (menu) {
-        nsMenuPopupFrame* popupFrame = menu->GetPopup();
-        if (popupFrame) {
-          pm->HidePopup(popupFrame->GetContent(), false, true, false, false);
-        }
+    else if (menu) {
+      nsMenuPopupFrame* popupFrame = menu->GetPopup();
+      if (popupFrame) {
+        pm->HidePopup(popupFrame->GetContent(), false, true, false, false);
       }
     }
   }
 }
 
 bool
 nsXULElement::PerformAccesskey(bool aKeyCausesActivation,
                                bool aIsTrustedEvent)
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/modules/bug-1498980.js
@@ -0,0 +1,24 @@
+dbgGlobal = newGlobal();
+dbg = new dbgGlobal.Debugger;
+dbg.addDebuggee(this);
+
+function f() {
+    dbg.getNewestFrame().older.eval("");
+}
+
+function execModule(source) {
+    m = parseModule(source);
+    m.declarationInstantiation();
+    m.evaluation();
+}
+
+execModule("f();");
+gc();
+
+let caught;
+try {
+    execModule("throw 'foo'");
+} catch (e) {
+    caught = e;
+}
+assertEq(caught, 'foo');
--- a/js/src/vm/EnvironmentObject.cpp
+++ b/js/src/vm/EnvironmentObject.cpp
@@ -2802,20 +2802,21 @@ DebugEnvironments::takeFrameSnapshot(JSC
      * copy of the unaliased variables' values in an array for later debugger
      * access via DebugEnvironmentProxy::handleUnaliasedAccess.
      *
      * Note: since it is simplest for this function to be infallible, failure
      * in this code will be silently ignored. This does not break any
      * invariants since DebugEnvironmentProxy::maybeSnapshot can already be nullptr.
      */
 
+    JSScript* script = frame.script();
+
     // Act like no snapshot was taken if we run OOM while taking the snapshot.
     Rooted<GCVector<Value>> vec(cx, GCVector<Value>(cx));
     if (debugEnv->environment().is<CallObject>()) {
-        JSScript* script = frame.script();
 
         FunctionScope* scope = &script->bodyScope()->as<FunctionScope>();
         uint32_t frameSlotCount = scope->nextFrameSlot();
         MOZ_ASSERT(frameSlotCount <= script->nfixed());
 
         // For simplicity, copy all frame slots from 0 to the frameSlotCount,
         // even if we don't need all of them (like in the case of a defaults
         // parameter scope having frame slots).
@@ -2843,32 +2844,38 @@ DebugEnvironments::takeFrameSnapshot(JSC
     } else {
         uint32_t frameSlotStart;
         uint32_t frameSlotEnd;
 
         if (debugEnv->environment().is<LexicalEnvironmentObject>()) {
             LexicalScope* scope = &debugEnv->environment().as<LexicalEnvironmentObject>().scope();
             frameSlotStart = scope->firstFrameSlot();
             frameSlotEnd = scope->nextFrameSlot();
-        } else {
+        } else if (debugEnv->environment().is<VarEnvironmentObject>()) {
             VarEnvironmentObject* env = &debugEnv->environment().as<VarEnvironmentObject>();
             if (frame.isFunctionFrame()) {
                 VarScope* scope = &env->scope().as<VarScope>();
                 frameSlotStart = scope->firstFrameSlot();
                 frameSlotEnd = scope->nextFrameSlot();
             } else {
                 EvalScope* scope = &env->scope().as<EvalScope>();
-                MOZ_ASSERT(scope == frame.script()->bodyScope());
+                MOZ_ASSERT(scope == script->bodyScope());
                 frameSlotStart = 0;
                 frameSlotEnd = scope->nextFrameSlot();
             }
+        } else {
+            MOZ_ASSERT(&debugEnv->environment().as<ModuleEnvironmentObject>() ==
+                       script->module()->environment());
+            ModuleScope* scope = &script->bodyScope()->as<ModuleScope>();
+            frameSlotStart = 0;
+            frameSlotEnd = scope->nextFrameSlot();
         }
 
         uint32_t frameSlotCount = frameSlotEnd - frameSlotStart;
-        MOZ_ASSERT(frameSlotCount <= frame.script()->nfixed());
+        MOZ_ASSERT(frameSlotCount <= script->nfixed());
 
         if (!vec.resize(frameSlotCount)) {
             cx->recoverFromOutOfMemory();
             return;
         }
         for (uint32_t slot = frameSlotStart; slot < frameSlotCount; slot++) {
             vec[slot - frameSlotStart].set(frame.unaliasedLocal(slot));
         }
@@ -3018,16 +3025,22 @@ DebugEnvironments::onPopWith(AbstractFra
 {
     Realm* realm = frame.realm();
     if (DebugEnvironments* envs = realm->debugEnvs()) {
         envs->liveEnvs.remove(&frame.environmentChain()->as<WithEnvironmentObject>());
     }
 }
 
 void
+DebugEnvironments::onPopModule(JSContext* cx, const EnvironmentIter& ei)
+{
+    onPopGeneric<ModuleEnvironmentObject, ModuleScope>(cx, ei);
+}
+
+void
 DebugEnvironments::onRealmUnsetIsDebuggee(Realm* realm)
 {
     if (DebugEnvironments* envs = realm->debugEnvs()) {
         envs->proxiedEnvs.clear();
         envs->missingEnvs.clear();
         envs->liveEnvs.clear();
     }
 }
--- a/js/src/vm/EnvironmentObject.h
+++ b/js/src/vm/EnvironmentObject.h
@@ -1049,16 +1049,17 @@ class DebugEnvironments
     // In debug-mode, these must be called whenever exiting a scope that might
     // have stack-allocated locals.
     static void onPopCall(JSContext* cx, AbstractFramePtr frame);
     static void onPopVar(JSContext* cx, const EnvironmentIter& ei);
     static void onPopVar(JSContext* cx, AbstractFramePtr frame, jsbytecode* pc);
     static void onPopLexical(JSContext* cx, const EnvironmentIter& ei);
     static void onPopLexical(JSContext* cx, AbstractFramePtr frame, jsbytecode* pc);
     static void onPopWith(AbstractFramePtr frame);
+    static void onPopModule(JSContext* cx, const EnvironmentIter& ei);
     static void onRealmUnsetIsDebuggee(Realm* realm);
 };
 
 }  /* namespace js */
 
 template <>
 inline bool
 JSObject::is<js::EnvironmentObject>() const
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -1238,20 +1238,24 @@ PopEnvironment(JSContext* cx, Environmen
       case ScopeKind::StrictEval:
         if (MOZ_UNLIKELY(cx->realm()->isDebuggee())) {
             DebugEnvironments::onPopVar(cx, ei);
         }
         if (ei.scope().hasEnvironment()) {
             ei.initialFrame().popOffEnvironmentChain<VarEnvironmentObject>();
         }
         break;
+      case ScopeKind::Module:
+        if (MOZ_UNLIKELY(cx->realm()->isDebuggee())) {
+            DebugEnvironments::onPopModule(cx, ei);
+        }
+        break;
       case ScopeKind::Eval:
       case ScopeKind::Global:
       case ScopeKind::NonSyntactic:
-      case ScopeKind::Module:
         break;
       case ScopeKind::WasmInstance:
       case ScopeKind::WasmFunction:
         MOZ_CRASH("wasm is not interpreted");
         break;
     }
 }
 
--- a/netwerk/base/PartiallySeekableInputStream.cpp
+++ b/netwerk/base/PartiallySeekableInputStream.cpp
@@ -12,16 +12,17 @@ namespace mozilla {
 namespace net {
 
 NS_IMPL_ADDREF(PartiallySeekableInputStream);
 NS_IMPL_RELEASE(PartiallySeekableInputStream);
 
 NS_INTERFACE_MAP_BEGIN(PartiallySeekableInputStream)
   NS_INTERFACE_MAP_ENTRY(nsIInputStream)
   NS_INTERFACE_MAP_ENTRY(nsISeekableStream)
+  NS_INTERFACE_MAP_ENTRY(nsITellableStream)
   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsICloneableInputStream,
                                      mWeakCloneableInputStream)
   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIIPCSerializableInputStream,
                                      mWeakIPCSerializableInputStream)
   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIAsyncInputStream,
                                      mWeakAsyncInputStream)
   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIInputStreamCallback,
                                      mWeakAsyncInputStream)
--- a/netwerk/base/PartiallySeekableInputStream.h
+++ b/netwerk/base/PartiallySeekableInputStream.h
@@ -29,16 +29,17 @@ class PartiallySeekableInputStream final
                                          , public nsIInputStreamLength
                                          , public nsIAsyncInputStreamLength
                                          , public nsIInputStreamLengthCallback
 {
 public:
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIINPUTSTREAM
   NS_DECL_NSISEEKABLESTREAM
+  NS_DECL_NSITELLABLESTREAM
   NS_DECL_NSIASYNCINPUTSTREAM
   NS_DECL_NSICLONEABLEINPUTSTREAM
   NS_DECL_NSIIPCSERIALIZABLEINPUTSTREAM
   NS_DECL_NSIINPUTSTREAMCALLBACK
   NS_DECL_NSIINPUTSTREAMLENGTH
   NS_DECL_NSIASYNCINPUTSTREAMLENGTH
   NS_DECL_NSIINPUTSTREAMLENGTHCALLBACK
 
--- a/netwerk/base/ThrottleQueue.cpp
+++ b/netwerk/base/ThrottleQueue.cpp
@@ -21,33 +21,35 @@ class ThrottleInputStream final
 {
 public:
 
   ThrottleInputStream(nsIInputStream* aStream, ThrottleQueue* aQueue);
 
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIINPUTSTREAM
   NS_DECL_NSISEEKABLESTREAM
+  NS_DECL_NSITELLABLESTREAM
   NS_DECL_NSIASYNCINPUTSTREAM
 
   void AllowInput();
 
 private:
 
   ~ThrottleInputStream();
 
   nsCOMPtr<nsIInputStream> mStream;
   RefPtr<ThrottleQueue> mQueue;
   nsresult mClosedStatus;
 
   nsCOMPtr<nsIInputStreamCallback> mCallback;
   nsCOMPtr<nsIEventTarget> mEventTarget;
 };
 
-NS_IMPL_ISUPPORTS(ThrottleInputStream, nsIAsyncInputStream, nsIInputStream, nsISeekableStream)
+NS_IMPL_ISUPPORTS(ThrottleInputStream, nsIAsyncInputStream, nsIInputStream,
+                  nsITellableStream, nsISeekableStream)
 
 ThrottleInputStream::ThrottleInputStream(nsIInputStream *aStream, ThrottleQueue* aQueue)
   : mStream(aStream)
   , mQueue(aQueue)
   , mClosedStatus(NS_OK)
 {
   MOZ_ASSERT(aQueue != nullptr);
 }
@@ -155,17 +157,17 @@ ThrottleInputStream::Seek(int32_t aWhenc
 
 NS_IMETHODIMP
 ThrottleInputStream::Tell(int64_t* aResult)
 {
   if (NS_FAILED(mClosedStatus)) {
     return mClosedStatus;
   }
 
-  nsCOMPtr<nsISeekableStream> sstream = do_QueryInterface(mStream);
+  nsCOMPtr<nsITellableStream> sstream = do_QueryInterface(mStream);
   if (!sstream) {
     return NS_ERROR_FAILURE;
   }
 
   return sstream->Tell(aResult);
 }
 
 NS_IMETHODIMP
--- a/netwerk/base/nsBufferedStreams.cpp
+++ b/netwerk/base/nsBufferedStreams.cpp
@@ -58,17 +58,17 @@ nsBufferedStream::nsBufferedStream()
 {
 }
 
 nsBufferedStream::~nsBufferedStream()
 {
     Close();
 }
 
-NS_IMPL_ISUPPORTS(nsBufferedStream, nsISeekableStream)
+NS_IMPL_ISUPPORTS(nsBufferedStream, nsITellableStream, nsISeekableStream)
 
 nsresult
 nsBufferedStream::Init(nsISupports* stream, uint32_t bufferSize)
 {
     NS_ASSERTION(stream, "need to supply a stream");
     NS_ASSERTION(mStream == nullptr, "already inited");
     mStream = stream;
     NS_IF_ADDREF(mStream);
@@ -300,16 +300,17 @@ NS_INTERFACE_MAP_BEGIN(nsBufferedInputSt
     NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIInputStreamLengthCallback, mIsAsyncInputStreamLength)
     NS_IMPL_QUERY_CLASSINFO(nsBufferedInputStream)
 NS_INTERFACE_MAP_END_INHERITING(nsBufferedStream)
 
 NS_IMPL_CI_INTERFACE_GETTER(nsBufferedInputStream,
                             nsIInputStream,
                             nsIBufferedInputStream,
                             nsISeekableStream,
+                            nsITellableStream,
                             nsIStreamBufferAccess)
 
 nsBufferedInputStream::nsBufferedInputStream()
    : nsBufferedStream()
    , mMutex("nsBufferedInputStream::mMutex")
    , mIsIPCSerializable(true)
    , mIsAsyncInputStream(false)
    , mIsCloneableInputStream(false)
--- a/netwerk/base/nsBufferedStreams.h
+++ b/netwerk/base/nsBufferedStreams.h
@@ -21,16 +21,17 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
 class nsBufferedStream : public nsISeekableStream
 {
 public:
     NS_DECL_THREADSAFE_ISUPPORTS
     NS_DECL_NSISEEKABLESTREAM
+    NS_DECL_NSITELLABLESTREAM
 
     nsBufferedStream();
 
     nsresult Close();
 
 protected:
     virtual ~nsBufferedStream();
 
--- a/netwerk/base/nsFileStreams.cpp
+++ b/netwerk/base/nsFileStreams.cpp
@@ -56,16 +56,17 @@ nsFileStreamBase::~nsFileStreamBase()
     // We don't want to try to rewrind the stream when shutting down.
     mBehaviorFlags &= ~nsIFileInputStream::REOPEN_ON_REWIND;
 
     Close();
 }
 
 NS_IMPL_ISUPPORTS(nsFileStreamBase,
                   nsISeekableStream,
+                  nsITellableStream,
                   nsIFileMetadata)
 
 NS_IMETHODIMP
 nsFileStreamBase::Seek(int32_t whence, int64_t offset)
 {
     nsresult rv = DoPendingOpen();
     NS_ENSURE_SUCCESS(rv, rv);
 
@@ -437,16 +438,17 @@ NS_INTERFACE_MAP_BEGIN(nsFileInputStream
     NS_IMPL_QUERY_CLASSINFO(nsFileInputStream)
     NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsICloneableInputStream, IsCloneable())
 NS_INTERFACE_MAP_END_INHERITING(nsFileStreamBase)
 
 NS_IMPL_CI_INTERFACE_GETTER(nsFileInputStream,
                             nsIInputStream,
                             nsIFileInputStream,
                             nsISeekableStream,
+                            nsITellableStream,
                             nsILineInputStream)
 
 nsresult
 nsFileInputStream::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult)
 {
     NS_ENSURE_NO_AGGREGATION(aOuter);
 
     RefPtr<nsFileInputStream> stream = new nsFileInputStream();
--- a/netwerk/base/nsFileStreams.h
+++ b/netwerk/base/nsFileStreams.h
@@ -26,16 +26,17 @@
 class nsFileStreamBase : public nsISeekableStream,
                          public nsIFileMetadata
 {
 public:
     // Record refcount changes to ensure that streams are destroyed on
     // consistent threads when recording/replaying.
     NS_DECL_THREADSAFE_ISUPPORTS_WITH_RECORDING(mozilla::recordreplay::Behavior::Preserve)
     NS_DECL_NSISEEKABLESTREAM
+    NS_DECL_NSITELLABLESTREAM
     NS_DECL_NSIFILEMETADATA
 
     nsFileStreamBase();
 
 protected:
     virtual ~nsFileStreamBase();
 
     nsresult Close();
--- a/netwerk/base/nsInputStreamPump.cpp
+++ b/netwerk/base/nsInputStreamPump.cpp
@@ -569,20 +569,20 @@ nsInputStreamPump::OnStateTransfer()
         //       however, many do not... mailnews... stream converters...
         //       cough, cough.  the input stream pump is fairly tolerant
         //       in this regard; however, if an ODA does not consume any
         //       data from the stream, then we could potentially end up in
         //       an infinite loop.  we do our best here to try to catch
         //       such an error.  (see bug 189672)
 
         // in most cases this QI will succeed (mAsyncStream is almost always
-        // a nsPipeInputStream, which implements nsISeekableStream::Tell).
+        // a nsPipeInputStream, which implements nsITellableStream::Tell).
         int64_t offsetBefore;
-        nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mAsyncStream);
-        if (seekable && NS_FAILED(seekable->Tell(&offsetBefore))) {
+        nsCOMPtr<nsITellableStream> tellable = do_QueryInterface(mAsyncStream);
+        if (tellable && NS_FAILED(tellable->Tell(&offsetBefore))) {
             MOZ_ASSERT_UNREACHABLE("Tell failed on readable stream");
             offsetBefore = 0;
         }
 
         uint32_t odaAvail =
             avail > UINT32_MAX ?
             UINT32_MAX : uint32_t(avail);
 
@@ -597,21 +597,21 @@ nsInputStreamPump::OnStateTransfer()
             rv = mListener->OnDataAvailable(this, mListenerContext,
                                             mAsyncStream, mStreamOffset,
                                             odaAvail);
         }
 
         // don't enter this code if ODA failed or called Cancel
         if (NS_SUCCEEDED(rv) && NS_SUCCEEDED(mStatus)) {
             // test to see if this ODA failed to consume data
-            if (seekable) {
+            if (tellable) {
                 // NOTE: if Tell fails, which can happen if the stream is
                 // now closed, then we assume that everything was read.
                 int64_t offsetAfter;
-                if (NS_FAILED(seekable->Tell(&offsetAfter)))
+                if (NS_FAILED(tellable->Tell(&offsetAfter)))
                     offsetAfter = offsetBefore + odaAvail;
                 if (offsetAfter > offsetBefore)
                     mStreamOffset += (offsetAfter - offsetBefore);
                 else if (mSuspendCount == 0) {
                     //
                     // possible infinite loop if we continue pumping data!
                     //
                     // NOTE: although not allowed by nsIStreamListener, we
--- a/netwerk/base/nsMIMEInputStream.cpp
+++ b/netwerk/base/nsMIMEInputStream.cpp
@@ -42,16 +42,17 @@ class nsMIMEInputStream : public nsIMIME
 
 public:
     nsMIMEInputStream();
 
     NS_DECL_THREADSAFE_ISUPPORTS
     NS_DECL_NSIINPUTSTREAM
     NS_DECL_NSIMIMEINPUTSTREAM
     NS_DECL_NSISEEKABLESTREAM
+    NS_DECL_NSITELLABLESTREAM
     NS_DECL_NSIIPCSERIALIZABLEINPUTSTREAM
     NS_DECL_NSIASYNCINPUTSTREAM
     NS_DECL_NSIINPUTSTREAMCALLBACK
     NS_DECL_NSIINPUTSTREAMLENGTH
     NS_DECL_NSIASYNCINPUTSTREAMLENGTH
     NS_DECL_NSIINPUTSTREAMLENGTHCALLBACK
     NS_DECL_NSICLONEABLEINPUTSTREAM
 
@@ -93,16 +94,17 @@ NS_IMPL_RELEASE(nsMIMEInputStream)
 
 NS_IMPL_CLASSINFO(nsMIMEInputStream, nullptr, nsIClassInfo::THREADSAFE,
                   NS_MIMEINPUTSTREAM_CID)
 
 NS_INTERFACE_MAP_BEGIN(nsMIMEInputStream)
   NS_INTERFACE_MAP_ENTRY(nsIMIMEInputStream)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsIInputStream, nsIMIMEInputStream)
   NS_INTERFACE_MAP_ENTRY(nsISeekableStream)
+  NS_INTERFACE_MAP_ENTRY(nsITellableStream)
   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIIPCSerializableInputStream,
                                      IsIPCSerializable())
   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIAsyncInputStream,
                                      IsAsyncInputStream())
   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIInputStreamCallback,
                                      IsAsyncInputStream())
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIMIMEInputStream)
   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIInputStreamLength,
@@ -115,17 +117,18 @@ NS_INTERFACE_MAP_BEGIN(nsMIMEInputStream
                                      IsCloneableInputStream())
   NS_IMPL_QUERY_CLASSINFO(nsMIMEInputStream)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CI_INTERFACE_GETTER(nsMIMEInputStream,
                             nsIMIMEInputStream,
                             nsIAsyncInputStream,
                             nsIInputStream,
-                            nsISeekableStream)
+                            nsISeekableStream,
+                            nsITellableStream)
 
 nsMIMEInputStream::nsMIMEInputStream()
   : mStartedReading(false)
   , mMutex("nsMIMEInputStream::mMutex")
 {
 }
 
 NS_IMETHODIMP
@@ -307,23 +310,25 @@ nsMIMEInputStream::OnInputStreamReady(ns
 
         callback.swap(mAsyncWaitCallback);
   }
 
   MOZ_ASSERT(callback);
   return callback->OnInputStreamReady(this);
 }
 
-// nsISeekableStream
+// nsITellableStream
 NS_IMETHODIMP nsMIMEInputStream::Tell(int64_t *_retval)
 {
     INITSTREAMS;
-    nsCOMPtr<nsISeekableStream> stream = do_QueryInterface(mStream);
+    nsCOMPtr<nsITellableStream> stream = do_QueryInterface(mStream);
     return stream->Tell(_retval);
 }
+
+// nsISeekableStream
 NS_IMETHODIMP nsMIMEInputStream::SetEOF(void) {
     INITSTREAMS;
     nsCOMPtr<nsISeekableStream> stream = do_QueryInterface(mStream);
     return stream->SetEOF();
 }
 
 
 /**
--- a/netwerk/cache2/CacheFileInputStream.cpp
+++ b/netwerk/cache2/CacheFileInputStream.cpp
@@ -33,16 +33,17 @@ CacheFileInputStream::Release()
 
   return count;
 }
 
 NS_INTERFACE_MAP_BEGIN(CacheFileInputStream)
   NS_INTERFACE_MAP_ENTRY(nsIInputStream)
   NS_INTERFACE_MAP_ENTRY(nsIAsyncInputStream)
   NS_INTERFACE_MAP_ENTRY(nsISeekableStream)
+  NS_INTERFACE_MAP_ENTRY(nsITellableStream)
   NS_INTERFACE_MAP_ENTRY(mozilla::net::CacheFileChunkListener)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIInputStream)
 NS_INTERFACE_MAP_END
 
 CacheFileInputStream::CacheFileInputStream(CacheFile *aFile,
                                            nsISupports *aEntry,
                                            bool aAlternativeData)
   : mFile(aFile)
@@ -384,16 +385,24 @@ CacheFileInputStream::Seek(int32_t whenc
   mPos = newPos;
   EnsureCorrectChunk(false);
 
   LOG(("CacheFileInputStream::Seek() [this=%p, pos=%" PRId64 "]", this, mPos));
   return NS_OK;
 }
 
 NS_IMETHODIMP
+CacheFileInputStream::SetEOF()
+{
+  MOZ_ASSERT(false, "Don't call SetEOF on cache input stream");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// nsITellableStream
+NS_IMETHODIMP
 CacheFileInputStream::Tell(int64_t *_retval)
 {
   CacheFileAutoLock lock(mFile);
 
   if (mClosed) {
     LOG(("CacheFileInputStream::Tell() - Stream is closed. [this=%p]", this));
     return NS_BASE_STREAM_CLOSED;
   }
@@ -403,23 +412,16 @@ CacheFileInputStream::Tell(int64_t *_ret
   if (mAlternativeData) {
     *_retval -= mFile->mAltDataOffset;
   }
 
   LOG(("CacheFileInputStream::Tell() [this=%p, retval=%" PRId64 "]", this, *_retval));
   return NS_OK;
 }
 
-NS_IMETHODIMP
-CacheFileInputStream::SetEOF()
-{
-  MOZ_ASSERT(false, "Don't call SetEOF on cache input stream");
-  return NS_ERROR_NOT_IMPLEMENTED;
-}
-
 // CacheFileChunkListener
 nsresult
 CacheFileInputStream::OnChunkRead(nsresult aResult, CacheFileChunk *aChunk)
 {
   MOZ_CRASH("CacheFileInputStream::OnChunkRead should not be called!");
   return NS_ERROR_UNEXPECTED;
 }
 
--- a/netwerk/cache2/CacheFileInputStream.h
+++ b/netwerk/cache2/CacheFileInputStream.h
@@ -19,16 +19,17 @@ class CacheFile;
 class CacheFileInputStream : public nsIAsyncInputStream
                            , public nsISeekableStream
                            , public CacheFileChunkListener
 {
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIINPUTSTREAM
   NS_DECL_NSIASYNCINPUTSTREAM
   NS_DECL_NSISEEKABLESTREAM
+  NS_DECL_NSITELLABLESTREAM
 
 public:
   explicit CacheFileInputStream(CacheFile *aFile, nsISupports *aEntry,
                                 bool aAlternativeData);
 
   NS_IMETHOD OnChunkRead(nsresult aResult, CacheFileChunk *aChunk) override;
   NS_IMETHOD OnChunkWritten(nsresult aResult, CacheFileChunk *aChunk) override;
   NS_IMETHOD OnChunkAvailable(nsresult aResult, uint32_t aChunkIdx,
--- a/netwerk/cache2/CacheFileOutputStream.cpp
+++ b/netwerk/cache2/CacheFileOutputStream.cpp
@@ -35,16 +35,17 @@ CacheFileOutputStream::Release()
 
   return count;
 }
 
 NS_INTERFACE_MAP_BEGIN(CacheFileOutputStream)
   NS_INTERFACE_MAP_ENTRY(nsIOutputStream)
   NS_INTERFACE_MAP_ENTRY(nsIAsyncOutputStream)
   NS_INTERFACE_MAP_ENTRY(nsISeekableStream)
+  NS_INTERFACE_MAP_ENTRY(nsITellableStream)
   NS_INTERFACE_MAP_ENTRY(mozilla::net::CacheFileChunkListener)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIOutputStream)
 NS_INTERFACE_MAP_END
 
 CacheFileOutputStream::CacheFileOutputStream(CacheFile *aFile,
                                              CacheOutputCloseListener *aCloseListener,
                                              bool aAlternativeData)
   : mFile(aFile)
@@ -284,16 +285,27 @@ CacheFileOutputStream::Seek(int32_t when
   mPos = newPos;
   EnsureCorrectChunk(true);
 
   LOG(("CacheFileOutputStream::Seek() [this=%p, pos=%" PRId64 "]", this, mPos));
   return NS_OK;
 }
 
 NS_IMETHODIMP
+CacheFileOutputStream::SetEOF()
+{
+  MOZ_ASSERT(false, "CacheFileOutputStream::SetEOF() not implemented");
+  // Right now we don't use SetEOF(). If we ever need this method, we need
+  // to think about what to do with input streams that already points beyond
+  // new EOF.
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// nsITellableStream
+NS_IMETHODIMP
 CacheFileOutputStream::Tell(int64_t *_retval)
 {
   CacheFileAutoLock lock(mFile);
 
   if (mClosed) {
     LOG(("CacheFileOutputStream::Tell() - Stream is closed. [this=%p]", this));
     return NS_BASE_STREAM_CLOSED;
   }
@@ -303,26 +315,16 @@ CacheFileOutputStream::Tell(int64_t *_re
   if (mAlternativeData) {
     *_retval -= mFile->mAltDataOffset;
   }
 
   LOG(("CacheFileOutputStream::Tell() [this=%p, retval=%" PRId64 "]", this, *_retval));
   return NS_OK;
 }
 
-NS_IMETHODIMP
-CacheFileOutputStream::SetEOF()
-{
-  MOZ_ASSERT(false, "CacheFileOutputStream::SetEOF() not implemented");
-  // Right now we don't use SetEOF(). If we ever need this method, we need
-  // to think about what to do with input streams that already points beyond
-  // new EOF.
-  return NS_ERROR_NOT_IMPLEMENTED;
-}
-
 // CacheFileChunkListener
 nsresult
 CacheFileOutputStream::OnChunkRead(nsresult aResult, CacheFileChunk *aChunk)
 {
   MOZ_CRASH("CacheFileOutputStream::OnChunkRead should not be called!");
   return NS_ERROR_UNEXPECTED;
 }
 
--- a/netwerk/cache2/CacheFileOutputStream.h
+++ b/netwerk/cache2/CacheFileOutputStream.h
@@ -21,16 +21,17 @@ class CacheOutputCloseListener;
 class CacheFileOutputStream : public nsIAsyncOutputStream
                             , public nsISeekableStream
                             , public CacheFileChunkListener
 {
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIOUTPUTSTREAM
   NS_DECL_NSIASYNCOUTPUTSTREAM
   NS_DECL_NSISEEKABLESTREAM
+  NS_DECL_NSITELLABLESTREAM
 
 public:
   CacheFileOutputStream(CacheFile *aFile,
                         CacheOutputCloseListener *aCloseListener,
                         bool aAlternativeData);
 
   NS_IMETHOD OnChunkRead(nsresult aResult, CacheFileChunk *aChunk) override;
   NS_IMETHOD OnChunkWritten(nsresult aResult, CacheFileChunk *aChunk) override;
--- a/netwerk/protocol/http/HttpBaseChannel.cpp
+++ b/netwerk/protocol/http/HttpBaseChannel.cpp
@@ -915,24 +915,20 @@ HttpBaseChannel::EnsureUploadStreamIsClo
   NS_ENSURE_FALSE(mUploadCloneableCallback, NS_ERROR_UNEXPECTED);
 
   // We can immediately exec the callback if we don't have an upload stream.
   if (!mUploadStream) {
     aCallback->Run();
     return NS_OK;
   }
 
-  // Some nsSeekableStreams do not implement ::Seek() (see nsPipeInputStream).
-  // In this case, we must clone the uploadStream into a memory stream in order
-  // to have it seekable.  If the CloneUploadStream() will succeed, then
-  // synchronously invoke the callback to indicate we're already cloneable.
+  // Upload nsIInputStream must be cloneable and seekable in order to be
+  // processed by devtools network inspector.
   nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mUploadStream);
-  if (seekable &&
-      NS_SUCCEEDED(seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0)) &&
-      NS_InputStreamIsCloneable(mUploadStream)) {
+  if (seekable && NS_InputStreamIsCloneable(mUploadStream)) {
     aCallback->Run();
     return NS_OK;
   }
 
   nsCOMPtr<nsIStorageStream> storageStream;
   nsresult rv = NS_NewStorageStream(4096, UINT32_MAX,
                                     getter_AddRefs(storageStream));
   NS_ENSURE_SUCCESS(rv, rv);
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/css/css-conditional/at-supports-040.html.ini
@@ -0,0 +1,3 @@
+[at-supports-040.html]
+  type: reftest
+  prefs: [layout.css.supports-selector.enabled:true]
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/css/css-conditional/at-supports-041.html.ini
@@ -0,0 +1,3 @@
+[at-supports-041.html]
+  type: reftest
+  prefs: [layout.css.supports-selector.enabled:true]
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/css/css-conditional/at-supports-042.html.ini
@@ -0,0 +1,3 @@
+[at-supports-042.html]
+  type: reftest
+  prefs: [layout.css.supports-selector.enabled:true]
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/css/cssom/CSS.html.ini
@@ -0,0 +1,2 @@
+[CSS.html]
+  prefs: [layout.css.supports-selector.enabled:true]
--- a/xpcom/io/InputStreamLengthWrapper.cpp
+++ b/xpcom/io/InputStreamLengthWrapper.cpp
@@ -19,16 +19,18 @@ NS_IMPL_RELEASE(InputStreamLengthWrapper
 NS_INTERFACE_MAP_BEGIN(InputStreamLengthWrapper)
   NS_INTERFACE_MAP_ENTRY(nsIInputStream)
   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsICloneableInputStream,
                                      mWeakCloneableInputStream || !mInputStream)
   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIIPCSerializableInputStream,
                                      mWeakIPCSerializableInputStream || !mInputStream)
   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsISeekableStream,
                                      mWeakSeekableInputStream || !mInputStream)
+  NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsITellableStream,
+                                     mWeakTellableInputStream || !mInputStream)
   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIAsyncInputStream,
                                      mWeakAsyncInputStream || !mInputStream)
   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIInputStreamCallback,
                                      mWeakAsyncInputStream || !mInputStream)
   NS_INTERFACE_MAP_ENTRY(nsIInputStreamLength)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIInputStream)
 NS_INTERFACE_MAP_END
 
@@ -58,31 +60,33 @@ InputStreamLengthWrapper::MaybeWrap(alre
   return inputStream.forget();
 }
 
 InputStreamLengthWrapper::InputStreamLengthWrapper(already_AddRefed<nsIInputStream> aInputStream,
                                                    int64_t aLength)
   : mWeakCloneableInputStream(nullptr)
   , mWeakIPCSerializableInputStream(nullptr)
   , mWeakSeekableInputStream(nullptr)
+  , mWeakTellableInputStream(nullptr)
   , mWeakAsyncInputStream(nullptr)
   , mLength(aLength)
   , mConsumed(false)
   , mMutex("InputStreamLengthWrapper::mMutex")
 {
   MOZ_ASSERT(mLength >= 0);
 
   nsCOMPtr<nsIInputStream> inputStream = std::move(aInputStream);
   SetSourceStream(inputStream.forget());
 }
 
 InputStreamLengthWrapper::InputStreamLengthWrapper()
   : mWeakCloneableInputStream(nullptr)
   , mWeakIPCSerializableInputStream(nullptr)
   , mWeakSeekableInputStream(nullptr)
+  , mWeakTellableInputStream(nullptr)
   , mWeakAsyncInputStream(nullptr)
   , mLength(-1)
   , mConsumed(false)
   , mMutex("InputStreamLengthWrapper::mMutex")
 {}
 
 InputStreamLengthWrapper::~InputStreamLengthWrapper() = default;
 
@@ -107,16 +111,22 @@ InputStreamLengthWrapper::SetSourceStrea
   }
 
   nsCOMPtr<nsISeekableStream> seekableStream =
     do_QueryInterface(mInputStream);
   if (seekableStream && SameCOMIdentity(mInputStream, seekableStream)) {
     mWeakSeekableInputStream = seekableStream;
   }
 
+  nsCOMPtr<nsITellableStream> tellableStream =
+    do_QueryInterface(mInputStream);
+  if (tellableStream && SameCOMIdentity(mInputStream, tellableStream)) {
+    mWeakTellableInputStream = tellableStream;
+  }
+
   nsCOMPtr<nsIAsyncInputStream> asyncInputStream =
     do_QueryInterface(mInputStream);
   if (asyncInputStream && SameCOMIdentity(mInputStream, asyncInputStream)) {
     mWeakAsyncInputStream = asyncInputStream;
   }
 }
 
 // nsIInputStream interface
@@ -326,34 +336,36 @@ InputStreamLengthWrapper::Seek(int32_t a
   NS_ENSURE_STATE(mInputStream);
   NS_ENSURE_STATE(mWeakSeekableInputStream);
 
   mConsumed = true;
   return mWeakSeekableInputStream->Seek(aWhence, aOffset);
 }
 
 NS_IMETHODIMP
-InputStreamLengthWrapper::Tell(int64_t *aResult)
-{
-  NS_ENSURE_STATE(mInputStream);
-  NS_ENSURE_STATE(mWeakSeekableInputStream);
-
-  return mWeakSeekableInputStream->Tell(aResult);
-}
-
-NS_IMETHODIMP
 InputStreamLengthWrapper::SetEOF()
 {
   NS_ENSURE_STATE(mInputStream);
   NS_ENSURE_STATE(mWeakSeekableInputStream);
 
   mConsumed = true;
   return mWeakSeekableInputStream->SetEOF();
 }
 
+// nsITellableStream
+
+NS_IMETHODIMP
+InputStreamLengthWrapper::Tell(int64_t *aResult)
+{
+  NS_ENSURE_STATE(mInputStream);
+  NS_ENSURE_STATE(mWeakTellableInputStream);
+
+  return mWeakTellableInputStream->Tell(aResult);
+}
+
 // nsIInputStreamLength
 
 NS_IMETHODIMP
 InputStreamLengthWrapper::Length(int64_t* aLength)
 {
   NS_ENSURE_STATE(mInputStream);
   *aLength = mLength;
   return NS_OK;
--- a/xpcom/io/InputStreamLengthWrapper.h
+++ b/xpcom/io/InputStreamLengthWrapper.h
@@ -31,16 +31,17 @@ class InputStreamLengthWrapper final : p
 {
 public:
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIINPUTSTREAM
   NS_DECL_NSIASYNCINPUTSTREAM
   NS_DECL_NSICLONEABLEINPUTSTREAM
   NS_DECL_NSIIPCSERIALIZABLEINPUTSTREAM
   NS_DECL_NSISEEKABLESTREAM
+  NS_DECL_NSITELLABLESTREAM
   NS_DECL_NSIINPUTSTREAMCALLBACK
   NS_DECL_NSIINPUTSTREAMLENGTH
 
   // This method creates a InputStreamLengthWrapper around aInputStream if
   // this doesn't implement nsIInputStreamLength or
   // nsIInputStreamAsyncLength interface, but it implements
   // nsIAsyncInputStream. For this kind of streams,
   // InputStreamLengthHelper is not able to retrieve the length. This
@@ -64,16 +65,17 @@ private:
   SetSourceStream(already_AddRefed<nsIInputStream> aInputStream);
 
   nsCOMPtr<nsIInputStream> mInputStream;
 
   // Raw pointers because these are just QI of mInputStream.
   nsICloneableInputStream* mWeakCloneableInputStream;
   nsIIPCSerializableInputStream* mWeakIPCSerializableInputStream;
   nsISeekableStream* mWeakSeekableInputStream;
+  nsITellableStream* mWeakTellableInputStream;
   nsIAsyncInputStream* mWeakAsyncInputStream;
 
   int64_t mLength;
   bool mConsumed;
 
   mozilla::Mutex mMutex;
 
   // This is used for AsyncWait and it's protected by mutex.
--- a/xpcom/io/NonBlockingAsyncInputStream.cpp
+++ b/xpcom/io/NonBlockingAsyncInputStream.cpp
@@ -46,16 +46,18 @@ NS_INTERFACE_MAP_BEGIN(NonBlockingAsyncI
   NS_INTERFACE_MAP_ENTRY(nsIInputStream)
   NS_INTERFACE_MAP_ENTRY(nsIAsyncInputStream)
   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsICloneableInputStream,
                                      mWeakCloneableInputStream)
   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIIPCSerializableInputStream,
                                      mWeakIPCSerializableInputStream)
   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsISeekableStream,
                                      mWeakSeekableInputStream)
+  NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsITellableStream,
+                                     mWeakTellableInputStream)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIInputStream)
 NS_INTERFACE_MAP_END
 
 /* static */ nsresult
 NonBlockingAsyncInputStream::Create(already_AddRefed<nsIInputStream> aInputStream,
                                     nsIAsyncInputStream** aResult)
 {
   MOZ_DIAGNOSTIC_ASSERT(aResult);
@@ -83,16 +85,17 @@ NonBlockingAsyncInputStream::Create(alre
   return NS_OK;
 }
 
 NonBlockingAsyncInputStream::NonBlockingAsyncInputStream(already_AddRefed<nsIInputStream> aInputStream)
   : mInputStream(std::move(aInputStream))
   , mWeakCloneableInputStream(nullptr)
   , mWeakIPCSerializableInputStream(nullptr)
   , mWeakSeekableInputStream(nullptr)
+  , mWeakTellableInputStream(nullptr)
   , mLock("NonBlockingAsyncInputStream::mLock")
   , mClosed(false)
 {
   MOZ_ASSERT(mInputStream);
 
   nsCOMPtr<nsICloneableInputStream> cloneableStream =
     do_QueryInterface(mInputStream);
   if (cloneableStream && SameCOMIdentity(mInputStream, cloneableStream)) {
@@ -106,16 +109,22 @@ NonBlockingAsyncInputStream::NonBlocking
     mWeakIPCSerializableInputStream = serializableStream;
   }
 
   nsCOMPtr<nsISeekableStream> seekableStream =
     do_QueryInterface(mInputStream);
   if (seekableStream && SameCOMIdentity(mInputStream, seekableStream)) {
     mWeakSeekableInputStream = seekableStream;
   }
+
+  nsCOMPtr<nsITellableStream> tellableStream =
+    do_QueryInterface(mInputStream);
+  if (tellableStream && SameCOMIdentity(mInputStream, tellableStream)) {
+    mWeakTellableInputStream = tellableStream;
+  }
 }
 
 NonBlockingAsyncInputStream::~NonBlockingAsyncInputStream()
 {}
 
 NS_IMETHODIMP
 NonBlockingAsyncInputStream::Close()
 {
@@ -346,29 +355,31 @@ NonBlockingAsyncInputStream::ExpectedSer
 NS_IMETHODIMP
 NonBlockingAsyncInputStream::Seek(int32_t aWhence, int64_t aOffset)
 {
   NS_ENSURE_STATE(mWeakSeekableInputStream);
   return mWeakSeekableInputStream->Seek(aWhence, aOffset);
 }
 
 NS_IMETHODIMP
-NonBlockingAsyncInputStream::Tell(int64_t* aResult)
-{
-  NS_ENSURE_STATE(mWeakSeekableInputStream);
-  return mWeakSeekableInputStream->Tell(aResult);
-}
-
-NS_IMETHODIMP
 NonBlockingAsyncInputStream::SetEOF()
 {
   NS_ENSURE_STATE(mWeakSeekableInputStream);
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
+// nsITellableStream
+
+NS_IMETHODIMP
+NonBlockingAsyncInputStream::Tell(int64_t* aResult)
+{
+  NS_ENSURE_STATE(mWeakTellableInputStream);
+  return mWeakTellableInputStream->Tell(aResult);
+}
+
 void
 NonBlockingAsyncInputStream::RunAsyncWaitCallback(NonBlockingAsyncInputStream::AsyncWaitRunnable* aRunnable,
                                                   already_AddRefed<nsIInputStreamCallback> aCallback)
 {
   nsCOMPtr<nsIInputStreamCallback> callback = std::move(aCallback);
 
   {
     MutexAutoLock lock(mLock);
--- a/xpcom/io/NonBlockingAsyncInputStream.h
+++ b/xpcom/io/NonBlockingAsyncInputStream.h
@@ -29,16 +29,17 @@ class NonBlockingAsyncInputStream final 
 {
 public:
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIINPUTSTREAM
   NS_DECL_NSIASYNCINPUTSTREAM
   NS_DECL_NSICLONEABLEINPUTSTREAM
   NS_DECL_NSIIPCSERIALIZABLEINPUTSTREAM
   NS_DECL_NSISEEKABLESTREAM
+  NS_DECL_NSITELLABLESTREAM
 
   // |aInputStream| must be a non-blocking, non-async inputSteam.
   static nsresult
   Create(already_AddRefed<nsIInputStream> aInputStream,
          nsIAsyncInputStream** aAsyncInputStream);
 
 private:
   explicit NonBlockingAsyncInputStream(already_AddRefed<nsIInputStream> aInputStream);
@@ -51,16 +52,17 @@ private:
                        already_AddRefed<nsIInputStreamCallback> aCallback);
 
   nsCOMPtr<nsIInputStream> mInputStream;
 
   // Raw pointers because these are just QI of mInputStream.
   nsICloneableInputStream* MOZ_NON_OWNING_REF mWeakCloneableInputStream;
   nsIIPCSerializableInputStream* MOZ_NON_OWNING_REF mWeakIPCSerializableInputStream;
   nsISeekableStream* MOZ_NON_OWNING_REF mWeakSeekableInputStream;
+  nsITellableStream* MOZ_NON_OWNING_REF mWeakTellableInputStream;
 
   Mutex mLock;
 
   struct WaitClosureOnly
   {
     WaitClosureOnly(AsyncWaitRunnable* aRunnable, nsIEventTarget* aEventTarget);
 
     RefPtr<AsyncWaitRunnable> mRunnable;
--- a/xpcom/io/SlicedInputStream.cpp
+++ b/xpcom/io/SlicedInputStream.cpp
@@ -21,16 +21,18 @@ NS_IMPL_RELEASE(SlicedInputStream);
 NS_INTERFACE_MAP_BEGIN(SlicedInputStream)
   NS_INTERFACE_MAP_ENTRY(nsIInputStream)
   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsICloneableInputStream,
                                      mWeakCloneableInputStream || !mInputStream)
   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIIPCSerializableInputStream,
                                      mWeakIPCSerializableInputStream || !mInputStream)
   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsISeekableStream,
                                      mWeakSeekableInputStream || !mInputStream)
+  NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsITellableStream,
+                                     mWeakTellableInputStream || !mInputStream)
   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIAsyncInputStream,
                                      mWeakAsyncInputStream || !mInputStream)
   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIInputStreamCallback,
                                      mWeakAsyncInputStream || !mInputStream)
   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIInputStreamLength,
                                      mWeakInputStreamLength || !mInputStream)
   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIAsyncInputStreamLength,
                                      mWeakAsyncInputStreamLength || !mInputStream)
@@ -39,16 +41,17 @@ NS_INTERFACE_MAP_BEGIN(SlicedInputStream
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIInputStream)
 NS_INTERFACE_MAP_END
 
 SlicedInputStream::SlicedInputStream(already_AddRefed<nsIInputStream> aInputStream,
                                      uint64_t aStart, uint64_t aLength)
   : mWeakCloneableInputStream(nullptr)
   , mWeakIPCSerializableInputStream(nullptr)
   , mWeakSeekableInputStream(nullptr)
+  , mWeakTellableInputStream(nullptr)
   , mWeakAsyncInputStream(nullptr)
   , mWeakInputStreamLength(nullptr)
   , mWeakAsyncInputStreamLength(nullptr)
   , mStart(aStart)
   , mLength(aLength)
   , mCurPos(0)
   , mClosed(false)
   , mAsyncWaitFlags(0)
@@ -58,16 +61,17 @@ SlicedInputStream::SlicedInputStream(alr
   nsCOMPtr<nsIInputStream> inputStream = std::move(aInputStream);
   SetSourceStream(inputStream.forget());
 }
 
 SlicedInputStream::SlicedInputStream()
   : mWeakCloneableInputStream(nullptr)
   , mWeakIPCSerializableInputStream(nullptr)
   , mWeakSeekableInputStream(nullptr)
+  , mWeakTellableInputStream(nullptr)
   , mWeakAsyncInputStream(nullptr)
   , mStart(0)
   , mLength(0)
   , mCurPos(0)
   , mClosed(false)
   , mAsyncWaitFlags(0)
   , mAsyncWaitRequestedCount(0)
   , mMutex("SlicedInputStream::mMutex")
@@ -97,16 +101,22 @@ SlicedInputStream::SetSourceStream(alrea
   }
 
   nsCOMPtr<nsISeekableStream> seekableStream =
     do_QueryInterface(mInputStream);
   if (seekableStream && SameCOMIdentity(mInputStream, seekableStream)) {
     mWeakSeekableInputStream = seekableStream;
   }
 
+  nsCOMPtr<nsITellableStream> tellableStream =
+    do_QueryInterface(mInputStream);
+  if (tellableStream && SameCOMIdentity(mInputStream, tellableStream)) {
+    mWeakTellableInputStream = tellableStream;
+  }
+
   nsCOMPtr<nsIAsyncInputStream> asyncInputStream =
     do_QueryInterface(mInputStream);
   if (asyncInputStream && SameCOMIdentity(mInputStream, asyncInputStream)) {
     mWeakAsyncInputStream = asyncInputStream;
   }
 
   nsCOMPtr<nsIInputStreamLength> streamLength = do_QueryInterface(mInputStream);
   if (streamLength &&
@@ -539,24 +549,36 @@ SlicedInputStream::Seek(int32_t aWhence,
     return rv;
   }
 
   mCurPos = offset;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-SlicedInputStream::Tell(int64_t *aResult)
+SlicedInputStream::SetEOF()
 {
   NS_ENSURE_STATE(mInputStream);
   NS_ENSURE_STATE(mWeakSeekableInputStream);
 
+  mClosed = true;
+  return mWeakSeekableInputStream->SetEOF();
+}
+
+// nsITellableStream
+
+NS_IMETHODIMP
+SlicedInputStream::Tell(int64_t *aResult)
+{
+  NS_ENSURE_STATE(mInputStream);
+  NS_ENSURE_STATE(mWeakTellableInputStream);
+
   int64_t tell = 0;
 
-  nsresult rv = mWeakSeekableInputStream->Tell(&tell);
+  nsresult rv = mWeakTellableInputStream->Tell(&tell);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   if (tell < (int64_t)mStart) {
     *aResult = 0;
     return NS_OK;
   }
@@ -564,26 +586,16 @@ SlicedInputStream::Tell(int64_t *aResult
   *aResult = tell - mStart;
   if (*aResult > (int64_t)mLength) {
     *aResult = mLength;
   }
 
   return NS_OK;
 }
 
-NS_IMETHODIMP
-SlicedInputStream::SetEOF()
-{
-  NS_ENSURE_STATE(mInputStream);
-  NS_ENSURE_STATE(mWeakSeekableInputStream);
-
-  mClosed = true;
-  return mWeakSeekableInputStream->SetEOF();
-}
-
 // nsIInputStreamLength
 
 NS_IMETHODIMP
 SlicedInputStream::Length(int64_t* aLength)
 {
   NS_ENSURE_STATE(mInputStream);
   NS_ENSURE_STATE(mWeakInputStreamLength);
 
--- a/xpcom/io/SlicedInputStream.h
+++ b/xpcom/io/SlicedInputStream.h
@@ -31,16 +31,17 @@ class SlicedInputStream final : public n
 {
 public:
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIINPUTSTREAM
   NS_DECL_NSIASYNCINPUTSTREAM
   NS_DECL_NSICLONEABLEINPUTSTREAM
   NS_DECL_NSIIPCSERIALIZABLEINPUTSTREAM
   NS_DECL_NSISEEKABLESTREAM
+  NS_DECL_NSITELLABLESTREAM
   NS_DECL_NSIINPUTSTREAMCALLBACK
   NS_DECL_NSIINPUTSTREAMLENGTH
   NS_DECL_NSIASYNCINPUTSTREAMLENGTH
   NS_DECL_NSIINPUTSTREAMLENGTHCALLBACK
 
   // Create an input stream whose data comes from a slice of aInputStream.  The
   // slice begins at aStart bytes beyond aInputStream's current position, and
   // extends for a maximum of aLength bytes.  If aInputStream contains fewer
@@ -68,16 +69,17 @@ private:
   AdjustRange(uint64_t aRange);
 
   nsCOMPtr<nsIInputStream> mInputStream;
 
   // Raw pointers because these are just QI of mInputStream.
   nsICloneableInputStream* mWeakCloneableInputStream;
   nsIIPCSerializableInputStream* mWeakIPCSerializableInputStream;
   nsISeekableStream* mWeakSeekableInputStream;
+  nsITellableStream* mWeakTellableInputStream;
   nsIAsyncInputStream* mWeakAsyncInputStream;
   nsIInputStreamLength* mWeakInputStreamLength;
   nsIAsyncInputStreamLength* mWeakAsyncInputStreamLength;
 
   uint64_t mStart;
   uint64_t mLength;
   uint64_t mCurPos;
 
--- a/xpcom/io/moz.build
+++ b/xpcom/io/moz.build
@@ -28,16 +28,17 @@ XPIDL_SOURCES += [
     'nsIPipe.idl',
     'nsISafeOutputStream.idl',
     'nsIScriptableBase64Encoder.idl',
     'nsIScriptableInputStream.idl',
     'nsISeekableStream.idl',
     'nsIStorageStream.idl',
     'nsIStreamBufferAccess.idl',
     'nsIStringStream.idl',
+    'nsITellableStream.idl',
     'nsIUnicharInputStream.idl',
     'nsIUnicharLineInputStream.idl',
     'nsIUnicharOutputStream.idl',
 ]
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
     XPIDL_SOURCES += [
         'nsILocalFileMac.idl',
--- a/xpcom/io/nsISeekableStream.idl
+++ b/xpcom/io/nsISeekableStream.idl
@@ -1,25 +1,27 @@
 /* -*- 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 "nsITellableStream.idl"
+
 /*
  * nsISeekableStream
  *
  * Note that a stream might not implement all methods (e.g., a readonly stream 
  * won't implement setEOF)
  */
 
 #include "nsISupports.idl"
 
 [scriptable, uuid(8429d350-1040-4661-8b71-f2a6ba455980)]
-interface nsISeekableStream : nsISupports
+interface nsISeekableStream : nsITellableStream
 {
     /* 
      * Sets the stream pointer to the value of the 'offset' parameter 
      */
     const int32_t NS_SEEK_SET = 0;
 
     /*  
      * Sets the stream pointer to its current location plus the value 
@@ -48,27 +50,16 @@ interface nsISeekableStream : nsISupport
      *                 implementing stream.  A negative value causes seeking in 
      *                 the reverse direction.
      *
      *   @throws NS_BASE_STREAM_CLOSED if called on a closed stream.
      */
     void seek(in long whence, in long long offset);
 
     /**
-     *  tell
-     *
-     *  This method reports the current offset, in bytes, from the start of the 
-     *  stream. 
-     *
-     *   @throws NS_BASE_STREAM_CLOSED if called on a closed stream.
-     */
-    long long tell();
-
-
-    /**
      *  setEOF
      *
      *  This method truncates the stream at the current offset.
      *
      *   @throws NS_BASE_STREAM_CLOSED if called on a closed stream.
      */
     void setEOF();
 };
new file mode 100644
--- /dev/null
+++ b/xpcom/io/nsITellableStream.idl
@@ -0,0 +1,34 @@
+/* -*- 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/. */
+
+
+/*
+ * nsITellableStream
+ *
+ * This class is separate from nsISeekableStream in order to let streams to
+ * implement ::Tell() without implementing the whole nsISeekableStream
+ * interface. Callers can QI the stream to know what is implemented. This is
+ * mainly done for nsPipeInputStream.
+ *
+ *
+ * Implementing this interface, streams are able to expose the current offset
+ * via ::tell().
+ */
+
+#include "nsISupports.idl"
+
+[scriptable, uuid(ee942946-4538-45d2-bf05-ffdbf5932621)]
+interface nsITellableStream : nsISupports
+{
+    /**
+     *  tell
+     *
+     *  This method reports the current offset, in bytes, from the start of the
+     *  stream.
+     *
+     *   @throws NS_BASE_STREAM_CLOSED if called on a closed stream.
+     */
+    long long tell();
+};
--- a/xpcom/io/nsMultiplexInputStream.cpp
+++ b/xpcom/io/nsMultiplexInputStream.cpp
@@ -51,16 +51,17 @@ class nsMultiplexInputStream final
 {
 public:
   nsMultiplexInputStream();
 
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIINPUTSTREAM
   NS_DECL_NSIMULTIPLEXINPUTSTREAM
   NS_DECL_NSISEEKABLESTREAM
+  NS_DECL_NSITELLABLESTREAM
   NS_DECL_NSIIPCSERIALIZABLEINPUTSTREAM
   NS_DECL_NSICLONEABLEINPUTSTREAM
   NS_DECL_NSIASYNCINPUTSTREAM
   NS_DECL_NSIINPUTSTREAMCALLBACK
   NS_DECL_NSIINPUTSTREAMLENGTH
   NS_DECL_NSIASYNCINPUTSTREAMLENGTH
 
   // This is used for nsIAsyncInputStream::AsyncWait
@@ -72,25 +73,28 @@ public:
 
   struct StreamData
   {
     void Initialize(nsIInputStream* aStream, bool aBuffered)
     {
       mStream = aStream;
       mAsyncStream = do_QueryInterface(aStream);
       mSeekableStream = do_QueryInterface(aStream);
+      mTellableStream = do_QueryInterface(aStream);
       mBuffered = aBuffered;
     }
 
     nsCOMPtr<nsIInputStream> mStream;
 
     // This can be null.
     nsCOMPtr<nsIAsyncInputStream> mAsyncStream;
     // This can be null.
     nsCOMPtr<nsISeekableStream> mSeekableStream;
+    // This can be null.
+    nsCOMPtr<nsITellableStream> mTellableStream;
 
     // True if the stream is wrapped with nsIBufferedInputStream.
     bool mBuffered;
   };
 
   Mutex& GetLock()
   {
     return mLock;
@@ -99,34 +103,35 @@ public:
 private:
   ~nsMultiplexInputStream()
   {
   }
 
   nsresult
   AsyncWaitInternal();
 
-  // This method updates mSeekableStreams, mIPCSerializableStreams,
-  // mCloneableStreams and mAsyncInputStreams values.
+  // This method updates mSeekableStreams, mTellableStreams,
+  // mIPCSerializableStreams, mCloneableStreams and mAsyncInputStreams values.
   void UpdateQIMap(StreamData& aStream, int32_t aCount);
 
   struct MOZ_STACK_CLASS ReadSegmentsState
   {
     nsCOMPtr<nsIInputStream> mThisStream;
     uint32_t mOffset;
     nsWriteSegmentFun mWriter;
     void* mClosure;
     bool mDone;
   };
 
   static nsresult ReadSegCb(nsIInputStream* aIn, void* aClosure,
                             const char* aFromRawSegment, uint32_t aToOffset,
                             uint32_t aCount, uint32_t* aWriteCount);
 
   bool IsSeekable() const;
+  bool IsTellable() const;
   bool IsIPCSerializable() const;
   bool IsCloneable() const;
   bool IsAsyncInputStream() const;
   bool IsInputStreamLength() const;
   bool IsAsyncInputStreamLength() const;
 
   Mutex mLock; // Protects access to all data members.
 
@@ -140,16 +145,17 @@ private:
   uint32_t mAsyncWaitRequestedCount;
   nsCOMPtr<nsIEventTarget> mAsyncWaitEventTarget;
   nsCOMPtr<nsIInputStreamLengthCallback> mAsyncWaitLengthCallback;
 
   class AsyncWaitLengthHelper;
   RefPtr<AsyncWaitLengthHelper> mAsyncWaitLengthHelper;
 
   uint32_t mSeekableStreams;
+  uint32_t mTellableStreams;
   uint32_t mIPCSerializableStreams;
   uint32_t mCloneableStreams;
   uint32_t mAsyncInputStreams;
   uint32_t mInputStreamLengths;
   uint32_t mAsyncInputStreamLengths;
 };
 
 NS_IMPL_ADDREF(nsMultiplexInputStream)
@@ -157,16 +163,17 @@ NS_IMPL_RELEASE(nsMultiplexInputStream)
 
 NS_IMPL_CLASSINFO(nsMultiplexInputStream, nullptr, nsIClassInfo::THREADSAFE,
                   NS_MULTIPLEXINPUTSTREAM_CID)
 
 NS_INTERFACE_MAP_BEGIN(nsMultiplexInputStream)
   NS_INTERFACE_MAP_ENTRY(nsIMultiplexInputStream)
   NS_INTERFACE_MAP_ENTRY(nsIInputStream)
   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsISeekableStream, IsSeekable())
+  NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsITellableStream, IsTellable())
   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIIPCSerializableInputStream,
                                      IsIPCSerializable())
   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsICloneableInputStream,
                                      IsCloneable())
   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIAsyncInputStream,
                                      IsAsyncInputStream())
   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIInputStreamCallback,
                                      IsAsyncInputStream())
@@ -176,17 +183,18 @@ NS_INTERFACE_MAP_BEGIN(nsMultiplexInputS
                                      IsAsyncInputStreamLength())
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIMultiplexInputStream)
   NS_IMPL_QUERY_CLASSINFO(nsMultiplexInputStream)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CI_INTERFACE_GETTER(nsMultiplexInputStream,
                             nsIMultiplexInputStream,
                             nsIInputStream,
-                            nsISeekableStream)
+                            nsISeekableStream,
+                            nsITellableStream)
 
 static nsresult
 AvailableMaybeSeek(nsMultiplexInputStream::StreamData& aStream,
                    uint64_t* aResult)
 {
   nsresult rv = aStream.mStream->Available(aResult);
   if (rv == NS_BASE_STREAM_CLOSED) {
     // Blindly seek to the current position if Available() returns
@@ -200,40 +208,42 @@ AvailableMaybeSeek(nsMultiplexInputStrea
         rv = aStream.mStream->Available(aResult);
       }
     }
   }
   return rv;
 }
 
 static nsresult
-TellMaybeSeek(nsISeekableStream* aSeekable, int64_t* aResult)
+TellMaybeSeek(nsITellableStream* aTellable, nsISeekableStream* aSeekable,
+              int64_t* aResult)
 {
-  nsresult rv = aSeekable->Tell(aResult);
-  if (rv == NS_BASE_STREAM_CLOSED) {
+  nsresult rv = aTellable->Tell(aResult);
+  if (rv == NS_BASE_STREAM_CLOSED && aSeekable) {
     // Blindly seek to the current position if Tell() returns
     // NS_BASE_STREAM_CLOSED.
     // If nsIFileInputStream is closed in Read() due to CLOSE_ON_EOF flag,
     // Seek() could reopen the file if REOPEN_ON_REWIND flag is set.
     nsresult rvSeek = aSeekable->Seek(nsISeekableStream::NS_SEEK_CUR, 0);
     if (NS_SUCCEEDED(rvSeek)) {
-      rv = aSeekable->Tell(aResult);
+      rv = aTellable->Tell(aResult);
     }
   }
   return rv;
 }
 
 nsMultiplexInputStream::nsMultiplexInputStream()
   : mLock("nsMultiplexInputStream lock")
   , mCurrentStream(0)
   , mStartedReadingCurrent(false)
   , mStatus(NS_OK)
   , mAsyncWaitFlags(0)
   , mAsyncWaitRequestedCount(0)
   , mSeekableStreams(0)
+  , mTellableStreams(0)
   , mIPCSerializableStreams(0)
   , mCloneableStreams(0)
   , mAsyncInputStreams(0)
   , mInputStreamLengths(0)
   , mAsyncInputStreamLengths(0)
 {}
 
 NS_IMETHODIMP
@@ -574,16 +584,18 @@ nsMultiplexInputStream::Seek(int32_t aWh
       mCurrentStream = 0;
     }
     for (uint32_t i = 0; i < mStreams.Length(); ++i) {
       nsCOMPtr<nsISeekableStream> stream = mStreams[i].mSeekableStream;
       if (!stream) {
         return NS_ERROR_FAILURE;
       }
 
+      MOZ_ASSERT(mStreams[i].mTellableStream);
+
       // See if all remaining streams should be rewound
       if (remaining == 0) {
         if (i < oldCurrentStream ||
             (i == oldCurrentStream && oldStartedReadingCurrent)) {
           rv = stream->Seek(NS_SEEK_SET, 0);
           if (NS_WARN_IF(NS_FAILED(rv))) {
             return rv;
           }
@@ -594,17 +606,17 @@ nsMultiplexInputStream::Seek(int32_t aWh
       }
 
       // Get position in current stream
       int64_t streamPos;
       if (i > oldCurrentStream ||
           (i == oldCurrentStream && !oldStartedReadingCurrent)) {
         streamPos = 0;
       } else {
-        rv = TellMaybeSeek(stream, &streamPos);
+        rv = TellMaybeSeek(mStreams[i].mTellableStream, stream, &streamPos);
         if (NS_WARN_IF(NS_FAILED(rv))) {
           return rv;
         }
       }
 
       // See if we need to seek current stream forward or backward
       if (remaining < streamPos) {
         rv = stream->Seek(NS_SEEK_SET, remaining);
@@ -677,17 +689,18 @@ nsMultiplexInputStream::Seek(int32_t aWh
 
     return NS_OK;
   }
 
   if (aWhence == NS_SEEK_CUR && aOffset < 0) {
     int64_t remaining = -aOffset;
     for (uint32_t i = mCurrentStream; remaining && i != (uint32_t)-1; --i) {
       int64_t pos;
-      rv = TellMaybeSeek(mStreams[i].mSeekableStream, &pos);
+      rv = TellMaybeSeek(mStreams[i].mTellableStream,
+                         mStreams[i].mSeekableStream, &pos);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
 
       int64_t seek = XPCOM_MIN(pos, remaining);
 
       rv = mStreams[i].mSeekableStream->Seek(NS_SEEK_CUR, -seek);
       if (NS_WARN_IF(NS_FAILED(rv))) {
@@ -756,17 +769,17 @@ nsMultiplexInputStream::Seek(int32_t aWh
         remaining = 0;
       } else if (DeprecatedAbs(remaining) > streamPos) {
         if (i > oldCurrentStream ||
             (i == oldCurrentStream && !oldStartedReadingCurrent)) {
           // We're already at start so no need to seek this stream
           remaining += streamPos;
         } else {
           int64_t avail;
-          rv = TellMaybeSeek(stream, &avail);
+          rv = TellMaybeSeek(mStreams[i].mTellableStream, stream, &avail);
           if (NS_WARN_IF(NS_FAILED(rv))) {
             return rv;
           }
 
           int64_t newPos = streamPos + XPCOM_MIN(avail, DeprecatedAbs(remaining));
 
           rv = stream->Seek(NS_SEEK_END, -newPos);
           if (NS_WARN_IF(NS_FAILED(rv))) {
@@ -800,22 +813,23 @@ nsMultiplexInputStream::Tell(int64_t* aR
     return mStatus;
   }
 
   nsresult rv;
   int64_t ret64 = 0;
   uint32_t i, last;
   last = mStartedReadingCurrent ? mCurrentStream + 1 : mCurrentStream;
   for (i = 0; i < last; ++i) {
-    if (NS_WARN_IF(!mStreams[i].mSeekableStream)) {
+    if (NS_WARN_IF(!mStreams[i].mTellableStream)) {
       return NS_ERROR_NO_INTERFACE;
     }
 
     int64_t pos;
-    rv = TellMaybeSeek(mStreams[i].mSeekableStream, &pos);
+    rv = TellMaybeSeek(mStreams[i].mTellableStream, mStreams[i].mSeekableStream,
+                       &pos);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
     ret64 += pos;
   }
   *aResult =  ret64;
 
   return NS_OK;
@@ -1510,32 +1524,39 @@ nsMultiplexInputStream::AsyncWaitComplet
   }
 
 void
 nsMultiplexInputStream::UpdateQIMap(StreamData& aStream, int32_t aCount)
 {
   MOZ_ASSERT(aCount == -1 || aCount == 1);
 
   MAYBE_UPDATE_VALUE_REAL(mSeekableStreams, aStream.mSeekableStream)
+  MAYBE_UPDATE_VALUE_REAL(mTellableStreams, aStream.mTellableStream)
   MAYBE_UPDATE_VALUE(mIPCSerializableStreams, nsIIPCSerializableInputStream)
   MAYBE_UPDATE_VALUE(mCloneableStreams, nsICloneableInputStream)
   MAYBE_UPDATE_VALUE_REAL(mAsyncInputStreams, aStream.mAsyncStream)
   MAYBE_UPDATE_VALUE(mInputStreamLengths, nsIInputStreamLength)
   MAYBE_UPDATE_VALUE(mAsyncInputStreamLengths, nsIAsyncInputStreamLength)
 }
 
 #undef MAYBE_UPDATE_VALUE
 
 bool
 nsMultiplexInputStream::IsSeekable() const
 {
   return mStreams.Length() == mSeekableStreams;
 }
 
 bool
+nsMultiplexInputStream::IsTellable() const
+{
+  return mStreams.Length() == mTellableStreams;
+}
+
+bool
 nsMultiplexInputStream::IsIPCSerializable() const
 {
   return mStreams.Length() == mIPCSerializableStreams;
 }
 
 bool
 nsMultiplexInputStream::IsCloneable() const
 {
--- a/xpcom/io/nsPipe3.cpp
+++ b/xpcom/io/nsPipe3.cpp
@@ -7,17 +7,17 @@
 #include <algorithm>
 #include "mozilla/Attributes.h"
 #include "mozilla/IntegerPrintfMacros.h"
 #include "mozilla/ReentrantMonitor.h"
 #include "nsIBufferedStreams.h"
 #include "nsICloneableInputStream.h"
 #include "nsIPipe.h"
 #include "nsIEventTarget.h"
-#include "nsISeekableStream.h"
+#include "nsITellableStream.h"
 #include "mozilla/RefPtr.h"
 #include "nsSegmentedBuffer.h"
 #include "nsStreamUtils.h"
 #include "nsCOMPtr.h"
 #include "nsCRT.h"
 #include "mozilla/Logging.h"
 #include "nsIClassInfoImpl.h"
 #include "nsAlgorithm.h"
@@ -137,30 +137,30 @@ struct nsPipeReadState
   bool     mNeedDrain;
 };
 
 //-----------------------------------------------------------------------------
 
 // an input end of a pipe (maintained as a list of refs within the pipe)
 class nsPipeInputStream final
   : public nsIAsyncInputStream
-  , public nsISeekableStream
+  , public nsITellableStream
   , public nsISearchableInputStream
   , public nsICloneableInputStream
   , public nsIClassInfo
   , public nsIBufferedInputStream
 {
 public:
   // Pipe input streams preserve their refcount changes when record/replaying,
   // as otherwise the thread which destroys the stream may vary between
   // recording and replaying.
   NS_DECL_THREADSAFE_ISUPPORTS_WITH_RECORDING(recordreplay::Behavior::Preserve)
   NS_DECL_NSIINPUTSTREAM
   NS_DECL_NSIASYNCINPUTSTREAM
-  NS_DECL_NSISEEKABLESTREAM
+  NS_DECL_NSITELLABLESTREAM
   NS_DECL_NSISEARCHABLEINPUTSTREAM
   NS_DECL_NSICLONEABLEINPUTSTREAM
   NS_DECL_NSICLASSINFO
   NS_DECL_NSIBUFFEREDINPUTSTREAM
 
   explicit nsPipeInputStream(nsPipe* aPipe)
     : mPipe(aPipe)
     , mLogicalOffset(0)
@@ -1245,32 +1245,32 @@ nsPipeEvents::~nsPipeEvents()
 //-----------------------------------------------------------------------------
 
 NS_IMPL_ADDREF(nsPipeInputStream);
 NS_IMPL_RELEASE(nsPipeInputStream);
 
 NS_INTERFACE_TABLE_HEAD(nsPipeInputStream)
   NS_INTERFACE_TABLE_BEGIN
     NS_INTERFACE_TABLE_ENTRY(nsPipeInputStream, nsIAsyncInputStream)
-    NS_INTERFACE_TABLE_ENTRY(nsPipeInputStream, nsISeekableStream)
+    NS_INTERFACE_TABLE_ENTRY(nsPipeInputStream, nsITellableStream)
     NS_INTERFACE_TABLE_ENTRY(nsPipeInputStream, nsISearchableInputStream)
     NS_INTERFACE_TABLE_ENTRY(nsPipeInputStream, nsICloneableInputStream)
     NS_INTERFACE_TABLE_ENTRY(nsPipeInputStream, nsIBufferedInputStream)
     NS_INTERFACE_TABLE_ENTRY(nsPipeInputStream, nsIClassInfo)
     NS_INTERFACE_TABLE_ENTRY_AMBIGUOUS(nsPipeInputStream, nsIInputStream,
                                        nsIAsyncInputStream)
     NS_INTERFACE_TABLE_ENTRY_AMBIGUOUS(nsPipeInputStream, nsISupports,
                                        nsIAsyncInputStream)
   NS_INTERFACE_TABLE_END
 NS_INTERFACE_TABLE_TAIL
 
 NS_IMPL_CI_INTERFACE_GETTER(nsPipeInputStream,
                             nsIInputStream,
                             nsIAsyncInputStream,
-                            nsISeekableStream,
+                            nsITellableStream,
                             nsISearchableInputStream,
                             nsICloneableInputStream,
                             nsIBufferedInputStream)
 
 NS_IMPL_THREADSAFE_CI(nsPipeInputStream)
 
 NS_IMETHODIMP
 nsPipeInputStream::Init(nsIInputStream*, uint32_t)
@@ -1521,42 +1521,29 @@ nsPipeInputStream::AsyncWait(nsIInputStr
       mCallback = aCallback;
       mCallbackFlags = aFlags;
     }
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsPipeInputStream::Seek(int32_t aWhence, int64_t aOffset)
-{
-  return NS_ERROR_NOT_IMPLEMENTED;
-}
-
-NS_IMETHODIMP
 nsPipeInputStream::Tell(int64_t* aOffset)
 {
   ReentrantMonitorAutoEnter mon(mPipe->mReentrantMonitor);
 
   // return error if closed
   if (!mReadState.mAvailable && NS_FAILED(Status(mon))) {
     return Status(mon);
   }
 
   *aOffset = mLogicalOffset;
   return NS_OK;
 }
 
-NS_IMETHODIMP
-nsPipeInputStream::SetEOF()
-{
-  MOZ_ASSERT_UNREACHABLE("nsPipeInputStream::SetEOF");
-  return NS_ERROR_NOT_IMPLEMENTED;
-}
-
 static bool strings_equal(bool aIgnoreCase,
                           const char* aS1, const char* aS2, uint32_t aLen)
 {
   return aIgnoreCase
     ? !nsCRT::strncasecmp(aS1, aS2, aLen) : !strncmp(aS1, aS2, aLen);
 }
 
 NS_IMETHODIMP
--- a/xpcom/io/nsStorageStream.cpp
+++ b/xpcom/io/nsStorageStream.cpp
@@ -344,16 +344,17 @@ public:
       mSegmentSize(aSegmentSize), mLogicalCursor(0),
       mStatus(NS_OK)
   {
   }
 
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIINPUTSTREAM
   NS_DECL_NSISEEKABLESTREAM
+  NS_DECL_NSITELLABLESTREAM
   NS_DECL_NSIIPCSERIALIZABLEINPUTSTREAM
   NS_DECL_NSICLONEABLEINPUTSTREAM
 
 private:
   ~nsStorageInputStream()
   {
   }
 
@@ -379,16 +380,17 @@ private:
   {
     return aPosition & (mSegmentSize - 1);
   }
 };
 
 NS_IMPL_ISUPPORTS(nsStorageInputStream,
                   nsIInputStream,
                   nsISeekableStream,
+                  nsITellableStream,
                   nsIIPCSerializableInputStream,
                   nsICloneableInputStream)
 
 NS_IMETHODIMP
 nsStorageStream::NewInputStream(int32_t aStartingOffset,
                                 nsIInputStream** aInputStream)
 {
   if (NS_WARN_IF(!mSegmentedBuffer)) {
--- a/xpcom/io/nsStringStream.cpp
+++ b/xpcom/io/nsStringStream.cpp
@@ -39,16 +39,17 @@ class nsStringInputStream final
   , public nsIIPCSerializableInputStream
   , public nsICloneableInputStream
 {
 public:
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIINPUTSTREAM
   NS_DECL_NSISTRINGINPUTSTREAM
   NS_DECL_NSISEEKABLESTREAM
+  NS_DECL_NSITELLABLESTREAM
   NS_DECL_NSISUPPORTSPRIMITIVE
   NS_DECL_NSISUPPORTSCSTRING
   NS_DECL_NSIIPCSERIALIZABLEINPUTSTREAM
   NS_DECL_NSICLONEABLEINPUTSTREAM
 
   nsStringInputStream()
     : mOffset(0)
   {
@@ -104,23 +105,25 @@ NS_IMPL_RELEASE(nsStringInputStream)
 
 NS_IMPL_CLASSINFO(nsStringInputStream, nullptr, nsIClassInfo::THREADSAFE,
                   NS_STRINGINPUTSTREAM_CID)
 NS_IMPL_QUERY_INTERFACE_CI(nsStringInputStream,
                            nsIStringInputStream,
                            nsIInputStream,
                            nsISupportsCString,
                            nsISeekableStream,
+                           nsITellableStream,
                            nsIIPCSerializableInputStream,
                            nsICloneableInputStream)
 NS_IMPL_CI_INTERFACE_GETTER(nsStringInputStream,
                             nsIStringInputStream,
                             nsIInputStream,
                             nsISupportsCString,
                             nsISeekableStream,
+                            nsITellableStream,
                             nsICloneableInputStream)
 
 /////////
 // nsISupportsCString implementation
 /////////
 
 NS_IMETHODIMP
 nsStringInputStream::GetType(uint16_t* aType)
@@ -318,37 +321,41 @@ nsStringInputStream::Seek(int32_t aWhenc
     return NS_ERROR_INVALID_ARG;
   }
 
   mOffset = (uint32_t)newPos;
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsStringInputStream::SetEOF()
+{
+  if (Closed()) {
+    return NS_BASE_STREAM_CLOSED;
+  }
+
+  mOffset = Length();
+  return NS_OK;
+}
+
+/////////
+// nsITellableStream implementation
+/////////
+
+NS_IMETHODIMP
 nsStringInputStream::Tell(int64_t* aOutWhere)
 {
   if (Closed()) {
     return NS_BASE_STREAM_CLOSED;
   }
 
   *aOutWhere = mOffset;
   return NS_OK;
 }
 
-NS_IMETHODIMP
-nsStringInputStream::SetEOF()
-{
-  if (Closed()) {
-    return NS_BASE_STREAM_CLOSED;
-  }
-
-  mOffset = Length();
-  return NS_OK;
-}
-
 /////////
 // nsIIPCSerializableInputStream implementation
 /////////
 
 void
 nsStringInputStream::Serialize(InputStreamParams& aParams,
                                FileDescriptorArray& /* aFDs */)
 {
--- a/xpcom/io/nsStringStream.h
+++ b/xpcom/io/nsStringStream.h
@@ -11,30 +11,31 @@
 #include "nsString.h"
 #include "nsMemory.h"
 
 /**
  * Implements:
  *   nsIStringInputStream
  *   nsIInputStream
  *   nsISeekableStream
+ *   nsITellableStream
  *   nsISupportsCString
  */
 #define NS_STRINGINPUTSTREAM_CONTRACTID "@mozilla.org/io/string-input-stream;1"
 #define NS_STRINGINPUTSTREAM_CID                     \
 { /* 0abb0835-5000-4790-af28-61b3ba17c295 */         \
     0x0abb0835,                                      \
     0x5000,                                          \
     0x4790,                                          \
     {0xaf, 0x28, 0x61, 0xb3, 0xba, 0x17, 0xc2, 0x95} \
 }
 
 /**
  * Factory method to get an nsInputStream from a byte buffer.  Result will
- * implement nsIStringInputStream and nsISeekableStream.
+ * implement nsIStringInputStream, nsITellableStream and nsISeekableStream.
  *
  * If aAssignment is NS_ASSIGNMENT_COPY, then the resulting stream holds a copy
  * of the given buffer (aStringToRead), and the caller is free to discard
  * aStringToRead after this function returns.
  *
  * If aAssignment is NS_ASSIGNMENT_DEPEND, then the resulting stream refers
  * directly to the given buffer (aStringToRead), so the caller must ensure that
  * the buffer remains valid for the lifetime of the stream object.  Use with
@@ -49,17 +50,17 @@
  */
 extern nsresult
 NS_NewByteInputStream(nsIInputStream** aStreamResult,
                       const char* aStringToRead, int32_t aLength = -1,
                       nsAssignmentType aAssignment = NS_ASSIGNMENT_DEPEND);
 
 /**
  * Factory method to get an nsInputStream from an nsACString.  Result will
- * implement nsIStringInputStream and nsISeekableStream.
+ * implement nsIStringInputStream, nsTellableStream and nsISeekableStream.
  */
 extern nsresult
 NS_NewCStringInputStream(nsIInputStream** aStreamResult,
                          const nsACString& aStringToRead);
 extern nsresult
 NS_NewCStringInputStream(nsIInputStream** aStreamResult,
                          nsCString&& aStringToRead);
 
--- a/xpcom/tests/gtest/TestPipes.cpp
+++ b/xpcom/tests/gtest/TestPipes.cpp
@@ -14,17 +14,17 @@
 #include "nsIAsyncInputStream.h"
 #include "nsIAsyncOutputStream.h"
 #include "nsIBufferedStreams.h"
 #include "nsIClassInfo.h"
 #include "nsICloneableInputStream.h"
 #include "nsIInputStream.h"
 #include "nsIOutputStream.h"
 #include "nsIPipe.h"
-#include "nsISeekableStream.h"
+#include "nsITellableStream.h"
 #include "nsIThread.h"
 #include "nsIRunnable.h"
 #include "nsStreamUtils.h"
 #include "nsString.h"
 #include "nsThreadUtils.h"
 #include "prinrval.h"
 
 using namespace mozilla;
@@ -1073,17 +1073,17 @@ TEST(Pipes, Interfaces)
   nsCOMPtr<nsIOutputStream> writer;
 
   nsresult rv = NS_NewPipe(getter_AddRefs(reader), getter_AddRefs(writer));
   ASSERT_TRUE(NS_SUCCEEDED(rv));
 
   nsCOMPtr<nsIAsyncInputStream> readerType1 = do_QueryInterface(reader);
   ASSERT_TRUE(readerType1);
 
-  nsCOMPtr<nsISeekableStream> readerType2 = do_QueryInterface(reader);
+  nsCOMPtr<nsITellableStream> readerType2 = do_QueryInterface(reader);
   ASSERT_TRUE(readerType2);
 
   nsCOMPtr<nsISearchableInputStream> readerType3 = do_QueryInterface(reader);
   ASSERT_TRUE(readerType3);
 
   nsCOMPtr<nsICloneableInputStream> readerType4 = do_QueryInterface(reader);
   ASSERT_TRUE(readerType4);