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:
bors-servo 2018-07-29 11:14:29 -04:00 committed by GitHub
commit 076198fe80
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 195 additions and 104 deletions

View file

@ -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.

View file

@ -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': {},

View file

@ -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();
}
}

View file

@ -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));
} }

View file

@ -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);
} }
} }

View file

@ -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)]

View file

@ -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

View file

@ -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.

View file

@ -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()
}
}

View file

@ -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;
}
} }
} }

View file

@ -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();

View file

@ -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,

View file

@ -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);
} }

View file

@ -1,5 +0,0 @@
[eventsource-request-cancellation.htm]
type: testharness
[EventSource: request cancellation]
expected: FAIL

View file

@ -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

View file

@ -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

View file

@ -1,3 +0,0 @@
[script-onerror-insertion-point-2.html]
type: testharness
expected: TIMEOUT

View file

@ -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

View file

@ -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