mirror of
https://github.com/servo/servo.git
synced 2025-06-06 16:45:39 +00:00
Auto merge of #21111 - gterzian:implement_document_load_cancellation, r=jdm
Implement document load cancellation <!-- Please describe your changes on the following line: --> --- <!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: --> - [ ] `./mach build -d` does not report any errors - [ ] `./mach test-tidy` does not report any errors - [ ] These changes fix #19309 fix #21114 fix #21113 (github issue number if applicable). <!-- Either: --> - [ ] There are tests for these changes OR - [ ] These changes do not require tests because _____ <!-- Also, please make sure that "Allow edits from maintainers" checkbox is checked, so that we can help you if you get stuck somewhere along the way.--> <!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. --> <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/21111) <!-- Reviewable:end -->
This commit is contained in:
commit
076198fe80
19 changed files with 195 additions and 104 deletions
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
use dom::bindings::root::Dom;
|
use dom::bindings::root::Dom;
|
||||||
use dom::document::Document;
|
use dom::document::Document;
|
||||||
|
use fetch::FetchCanceller;
|
||||||
use ipc_channel::ipc::IpcSender;
|
use ipc_channel::ipc::IpcSender;
|
||||||
use net_traits::{CoreResourceMsg, FetchChannels, FetchResponseMsg};
|
use net_traits::{CoreResourceMsg, FetchChannels, FetchResponseMsg};
|
||||||
use net_traits::{ResourceThreads, IpcSend};
|
use net_traits::{ResourceThreads, IpcSend};
|
||||||
|
@ -87,6 +88,7 @@ pub struct DocumentLoader {
|
||||||
resource_threads: ResourceThreads,
|
resource_threads: ResourceThreads,
|
||||||
blocking_loads: Vec<LoadType>,
|
blocking_loads: Vec<LoadType>,
|
||||||
events_inhibited: bool,
|
events_inhibited: bool,
|
||||||
|
cancellers: Vec<FetchCanceller>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DocumentLoader {
|
impl DocumentLoader {
|
||||||
|
@ -103,9 +105,17 @@ impl DocumentLoader {
|
||||||
resource_threads: resource_threads,
|
resource_threads: resource_threads,
|
||||||
blocking_loads: initial_loads,
|
blocking_loads: initial_loads,
|
||||||
events_inhibited: false,
|
events_inhibited: false,
|
||||||
|
cancellers: Vec::new()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn cancel_all_loads(&mut self) -> bool {
|
||||||
|
let canceled_any = !self.cancellers.is_empty();
|
||||||
|
// Associated fetches will be canceled when dropping the canceller.
|
||||||
|
self.cancellers.clear();
|
||||||
|
canceled_any
|
||||||
|
}
|
||||||
|
|
||||||
/// Add a load to the list of blocking loads.
|
/// Add a load to the list of blocking loads.
|
||||||
fn add_blocking_load(&mut self, load: LoadType) {
|
fn add_blocking_load(&mut self, load: LoadType) {
|
||||||
debug!("Adding blocking load {:?} ({}).", load, self.blocking_loads.len());
|
debug!("Adding blocking load {:?} ({}).", load, self.blocking_loads.len());
|
||||||
|
@ -122,11 +132,14 @@ impl DocumentLoader {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Initiate a new fetch that does not block the document load event.
|
/// Initiate a new fetch that does not block the document load event.
|
||||||
pub fn fetch_async_background(&self,
|
pub fn fetch_async_background(&mut self,
|
||||||
request: RequestInit,
|
request: RequestInit,
|
||||||
fetch_target: IpcSender<FetchResponseMsg>) {
|
fetch_target: IpcSender<FetchResponseMsg>) {
|
||||||
|
let mut canceller = FetchCanceller::new();
|
||||||
|
let cancel_receiver = canceller.initialize();
|
||||||
|
self.cancellers.push(canceller);
|
||||||
self.resource_threads.sender().send(
|
self.resource_threads.sender().send(
|
||||||
CoreResourceMsg::Fetch(request, FetchChannels::ResponseMsg(fetch_target, None))).unwrap();
|
CoreResourceMsg::Fetch(request, FetchChannels::ResponseMsg(fetch_target, Some(cancel_receiver)))).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Mark an in-progress network request complete.
|
/// Mark an in-progress network request complete.
|
||||||
|
|
|
@ -26,6 +26,10 @@ DOMInterfaces = {
|
||||||
'weakReferenceable': True,
|
'weakReferenceable': True,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
'EventSource': {
|
||||||
|
'weakReferenceable': True,
|
||||||
|
},
|
||||||
|
|
||||||
#FIXME(jdm): This should be 'register': False, but then we don't generate enum types
|
#FIXME(jdm): This should be 'register': False, but then we don't generate enum types
|
||||||
'TestBinding': {},
|
'TestBinding': {},
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
//! slot. When all associated `WeakRef` values are dropped, the
|
//! slot. When all associated `WeakRef` values are dropped, the
|
||||||
//! `WeakBox` itself is dropped too.
|
//! `WeakBox` itself is dropped too.
|
||||||
|
|
||||||
|
use dom::bindings::cell::DomRefCell;
|
||||||
use dom::bindings::reflector::DomObject;
|
use dom::bindings::reflector::DomObject;
|
||||||
use dom::bindings::root::DomRoot;
|
use dom::bindings::root::DomRoot;
|
||||||
use dom::bindings::trace::JSTraceable;
|
use dom::bindings::trace::JSTraceable;
|
||||||
|
@ -286,3 +287,34 @@ impl<'a, T: WeakReferenceable + 'a> Drop for WeakRefEntry<'a, T> {
|
||||||
*self.index += 1;
|
*self.index += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(MallocSizeOf)]
|
||||||
|
pub struct DOMTracker<T: WeakReferenceable> {
|
||||||
|
dom_objects: DomRefCell<WeakRefVec<T>>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: WeakReferenceable> DOMTracker<T> {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
dom_objects: DomRefCell::new(WeakRefVec::new())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn track(&self, dom_object: &T) {
|
||||||
|
self.dom_objects.borrow_mut().push(WeakRef::new(dom_object));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn for_each<F: FnMut(DomRoot<T>)>(&self, mut f: F) {
|
||||||
|
self.dom_objects.borrow_mut().update(|weak_ref| {
|
||||||
|
let root = weak_ref.root().unwrap();
|
||||||
|
f(root);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unsafe_code)]
|
||||||
|
unsafe impl<T: WeakReferenceable> JSTraceable for DOMTracker<T> {
|
||||||
|
unsafe fn trace(&self, _: *mut JSTracer) {
|
||||||
|
self.dom_objects.borrow_mut().retain_alive();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -141,7 +141,7 @@ use style::shared_lock::{SharedRwLock as StyleSharedRwLock, SharedRwLockReadGuar
|
||||||
use style::str::{split_html_space_chars, str_join};
|
use style::str::{split_html_space_chars, str_join};
|
||||||
use style::stylesheet_set::DocumentStylesheetSet;
|
use style::stylesheet_set::DocumentStylesheetSet;
|
||||||
use style::stylesheets::{CssRule, Stylesheet, Origin, OriginSet};
|
use style::stylesheets::{CssRule, Stylesheet, Origin, OriginSet};
|
||||||
use task_source::TaskSource;
|
use task_source::{TaskSource, TaskSourceName};
|
||||||
use time;
|
use time;
|
||||||
use timers::OneshotTimerCallback;
|
use timers::OneshotTimerCallback;
|
||||||
use url::Host;
|
use url::Host;
|
||||||
|
@ -1765,8 +1765,11 @@ impl Document {
|
||||||
// Step 10, 14
|
// Step 10, 14
|
||||||
if !self.salvageable.get() {
|
if !self.salvageable.get() {
|
||||||
// https://html.spec.whatwg.org/multipage/#unloading-document-cleanup-steps
|
// https://html.spec.whatwg.org/multipage/#unloading-document-cleanup-steps
|
||||||
|
let global_scope = self.window.upcast::<GlobalScope>();
|
||||||
|
// Step 1 of clean-up steps.
|
||||||
|
global_scope.close_event_sources();
|
||||||
let msg = ScriptMsg::DiscardDocument;
|
let msg = ScriptMsg::DiscardDocument;
|
||||||
let _ = self.window.upcast::<GlobalScope>().script_to_constellation_chan().send(msg);
|
let _ = global_scope.script_to_constellation_chan().send(msg);
|
||||||
}
|
}
|
||||||
// Step 15, End
|
// Step 15, End
|
||||||
self.decr_ignore_opens_during_unload_counter();
|
self.decr_ignore_opens_during_unload_counter();
|
||||||
|
@ -2010,7 +2013,7 @@ impl Document {
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/#abort-a-document
|
// https://html.spec.whatwg.org/multipage/#abort-a-document
|
||||||
fn abort(&self) {
|
pub fn abort(&self) {
|
||||||
// We need to inhibit the loader before anything else.
|
// We need to inhibit the loader before anything else.
|
||||||
self.loader.borrow_mut().inhibit_events();
|
self.loader.borrow_mut().inhibit_events();
|
||||||
|
|
||||||
|
@ -2029,14 +2032,24 @@ impl Document {
|
||||||
*self.asap_scripts_set.borrow_mut() = vec![];
|
*self.asap_scripts_set.borrow_mut() = vec![];
|
||||||
self.asap_in_order_scripts_list.clear();
|
self.asap_in_order_scripts_list.clear();
|
||||||
self.deferred_scripts.clear();
|
self.deferred_scripts.clear();
|
||||||
|
let global_scope = self.window.upcast::<GlobalScope>();
|
||||||
|
let loads_cancelled = self.loader.borrow_mut().cancel_all_loads();
|
||||||
|
let event_sources_canceled = global_scope.close_event_sources();
|
||||||
|
if loads_cancelled || event_sources_canceled {
|
||||||
|
// If any loads were canceled.
|
||||||
|
self.salvageable.set(false);
|
||||||
|
};
|
||||||
|
|
||||||
// TODO: https://github.com/servo/servo/issues/15236
|
// Also Step 2.
|
||||||
self.window.cancel_all_tasks();
|
// Note: the spec says to discard any tasks queued for fetch.
|
||||||
|
// This cancels all tasks on the networking task source, which might be too broad.
|
||||||
|
// See https://github.com/whatwg/html/issues/3837
|
||||||
|
self.window.cancel_all_tasks_from_source(TaskSourceName::Networking);
|
||||||
|
|
||||||
// Step 3.
|
// Step 3.
|
||||||
if let Some(parser) = self.get_current_parser() {
|
if let Some(parser) = self.get_current_parser() {
|
||||||
parser.abort();
|
parser.abort();
|
||||||
// TODO: salvageable flag.
|
self.salvageable.set(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3917,7 +3930,7 @@ impl DocumentMethods for Document {
|
||||||
return Err(Error::Security);
|
return Err(Error::Security);
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.get_current_parser().map_or(false, |parser| parser.script_nesting_level() > 0) {
|
if self.get_current_parser().map_or(false, |parser| parser.is_active()) {
|
||||||
// Step 5.
|
// Step 5.
|
||||||
return Ok(DomRoot::from_ref(self));
|
return Ok(DomRoot::from_ref(self));
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@ use dom::globalscope::GlobalScope;
|
||||||
use dom::messageevent::MessageEvent;
|
use dom::messageevent::MessageEvent;
|
||||||
use dom_struct::dom_struct;
|
use dom_struct::dom_struct;
|
||||||
use euclid::Length;
|
use euclid::Length;
|
||||||
|
use fetch::FetchCanceller;
|
||||||
use hyper::header::{Accept, qitem};
|
use hyper::header::{Accept, qitem};
|
||||||
use ipc_channel::ipc;
|
use ipc_channel::ipc;
|
||||||
use ipc_channel::router::ROUTER;
|
use ipc_channel::router::ROUTER;
|
||||||
|
@ -64,6 +65,7 @@ pub struct EventSource {
|
||||||
|
|
||||||
ready_state: Cell<ReadyState>,
|
ready_state: Cell<ReadyState>,
|
||||||
with_credentials: bool,
|
with_credentials: bool,
|
||||||
|
canceller: DomRefCell<FetchCanceller>,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ParserState {
|
enum ParserState {
|
||||||
|
@ -118,19 +120,7 @@ impl EventSourceContext {
|
||||||
if self.gen_id != event_source.generation_id.get() {
|
if self.gen_id != event_source.generation_id.get() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let global = event_source.global();
|
event_source.fail_the_connection();
|
||||||
let event_source = self.event_source.clone();
|
|
||||||
// FIXME(nox): Why are errors silenced here?
|
|
||||||
let _ = global.remote_event_task_source().queue(
|
|
||||||
task!(fail_the_event_source_connection: move || {
|
|
||||||
let event_source = event_source.root();
|
|
||||||
if event_source.ready_state.get() != ReadyState::Closed {
|
|
||||||
event_source.ready_state.set(ReadyState::Closed);
|
|
||||||
event_source.upcast::<EventTarget>().fire_event(atom!("error"));
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
&global,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/#reestablish-the-connection
|
// https://html.spec.whatwg.org/multipage/#reestablish-the-connection
|
||||||
|
@ -410,6 +400,7 @@ impl EventSource {
|
||||||
|
|
||||||
ready_state: Cell::new(ReadyState::Connecting),
|
ready_state: Cell::new(ReadyState::Connecting),
|
||||||
with_credentials: with_credentials,
|
with_credentials: with_credentials,
|
||||||
|
canceller: DomRefCell::new(Default::default())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -419,6 +410,29 @@ impl EventSource {
|
||||||
Wrap)
|
Wrap)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://html.spec.whatwg.org/multipage/#sse-processing-model:fail-the-connection-3
|
||||||
|
pub fn cancel(&self) {
|
||||||
|
self.canceller.borrow_mut().cancel();
|
||||||
|
self.fail_the_connection();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <https://html.spec.whatwg.org/multipage/#fail-the-connection>
|
||||||
|
pub fn fail_the_connection(&self) {
|
||||||
|
let global = self.global();
|
||||||
|
let event_source = Trusted::new(self);
|
||||||
|
// FIXME(nox): Why are errors silenced here?
|
||||||
|
let _ = global.remote_event_task_source().queue(
|
||||||
|
task!(fail_the_event_source_connection: move || {
|
||||||
|
let event_source = event_source.root();
|
||||||
|
if event_source.ready_state.get() != ReadyState::Closed {
|
||||||
|
event_source.ready_state.set(ReadyState::Closed);
|
||||||
|
event_source.upcast::<EventTarget>().fire_event(atom!("error"));
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
&global,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn request(&self) -> RequestInit {
|
pub fn request(&self) -> RequestInit {
|
||||||
self.request.borrow().clone().unwrap()
|
self.request.borrow().clone().unwrap()
|
||||||
}
|
}
|
||||||
|
@ -437,6 +451,7 @@ impl EventSource {
|
||||||
};
|
};
|
||||||
// Step 1, 5
|
// Step 1, 5
|
||||||
let ev = EventSource::new(global, url_record.clone(), event_source_init.withCredentials);
|
let ev = EventSource::new(global, url_record.clone(), event_source_init.withCredentials);
|
||||||
|
global.track_event_source(&ev);
|
||||||
// Steps 6-7
|
// Steps 6-7
|
||||||
let cors_attribute_state = if event_source_init.withCredentials {
|
let cors_attribute_state = if event_source_init.withCredentials {
|
||||||
CorsSettings::UseCredentials
|
CorsSettings::UseCredentials
|
||||||
|
@ -491,13 +506,23 @@ impl EventSource {
|
||||||
ROUTER.add_route(action_receiver.to_opaque(), Box::new(move |message| {
|
ROUTER.add_route(action_receiver.to_opaque(), Box::new(move |message| {
|
||||||
listener.notify_fetch(message.to().unwrap());
|
listener.notify_fetch(message.to().unwrap());
|
||||||
}));
|
}));
|
||||||
|
let cancel_receiver = ev.canceller.borrow_mut().initialize();
|
||||||
global.core_resource_thread().send(
|
global.core_resource_thread().send(
|
||||||
CoreResourceMsg::Fetch(request, FetchChannels::ResponseMsg(action_sender, None))).unwrap();
|
CoreResourceMsg::Fetch(request, FetchChannels::ResponseMsg(action_sender, Some(cancel_receiver)))).unwrap();
|
||||||
// Step 13
|
// Step 13
|
||||||
Ok(ev)
|
Ok(ev)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://html.spec.whatwg.org/multipage/#garbage-collection-2
|
||||||
|
impl Drop for EventSource {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
// If an EventSource object is garbage collected while its connection is still open,
|
||||||
|
// the user agent must abort any instance of the fetch algorithm opened by this EventSource.
|
||||||
|
self.canceller.borrow_mut().cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl EventSourceMethods for EventSource {
|
impl EventSourceMethods for EventSource {
|
||||||
// https://html.spec.whatwg.org/multipage/#handler-eventsource-onopen
|
// https://html.spec.whatwg.org/multipage/#handler-eventsource-onopen
|
||||||
event_handler!(open, GetOnopen, SetOnopen);
|
event_handler!(open, GetOnopen, SetOnopen);
|
||||||
|
@ -527,6 +552,7 @@ impl EventSourceMethods for EventSource {
|
||||||
fn Close(&self) {
|
fn Close(&self) {
|
||||||
let GenerationId(prev_id) = self.generation_id.get();
|
let GenerationId(prev_id) = self.generation_id.get();
|
||||||
self.generation_id.set(GenerationId(prev_id + 1));
|
self.generation_id.set(GenerationId(prev_id + 1));
|
||||||
|
self.canceller.borrow_mut().cancel();
|
||||||
self.ready_state.set(ReadyState::Closed);
|
self.ready_state.set(ReadyState::Closed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
use devtools_traits::{ScriptToDevtoolsControlMsg, WorkerId};
|
use devtools_traits::{ScriptToDevtoolsControlMsg, WorkerId};
|
||||||
use dom::bindings::cell::DomRefCell;
|
use dom::bindings::cell::DomRefCell;
|
||||||
|
use dom::bindings::codegen::Bindings::EventSourceBinding::EventSourceBinding::EventSourceMethods;
|
||||||
use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
|
use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
|
||||||
use dom::bindings::codegen::Bindings::WorkerGlobalScopeBinding::WorkerGlobalScopeMethods;
|
use dom::bindings::codegen::Bindings::WorkerGlobalScopeBinding::WorkerGlobalScopeMethods;
|
||||||
use dom::bindings::conversions::root_from_object;
|
use dom::bindings::conversions::root_from_object;
|
||||||
|
@ -13,10 +14,12 @@ use dom::bindings::reflector::DomObject;
|
||||||
use dom::bindings::root::{DomRoot, MutNullableDom};
|
use dom::bindings::root::{DomRoot, MutNullableDom};
|
||||||
use dom::bindings::settings_stack::{AutoEntryScript, entry_global, incumbent_global};
|
use dom::bindings::settings_stack::{AutoEntryScript, entry_global, incumbent_global};
|
||||||
use dom::bindings::str::DOMString;
|
use dom::bindings::str::DOMString;
|
||||||
|
use dom::bindings::weakref::DOMTracker;
|
||||||
use dom::crypto::Crypto;
|
use dom::crypto::Crypto;
|
||||||
use dom::dedicatedworkerglobalscope::DedicatedWorkerGlobalScope;
|
use dom::dedicatedworkerglobalscope::DedicatedWorkerGlobalScope;
|
||||||
use dom::errorevent::ErrorEvent;
|
use dom::errorevent::ErrorEvent;
|
||||||
use dom::event::{Event, EventBubbles, EventCancelable, EventStatus};
|
use dom::event::{Event, EventBubbles, EventCancelable, EventStatus};
|
||||||
|
use dom::eventsource::EventSource;
|
||||||
use dom::eventtarget::EventTarget;
|
use dom::eventtarget::EventTarget;
|
||||||
use dom::performance::Performance;
|
use dom::performance::Performance;
|
||||||
use dom::window::Window;
|
use dom::window::Window;
|
||||||
|
@ -131,6 +134,9 @@ pub struct GlobalScope {
|
||||||
/// Vector storing closing references of all workers
|
/// Vector storing closing references of all workers
|
||||||
#[ignore_malloc_size_of = "Arc"]
|
#[ignore_malloc_size_of = "Arc"]
|
||||||
list_auto_close_worker: DomRefCell<Vec<AutoCloseWorker>>,
|
list_auto_close_worker: DomRefCell<Vec<AutoCloseWorker>>,
|
||||||
|
|
||||||
|
/// Vector storing references of all eventsources.
|
||||||
|
event_source_tracker: DOMTracker<EventSource>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GlobalScope {
|
impl GlobalScope {
|
||||||
|
@ -164,6 +170,7 @@ impl GlobalScope {
|
||||||
origin,
|
origin,
|
||||||
microtask_queue,
|
microtask_queue,
|
||||||
list_auto_close_worker: Default::default(),
|
list_auto_close_worker: Default::default(),
|
||||||
|
event_source_tracker: DOMTracker::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -171,6 +178,24 @@ impl GlobalScope {
|
||||||
self.list_auto_close_worker.borrow_mut().push(AutoCloseWorker(closing_worker));
|
self.list_auto_close_worker.borrow_mut().push(AutoCloseWorker(closing_worker));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn track_event_source(&self, event_source: &EventSource) {
|
||||||
|
self.event_source_tracker.track(event_source);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn close_event_sources(&self) -> bool {
|
||||||
|
let mut canceled_any_fetch = false;
|
||||||
|
self.event_source_tracker.for_each(|event_source: DomRoot<EventSource>| {
|
||||||
|
match event_source.ReadyState() {
|
||||||
|
2 => {},
|
||||||
|
_ => {
|
||||||
|
event_source.cancel();
|
||||||
|
canceled_any_fetch = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
canceled_any_fetch
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the global scope of the realm that the given DOM object's reflector
|
/// Returns the global scope of the realm that the given DOM object's reflector
|
||||||
/// was created in.
|
/// was created in.
|
||||||
#[allow(unsafe_code)]
|
#[allow(unsafe_code)]
|
||||||
|
|
|
@ -300,7 +300,7 @@ impl HTMLImageElement {
|
||||||
|
|
||||||
// This is a background load because the load blocker already fulfills the
|
// This is a background load because the load blocker already fulfills the
|
||||||
// purpose of delaying the document's load event.
|
// purpose of delaying the document's load event.
|
||||||
document.loader().fetch_async_background(request, action_sender);
|
document.loader_mut().fetch_async_background(request, action_sender);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Step 14 of https://html.spec.whatwg.org/multipage/#update-the-image-data
|
/// Step 14 of https://html.spec.whatwg.org/multipage/#update-the-image-data
|
||||||
|
|
|
@ -613,7 +613,7 @@ impl HTMLMediaElement {
|
||||||
ROUTER.add_route(action_receiver.to_opaque(), Box::new(move |message| {
|
ROUTER.add_route(action_receiver.to_opaque(), Box::new(move |message| {
|
||||||
listener.notify_fetch(message.to().unwrap());
|
listener.notify_fetch(message.to().unwrap());
|
||||||
}));
|
}));
|
||||||
document.loader().fetch_async_background(request, action_sender);
|
document.loader_mut().fetch_async_background(request, action_sender);
|
||||||
},
|
},
|
||||||
Resource::Object => {
|
Resource::Object => {
|
||||||
// FIXME(nox): Actually do something with the object.
|
// FIXME(nox): Actually do something with the object.
|
||||||
|
|
|
@ -2,24 +2,17 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* 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/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
use dom::bindings::cell::DomRefCell;
|
|
||||||
use dom::bindings::codegen::Bindings::EventListenerBinding::EventListener;
|
use dom::bindings::codegen::Bindings::EventListenerBinding::EventListener;
|
||||||
use dom::bindings::codegen::Bindings::EventTargetBinding::AddEventListenerOptions;
|
use dom::bindings::codegen::Bindings::EventTargetBinding::AddEventListenerOptions;
|
||||||
use dom::bindings::codegen::Bindings::EventTargetBinding::EventListenerOptions;
|
use dom::bindings::codegen::Bindings::EventTargetBinding::EventListenerOptions;
|
||||||
use dom::bindings::codegen::Bindings::MediaQueryListBinding::{self, MediaQueryListMethods};
|
use dom::bindings::codegen::Bindings::MediaQueryListBinding::{self, MediaQueryListMethods};
|
||||||
use dom::bindings::inheritance::Castable;
|
use dom::bindings::inheritance::Castable;
|
||||||
use dom::bindings::reflector::DomObject;
|
|
||||||
use dom::bindings::reflector::reflect_dom_object;
|
use dom::bindings::reflector::reflect_dom_object;
|
||||||
use dom::bindings::root::{Dom, DomRoot};
|
use dom::bindings::root::{Dom, DomRoot};
|
||||||
use dom::bindings::str::DOMString;
|
use dom::bindings::str::DOMString;
|
||||||
use dom::bindings::trace::JSTraceable;
|
|
||||||
use dom::bindings::weakref::{WeakRef, WeakRefVec};
|
|
||||||
use dom::document::Document;
|
use dom::document::Document;
|
||||||
use dom::event::Event;
|
|
||||||
use dom::eventtarget::EventTarget;
|
use dom::eventtarget::EventTarget;
|
||||||
use dom::mediaquerylistevent::MediaQueryListEvent;
|
|
||||||
use dom_struct::dom_struct;
|
use dom_struct::dom_struct;
|
||||||
use js::jsapi::JSTracer;
|
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use style::media_queries::MediaList;
|
use style::media_queries::MediaList;
|
||||||
|
@ -56,7 +49,7 @@ impl MediaQueryList {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MediaQueryList {
|
impl MediaQueryList {
|
||||||
fn evaluate_changes(&self) -> MediaQueryListMatchState {
|
pub fn evaluate_changes(&self) -> MediaQueryListMatchState {
|
||||||
let matches = self.evaluate();
|
let matches = self.evaluate();
|
||||||
|
|
||||||
let result = if let Some(old_matches) = self.last_match_state.get() {
|
let result = if let Some(old_matches) = self.last_match_state.get() {
|
||||||
|
@ -115,48 +108,3 @@ impl MediaQueryListMethods for MediaQueryList {
|
||||||
// https://drafts.csswg.org/cssom-view/#dom-mediaquerylist-onchange
|
// https://drafts.csswg.org/cssom-view/#dom-mediaquerylist-onchange
|
||||||
event_handler!(change, GetOnchange, SetOnchange);
|
event_handler!(change, GetOnchange, SetOnchange);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(MallocSizeOf)]
|
|
||||||
pub struct WeakMediaQueryListVec {
|
|
||||||
cell: DomRefCell<WeakRefVec<MediaQueryList>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl WeakMediaQueryListVec {
|
|
||||||
/// Create a new vector of weak references to MediaQueryList
|
|
||||||
pub fn new() -> Self {
|
|
||||||
WeakMediaQueryListVec { cell: DomRefCell::new(WeakRefVec::new()) }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn push(&self, mql: &MediaQueryList) {
|
|
||||||
self.cell.borrow_mut().push(WeakRef::new(mql));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Evaluate media query lists and report changes
|
|
||||||
/// <https://drafts.csswg.org/cssom-view/#evaluate-media-queries-and-report-changes>
|
|
||||||
pub fn evaluate_and_report_changes(&self) {
|
|
||||||
rooted_vec!(let mut mql_list);
|
|
||||||
self.cell.borrow_mut().update(|mql| {
|
|
||||||
let mql = mql.root().unwrap();
|
|
||||||
if let MediaQueryListMatchState::Changed(_) = mql.evaluate_changes() {
|
|
||||||
// Recording list of changed Media Queries
|
|
||||||
mql_list.push(Dom::from_ref(&*mql));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
// Sending change events for all changed Media Queries
|
|
||||||
for mql in mql_list.iter() {
|
|
||||||
let event = MediaQueryListEvent::new(&mql.global(),
|
|
||||||
atom!("change"),
|
|
||||||
false, false,
|
|
||||||
mql.Media(),
|
|
||||||
mql.Matches());
|
|
||||||
event.upcast::<Event>().fire(mql.upcast::<EventTarget>());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(unsafe_code)]
|
|
||||||
unsafe impl JSTraceable for WeakMediaQueryListVec {
|
|
||||||
unsafe fn trace(&self, _: *mut JSTracer) {
|
|
||||||
self.cell.borrow_mut().retain_alive()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -344,6 +344,11 @@ impl ServoParser {
|
||||||
self.document.set_ready_state(DocumentReadyState::Interactive);
|
self.document.set_ready_state(DocumentReadyState::Interactive);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://html.spec.whatwg.org/multipage/#active-parser
|
||||||
|
pub fn is_active(&self) -> bool {
|
||||||
|
self.script_nesting_level() > 0 && !self.aborted.get()
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(unrooted_must_root)]
|
#[allow(unrooted_must_root)]
|
||||||
fn new_inherited(document: &Document,
|
fn new_inherited(document: &Document,
|
||||||
tokenizer: Tokenizer,
|
tokenizer: Tokenizer,
|
||||||
|
@ -477,6 +482,9 @@ impl ServoParser {
|
||||||
self.suspended.set(true);
|
self.suspended.set(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if self.aborted.get() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
attribute DOMString status;
|
attribute DOMString status;
|
||||||
void close();
|
void close();
|
||||||
readonly attribute boolean closed;
|
readonly attribute boolean closed;
|
||||||
//void stop();
|
void stop();
|
||||||
//void focus();
|
//void focus();
|
||||||
//void blur();
|
//void blur();
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ use dom::bindings::cell::DomRefCell;
|
||||||
use dom::bindings::codegen::Bindings::DocumentBinding::{DocumentMethods, DocumentReadyState};
|
use dom::bindings::codegen::Bindings::DocumentBinding::{DocumentMethods, DocumentReadyState};
|
||||||
use dom::bindings::codegen::Bindings::FunctionBinding::Function;
|
use dom::bindings::codegen::Bindings::FunctionBinding::Function;
|
||||||
use dom::bindings::codegen::Bindings::HistoryBinding::HistoryBinding::HistoryMethods;
|
use dom::bindings::codegen::Bindings::HistoryBinding::HistoryBinding::HistoryMethods;
|
||||||
|
use dom::bindings::codegen::Bindings::MediaQueryListBinding::MediaQueryListBinding::MediaQueryListMethods;
|
||||||
use dom::bindings::codegen::Bindings::PermissionStatusBinding::PermissionState;
|
use dom::bindings::codegen::Bindings::PermissionStatusBinding::PermissionState;
|
||||||
use dom::bindings::codegen::Bindings::RequestBinding::RequestInit;
|
use dom::bindings::codegen::Bindings::RequestBinding::RequestInit;
|
||||||
use dom::bindings::codegen::Bindings::WindowBinding::{self, FrameRequestCallback, WindowMethods};
|
use dom::bindings::codegen::Bindings::WindowBinding::{self, FrameRequestCallback, WindowMethods};
|
||||||
|
@ -27,6 +28,7 @@ use dom::bindings::str::{DOMString, USVString};
|
||||||
use dom::bindings::structuredclone::StructuredCloneData;
|
use dom::bindings::structuredclone::StructuredCloneData;
|
||||||
use dom::bindings::trace::RootedTraceableBox;
|
use dom::bindings::trace::RootedTraceableBox;
|
||||||
use dom::bindings::utils::{GlobalStaticData, WindowProxyHandler};
|
use dom::bindings::utils::{GlobalStaticData, WindowProxyHandler};
|
||||||
|
use dom::bindings::weakref::DOMTracker;
|
||||||
use dom::bluetooth::BluetoothExtraPermissionData;
|
use dom::bluetooth::BluetoothExtraPermissionData;
|
||||||
use dom::crypto::Crypto;
|
use dom::crypto::Crypto;
|
||||||
use dom::cssstyledeclaration::{CSSModificationAccess, CSSStyleDeclaration, CSSStyleOwner};
|
use dom::cssstyledeclaration::{CSSModificationAccess, CSSStyleDeclaration, CSSStyleOwner};
|
||||||
|
@ -39,7 +41,8 @@ use dom::globalscope::GlobalScope;
|
||||||
use dom::hashchangeevent::HashChangeEvent;
|
use dom::hashchangeevent::HashChangeEvent;
|
||||||
use dom::history::History;
|
use dom::history::History;
|
||||||
use dom::location::Location;
|
use dom::location::Location;
|
||||||
use dom::mediaquerylist::{MediaQueryList, WeakMediaQueryListVec};
|
use dom::mediaquerylist::{MediaQueryList, MediaQueryListMatchState};
|
||||||
|
use dom::mediaquerylistevent::MediaQueryListEvent;
|
||||||
use dom::messageevent::MessageEvent;
|
use dom::messageevent::MessageEvent;
|
||||||
use dom::navigator::Navigator;
|
use dom::navigator::Navigator;
|
||||||
use dom::node::{Node, NodeDamage, document_from_node, from_untrusted_node_address};
|
use dom::node::{Node, NodeDamage, document_from_node, from_untrusted_node_address};
|
||||||
|
@ -263,7 +266,7 @@ pub struct Window {
|
||||||
scroll_offsets: DomRefCell<HashMap<UntrustedNodeAddress, Vector2D<f32>>>,
|
scroll_offsets: DomRefCell<HashMap<UntrustedNodeAddress, Vector2D<f32>>>,
|
||||||
|
|
||||||
/// All the MediaQueryLists we need to update
|
/// All the MediaQueryLists we need to update
|
||||||
media_query_lists: WeakMediaQueryListVec,
|
media_query_lists: DOMTracker<MediaQueryList>,
|
||||||
|
|
||||||
test_runner: MutNullableDom<TestRunner>,
|
test_runner: MutNullableDom<TestRunner>,
|
||||||
|
|
||||||
|
@ -555,6 +558,13 @@ impl WindowMethods for Window {
|
||||||
receiver.recv().unwrap();
|
receiver.recv().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://html.spec.whatwg.org/multipage/#dom-window-stop
|
||||||
|
fn Stop(&self) {
|
||||||
|
// TODO: Cancel ongoing navigation.
|
||||||
|
let doc = self.Document();
|
||||||
|
doc.abort();
|
||||||
|
}
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/#dom-window-closed
|
// https://html.spec.whatwg.org/multipage/#dom-window-closed
|
||||||
fn Closed(&self) -> bool {
|
fn Closed(&self) -> bool {
|
||||||
self.window_proxy.get()
|
self.window_proxy.get()
|
||||||
|
@ -1048,7 +1058,7 @@ impl WindowMethods for Window {
|
||||||
media_queries::MediaList::parse(&context, &mut parser);
|
media_queries::MediaList::parse(&context, &mut parser);
|
||||||
let document = self.Document();
|
let document = self.Document();
|
||||||
let mql = MediaQueryList::new(&document, media_query_list);
|
let mql = MediaQueryList::new(&document, media_query_list);
|
||||||
self.media_query_lists.push(&*mql);
|
self.media_query_lists.track(&*mql);
|
||||||
mql
|
mql
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1113,6 +1123,16 @@ impl Window {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Cancels all the tasks from a given task source.
|
||||||
|
/// This sets the current sentinel value to
|
||||||
|
/// `true` and replaces it with a brand new one for future tasks.
|
||||||
|
pub fn cancel_all_tasks_from_source(&self, task_source_name: TaskSourceName) {
|
||||||
|
let mut ignore_flags = self.ignore_further_async_events.borrow_mut();
|
||||||
|
let flag = ignore_flags.entry(task_source_name).or_insert(Default::default());
|
||||||
|
let cancelled = mem::replace(&mut *flag, Default::default());
|
||||||
|
cancelled.store(true, Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn clear_js_runtime(&self) {
|
pub fn clear_js_runtime(&self) {
|
||||||
// We tear down the active document, which causes all the attached
|
// We tear down the active document, which causes all the attached
|
||||||
// nodes to dispose of their layout data. This messages the layout
|
// nodes to dispose of their layout data. This messages the layout
|
||||||
|
@ -1777,8 +1797,25 @@ impl Window {
|
||||||
self.parent_info.is_none()
|
self.parent_info.is_none()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Evaluate media query lists and report changes
|
||||||
|
/// <https://drafts.csswg.org/cssom-view/#evaluate-media-queries-and-report-changes>
|
||||||
pub fn evaluate_media_queries_and_report_changes(&self) {
|
pub fn evaluate_media_queries_and_report_changes(&self) {
|
||||||
self.media_query_lists.evaluate_and_report_changes();
|
rooted_vec!(let mut mql_list);
|
||||||
|
self.media_query_lists.for_each(|mql| {
|
||||||
|
if let MediaQueryListMatchState::Changed(_) = mql.evaluate_changes() {
|
||||||
|
// Recording list of changed Media Queries
|
||||||
|
mql_list.push(Dom::from_ref(&*mql));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// Sending change events for all changed Media Queries
|
||||||
|
for mql in mql_list.iter() {
|
||||||
|
let event = MediaQueryListEvent::new(&mql.global(),
|
||||||
|
atom!("change"),
|
||||||
|
false, false,
|
||||||
|
mql.Media(),
|
||||||
|
mql.Matches());
|
||||||
|
event.upcast::<Event>().fire(mql.upcast::<EventTarget>());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Slow down/speed up timers based on visibility.
|
/// Slow down/speed up timers based on visibility.
|
||||||
|
@ -1917,7 +1954,7 @@ impl Window {
|
||||||
ignore_further_async_events: Default::default(),
|
ignore_further_async_events: Default::default(),
|
||||||
error_reporter,
|
error_reporter,
|
||||||
scroll_offsets: Default::default(),
|
scroll_offsets: Default::default(),
|
||||||
media_query_lists: WeakMediaQueryListVec::new(),
|
media_query_lists: DOMTracker::new(),
|
||||||
test_runner: Default::default(),
|
test_runner: Default::default(),
|
||||||
webgl_chan,
|
webgl_chan,
|
||||||
webvr_chan,
|
webvr_chan,
|
||||||
|
|
|
@ -78,5 +78,5 @@ pub fn fetch_image_for_layout(url: ServoUrl,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Layout image loads do not delay the document load event.
|
// Layout image loads do not delay the document load event.
|
||||||
document.loader().fetch_async_background(request, action_sender);
|
document.loader_mut().fetch_async_background(request, action_sender);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
[eventsource-request-cancellation.htm]
|
|
||||||
type: testharness
|
|
||||||
[EventSource: request cancellation]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
[010.html]
|
[010.html]
|
||||||
type: testharness
|
type: testharness
|
||||||
expected: TIMEOUT
|
[Link with onclick form submit to javascript url with delayed document.write and href navigation ]
|
||||||
|
expected: FAIL
|
||||||
|
|
|
@ -1,7 +1,4 @@
|
||||||
[window-properties.https.html]
|
[window-properties.https.html]
|
||||||
[Window method: stop]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Window method: focus]
|
[Window method: focus]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
[script-onerror-insertion-point-2.html]
|
|
||||||
type: testharness
|
|
||||||
expected: TIMEOUT
|
|
|
@ -1,8 +1,3 @@
|
||||||
[aborted-parser.window.html]
|
[aborted-parser.window.html]
|
||||||
expected: TIMEOUT
|
|
||||||
[document.open() after parser is aborted]
|
[document.open() after parser is aborted]
|
||||||
expected: TIMEOUT
|
expected: FAIL
|
||||||
|
|
||||||
[async document.open() after parser is aborted]
|
|
||||||
expected: TIMEOUT
|
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
[webtiming-resolution.any.worker.html]
|
[webtiming-resolution.any.worker.html]
|
||||||
expected: TIMEOUT
|
expected: TIMEOUT
|
||||||
[Verifies the resolution of performance.now() is at least 20 microseconds.]
|
[Verifies the resolution of performance.now() is at least 20 microseconds.]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[Verifies the resolution of entry.startTime is at least 20 microseconds.]
|
[Verifies the resolution of entry.startTime is at least 20 microseconds.]
|
||||||
expected: TIMEOUT
|
expected: TIMEOUT
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue