| /* |
| * Copyright (C) 2010 Julien Chaffraix <jchaffraix@webkit.org> All right |
| * reserved. |
| * Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies) |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "third_party/blink/renderer/core/xmlhttprequest/xml_http_request_progress_event_throttle.h" |
| |
| #include "third_party/blink/public/platform/platform.h" |
| #include "third_party/blink/renderer/core/event_type_names.h" |
| #include "third_party/blink/renderer/core/events/progress_event.h" |
| #include "third_party/blink/renderer/core/inspector/inspector_trace_events.h" |
| #include "third_party/blink/renderer/core/probe/core_probes.h" |
| #include "third_party/blink/renderer/core/xmlhttprequest/xml_http_request.h" |
| #include "third_party/blink/renderer/platform/scheduler/public/thread.h" |
| #include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h" |
| #include "third_party/blink/renderer/platform/wtf/assertions.h" |
| #include "third_party/blink/renderer/platform/wtf/text/atomic_string.h" |
| |
| namespace blink { |
| |
| static constexpr TimeDelta kMinimumProgressEventDispatchingInterval = |
| TimeDelta::FromMilliseconds(50); // 50 ms per specification. |
| |
| XMLHttpRequestProgressEventThrottle::DeferredEvent::DeferredEvent() { |
| Clear(); |
| } |
| |
| void XMLHttpRequestProgressEventThrottle::DeferredEvent::Set( |
| bool length_computable, |
| unsigned long long loaded, |
| unsigned long long total) { |
| is_set_ = true; |
| |
| length_computable_ = length_computable; |
| loaded_ = loaded; |
| total_ = total; |
| } |
| |
| void XMLHttpRequestProgressEventThrottle::DeferredEvent::Clear() { |
| is_set_ = false; |
| |
| length_computable_ = false; |
| loaded_ = 0; |
| total_ = 0; |
| } |
| |
| Event* XMLHttpRequestProgressEventThrottle::DeferredEvent::Take() { |
| DCHECK(is_set_); |
| |
| Event* event = ProgressEvent::Create(event_type_names::kProgress, |
| length_computable_, loaded_, total_); |
| Clear(); |
| return event; |
| } |
| |
| XMLHttpRequestProgressEventThrottle::XMLHttpRequestProgressEventThrottle( |
| XMLHttpRequest* target) |
| : TimerBase( |
| target->GetExecutionContext()->GetTaskRunner(TaskType::kNetworking)), |
| target_(target), |
| has_dispatched_progress_progress_event_(false) { |
| DCHECK(target); |
| } |
| |
| XMLHttpRequestProgressEventThrottle::~XMLHttpRequestProgressEventThrottle() = |
| default; |
| |
| void XMLHttpRequestProgressEventThrottle::DispatchProgressEvent( |
| const AtomicString& type, |
| bool length_computable, |
| unsigned long long loaded, |
| unsigned long long total) { |
| // Given that ResourceDispatcher doesn't deliver an event when suspended, |
| // we don't have to worry about event dispatching while suspended. |
| if (type != event_type_names::kProgress) { |
| target_->DispatchEvent( |
| *ProgressEvent::Create(type, length_computable, loaded, total)); |
| return; |
| } |
| |
| if (IsActive()) { |
| deferred_.Set(length_computable, loaded, total); |
| } else { |
| DispatchProgressProgressEvent(ProgressEvent::Create( |
| event_type_names::kProgress, length_computable, loaded, total)); |
| StartOneShot(kMinimumProgressEventDispatchingInterval, FROM_HERE); |
| } |
| } |
| |
| void XMLHttpRequestProgressEventThrottle::DispatchReadyStateChangeEvent( |
| Event* event, |
| DeferredEventAction action) { |
| XMLHttpRequest::State state = target_->readyState(); |
| // Given that ResourceDispatcher doesn't deliver an event when suspended, |
| // we don't have to worry about event dispatching while suspended. |
| if (action == kFlush) { |
| if (deferred_.IsSet()) |
| DispatchProgressProgressEvent(deferred_.Take()); |
| |
| Stop(); |
| } else if (action == kClear) { |
| deferred_.Clear(); |
| Stop(); |
| } |
| |
| has_dispatched_progress_progress_event_ = false; |
| if (state == target_->readyState()) { |
| // We don't dispatch the event when an event handler associated with |
| // the previously dispatched event changes the readyState (e.g. when |
| // the event handler calls xhr.abort()). In such cases a |
| // readystatechange should have been already dispatched if necessary. |
| probe::AsyncTask async_task(target_->GetExecutionContext(), target_, |
| "progress", target_->IsAsync()); |
| target_->DispatchEvent(*event); |
| } |
| } |
| |
| void XMLHttpRequestProgressEventThrottle::DispatchProgressProgressEvent( |
| Event* progress_event) { |
| XMLHttpRequest::State state = target_->readyState(); |
| if (target_->readyState() == XMLHttpRequest::kLoading && |
| has_dispatched_progress_progress_event_) { |
| TRACE_EVENT1("devtools.timeline", "XHRReadyStateChange", "data", |
| inspector_xhr_ready_state_change_event::Data( |
| target_->GetExecutionContext(), target_)); |
| probe::AsyncTask async_task(target_->GetExecutionContext(), target_, |
| "progress", target_->IsAsync()); |
| target_->DispatchEvent(*Event::Create(event_type_names::kReadystatechange)); |
| } |
| |
| if (target_->readyState() != state) |
| return; |
| |
| has_dispatched_progress_progress_event_ = true; |
| probe::AsyncTask async_task(target_->GetExecutionContext(), target_, |
| "progress", target_->IsAsync()); |
| target_->DispatchEvent(*progress_event); |
| } |
| |
| void XMLHttpRequestProgressEventThrottle::Fired() { |
| if (!deferred_.IsSet()) { |
| // No "progress" event was queued since the previous dispatch, we can |
| // safely stop the timer. |
| return; |
| } |
| |
| DispatchProgressProgressEvent(deferred_.Take()); |
| |
| // Watch if another "progress" ProgressEvent arrives in the next 50ms. |
| StartOneShot(kMinimumProgressEventDispatchingInterval, FROM_HERE); |
| } |
| |
| void XMLHttpRequestProgressEventThrottle::Trace(blink::Visitor* visitor) { |
| visitor->Trace(target_); |
| } |
| |
| } // namespace blink |