Merge autoland to central, a=merge
authorWes Kocher <wkocher@mozilla.com>
Fri, 03 Mar 2017 17:27:33 -0800
changeset 345842 80c06df83395314697d464f88f8daa98bf05465c
parent 345825 9a117f52a1371a223ff79a1f67a4b8cf08f9b69b (current diff)
parent 345841 d67caca960aa9f4b1b59d0d79d4ecfb83eb18e68 (diff)
child 345843 84ef4d4530eeac074d403bf9a6251710bffa076a
child 345917 b691557cb7a31c942cbdf3c1388140f60c24fdf9
child 346005 76a96fd623d4304b9a6149deaf723c80e66d351f
push id31448
push userkwierso@gmail.com
push dateSat, 04 Mar 2017 01:27:38 +0000
treeherdermozilla-central@80c06df83395 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone54.0a1
first release with
nightly linux32
80c06df83395 / 54.0a1 / 20170304110210 / files
nightly linux64
80c06df83395 / 54.0a1 / 20170304110210 / files
nightly mac
80c06df83395 / 54.0a1 / 20170304030205 / files
nightly win32
80c06df83395 / 54.0a1 / 20170304030205 / files
nightly win64
80c06df83395 / 54.0a1 / 20170304030205 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge autoland to central, a=merge MozReview-Commit-ID: tSEg2GfeWi
--- a/browser/components/newtab/PlacesProvider.jsm
+++ b/browser/components/newtab/PlacesProvider.jsm
@@ -110,17 +110,17 @@ Links.prototype = {
       // See toolkit/components/places/tests/unit/test_frecency_observers.js
       gLinks.emit("manyLinksChanged");
     },
 
     onVisit(aURI, aVisitId, aTime, aSessionId, aReferrerVisitId, aTransitionType,
             aGuid, aHidden, aVisitCount, aTyped, aLastKnownTitle) {
       // For new visits, if we're not batch processing, notify for a title update
       if (!this._batchProcessingDepth && aVisitCount == 1 && aLastKnownTitle) {
-        this.onTitleChanged(aURI, aTitle, aGuid);
+        this.onTitleChanged(aURI, aLastKnownTitle, aGuid);
       }
     },
 
     onTitleChanged: function historyObserver_onTitleChanged(aURI, aNewTitle) {
       if (NewTabUtils.linkChecker.checkLoadURI(aURI.spec)) {
         gLinks.emit("linkChanged", {
           url: aURI.spec,
           title: aNewTitle
--- a/dom/media/MediaFormatReader.cpp
+++ b/dom/media/MediaFormatReader.cpp
@@ -369,16 +369,101 @@ MediaFormatReader::ShutdownPromisePool::
       MOZ_DIAGNOSTIC_ASSERT(mPromises.Contains(aPromise));
       mPromises.RemoveEntry(aPromise);
       if (mShutdown && mPromises.Count() == 0) {
         mOnShutdownComplete->Resolve(true, __func__);
       }
     });
 }
 
+void
+MediaFormatReader::DecoderData::ShutdownDecoder()
+{
+  MutexAutoLock lock(mMutex);
+
+  if (!mDecoder) {
+    // No decoder to shut down.
+    return;
+  }
+
+  if (mFlushing) {
+    // Flush is is in action. Shutdown will be initiated after flush completes.
+    MOZ_DIAGNOSTIC_ASSERT(mShutdownPromise);
+    mOwner->mShutdownPromisePool->Track(mShutdownPromise->Ensure(__func__));
+    // The order of decoder creation and shutdown is handled by LocalAllocPolicy
+    // and ShutdownPromisePool. MFR can now reset these members to a fresh state
+    // and be ready to create new decoders again without explicitly waiting for
+    // flush/shutdown to complete.
+    mShutdownPromise = nullptr;
+    mFlushing = false;
+  } else {
+    // No flush is in action. We can shut down the decoder now.
+    mOwner->mShutdownPromisePool->Track(mDecoder->Shutdown());
+  }
+
+  // mShutdownPromisePool will handle the order of decoder shutdown so
+  // we can forget mDecoder and be ready to create a new one.
+  mDecoder = nullptr;
+  mDescription = "shutdown";
+  mOwner->ScheduleUpdate(mType == MediaData::AUDIO_DATA
+                         ? TrackType::kAudioTrack
+                         : TrackType::kVideoTrack);
+}
+
+void
+MediaFormatReader::DecoderData::Flush()
+{
+  if (mFlushing || mFlushed) {
+    // Flush still pending or already flushed, nothing more to do.
+    return;
+  }
+  mDecodeRequest.DisconnectIfExists();
+  mDrainRequest.DisconnectIfExists();
+  mDrainState = DrainState::None;
+  CancelWaitingForKey();
+  mOutput.Clear();
+  mNumSamplesInput = 0;
+  mNumSamplesOutput = 0;
+  mSizeOfQueue = 0;
+  if (mDecoder) {
+    TrackType type = mType == MediaData::AUDIO_DATA
+                     ? TrackType::kAudioTrack
+                     : TrackType::kVideoTrack;
+    mFlushing = true;
+    MOZ_DIAGNOSTIC_ASSERT(!mShutdownPromise);
+    mShutdownPromise = new SharedShutdownPromiseHolder();
+    RefPtr<SharedShutdownPromiseHolder> p = mShutdownPromise;
+    RefPtr<MediaDataDecoder> d = mDecoder;
+    mDecoder->Flush()
+      ->Then(mOwner->OwnerThread(), __func__,
+             [type, this, p, d]() {
+               if (!p->IsEmpty()) {
+                 // Shutdown happened before flush completes. Let's continue to
+                 // shut down the decoder. Note we don't access |this| because
+                 // this decoder is no longer managed by MFR::DecoderData.
+                 d->Shutdown()->ChainTo(p->Steal(), __func__);
+                 return;
+               }
+               mFlushing = false;
+               mShutdownPromise = nullptr;
+               mOwner->ScheduleUpdate(type);
+             },
+             [type, this, p, d](const MediaResult& aError) {
+               if (!p->IsEmpty()) {
+                 d->Shutdown()->ChainTo(p->Steal(), __func__);
+                 return;
+               }
+               mFlushing = false;
+               mShutdownPromise = nullptr;
+               mOwner->NotifyError(type, aError);
+             });
+  }
+  mFlushed = true;
+}
+
 class MediaFormatReader::DecoderFactory
 {
   using InitPromise = MediaDataDecoder::InitPromise;
   using TokenPromise = GlobalAllocPolicy::Promise;
   using Token = GlobalAllocPolicy::Token;
 
 public:
   explicit DecoderFactory(MediaFormatReader* aOwner)
@@ -1042,88 +1127,53 @@ MediaFormatReader::Shutdown()
     mVideo.RejectPromise(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
   }
 
   if (HasAudio()) {
     mAudio.ResetDemuxer();
     mAudio.mTrackDemuxer->BreakCycles();
     mAudio.mTrackDemuxer = nullptr;
     mAudio.ResetState();
-    mShutdownPromisePool->Track(ShutdownDecoderWithPromise(TrackInfo::kAudioTrack));
+    ShutdownDecoder(TrackInfo::kAudioTrack);
   }
 
   if (HasVideo()) {
     mVideo.ResetDemuxer();
     mVideo.mTrackDemuxer->BreakCycles();
     mVideo.mTrackDemuxer = nullptr;
     mVideo.ResetState();
-    mShutdownPromisePool->Track(ShutdownDecoderWithPromise(TrackInfo::kVideoTrack));
+    ShutdownDecoder(TrackInfo::kVideoTrack);
   }
 
   mShutdownPromisePool->Track(mDemuxer->Shutdown());
   mDemuxer = nullptr;
 
   mCompositorUpdatedListener.DisconnectIfExists();
   mOnTrackWaitingForKeyListener.Disconnect();
 
   mShutdown = true;
   return mShutdownPromisePool->Shutdown()
     ->Then(OwnerThread(), __func__, this,
            &MediaFormatReader::TearDownDecoders,
            &MediaFormatReader::TearDownDecoders);
 }
 
-RefPtr<ShutdownPromise>
-MediaFormatReader::ShutdownDecoderWithPromise(TrackType aTrack)
-{
-  LOGV("%s", TrackTypeToStr(aTrack));
-
-  auto& decoder = GetDecoderData(aTrack);
-  if (!decoder.mFlushed && decoder.mDecoder) {
-    // The decoder has yet to be flushed.
-    // We always flush the decoder prior to a shutdown to ensure that all the
-    // potentially pending operations on the decoder are completed.
-    decoder.Flush();
-    return decoder.mShutdownPromise.Ensure(__func__);
-  }
-
-  if (decoder.mFlushRequest.Exists() || decoder.mShutdownRequest.Exists()) {
-    // Let the current flush or shutdown operation complete, Flush will continue
-    // shutting down the current decoder now that the shutdown promise is set.
-    return decoder.mShutdownPromise.Ensure(__func__);
-  }
-
-  if (!decoder.mDecoder) {
-    // Shutdown any decoders that may be in the process of being initialized
-    // in the Decoder Factory.
-    // This will be a no-op until we're processing the final decoder shutdown
-    // prior to the MediaFormatReader being shutdown.
-    mDecoderFactory->ShutdownDecoder(aTrack);
-    return ShutdownPromise::CreateAndResolve(true, __func__);
-  }
-
-  // Finally, let's just shut down the currently active decoder.
-  decoder.ShutdownDecoder();
-  return decoder.mShutdownPromise.Ensure(__func__);
-}
-
 void
 MediaFormatReader::ShutdownDecoder(TrackType aTrack)
 {
-  LOG("%s", TrackTypeToStr(aTrack));
+  LOGV("%s", TrackTypeToStr(aTrack));
+
+  // Shut down the pending decoder if any.
+  mDecoderFactory->ShutdownDecoder(aTrack);
+
   auto& decoder = GetDecoderData(aTrack);
-  if (!decoder.mDecoder) {
-    LOGV("Already shut down");
-    return;
-  }
-  if (!decoder.mShutdownPromise.IsEmpty()) {
-    LOGV("Shutdown already in progress");
-    return;
-  }
-  Unused << ShutdownDecoderWithPromise(aTrack);
+  // Flush the decoder if necessary.
+  decoder.Flush();
+  // Shut down the decoder if any.
+  decoder.ShutdownDecoder();
 }
 
 RefPtr<ShutdownPromise>
 MediaFormatReader::TearDownDecoders()
 {
   if (mAudio.mTaskQueue) {
     mAudio.mTaskQueue->BeginShutdown();
     mAudio.mTaskQueue->AwaitShutdownAndIdle();
@@ -1887,17 +1937,17 @@ MediaFormatReader::DecodeDemuxedSamples(
 void
 MediaFormatReader::HandleDemuxedSamples(
   TrackType aTrack, AbstractMediaDecoder::AutoNotifyDecoded& aA)
 {
   MOZ_ASSERT(OnTaskQueue());
 
   auto& decoder = GetDecoderData(aTrack);
 
-  if (decoder.mFlushRequest.Exists() || decoder.mShutdownRequest.Exists()) {
+  if (decoder.mFlushing) {
     LOGV("Decoder operation in progress, let it complete.");
     return;
   }
 
   if (decoder.mQueuedSamples.IsEmpty()) {
     return;
   }
 
@@ -2261,26 +2311,26 @@ MediaFormatReader::Update(TrackType aTra
     }
     return;
   }
 
   bool needInput = NeedInput(decoder);
 
   LOGV(
     "Update(%s) ni=%d no=%d in:%" PRIu64 " out:%" PRIu64
-    " qs=%u decoding:%d flushing:%d shutdown:%d pending:%u waiting:%d sid:%u",
+    " qs=%u decoding:%d flushing:%d desc:%s pending:%u waiting:%d sid:%u",
     TrackTypeToStr(aTrack),
     needInput,
     needOutput,
     decoder.mNumSamplesInput,
     decoder.mNumSamplesOutput,
     uint32_t(size_t(decoder.mSizeOfQueue)),
     decoder.mDecodeRequest.Exists(),
-    decoder.mFlushRequest.Exists(),
-    decoder.mShutdownRequest.Exists(),
+    decoder.mFlushing,
+    decoder.mDescription,
     uint32_t(decoder.mOutput.Length()),
     decoder.mWaitingForData,
     decoder.mLastStreamSourceID);
 
   if ((decoder.mWaitingForData
        && (!decoder.mTimeThreshold || decoder.mTimeThreshold.ref().mWaiting))
       || (decoder.mWaitingForKey && decoder.mDecodeRequest.Exists())) {
     // Nothing more we can do at present.
--- a/dom/media/MediaFormatReader.h
+++ b/dom/media/MediaFormatReader.h
@@ -166,30 +166,38 @@ private:
     None,
     DrainRequested,
     Draining,
     PartialDrainPending,
     DrainCompleted,
     DrainAborted,
   };
 
+  class SharedShutdownPromiseHolder : public MozPromiseHolder<ShutdownPromise>
+  {
+    NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SharedShutdownPromiseHolder)
+  private:
+    ~SharedShutdownPromiseHolder() { }
+  };
+
   struct DecoderData
   {
     DecoderData(MediaFormatReader* aOwner,
                 MediaData::Type aType,
                 uint32_t aNumOfMaxError)
       : mOwner(aOwner)
       , mType(aType)
       , mMutex("DecoderData")
       , mDescription("shutdown")
       , mUpdateScheduled(false)
       , mDemuxEOS(false)
       , mWaitingForData(false)
       , mWaitingForKey(false)
       , mReceivedNewData(false)
+      , mFlushing(false)
       , mFlushed(true)
       , mDrainState(DrainState::None)
       , mNumOfConsecutiveError(0)
       , mMaxConsecutiveError(aNumOfMaxError)
       , mNumSamplesInput(0)
       , mNumSamplesOutput(0)
       , mNumSamplesOutputTotal(0)
       , mNumSamplesSkippedTotal(0)
@@ -208,37 +216,17 @@ private:
     // Only non-null up until the decoder is created.
     RefPtr<TaskQueue> mTaskQueue;
 
     // Mutex protecting mDescription and mDecoder.
     Mutex mMutex;
     // The platform decoder.
     RefPtr<MediaDataDecoder> mDecoder;
     const char* mDescription;
-    void ShutdownDecoder()
-    {
-      MutexAutoLock lock(mMutex);
-      if (mDecoder) {
-        RefPtr<MediaFormatReader> owner = mOwner;
-        TrackType type = mType == MediaData::AUDIO_DATA
-                         ? TrackType::kAudioTrack
-                         : TrackType::kVideoTrack;
-        mDecoder->Shutdown()
-          ->Then(mOwner->OwnerThread(), __func__,
-                 [owner, this, type]() {
-                   mShutdownRequest.Complete();
-                   mShutdownPromise.ResolveIfExists(true, __func__);
-                   owner->ScheduleUpdate(type);
-                 },
-                 []() { MOZ_RELEASE_ASSERT(false, "Can't ever be here"); })
-          ->Track(mShutdownRequest);
-      }
-      mDescription = "shutdown";
-      mDecoder = nullptr;
-    }
+    void ShutdownDecoder();
 
     // Only accessed from reader's task queue.
     bool mUpdateScheduled;
     bool mDemuxEOS;
     bool mWaitingForData;
     bool mWaitingForKey;
     bool mReceivedNewData;
 
@@ -259,21 +247,20 @@ private:
     bool IsWaiting() const
     {
       MOZ_ASSERT(mOwner->OnTaskQueue());
       return mWaitingForData || mWaitingForKey;
     }
 
     // MediaDataDecoder handler's variables.
     MozPromiseRequestHolder<MediaDataDecoder::DecodePromise> mDecodeRequest;
-    MozPromiseRequestHolder<MediaDataDecoder::FlushPromise> mFlushRequest;
+    bool mFlushing; // True if flush is in action.
     // Set to true if the last operation run on the decoder was a flush.
     bool mFlushed;
-    MozPromiseHolder<ShutdownPromise> mShutdownPromise;
-    MozPromiseRequestHolder<ShutdownPromise> mShutdownRequest;
+    RefPtr<SharedShutdownPromiseHolder> mShutdownPromise;
 
     MozPromiseRequestHolder<MediaDataDecoder::DecodePromise> mDrainRequest;
     DrainState mDrainState;
     bool HasPendingDrain() const
     {
       return mDrainState != DrainState::None;
     }
     void RequestDrain()
@@ -335,57 +322,17 @@ private:
       mDemuxRequest.DisconnectIfExists();
       mSeekRequest.DisconnectIfExists();
       mTrackDemuxer->Reset();
       mQueuedSamples.Clear();
     }
 
     // Flush the decoder if present and reset decoding related data.
     // Following a flush, the decoder is ready to accept any new data.
-    void Flush()
-    {
-      if (mFlushRequest.Exists() || mFlushed) {
-        // Flush still pending or already flushed, nothing more to do.
-        return;
-      }
-      mDecodeRequest.DisconnectIfExists();
-      mDrainRequest.DisconnectIfExists();
-      mDrainState = DrainState::None;
-      CancelWaitingForKey();
-      mOutput.Clear();
-      mNumSamplesInput = 0;
-      mNumSamplesOutput = 0;
-      mSizeOfQueue = 0;
-      if (mDecoder && !mFlushed) {
-        RefPtr<MediaFormatReader> owner = mOwner;
-        TrackType type = mType == MediaData::AUDIO_DATA
-                         ? TrackType::kAudioTrack
-                         : TrackType::kVideoTrack;
-        mDecoder->Flush()
-          ->Then(mOwner->OwnerThread(), __func__,
-                 [owner, type, this]() {
-                   mFlushRequest.Complete();
-                   if (!mShutdownPromise.IsEmpty()) {
-                     ShutdownDecoder();
-                     return;
-                   }
-                   owner->ScheduleUpdate(type);
-                 },
-                 [owner, type, this](const MediaResult& aError) {
-                   mFlushRequest.Complete();
-                   if (!mShutdownPromise.IsEmpty()) {
-                     ShutdownDecoder();
-                     return;
-                   }
-                   owner->NotifyError(type, aError);
-                 })
-          ->Track(mFlushRequest);
-      }
-      mFlushed = true;
-    }
+    void Flush();
 
     bool CancelWaitingForKey()
     {
       if (!mWaitingForKey) {
         return false;
       }
       mWaitingForKey = false;
       if (IsWaiting() || !HasWaitingPromise()) {
@@ -603,15 +550,14 @@ private:
   void MaybeResolveMetadataPromise();
 
   UniquePtr<MetadataTags> mTags;
 
   // A flag indicating if the start time is known or not.
   bool mHasStartTime = false;
 
   void ShutdownDecoder(TrackType aTrack);
-  RefPtr<ShutdownPromise> ShutdownDecoderWithPromise(TrackType aTrack);
   RefPtr<ShutdownPromise> TearDownDecoders();
 };
 
 } // namespace mozilla
 
 #endif
--- a/dom/media/platforms/agnostic/eme/EMEDecoderModule.cpp
+++ b/dom/media/platforms/agnostic/eme/EMEDecoderModule.cpp
@@ -167,24 +167,19 @@ public:
     mDecodeRequest.DisconnectIfExists();
     mDecodePromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
     mThroughputLimiter.Flush();
     for (auto iter = mDecrypts.Iter(); !iter.Done(); iter.Next()) {
       nsAutoPtr<DecryptPromiseRequestHolder>& holder = iter.Data();
       holder->DisconnectIfExists();
       iter.Remove();
     }
-    RefPtr<EMEDecryptor> self = this;
+    RefPtr<SamplesWaitingForKey> k = mSamplesWaitingForKey;
     return mDecoder->Flush()->Then(mTaskQueue, __func__,
-                                   [self, this]() {
-                                     mSamplesWaitingForKey->Flush();
-                                   },
-                                   [self, this]() {
-                                     mSamplesWaitingForKey->Flush();
-                                   });
+                                   [k]() { k->Flush(); });
   }
 
   RefPtr<DecodePromise> Drain() override
   {
     MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
     MOZ_ASSERT(!mIsShutdown);
     MOZ_ASSERT(mDecodePromise.IsEmpty() && !mDecodeRequest.Exists(),
                "Must wait for decoding to complete");
--- a/mobile/android/base/java/org/mozilla/gecko/preferences/GeckoPreferences.java
+++ b/mobile/android/base/java/org/mozilla/gecko/preferences/GeckoPreferences.java
@@ -168,16 +168,17 @@ public class GeckoPreferences
     public static final String PREFS_READ_PARTNER_CUSTOMIZATIONS_PROVIDER = NON_PREF_PREFIX + "distribution.read_partner_customizations_provider";
     public static final String PREFS_READ_PARTNER_BOOKMARKS_PROVIDER = NON_PREF_PREFIX + "distribution.read_partner_bookmarks_provider";
     public static final String PREFS_CUSTOM_TABS = NON_PREF_PREFIX + "customtabs";
     public static final String PREFS_ACTIVITY_STREAM = NON_PREF_PREFIX + "activitystream";
     public static final String PREFS_CATEGORY_EXPERIMENTAL_FEATURES = NON_PREF_PREFIX + "category_experimental";
     public static final String PREFS_COMPACT_TABS = NON_PREF_PREFIX + "compact_tabs";
     public static final String PREFS_SHOW_QUIT_MENU = NON_PREF_PREFIX + "distribution.show_quit_menu";
     public static final String PREFS_SEARCH_SUGGESTIONS_ENABLED = "browser.search.suggest.enabled";
+    public static final String PREFS_DEFAULT_BROWSER = NON_PREF_PREFIX + "default_browser.link";
 
     private static final String ACTION_STUMBLER_UPLOAD_PREF = "STUMBLER_PREF";
 
 
     // This isn't a Gecko pref, even if it looks like one.
     private static final String PREFS_BROWSER_LOCALE = "locale";
 
     public static final String PREFS_RESTORE_SESSION = NON_PREF_PREFIX + "restoreSession3";
--- a/mobile/android/base/java/org/mozilla/gecko/preferences/LinkPreference.java
+++ b/mobile/android/base/java/org/mozilla/gecko/preferences/LinkPreference.java
@@ -29,17 +29,20 @@ class LinkPreference extends Preference 
         mUrl = url;
     }
 
     /**
      * Open Default apps screen of Settings for API Levels>=24. Support URL will open for lower API levels
      */
     @Override
     protected void onClick() {
-        if (AppConstants.Versions.feature24Plus) {
+        if (GeckoPreferences.PREFS_DEFAULT_BROWSER.equals(getKey()) && AppConstants.Versions.feature24Plus) {
+            // We are special casing the link to set the default browser here: On old Android versions we
+            // link to a SUMO page but on new Android versions we can link to the default app settings where
+            // the user can actually set a default browser (Bug 1312686).
             Intent changeDefaultApps = new Intent("android.settings.MANAGE_DEFAULT_APPS_SETTINGS");
             getContext().startActivity(changeDefaultApps);
         } else {
             Tabs.getInstance().loadUrlInTab(mUrl);
             callChangeListener(mUrl);
         }
     }
 }
--- a/servo/components/script/dom/blob.rs
+++ b/servo/components/script/dom/blob.rs
@@ -13,17 +13,16 @@ use dom::bindings::str::DOMString;
 use dom::globalscope::GlobalScope;
 use dom_struct::dom_struct;
 use encoding::all::UTF_8;
 use encoding::types::{EncoderTrap, Encoding};
 use ipc_channel::ipc;
 use net_traits::{CoreResourceMsg, IpcSend};
 use net_traits::blob_url_store::{BlobBuf, get_blob_origin};
 use net_traits::filemanager_thread::{FileManagerThreadMsg, ReadFileProgress, RelativePos};
-use std::cell::Cell;
 use std::mem;
 use std::ops::Index;
 use std::path::PathBuf;
 use uuid::Uuid;
 
 /// File-based blob
 #[derive(JSTraceable)]
 pub struct FileBlob {
@@ -70,17 +69,16 @@ impl BlobImpl {
 // https://w3c.github.io/FileAPI/#blob
 #[dom_struct]
 pub struct Blob {
     reflector_: Reflector,
     #[ignore_heap_size_of = "No clear owner"]
     blob_impl: DOMRefCell<BlobImpl>,
     /// content-type string
     type_string: String,
-    is_closed: Cell<bool>,
 }
 
 impl Blob {
     #[allow(unrooted_must_root)]
     pub fn new(
             global: &GlobalScope, blob_impl: BlobImpl, typeString: String)
             -> Root<Blob> {
         let boxed_blob = box Blob::new_inherited(blob_impl, typeString);
@@ -90,17 +88,16 @@ impl Blob {
     #[allow(unrooted_must_root)]
     pub fn new_inherited(blob_impl: BlobImpl, type_string: String) -> Blob {
         Blob {
             reflector_: Reflector::new(),
             blob_impl: DOMRefCell::new(blob_impl),
             // NOTE: Guarding the format correctness here,
             // https://w3c.github.io/FileAPI/#dfn-type
             type_string: normalize_type_string(&type_string),
-            is_closed: Cell::new(false),
         }
     }
 
     #[allow(unrooted_must_root)]
     fn new_sliced(parent: &Blob, rel_pos: RelativePos,
                   relative_content_type: DOMString) -> Root<Blob> {
         let blob_impl = match *parent.blob_impl.borrow() {
             BlobImpl::File(_) => {
@@ -292,19 +289,17 @@ impl Blob {
         let global = self.global();
         let resource_threads = global.resource_threads();
         let _ = resource_threads.send(CoreResourceMsg::ToFileManager(msg));
     }
 }
 
 impl Drop for Blob {
     fn drop(&mut self) {
-        if !self.IsClosed() {
-            self.clean_up_file_resource();
-        }
+        self.clean_up_file_resource();
     }
 }
 
 fn read_file(global: &GlobalScope, id: Uuid) -> Result<Vec<u8>, ()> {
     let resource_threads = global.resource_threads();
     let (chan, recv) = ipc::channel().map_err(|_|())?;
     let origin = get_blob_origin(&global.get_url());
     let check_url_validity = false;
@@ -370,35 +365,16 @@ impl BlobMethods for Blob {
     fn Slice(&self,
              start: Option<i64>,
              end: Option<i64>,
              content_type: Option<DOMString>)
              -> Root<Blob> {
         let rel_pos = RelativePos::from_opts(start, end);
         Blob::new_sliced(self, rel_pos, content_type.unwrap_or(DOMString::from("")))
     }
-
-    // https://w3c.github.io/FileAPI/#dfn-isClosed
-    fn IsClosed(&self) -> bool {
-        self.is_closed.get()
-    }
-
-    // https://w3c.github.io/FileAPI/#dfn-close
-    fn Close(&self) {
-        // Step 1
-        if self.is_closed.get() {
-            return;
-        }
-
-        // Step 2
-        self.is_closed.set(true);
-
-        // Step 3
-        self.clean_up_file_resource();
-    }
 }
 
 /// Get the normalized, MIME-parsable type string
 /// https://w3c.github.io/FileAPI/#dfn-type
 /// XXX: We will relax the restriction here,
 /// since the spec has some problem over this part.
 /// see https://github.com/w3c/FileAPI/issues/43
 fn normalize_type_string(s: &str) -> String {
--- a/servo/components/script/dom/filereader.rs
+++ b/servo/components/script/dom/filereader.rs
@@ -370,30 +370,21 @@ impl FileReader {
         self.generation_id.set(GenerationId(prev_id + 1));
     }
 
     fn read(&self, function: FileReaderFunction, blob: &Blob, label: Option<DOMString>) -> ErrorResult {
         // Step 1
         if self.ready_state.get() == FileReaderReadyState::Loading {
             return Err(Error::InvalidState);
         }
+
         // Step 2
-        let global = self.global();
-        if blob.IsClosed() {
-            let exception = DOMException::new(&global, DOMErrorName::InvalidStateError);
-            self.error.set(Some(&exception));
-
-            self.dispatch_progress_event(atom!("error"), 0, None);
-            return Ok(());
-        }
+        self.change_ready_state(FileReaderReadyState::Loading);
 
         // Step 3
-        self.change_ready_state(FileReaderReadyState::Loading);
-
-        // Step 4
         let blob_contents = Arc::new(blob.get_bytes().unwrap_or(vec![]));
 
         let type_ = blob.Type();
 
         let load_data = ReadMetaData::new(String::from(type_), label.map(String::from), function);
 
         let fr = Trusted::new(self);
         let gen_id = self.generation_id.get();
--- a/servo/components/script/dom/url.rs
+++ b/servo/components/script/dom/url.rs
@@ -1,14 +1,13 @@
 /* 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/. */
 
 use dom::bindings::cell::DOMRefCell;
-use dom::bindings::codegen::Bindings::BlobBinding::BlobMethods;
 use dom::bindings::codegen::Bindings::URLBinding::{self, URLMethods};
 use dom::bindings::error::{Error, ErrorResult, Fallible};
 use dom::bindings::js::{MutNullableJS, Root};
 use dom::bindings::reflector::{DomObject, Reflector, reflect_dom_object};
 use dom::bindings::str::{DOMString, USVString};
 use dom::blob::Blob;
 use dom::globalscope::GlobalScope;
 use dom::urlhelper::UrlHelper;
@@ -97,37 +96,28 @@ impl URL {
     }
 
     // https://w3c.github.io/FileAPI/#dfn-createObjectURL
     pub fn CreateObjectURL(global: &GlobalScope, blob: &Blob) -> DOMString {
         /// XXX: Second field is an unicode-serialized Origin, it is a temporary workaround
         ///      and should not be trusted. See issue https://github.com/servo/servo/issues/11722
         let origin = get_blob_origin(&global.get_url());
 
-        if blob.IsClosed() {
-            // Generate a dummy id
-            let id = Uuid::new_v4();
-            return DOMString::from(URL::unicode_serialization_blob_url(&origin, &id));
-        }
-
         let id = blob.get_blob_url_id();
 
         DOMString::from(URL::unicode_serialization_blob_url(&origin, &id))
     }
 
     // https://w3c.github.io/FileAPI/#dfn-revokeObjectURL
     pub fn RevokeObjectURL(global: &GlobalScope, url: DOMString) {
         /*
-            If the url refers to a Blob that has a readability state of CLOSED OR
-            if the value provided for the url argument is not a Blob URL, OR
+            If the value provided for the url argument is not a Blob URL OR
             if the value provided for the url argument does not have an entry in the Blob URL Store,
 
             this method call does nothing. User agents may display a message on the error console.
-
-            NOTE: The first step is unnecessary, since closed blobs do not exist in the store
         */
         let origin = get_blob_origin(&global.get_url());
 
         if let Ok(url) = ServoUrl::parse(&url) {
              if let Ok((id, _)) = parse_blob_url(&url) {
                 let resource_threads = global.resource_threads();
                 let (tx, rx) = ipc::channel().unwrap();
                 let msg = FileManagerThreadMsg::RevokeBlobURL(id, origin, tx);
--- a/servo/components/script/dom/webidls/Blob.webidl
+++ b/servo/components/script/dom/webidls/Blob.webidl
@@ -6,26 +6,20 @@
 
 [Constructor(optional sequence<BlobPart> blobParts,
   optional BlobPropertyBag options),
  Exposed=(Window,Worker)]
 interface Blob {
 
   readonly attribute unsigned long long size;
   readonly attribute DOMString type;
-  readonly attribute boolean isClosed;
 
-  //slice Blob into byte-ranged chunks
-
+  // slice Blob into byte-ranged chunks
   Blob slice([Clamp] optional long long start,
              [Clamp] optional long long end,
              optional DOMString contentType);
-  void close();
-
 };
 
 dictionary BlobPropertyBag {
-
   DOMString type = "";
-
 };
 
 typedef (/*ArrayBuffer or ArrayBufferView or */Blob or DOMString) BlobPart;
--- a/servo/components/script/dom/xmlhttprequest.rs
+++ b/servo/components/script/dom/xmlhttprequest.rs
@@ -38,17 +38,17 @@ use dom::xmlhttprequesteventtarget::XMLH
 use dom::xmlhttprequestupload::XMLHttpRequestUpload;
 use dom_struct::dom_struct;
 use encoding::all::UTF_8;
 use encoding::label::encoding_from_whatwg_label;
 use encoding::types::{DecoderTrap, EncoderTrap, Encoding, EncodingRef};
 use euclid::length::Length;
 use html5ever::serialize;
 use html5ever::serialize::SerializeOpts;
-use hyper::header::{ContentLength, ContentType};
+use hyper::header::{ContentLength, ContentType, ContentEncoding};
 use hyper::header::Headers;
 use hyper::method::Method;
 use hyper::mime::{self, Attr as MimeAttr, Mime, Value as MimeValue};
 use hyper_serde::Serde;
 use ipc_channel::ipc;
 use ipc_channel::router::ROUTER;
 use js::jsapi::{JSContext, JS_ParseJSON};
 use js::jsapi::JS_ClearPendingException;
@@ -975,24 +975,22 @@ impl XMLHttpRequest {
                         self.ready_state.get() == XMLHttpRequestState::Loading ||
                         self.sync.get());
 
                 self.cancel_timeout();
 
                 // Part of step 11, send() (processing response end of file)
                 // XXXManishearth handle errors, if any (substep 2)
 
-                // Subsubsteps 5-7
+                // Subsubsteps 6-8
                 self.send_flag.set(false);
 
                 self.change_ready_state(XMLHttpRequestState::Done);
                 return_if_fetch_was_terminated!();
-                // Subsubsteps 10-12
-                self.dispatch_response_progress_event(atom!("progress"));
-                return_if_fetch_was_terminated!();
+                // Subsubsteps 11-12
                 self.dispatch_response_progress_event(atom!("load"));
                 return_if_fetch_was_terminated!();
                 self.dispatch_response_progress_event(atom!("loadend"));
             },
             XHRProgress::Errored(_, e) => {
                 self.cancel_timeout();
 
                 self.discard_subsequent_responses();
@@ -1005,45 +1003,46 @@ impl XMLHttpRequest {
                     Error::Abort => "abort",
                     Error::Timeout => "timeout",
                     _ => "error",
                 };
 
                 let upload_complete = &self.upload_complete;
                 if !upload_complete.get() {
                     upload_complete.set(true);
-                    self.dispatch_upload_progress_event(atom!("progress"), None);
-                    return_if_fetch_was_terminated!();
                     self.dispatch_upload_progress_event(Atom::from(errormsg), None);
                     return_if_fetch_was_terminated!();
                     self.dispatch_upload_progress_event(atom!("loadend"), None);
                     return_if_fetch_was_terminated!();
                 }
-                self.dispatch_response_progress_event(atom!("progress"));
-                return_if_fetch_was_terminated!();
                 self.dispatch_response_progress_event(Atom::from(errormsg));
                 return_if_fetch_was_terminated!();
                 self.dispatch_response_progress_event(atom!("loadend"));
             }
         }
     }
 
     fn terminate_ongoing_fetch(&self) {
         let GenerationId(prev_id) = self.generation_id.get();
         self.generation_id.set(GenerationId(prev_id + 1));
         self.response_status.set(Ok(()));
     }
 
     fn dispatch_progress_event(&self, upload: bool, type_: Atom, loaded: u64, total: Option<u64>) {
+        let (total_length, length_computable) = if self.response_headers.borrow().has::<ContentEncoding>() {
+            (0, false)
+        } else {
+            (total.unwrap_or(0), total.is_some())
+        };
         let progressevent = ProgressEvent::new(&self.global(),
                                                type_,
                                                EventBubbles::DoesNotBubble,
                                                EventCancelable::NotCancelable,
-                                               total.is_some(), loaded,
-                                               total.unwrap_or(0));
+                                               length_computable, loaded,
+                                               total_length);
         let target = if upload {
             self.upload.upcast()
         } else {
             self.upcast()
         };
         progressevent.upcast::<Event>().fire(target);
     }
 
--- a/toolkit/components/thumbnails/BackgroundPageThumbs.jsm
+++ b/toolkit/components/thumbnails/BackgroundPageThumbs.jsm
@@ -160,34 +160,35 @@ const BackgroundPageThumbs = {
 
     this._startedParentWinInit = true;
 
     // Create a windowless browser and load our hosting
     // (privileged) document in it.
     let wlBrowser = Services.appShell.createWindowlessBrowser(true);
     wlBrowser.QueryInterface(Ci.nsIInterfaceRequestor);
     let webProgress = wlBrowser.getInterface(Ci.nsIWebProgress);
-    let listener = {
+    this._listener = {
       QueryInterface: XPCOMUtils.generateQI([
         Ci.nsIWebProgressListener, Ci.nsIWebProgressListener2,
         Ci.nsISupportsWeakReference]),
     };
-    listener.onStateChange = (wbp, request, stateFlags, status) => {
+    this._listener.onStateChange = (wbp, request, stateFlags, status) => {
       if (!request) {
         return;
       }
       if (stateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
           stateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK) {
-        webProgress.removeProgressListener(listener);
+        webProgress.removeProgressListener(this._listener);
+        delete this._listener;
         // Get the window reference via the document.
         this._parentWin = wlBrowser.document.defaultView;
         this._processCaptureQueue();
       }
     };
-    webProgress.addProgressListener(listener, Ci.nsIWebProgress.NOTIFY_STATE_ALL);
+    webProgress.addProgressListener(this._listener, Ci.nsIWebProgress.NOTIFY_STATE_ALL);
     wlBrowser.loadURI("chrome://global/content/backgroundPageThumbs.xhtml", 0, null, null, null);
     this._windowlessContainer = wlBrowser;
 
     return false;
   },
 
   /**
    * Destroys the service.  Queued and pending captures will never complete, and
@@ -198,16 +199,17 @@ const BackgroundPageThumbs = {
       this._captureQueue.forEach(cap => cap.destroy());
     this._destroyBrowser();
     if (this._windowlessContainer)
       this._windowlessContainer.close();
     delete this._captureQueue;
     delete this._windowlessContainer;
     delete this._startedParentWinInit;
     delete this._parentWin;
+    delete this._listener;
   },
 
   /**
    * Creates the thumbnail browser if it doesn't already exist.
    */
   _ensureBrowser() {
     if (this._thumbBrowser && !this._renewThumbBrowser)
       return;