Merge inbound to mozilla-central. a=merge
authorCiure Andrei <aciure@mozilla.com>
Thu, 18 Oct 2018 19:37:23 +0300
changeset 500412 733484af9034cb0372ed1fd745ec3d321d48e8b9
parent 500382 5be96992e33cfdbd85374a419aeb1b8734a2edab (current diff)
parent 500411 cec3c13a4ad5edbcc528d86abe8619ae2f9051fa (diff)
child 500413 a58112b87de672f400dc5d23ad8bac80d292a2fd
child 500429 fc8ae615f7268108e364437b261bccc2ba65c1bb
push id1864
push userffxbld-merge
push dateMon, 03 Dec 2018 15:51:40 +0000
treeherdermozilla-release@f040763d99ad [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone64.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 inbound to mozilla-central. a=merge
--- 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);