author | Wes Kocher <wkocher@mozilla.com> |
Fri, 03 Mar 2017 17:27:33 -0800 | |
changeset 345857 | 80c06df83395314697d464f88f8daa98bf05465c |
parent 345856 | 9a117f52a1371a223ff79a1f67a4b8cf08f9b69b (current diff) |
parent 345855 | d67caca960aa9f4b1b59d0d79d4ecfb83eb18e68 (diff) |
child 345858 | 76a96fd623d4304b9a6149deaf723c80e66d351f |
child 345907 | 84ef4d4530eeac074d403bf9a6251710bffa076a |
child 345981 | b691557cb7a31c942cbdf3c1388140f60c24fdf9 |
push id | 87682 |
push user | kwierso@gmail.com |
push date | Sat, 04 Mar 2017 01:29:33 +0000 |
treeherder | mozilla-inbound@76a96fd623d4 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | merge |
milestone | 54.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
54.0a1
/
20170304110210
/
pushlog to previous
nightly linux64
54.0a1
/
20170304110210
/
pushlog to previous
nightly mac
54.0a1
/
20170304030205
/
pushlog to previous
nightly win32
54.0a1
/
20170304030205
/
pushlog to previous
nightly win64
54.0a1
/
20170304030205
/
pushlog to previous
|
--- 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;