diff options
Diffstat (limited to 'components/script')
32 files changed, 964 insertions, 437 deletions
diff --git a/components/script/Cargo.toml b/components/script/Cargo.toml index 81bf9bde314..e096dae1347 100644 --- a/components/script/Cargo.toml +++ b/components/script/Cargo.toml @@ -37,7 +37,7 @@ fnv = "1.0" gfx_traits = {path = "../gfx_traits"} heapsize = "0.3.6" heapsize_derive = "0.1" -html5ever = {version = "0.9.0", features = ["heap_size", "unstable"]} +html5ever = {version = "0.10.1", features = ["heap_size", "unstable"]} html5ever-atoms = {version = "0.1", features = ["heap_size"]} hyper = "0.9.9" hyper_serde = "0.1.4" diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs index b8274aec90c..b38d5d4ec91 100644 --- a/components/script/dom/bindings/trace.rs +++ b/components/script/dom/bindings/trace.rs @@ -46,6 +46,7 @@ use euclid::{Matrix2D, Matrix4D, Point2D}; use euclid::length::Length as EuclidLength; use euclid::rect::Rect; use euclid::size::Size2D; +use html5ever::tokenizer::buffer_queue::BufferQueue; use html5ever::tree_builder::QuirksMode; use html5ever_atoms::{Prefix, LocalName, Namespace, QualName}; use hyper::header::Headers; @@ -63,7 +64,7 @@ use net_traits::{Metadata, NetworkError, ReferrerPolicy, ResourceThreads}; use net_traits::filemanager_thread::RelativePos; use net_traits::image::base::{Image, ImageMetadata}; use net_traits::image_cache_thread::{ImageCacheChan, ImageCacheThread}; -use net_traits::request::Request; +use net_traits::request::{Request, RequestInit}; use net_traits::response::{Response, ResponseBody}; use net_traits::response::HttpsState; use net_traits::storage_thread::StorageType; @@ -322,7 +323,7 @@ no_jsmanaged_fields!(HashSet<T>); no_jsmanaged_fields!(FrameId, FrameType, WindowSizeData, WindowSizeType, PipelineId); no_jsmanaged_fields!(TimerEventId, TimerSource); no_jsmanaged_fields!(WorkerId); -no_jsmanaged_fields!(QuirksMode); +no_jsmanaged_fields!(BufferQueue, QuirksMode); no_jsmanaged_fields!(Runtime); no_jsmanaged_fields!(Headers, Method); no_jsmanaged_fields!(WindowProxyHandler); @@ -349,6 +350,7 @@ no_jsmanaged_fields!(AttrValue); no_jsmanaged_fields!(Snapshot); no_jsmanaged_fields!(HttpsState); no_jsmanaged_fields!(Request); +no_jsmanaged_fields!(RequestInit); no_jsmanaged_fields!(SharedRt); no_jsmanaged_fields!(TouchpadPressurePhase); no_jsmanaged_fields!(USVString); diff --git a/components/script/dom/bluetooth.rs b/components/script/dom/bluetooth.rs index 1f5fd15c49f..6125680d637 100644 --- a/components/script/dom/bluetooth.rs +++ b/components/script/dom/bluetooth.rs @@ -148,14 +148,14 @@ pub fn response_async<T: AsyncBluetoothListener + Reflectable + 'static>( promise: &Rc<Promise>, receiver: &T) -> IpcSender<BluetoothResponseResult> { let (action_sender, action_receiver) = ipc::channel().unwrap(); - let chan = receiver.global().networking_task_source(); + let task_source = receiver.global().networking_task_source(); let context = Arc::new(Mutex::new(BluetoothContext { promise: Some(TrustedPromise::new(promise.clone())), receiver: Trusted::new(receiver), })); let listener = NetworkListener { context: context, - script_chan: chan, + task_source: task_source, wrapper: None, }; ROUTER.add_route(action_receiver.to_opaque(), box move |message| { @@ -194,7 +194,7 @@ fn convert_request_device_options(filters: &Option<Vec<BluetoothRequestDeviceFil // Step 2.7. // Note: What we are doing here is adding the not blacklisted UUIDs to the result vector, - // insted of removing them from an already filled vector. + // instead of removing them from an already filled vector. if !uuid_is_blacklisted(uuid.as_ref(), Blacklist::All) { optional_services_uuids.push(uuid); } @@ -337,11 +337,7 @@ impl BluetoothMethods for Bluetooth { return p; } // Step 2. - if !option.acceptAllDevices { - self.request_bluetooth_devices(&p, &option.filters, &option.optionalServices); - } else { - self.request_bluetooth_devices(&p, &None, &option.optionalServices); - } + self.request_bluetooth_devices(&p, &option.filters, &option.optionalServices); // TODO(#4282): Step 3-5: Reject and resolve promise. return p; } diff --git a/components/script/dom/domparser.rs b/components/script/dom/domparser.rs index 0186b7e8b2a..2a770aeb3a0 100644 --- a/components/script/dom/domparser.rs +++ b/components/script/dom/domparser.rs @@ -18,8 +18,7 @@ use dom::bindings::str::DOMString; use dom::document::{Document, IsHTMLDocument}; use dom::document::DocumentSource; use dom::globalscope::GlobalScope; -use dom::servoparser::html::{ParseContext, parse_html}; -use dom::servoparser::xml::{self, parse_xml}; +use dom::servoparser::ServoParser; use dom::window::Window; #[dom_struct] @@ -70,7 +69,7 @@ impl DOMParserMethods for DOMParser { loader, None, None); - parse_html(&document, s, url, ParseContext::Owner(None)); + ServoParser::parse_html_document(&document, s, url, None); document.set_ready_state(DocumentReadyState::Complete); Ok(document) } @@ -86,7 +85,7 @@ impl DOMParserMethods for DOMParser { loader, None, None); - parse_xml(&document, s, url, xml::ParseContext::Owner(None)); + ServoParser::parse_xml_document(&document, s, url, None); Ok(document) } } diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs index 15a4ef816c8..ac65c01e24c 100644 --- a/components/script/dom/element.rs +++ b/components/script/dom/element.rs @@ -368,7 +368,7 @@ impl LayoutElementHelpers for LayoutJS<Element> { PropertyDeclaration::BackgroundImage(DeclaredValue::Value( background_image::SpecifiedValue(vec![ background_image::single_value::SpecifiedValue(Some( - specified::Image::Url(url, specified::UrlExtraData { }) + specified::Image::for_cascade(Some(Arc::new(url)), specified::url::UrlExtraData { }) )) ]))))); } diff --git a/components/script/dom/eventsource.rs b/components/script/dom/eventsource.rs index f05e73beafa..2eabfcbc8b0 100644 --- a/components/script/dom/eventsource.rs +++ b/components/script/dom/eventsource.rs @@ -6,18 +6,50 @@ use dom::bindings::cell::DOMRefCell; use dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull; use dom::bindings::codegen::Bindings::EventSourceBinding::{EventSourceInit, EventSourceMethods, Wrap}; use dom::bindings::error::{Error, Fallible}; +use dom::bindings::inheritance::Castable; use dom::bindings::js::Root; -use dom::bindings::reflector::reflect_dom_object; +use dom::bindings::refcounted::Trusted; +use dom::bindings::reflector::{Reflectable, reflect_dom_object}; use dom::bindings::str::DOMString; +use dom::event::Event; use dom::eventtarget::EventTarget; use dom::globalscope::GlobalScope; +use dom::messageevent::MessageEvent; +use encoding::Encoding; +use encoding::all::UTF_8; +use euclid::length::Length; +use hyper::header::{Accept, qitem}; +use ipc_channel::ipc; +use ipc_channel::router::ROUTER; +use js::conversions::ToJSValConvertible; +use js::jsapi::JSAutoCompartment; +use js::jsval::UndefinedValue; +use mime::{Mime, TopLevel, SubLevel}; +use net_traits::{CoreResourceMsg, FetchMetadata, FetchResponseMsg, FetchResponseListener, NetworkError}; +use net_traits::request::{CacheMode, CorsSettings, CredentialsMode}; +use net_traits::request::{RequestInit, RequestMode}; +use network_listener::{NetworkListener, PreInvoke}; +use script_thread::Runnable; +use servo_atoms::Atom; use std::cell::Cell; +use std::mem; +use std::str::{Chars, FromStr}; +use std::sync::{Arc, Mutex}; +use task_source::TaskSource; +use timers::OneshotTimerCallback; use url::Url; +header! { (LastEventId, "Last-Event-ID") => [String] } + +const DEFAULT_RECONNECTION_TIME: u64 = 5000; + +#[derive(JSTraceable, PartialEq, Copy, Clone, Debug, HeapSizeOf)] +struct GenerationId(u32); + #[derive(JSTraceable, PartialEq, Copy, Clone, Debug, HeapSizeOf)] -enum EventSourceReadyState { +/// https://html.spec.whatwg.org/multipage/#dom-eventsource-readystate +enum ReadyState { Connecting = 0, - #[allow(dead_code)] Open = 1, Closed = 2 } @@ -26,9 +58,254 @@ enum EventSourceReadyState { pub struct EventSource { eventtarget: EventTarget, url: Url, - ready_state: Cell<EventSourceReadyState>, + request: DOMRefCell<Option<RequestInit>>, + last_event_id: DOMRefCell<DOMString>, + reconnection_time: Cell<u64>, + generation_id: Cell<GenerationId>, + + ready_state: Cell<ReadyState>, with_credentials: bool, - last_event_id: DOMRefCell<DOMString> +} + +enum ParserState { + Field, + Comment, + Value, + Eol +} + +struct EventSourceContext { + event_source: Trusted<EventSource>, + gen_id: GenerationId, + action_sender: ipc::IpcSender<FetchResponseMsg>, + + parser_state: ParserState, + field: String, + value: String, + origin: String, + + event_type: String, + data: String, + last_event_id: String, +} + +impl EventSourceContext { + fn announce_the_connection(&self) { + let event_source = self.event_source.root(); + if self.gen_id != event_source.generation_id.get() { + return; + } + let runnable = box AnnounceConnectionRunnable { + event_source: self.event_source.clone() + }; + let _ = event_source.global().networking_task_source().queue(runnable, &*event_source.global()); + } + + fn fail_the_connection(&self) { + let event_source = self.event_source.root(); + if self.gen_id != event_source.generation_id.get() { + return; + } + let runnable = box FailConnectionRunnable { + event_source: self.event_source.clone() + }; + let _ = event_source.global().networking_task_source().queue(runnable, &*event_source.global()); + } + + // https://html.spec.whatwg.org/multipage/#reestablish-the-connection + fn reestablish_the_connection(&self) { + let event_source = self.event_source.root(); + + if self.gen_id != event_source.generation_id.get() { + return; + } + + // Step 1 + let runnable = box ReestablishConnectionRunnable { + event_source: self.event_source.clone(), + action_sender: self.action_sender.clone() + }; + let _ = event_source.global().networking_task_source().queue(runnable, &*event_source.global()); + } + + // https://html.spec.whatwg.org/multipage/#processField + fn process_field(&mut self) { + match &*self.field { + "event" => mem::swap(&mut self.event_type, &mut self.value), + "data" => { + self.data.push_str(&self.value); + self.data.push('\n'); + } + "id" => mem::swap(&mut self.last_event_id, &mut self.value), + "retry" => if let Ok(time) = u64::from_str(&self.value) { + self.event_source.root().reconnection_time.set(time); + }, + _ => () + } + + self.field.clear(); + self.value.clear(); + } + + // https://html.spec.whatwg.org/multipage/#dispatchMessage + #[allow(unsafe_code)] + fn dispatch_event(&mut self) { + let event_source = self.event_source.root(); + // Step 1 + *event_source.last_event_id.borrow_mut() = DOMString::from(self.last_event_id.clone()); + // Step 2 + if self.data.is_empty() { + self.data.clear(); + self.event_type.clear(); + return; + } + // Step 3 + if let Some(last) = self.data.pop() { + if last != '\n' { + self.data.push(last); + } + } + // Step 6 + let type_ = if !self.event_type.is_empty() { + Atom::from(self.event_type.clone()) + } else { + atom!("message") + }; + // Steps 4-5 + let event = { + let _ac = JSAutoCompartment::new(event_source.global().get_cx(), + event_source.reflector().get_jsobject().get()); + rooted!(in(event_source.global().get_cx()) let mut data = UndefinedValue()); + unsafe { self.data.to_jsval(event_source.global().get_cx(), data.handle_mut()) }; + MessageEvent::new(&*event_source.global(), type_, false, false, data.handle(), + DOMString::from(self.origin.clone()), + event_source.last_event_id.borrow().clone()) + }; + // Step 7 + self.event_type.clear(); + self.data.clear(); + // Step 8 + let runnable = box DispatchEventRunnable { + event_source: self.event_source.clone(), + event: Trusted::new(&event) + }; + let _ = event_source.global().networking_task_source().queue(runnable, &*event_source.global()); + } + + // https://html.spec.whatwg.org/multipage/#event-stream-interpretation + fn parse(&mut self, stream: Chars) { + let mut stream = stream.peekable(); + + while let Some(ch) = stream.next() { + match (ch, &self.parser_state) { + (':', &ParserState::Eol) => self.parser_state = ParserState::Comment, + (':', &ParserState::Field) => { + self.parser_state = ParserState::Value; + if let Some(&' ') = stream.peek() { + stream.next(); + } + } + + ('\n', &ParserState::Value) => { + self.parser_state = ParserState::Eol; + self.process_field(); + } + ('\r', &ParserState::Value) => { + if let Some(&'\n') = stream.peek() { + continue; + } + self.parser_state = ParserState::Eol; + self.process_field(); + } + + ('\n', &ParserState::Field) => { + self.parser_state = ParserState::Eol; + self.process_field(); + } + ('\r', &ParserState::Field) => { + if let Some(&'\n') = stream.peek() { + continue; + } + self.parser_state = ParserState::Eol; + self.process_field(); + } + + ('\n', &ParserState::Eol) => self.dispatch_event(), + ('\r', &ParserState::Eol) => { + if let Some(&'\n') = stream.peek() { + continue; + } + self.dispatch_event(); + } + + ('\n', &ParserState::Comment) => self.parser_state = ParserState::Eol, + ('\r', &ParserState::Comment) => { + if let Some(&'\n') = stream.peek() { + continue; + } + self.parser_state = ParserState::Eol; + } + + (_, &ParserState::Field) => self.field.push(ch), + (_, &ParserState::Value) => self.value.push(ch), + (_, &ParserState::Eol) => { + self.parser_state = ParserState::Field; + self.field.push(ch); + } + (_, &ParserState::Comment) => (), + } + } + } +} + +impl FetchResponseListener for EventSourceContext { + fn process_request_body(&mut self) { + // TODO + } + + fn process_request_eof(&mut self) { + // TODO + } + + fn process_response(&mut self, metadata: Result<FetchMetadata, NetworkError>) { + match metadata { + Ok(fm) => { + let meta = match fm { + FetchMetadata::Unfiltered(m) => m, + FetchMetadata::Filtered { unsafe_, .. } => unsafe_ + }; + match meta.content_type { + None => self.fail_the_connection(), + Some(ct) => match ct.into_inner().0 { + Mime(TopLevel::Text, SubLevel::EventStream, _) => { + self.origin = meta.final_url.origin().unicode_serialization(); + self.announce_the_connection(); + } + _ => self.fail_the_connection() + } + } + } + Err(_) => { + self.reestablish_the_connection(); + } + } + } + + fn process_response_chunk(&mut self, chunk: Vec<u8>) { + let mut stream = String::new(); + UTF_8.raw_decoder().raw_feed(&chunk, &mut stream); + self.parse(stream.chars()) + } + + fn process_response_eof(&mut self, _response: Result<(), NetworkError>) { + self.reestablish_the_connection(); + } +} + +impl PreInvoke for EventSourceContext { + fn should_invoke(&self) -> bool { + self.event_source.root().generation_id.get() == self.gen_id + } } impl EventSource { @@ -36,9 +313,13 @@ impl EventSource { EventSource { eventtarget: EventTarget::new_inherited(), url: url, - ready_state: Cell::new(EventSourceReadyState::Connecting), + request: DOMRefCell::new(None), + last_event_id: DOMRefCell::new(DOMString::from("")), + reconnection_time: Cell::new(DEFAULT_RECONNECTION_TIME), + generation_id: Cell::new(GenerationId(0)), + + ready_state: Cell::new(ReadyState::Connecting), with_credentials: with_credentials, - last_event_id: DOMRefCell::new(DOMString::from("")) } } @@ -48,27 +329,78 @@ impl EventSource { Wrap) } + pub fn request(&self) -> RequestInit { + self.request.borrow().clone().unwrap() + } + pub fn Constructor(global: &GlobalScope, - url_str: DOMString, + url: DOMString, event_source_init: &EventSourceInit) -> Fallible<Root<EventSource>> { - // Steps 1-2 - let base_url = global.get_url(); - let url = match base_url.join(&*url_str) { + // TODO: Step 2 relevant settings object + // Step 3 + let base_url = global.api_base_url(); + let url_record = match base_url.join(&*url) { Ok(u) => u, + // Step 4 Err(_) => return Err(Error::Syntax) }; - // Step 3 - let event_source = EventSource::new(global, url, event_source_init.withCredentials); - // Step 4 - // Step 5 - // Step 6 - // Step 7 + // Step 1, 5 + let ev = EventSource::new(global, url_record.clone(), event_source_init.withCredentials); + // Steps 6-7 + let cors_attribute_state = if event_source_init.withCredentials { + CorsSettings::UseCredentials + } else { + CorsSettings::Anonymous + }; // Step 8 - // Step 9 + // TODO: Step 9 set request's client settings + let mut request = RequestInit { + url: url_record, + origin: global.get_url(), + pipeline_id: Some(global.pipeline_id()), + // https://html.spec.whatwg.org/multipage/#create-a-potential-cors-request + use_url_credentials: true, + mode: RequestMode::CorsMode, + credentials_mode: if cors_attribute_state == CorsSettings::Anonymous { + CredentialsMode::CredentialsSameOrigin + } else { + CredentialsMode::Include + }, + ..RequestInit::default() + }; // Step 10 + request.headers.set(Accept(vec![qitem(mime!(Text / EventStream))])); // Step 11 - Ok(event_source) + request.cache_mode = CacheMode::NoStore; // Step 12 + *ev.request.borrow_mut() = Some(request.clone()); + // Step 14 + let (action_sender, action_receiver) = ipc::channel().unwrap(); + let context = EventSourceContext { + event_source: Trusted::new(&ev), + gen_id: ev.generation_id.get(), + action_sender: action_sender.clone(), + + parser_state: ParserState::Eol, + field: String::new(), + value: String::new(), + origin: String::new(), + + event_type: String::new(), + data: String::new(), + last_event_id: String::new(), + }; + let listener = NetworkListener { + context: Arc::new(Mutex::new(context)), + task_source: global.networking_task_source(), + wrapper: Some(global.get_runnable_wrapper()) + }; + ROUTER.add_route(action_receiver.to_opaque(), box move |message| { + listener.notify_fetch(message.to().unwrap()); + }); + global.core_resource_thread().send(CoreResourceMsg::Fetch(request, action_sender)).unwrap(); + // Step 13 + Ok(ev) } } @@ -99,7 +431,119 @@ impl EventSourceMethods for EventSource { // https://html.spec.whatwg.org/multipage/#dom-eventsource-close fn Close(&self) { - self.ready_state.set(EventSourceReadyState::Closed); - // TODO: Terminate ongoing fetch + let GenerationId(prev_id) = self.generation_id.get(); + self.generation_id.set(GenerationId(prev_id + 1)); + self.ready_state.set(ReadyState::Closed); + } +} + +pub struct AnnounceConnectionRunnable { + event_source: Trusted<EventSource>, +} + +impl Runnable for AnnounceConnectionRunnable { + fn name(&self) -> &'static str { "EventSource AnnounceConnectionRunnable" } + + // https://html.spec.whatwg.org/multipage/#announce-the-connection + fn handler(self: Box<AnnounceConnectionRunnable>) { + let event_source = self.event_source.root(); + if event_source.ready_state.get() != ReadyState::Closed { + event_source.ready_state.set(ReadyState::Open); + event_source.upcast::<EventTarget>().fire_event(atom!("open")); + } + } +} + +pub struct FailConnectionRunnable { + event_source: Trusted<EventSource>, +} + +impl Runnable for FailConnectionRunnable { + fn name(&self) -> &'static str { "EventSource FailConnectionRunnable" } + + // https://html.spec.whatwg.org/multipage/#fail-the-connection + fn handler(self: Box<FailConnectionRunnable>) { + let event_source = self.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")); + } + } +} + +pub struct ReestablishConnectionRunnable { + event_source: Trusted<EventSource>, + action_sender: ipc::IpcSender<FetchResponseMsg>, +} + +impl Runnable for ReestablishConnectionRunnable { + fn name(&self) -> &'static str { "EventSource ReestablishConnectionRunnable" } + + // https://html.spec.whatwg.org/multipage/#reestablish-the-connection + fn handler(self: Box<ReestablishConnectionRunnable>) { + let event_source = self.event_source.root(); + // Step 1.1 + if event_source.ready_state.get() == ReadyState::Closed { + return; + } + // Step 1.2 + event_source.ready_state.set(ReadyState::Connecting); + // Step 1.3 + event_source.upcast::<EventTarget>().fire_event(atom!("error")); + // Step 2 + let duration = Length::new(event_source.reconnection_time.get()); + // TODO Step 3: Optionally wait some more + // Steps 4-5 + let callback = OneshotTimerCallback::EventSourceTimeout(EventSourceTimeoutCallback { + event_source: self.event_source.clone(), + action_sender: self.action_sender.clone() + }); + let _ = event_source.global().schedule_callback(callback, duration); + } +} + +#[derive(JSTraceable, HeapSizeOf)] +pub struct EventSourceTimeoutCallback { + #[ignore_heap_size_of = "Because it is non-owning"] + event_source: Trusted<EventSource>, + #[ignore_heap_size_of = "Because it is non-owning"] + action_sender: ipc::IpcSender<FetchResponseMsg>, +} + +impl EventSourceTimeoutCallback { + // https://html.spec.whatwg.org/multipage/#reestablish-the-connection + pub fn invoke(self) { + let event_source = self.event_source.root(); + let global = event_source.global(); + // Step 5.1 + if event_source.ready_state.get() != ReadyState::Connecting { + return; + } + // Step 5.2 + let mut request = event_source.request(); + // Step 5.3 + if !event_source.last_event_id.borrow().is_empty() { + request.headers.set(LastEventId(String::from(event_source.last_event_id.borrow().clone()))); + } + // Step 5.4 + global.core_resource_thread().send(CoreResourceMsg::Fetch(request, self.action_sender)).unwrap(); + } +} + +pub struct DispatchEventRunnable { + event_source: Trusted<EventSource>, + event: Trusted<MessageEvent>, +} + +impl Runnable for DispatchEventRunnable { + fn name(&self) -> &'static str { "EventSource DispatchEventRunnable" } + + // https://html.spec.whatwg.org/multipage/#dispatchMessage + fn handler(self: Box<DispatchEventRunnable>) { + let event_source = self.event_source.root(); + // Step 8 + if event_source.ready_state.get() != ReadyState::Closed { + self.event.root().upcast::<Event>().fire(&event_source.upcast()); + } } } diff --git a/components/script/dom/globalscope.rs b/components/script/dom/globalscope.rs index fc160675df5..2338dcf32e7 100644 --- a/components/script/dom/globalscope.rs +++ b/components/script/dom/globalscope.rs @@ -42,6 +42,7 @@ use std::collections::hash_map::Entry; use std::ffi::CString; use std::panic; use task_source::file_reading::FileReadingTaskSource; +use task_source::networking::NetworkingTaskSource; use time::{Timespec, get_time}; use timers::{IsInterval, OneshotTimerCallback, OneshotTimerHandle}; use timers::{OneshotTimers, TimerCallback}; @@ -325,12 +326,12 @@ impl GlobalScope { /// `ScriptChan` to send messages to the networking task source of /// this of this global scope. - pub fn networking_task_source(&self) -> Box<ScriptChan + Send> { + pub fn networking_task_source(&self) -> NetworkingTaskSource { if let Some(window) = self.downcast::<Window>() { return window.networking_task_source(); } if let Some(worker) = self.downcast::<WorkerGlobalScope>() { - return worker.script_chan(); + return worker.networking_task_source(); } unreachable!(); } diff --git a/components/script/dom/htmlimageelement.rs b/components/script/dom/htmlimageelement.rs index e8a269c782c..d8ecda30943 100644 --- a/components/script/dom/htmlimageelement.rs +++ b/components/script/dom/htmlimageelement.rs @@ -26,8 +26,6 @@ use ipc_channel::ipc; use ipc_channel::router::ROUTER; use net_traits::image::base::{Image, ImageMetadata}; use net_traits::image_cache_thread::{ImageResponder, ImageResponse}; -use script_runtime::CommonScriptMsg; -use script_runtime::ScriptThreadEventCategory::UpdateReplacedElement; use script_thread::Runnable; use std::i32; use std::sync::Arc; @@ -140,7 +138,7 @@ impl HTMLImageElement { let trusted_node = Trusted::new(self); let (responder_sender, responder_receiver) = ipc::channel().unwrap(); - let script_chan = window.networking_task_source(); + let task_source = window.networking_task_source(); let wrapper = window.get_runnable_wrapper(); ROUTER.add_route(responder_receiver.to_opaque(), box move |message| { // Return the image via a message to the script thread, which marks the element @@ -148,9 +146,7 @@ impl HTMLImageElement { let image_response = message.to().unwrap(); let runnable = box ImageResponseHandlerRunnable::new( trusted_node.clone(), image_response); - let runnable = wrapper.wrap_runnable(runnable); - let _ = script_chan.send(CommonScriptMsg::RunnableMsg( - UpdateReplacedElement, runnable)); + let _ = task_source.queue_with_wrapper(runnable, &wrapper); }); image_cache.request_image_and_metadata(img_url, diff --git a/components/script/dom/htmlinputelement.rs b/components/script/dom/htmlinputelement.rs index b599d4369dc..ddbd57d2438 100644 --- a/components/script/dom/htmlinputelement.rs +++ b/components/script/dom/htmlinputelement.rs @@ -35,7 +35,6 @@ use dom::virtualmethods::VirtualMethods; use html5ever_atoms::LocalName; use ipc_channel::ipc::{self, IpcSender}; use mime_guess; -use msg::constellation_msg::Key; use net_traits::{CoreResourceMsg, IpcSend}; use net_traits::blob_url_store::get_blob_origin; use net_traits::filemanager_thread::{FileManagerThreadMsg, FilterPattern}; @@ -1097,18 +1096,10 @@ impl VirtualMethods for HTMLInputElement { let action = self.textinput.borrow_mut().handle_keydown(keyevent); match action { TriggerDefaultAction => { - if let Some(key) = keyevent.get_key() { - match key { - Key::Enter | Key::KpEnter => - self.implicit_submission(keyevent.CtrlKey(), - keyevent.ShiftKey(), - keyevent.AltKey(), - keyevent.MetaKey()), - // Issue #12071: Tab should not submit forms - // TODO(3982): Implement form keyboard navigation - _ => (), - } - }; + self.implicit_submission(keyevent.CtrlKey(), + keyevent.ShiftKey(), + keyevent.AltKey(), + keyevent.MetaKey()); }, DispatchInput => { self.value_changed.set(true); diff --git a/components/script/dom/htmllinkelement.rs b/components/script/dom/htmllinkelement.rs index 90499388153..5df64f62ca0 100644 --- a/components/script/dom/htmllinkelement.rs +++ b/components/script/dom/htmllinkelement.rs @@ -243,8 +243,8 @@ impl HTMLLinkElement { let (action_sender, action_receiver) = ipc::channel().unwrap(); let listener = NetworkListener { context: context, - script_chan: document.window().networking_task_source(), - wrapper: Some(document.window().get_runnable_wrapper()), + task_source: document.window().networking_task_source(), + wrapper: Some(document.window().get_runnable_wrapper()) }; ROUTER.add_route(action_receiver.to_opaque(), box move |message| { listener.notify_fetch(message.to().unwrap()); diff --git a/components/script/dom/htmlmediaelement.rs b/components/script/dom/htmlmediaelement.rs index 0627bcf0ffb..09a1b7cc438 100644 --- a/components/script/dom/htmlmediaelement.rs +++ b/components/script/dom/htmlmediaelement.rs @@ -521,11 +521,10 @@ impl HTMLMediaElement { let context = Arc::new(Mutex::new(HTMLMediaElementContext::new(self, url.clone()))); let (action_sender, action_receiver) = ipc::channel().unwrap(); let window = window_from_node(self); - let script_chan = window.networking_task_source(); let listener = NetworkListener { context: context, - script_chan: script_chan, - wrapper: Some(window.get_runnable_wrapper()), + task_source: window.networking_task_source(), + wrapper: Some(window.get_runnable_wrapper()) }; ROUTER.add_route(action_receiver.to_opaque(), box move |message| { diff --git a/components/script/dom/htmlmetaelement.rs b/components/script/dom/htmlmetaelement.rs index 7ddca9f2f8a..82855472589 100644 --- a/components/script/dom/htmlmetaelement.rs +++ b/components/script/dom/htmlmetaelement.rs @@ -22,7 +22,7 @@ use std::ascii::AsciiExt; use std::sync::Arc; use style::attr::AttrValue; use style::str::HTML_SPACE_CHARACTERS; -use style::stylesheets::{Stylesheet, CSSRule, Origin}; +use style::stylesheets::{Stylesheet, CssRule, Origin}; use style::viewport::ViewportRule; #[dom_struct] @@ -81,7 +81,7 @@ impl HTMLMetaElement { if !content.is_empty() { if let Some(translated_rule) = ViewportRule::from_meta(&**content) { *self.stylesheet.borrow_mut() = Some(Arc::new(Stylesheet { - rules: vec![CSSRule::Viewport(Arc::new(RwLock::new(translated_rule)))], + rules: vec![CssRule::Viewport(Arc::new(RwLock::new(translated_rule)))], origin: Origin::Author, media: Default::default(), // Viewport constraints are always recomputed on resize; they don't need to diff --git a/components/script/dom/htmlscriptelement.rs b/components/script/dom/htmlscriptelement.rs index 3b2057e4338..68b65f93142 100644 --- a/components/script/dom/htmlscriptelement.rs +++ b/components/script/dom/htmlscriptelement.rs @@ -27,13 +27,12 @@ use dom::node::{document_from_node, window_from_node}; use dom::virtualmethods::VirtualMethods; use encoding::label::encoding_from_whatwg_label; use encoding::types::{DecoderTrap, EncodingRef}; -use html5ever::tree_builder::NextParserState; use html5ever_atoms::LocalName; use ipc_channel::ipc; use ipc_channel::router::ROUTER; use js::jsval::UndefinedValue; use net_traits::{FetchMetadata, FetchResponseListener, Metadata, NetworkError}; -use net_traits::request::{CORSSettings, CredentialsMode, Destination, RequestInit, RequestMode, Type as RequestType}; +use net_traits::request::{CorsSettings, CredentialsMode, Destination, RequestInit, RequestMode, Type as RequestType}; use network_listener::{NetworkListener, PreInvoke}; use servo_atoms::Atom; use std::ascii::AsciiExt; @@ -221,7 +220,7 @@ impl PreInvoke for ScriptContext {} /// https://html.spec.whatwg.org/multipage/#fetch-a-classic-script fn fetch_a_classic_script(script: &HTMLScriptElement, url: Url, - cors_setting: Option<CORSSettings>, + cors_setting: Option<CorsSettings>, character_encoding: EncodingRef) { let doc = document_from_node(script); @@ -233,13 +232,13 @@ fn fetch_a_classic_script(script: &HTMLScriptElement, // https://html.spec.whatwg.org/multipage/#create-a-potential-cors-request // Step 1 mode: match cors_setting { - Some(_) => RequestMode::CORSMode, - None => RequestMode::NoCORS, + Some(_) => RequestMode::CorsMode, + None => RequestMode::NoCors, }, // https://html.spec.whatwg.org/multipage/#create-a-potential-cors-request // Step 3-4 credentials_mode: match cors_setting { - Some(CORSSettings::Anonymous) => CredentialsMode::CredentialsSameOrigin, + Some(CorsSettings::Anonymous) => CredentialsMode::CredentialsSameOrigin, _ => CredentialsMode::Include, }, origin: doc.url().clone(), @@ -263,8 +262,8 @@ fn fetch_a_classic_script(script: &HTMLScriptElement, let (action_sender, action_receiver) = ipc::channel().unwrap(); let listener = NetworkListener { context: context, - script_chan: doc.window().networking_task_source(), - wrapper: Some(doc.window().get_runnable_wrapper()), + task_source: doc.window().networking_task_source(), + wrapper: Some(doc.window().get_runnable_wrapper()) }; ROUTER.add_route(action_receiver.to_opaque(), box move |message| { @@ -275,10 +274,12 @@ fn fetch_a_classic_script(script: &HTMLScriptElement, impl HTMLScriptElement { /// https://html.spec.whatwg.org/multipage/#prepare-a-script - pub fn prepare(&self) -> NextParserState { + /// + /// Returns true if tokenization should continue, false otherwise. + pub fn prepare(&self) -> bool { // Step 1. if self.already_started.get() { - return NextParserState::Continue; + return true; } // Step 2. @@ -296,17 +297,17 @@ impl HTMLScriptElement { // Step 4. let text = self.Text(); if text.is_empty() && !element.has_attribute(&local_name!("src")) { - return NextParserState::Continue; + return true; } // Step 5. if !self.upcast::<Node>().is_in_doc() { - return NextParserState::Continue; + return true; } // Step 6. if !self.is_javascript() { - return NextParserState::Continue; + return true; } // Step 7. @@ -321,12 +322,12 @@ impl HTMLScriptElement { // Step 9. let doc = document_from_node(self); if self.parser_inserted.get() && &*self.parser_document != &*doc { - return NextParserState::Continue; + return true; } // Step 10. if !doc.is_scripting_enabled() { - return NextParserState::Continue; + return true; } // TODO(#4577): Step 11: CSP. @@ -339,13 +340,13 @@ impl HTMLScriptElement { let for_value = for_attribute.value().to_ascii_lowercase(); let for_value = for_value.trim_matches(HTML_SPACE_CHARACTERS); if for_value != "window" { - return NextParserState::Continue; + return true; } let event_value = event_attribute.value().to_ascii_lowercase(); let event_value = event_value.trim_matches(HTML_SPACE_CHARACTERS); if event_value != "onload" && event_value != "onload()" { - return NextParserState::Continue; + return true; } }, (_, _) => (), @@ -358,8 +359,8 @@ impl HTMLScriptElement { // Step 14. let cors_setting = match self.GetCrossOrigin() { - Some(ref s) if *s == "anonymous" => Some(CORSSettings::Anonymous), - Some(ref s) if *s == "use-credentials" => Some(CORSSettings::UseCredentials), + Some(ref s) if *s == "anonymous" => Some(CorsSettings::Anonymous), + Some(ref s) if *s == "use-credentials" => Some(CorsSettings::UseCredentials), None => None, _ => unreachable!() }; @@ -380,7 +381,7 @@ impl HTMLScriptElement { // Step 18.2. if src.is_empty() { self.queue_error_event(); - return NextParserState::Continue; + return true; } // Step 18.4-18.5. @@ -388,7 +389,7 @@ impl HTMLScriptElement { Err(_) => { warn!("error parsing URL for script {}", &**src); self.queue_error_event(); - return NextParserState::Continue; + return true; } Ok(url) => url, }; @@ -411,7 +412,7 @@ impl HTMLScriptElement { !async { doc.add_deferred_script(self); // Second part implemented in Document::process_deferred_scripts. - return NextParserState::Continue; + return true; // Step 20.b: classic, has src, was parser-inserted, is not async. } else if is_external && was_parser_inserted && @@ -442,7 +443,7 @@ impl HTMLScriptElement { self.ready_to_be_parser_executed.set(true); *self.load.borrow_mut() = Some(Ok(ScriptOrigin::internal(text, base_url))); self.execute(); - return NextParserState::Continue; + return true; } // TODO: make this suspension happen automatically. @@ -451,7 +452,7 @@ impl HTMLScriptElement { parser.suspend(); } } - NextParserState::Suspend + false } pub fn is_ready_to_be_executed(&self) -> bool { diff --git a/components/script/dom/node.rs b/components/script/dom/node.rs index 5fcfd74f1f6..0f510719640 100644 --- a/components/script/dom/node.rs +++ b/components/script/dom/node.rs @@ -47,7 +47,7 @@ use dom::htmltextareaelement::{HTMLTextAreaElement, LayoutHTMLTextAreaElementHel use dom::nodelist::NodeList; use dom::processinginstruction::ProcessingInstruction; use dom::range::WeakRangeVec; -use dom::servoparser::html::parse_html_fragment; +use dom::servoparser::ServoParser; use dom::svgsvgelement::{SVGSVGElement, LayoutSVGSVGElementHelpers}; use dom::text::Text; use dom::virtualmethods::{VirtualMethods, vtable_for}; @@ -833,7 +833,7 @@ impl Node { let context_document = document_from_node(self); let fragment = DocumentFragment::new(&context_document); if context_document.is_html_document() { - parse_html_fragment(self.upcast(), markup, fragment.upcast()); + ServoParser::parse_html_fragment(self.upcast(), markup, fragment.upcast()); } else { // FIXME: XML case unimplemented!(); diff --git a/components/script/dom/request.rs b/components/script/dom/request.rs index fa649702259..614c51398e8 100644 --- a/components/script/dom/request.rs +++ b/components/script/dom/request.rs @@ -114,7 +114,7 @@ impl Request { url, false); // Step 5.5 - fallback_mode = Some(NetTraitsRequestMode::CORSMode); + fallback_mode = Some(NetTraitsRequestMode::CorsMode); // Step 5.6 fallback_credentials = Some(NetTraitsRequestCredentials::Omit); } @@ -335,7 +335,7 @@ impl Request { // deep copied headers in Step 27. // Step 30 - if r.request.borrow().mode == NetTraitsRequestMode::NoCORS { + if r.request.borrow().mode == NetTraitsRequestMode::NoCors { let borrowed_request = r.request.borrow(); // Step 30.1 if !is_cors_safelisted_method(&borrowed_request.method.borrow()) { @@ -814,8 +814,8 @@ impl Into<NetTraitsRequestMode> for RequestMode { match self { RequestMode::Navigate => NetTraitsRequestMode::Navigate, RequestMode::Same_origin => NetTraitsRequestMode::SameOrigin, - RequestMode::No_cors => NetTraitsRequestMode::NoCORS, - RequestMode::Cors => NetTraitsRequestMode::CORSMode, + RequestMode::No_cors => NetTraitsRequestMode::NoCors, + RequestMode::Cors => NetTraitsRequestMode::CorsMode, } } } @@ -825,8 +825,8 @@ impl Into<RequestMode> for NetTraitsRequestMode { match self { NetTraitsRequestMode::Navigate => RequestMode::Navigate, NetTraitsRequestMode::SameOrigin => RequestMode::Same_origin, - NetTraitsRequestMode::NoCORS => RequestMode::No_cors, - NetTraitsRequestMode::CORSMode => RequestMode::Cors, + NetTraitsRequestMode::NoCors => RequestMode::No_cors, + NetTraitsRequestMode::CorsMode => RequestMode::Cors, } } } diff --git a/components/script/dom/servoparser/html.rs b/components/script/dom/servoparser/html.rs index fd4a5a923b9..a2d636da6f3 100644 --- a/components/script/dom/servoparser/html.rs +++ b/components/script/dom/servoparser/html.rs @@ -4,23 +4,19 @@ #![allow(unrooted_must_root)] -use document_loader::DocumentLoader; -use dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods; use dom::bindings::codegen::Bindings::HTMLTemplateElementBinding::HTMLTemplateElementMethods; use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods; use dom::bindings::inheritance::{Castable, CharacterDataTypeId, NodeTypeId}; -use dom::bindings::js::{JS, RootedReference}; +use dom::bindings::js::{JS, Root}; use dom::bindings::str::DOMString; +use dom::bindings::trace::JSTraceable; use dom::characterdata::CharacterData; use dom::comment::Comment; -use dom::document::{DocumentSource, IsHTMLDocument}; use dom::document::Document; use dom::documenttype::DocumentType; use dom::element::{Element, ElementCreator}; -use dom::htmlformelement::HTMLFormElement; use dom::htmlscriptelement::HTMLScriptElement; use dom::htmltemplateelement::HTMLTemplateElement; -use dom::node::{document_from_node, window_from_node}; use dom::node::Node; use dom::processinginstruction::ProcessingInstruction; use dom::text::Text; @@ -29,28 +25,107 @@ use html5ever::serialize::{AttrRef, Serializable, Serializer}; use html5ever::serialize::TraversalScope; use html5ever::serialize::TraversalScope::{ChildrenOnly, IncludeNode}; use html5ever::tendril::StrTendril; -use html5ever::tokenizer::{Tokenizer as H5ETokenizer, TokenizerOpts}; -use html5ever::tree_builder::{NextParserState, NodeOrText, QuirksMode}; -use html5ever::tree_builder::{TreeBuilder, TreeBuilderOpts, TreeSink}; +use html5ever::tokenizer::{Tokenizer as HtmlTokenizer, TokenizerOpts, TokenizerResult}; +use html5ever::tokenizer::buffer_queue::BufferQueue; +use html5ever::tree_builder::{NodeOrText, QuirksMode}; +use html5ever::tree_builder::{Tracer as HtmlTracer, TreeBuilder, TreeBuilderOpts, TreeSink}; use html5ever_atoms::QualName; -use msg::constellation_msg::PipelineId; +use js::jsapi::JSTracer; use std::borrow::Cow; use std::io::{self, Write}; -use super::{HtmlTokenizer, LastChunkState, ServoParser, Sink, Tokenizer}; +use super::{FragmentContext, Sink}; use url::Url; -fn insert(parent: &Node, reference_child: Option<&Node>, child: NodeOrText<JS<Node>>) { - match child { - NodeOrText::AppendNode(n) => { - assert!(parent.InsertBefore(&n, reference_child).is_ok()); - }, - NodeOrText::AppendText(t) => { - // FIXME(ajeffrey): convert directly from tendrils to DOMStrings - let s: String = t.into(); - let text = Text::new(DOMString::from(s), &parent.owner_doc()); - assert!(parent.InsertBefore(text.upcast(), reference_child).is_ok()); +#[derive(HeapSizeOf, JSTraceable)] +#[must_root] +pub struct Tokenizer { + #[ignore_heap_size_of = "Defined in html5ever"] + inner: HtmlTokenizer<TreeBuilder<JS<Node>, Sink>>, + #[ignore_heap_size_of = "Defined in html5ever"] + input_buffer: BufferQueue, +} + +impl Tokenizer { + pub fn new( + document: &Document, + url: Url, + fragment_context: Option<FragmentContext>) + -> Self { + let sink = Sink { + base_url: url, + document: JS::from_ref(document), + }; + + let options = TreeBuilderOpts { + ignore_missing_rules: true, + .. Default::default() + }; + + let inner = if let Some(fc) = fragment_context { + let tb = TreeBuilder::new_for_fragment( + sink, + JS::from_ref(fc.context_elem), + fc.form_elem.map(|n| JS::from_ref(n)), + options); + + let tok_options = TokenizerOpts { + initial_state: Some(tb.tokenizer_state_for_context_elem()), + .. Default::default() + }; + + HtmlTokenizer::new(tb, tok_options) + } else { + HtmlTokenizer::new(TreeBuilder::new(sink, options), Default::default()) + }; + + Tokenizer { + inner: inner, + input_buffer: BufferQueue::new(), } } + + pub fn feed(&mut self, input: String) { + self.input_buffer.push_back(input.into()); + self.run(); + } + + #[allow(unrooted_must_root)] + pub fn run(&mut self) { + while let TokenizerResult::Script(script) = self.inner.feed(&mut self.input_buffer) { + let script = Root::from_ref(script.downcast::<HTMLScriptElement>().unwrap()); + if !script.prepare() { + break; + } + } + } + + pub fn end(&mut self) { + assert!(self.input_buffer.is_empty()); + self.inner.end(); + } + + pub fn set_plaintext_state(&mut self) { + self.inner.set_plaintext_state(); + } +} + +impl JSTraceable for HtmlTokenizer<TreeBuilder<JS<Node>, Sink>> { + fn trace(&self, trc: *mut JSTracer) { + struct Tracer(*mut JSTracer); + let tracer = Tracer(trc); + + impl HtmlTracer for Tracer { + type Handle = JS<Node>; + #[allow(unrooted_must_root)] + fn trace_handle(&self, node: &JS<Node>) { + node.trace(self.0); + } + } + + let tree_builder = self.sink(); + tree_builder.trace_handles(&tracer); + tree_builder.sink().trace(trc); + } } impl<'a> TreeSink for Sink { @@ -153,14 +228,6 @@ impl<'a> TreeSink for Sink { script.map(|script| script.set_already_started(true)); } - fn complete_script(&mut self, node: JS<Node>) -> NextParserState { - let script = node.downcast::<HTMLScriptElement>(); - if let Some(script) = script { - return script.prepare(); - } - NextParserState::Continue - } - fn reparent_children(&mut self, node: JS<Node>, new_parent: JS<Node>) { while let Some(ref child) = node.GetFirstChild() { new_parent.AppendChild(&child).unwrap(); @@ -169,6 +236,20 @@ impl<'a> TreeSink for Sink { } } +fn insert(parent: &Node, reference_child: Option<&Node>, child: NodeOrText<JS<Node>>) { + match child { + NodeOrText::AppendNode(n) => { + assert!(parent.InsertBefore(&n, reference_child).is_ok()); + }, + NodeOrText::AppendText(t) => { + // FIXME(ajeffrey): convert directly from tendrils to DOMStrings + let s: String = t.into(); + let text = Text::new(DOMString::from(s), &parent.owner_doc()); + assert!(parent.InsertBefore(text.upcast(), reference_child).is_ok()); + } + } +} + impl<'a> Serializable for &'a Node { fn serialize<'wr, Wr: Write>(&self, serializer: &mut Serializer<'wr, Wr>, traversal_scope: TraversalScope) -> io::Result<()> { @@ -245,100 +326,3 @@ impl<'a> Serializable for &'a Node { } } } - -/// FragmentContext is used only to pass this group of related values -/// into functions. -#[derive(Copy, Clone)] -pub struct FragmentContext<'a> { - pub context_elem: &'a Node, - pub form_elem: Option<&'a Node>, -} - -pub enum ParseContext<'a> { - Fragment(FragmentContext<'a>), - Owner(Option<PipelineId>), -} - -pub fn parse_html(document: &Document, - input: DOMString, - url: Url, - context: ParseContext) { - let sink = Sink { - base_url: url, - document: JS::from_ref(document), - }; - - let options = TreeBuilderOpts { - ignore_missing_rules: true, - .. Default::default() - }; - - let parser = match context { - ParseContext::Owner(owner) => { - let tb = TreeBuilder::new(sink, options); - let tok = H5ETokenizer::new(tb, Default::default()); - - ServoParser::new( - document, - owner, - Tokenizer::HTML(HtmlTokenizer::new(tok)), - LastChunkState::NotReceived) - }, - ParseContext::Fragment(fc) => { - let tb = TreeBuilder::new_for_fragment( - sink, - JS::from_ref(fc.context_elem), - fc.form_elem.map(|n| JS::from_ref(n)), - options); - - let tok_options = TokenizerOpts { - initial_state: Some(tb.tokenizer_state_for_context_elem()), - .. Default::default() - }; - let tok = H5ETokenizer::new(tb, tok_options); - - ServoParser::new( - document, - None, - Tokenizer::HTML(HtmlTokenizer::new(tok)), - LastChunkState::Received) - } - }; - parser.parse_chunk(String::from(input)); -} - -// https://html.spec.whatwg.org/multipage/#parsing-html-fragments -pub fn parse_html_fragment(context_node: &Node, - input: DOMString, - output: &Node) { - let window = window_from_node(context_node); - let context_document = document_from_node(context_node); - let url = context_document.url(); - - // Step 1. - let loader = DocumentLoader::new(&*context_document.loader()); - let document = Document::new(&window, None, Some(url.clone()), - IsHTMLDocument::HTMLDocument, - None, None, - DocumentSource::FromParser, - loader, - None, None); - - // Step 2. - document.set_quirks_mode(context_document.quirks_mode()); - - // Step 11. - let form = context_node.inclusive_ancestors() - .find(|element| element.is::<HTMLFormElement>()); - let fragment_context = FragmentContext { - context_elem: context_node, - form_elem: form.r(), - }; - parse_html(&document, input, url.clone(), ParseContext::Fragment(fragment_context)); - - // Step 14. - let root_element = document.GetDocumentElement().expect("no document element"); - for child in root_element.upcast::<Node>().children() { - output.AppendChild(&child).unwrap(); - } -} diff --git a/components/script/dom/servoparser/mod.rs b/components/script/dom/servoparser/mod.rs index 4df82a457a7..7b5c5c7f4ab 100644 --- a/components/script/dom/servoparser/mod.rs +++ b/components/script/dom/servoparser/mod.rs @@ -2,32 +2,27 @@ * 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 document_loader::LoadType; +use document_loader::{DocumentLoader, LoadType}; use dom::bindings::cell::DOMRefCell; use dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods; use dom::bindings::codegen::Bindings::HTMLImageElementBinding::HTMLImageElementMethods; use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods; use dom::bindings::codegen::Bindings::ServoParserBinding; use dom::bindings::inheritance::Castable; -use dom::bindings::js::{JS, Root}; +use dom::bindings::js::{JS, Root, RootedReference}; use dom::bindings::refcounted::Trusted; use dom::bindings::reflector::{Reflector, reflect_dom_object}; use dom::bindings::str::DOMString; -use dom::bindings::trace::JSTraceable; -use dom::document::Document; +use dom::document::{Document, DocumentSource, IsHTMLDocument}; use dom::globalscope::GlobalScope; +use dom::htmlformelement::HTMLFormElement; use dom::htmlimageelement::HTMLImageElement; -use dom::node::Node; +use dom::node::{Node, document_from_node, window_from_node}; use encoding::all::UTF_8; use encoding::types::{DecoderTrap, Encoding}; -use html5ever::tokenizer::Tokenizer as H5ETokenizer; -use html5ever::tokenizer::buffer_queue::BufferQueue; -use html5ever::tree_builder::Tracer as HtmlTracer; -use html5ever::tree_builder::TreeBuilder as HtmlTreeBuilder; use hyper::header::ContentType; use hyper::mime::{Mime, SubLevel, TopLevel}; use hyper_serde::Serde; -use js::jsapi::JSTracer; use msg::constellation_msg::PipelineId; use net_traits::{FetchMetadata, FetchResponseListener, Metadata, NetworkError}; use network_listener::PreInvoke; @@ -38,11 +33,9 @@ use std::cell::Cell; use std::collections::VecDeque; use url::Url; use util::resource_files::read_resource_file; -use xml5ever::tokenizer::XmlTokenizer; -use xml5ever::tree_builder::{Tracer as XmlTracer, XmlTreeBuilder}; -pub mod html; -pub mod xml; +mod html; +mod xml; #[dom_struct] pub struct ServoParser { @@ -69,6 +62,76 @@ enum LastChunkState { } impl ServoParser { + pub fn parse_html_document( + document: &Document, + input: DOMString, + url: Url, + owner: Option<PipelineId>) { + let parser = ServoParser::new( + document, + owner, + Tokenizer::Html(self::html::Tokenizer::new(document, url, None)), + LastChunkState::NotReceived); + parser.parse_chunk(String::from(input)); + } + + // https://html.spec.whatwg.org/multipage/#parsing-html-fragments + pub fn parse_html_fragment( + context_node: &Node, + input: DOMString, + output: &Node) { + let window = window_from_node(context_node); + let context_document = document_from_node(context_node); + let url = context_document.url(); + + // Step 1. + let loader = DocumentLoader::new(&*context_document.loader()); + let document = Document::new(&window, None, Some(url.clone()), + IsHTMLDocument::HTMLDocument, + None, None, + DocumentSource::FromParser, + loader, + None, None); + + // Step 2. + document.set_quirks_mode(context_document.quirks_mode()); + + // Step 11. + let form = context_node.inclusive_ancestors() + .find(|element| element.is::<HTMLFormElement>()); + let fragment_context = FragmentContext { + context_elem: context_node, + form_elem: form.r(), + }; + + let parser = ServoParser::new( + &document, + None, + Tokenizer::Html( + self::html::Tokenizer::new(&document, url.clone(), Some(fragment_context))), + LastChunkState::Received); + parser.parse_chunk(String::from(input)); + + // Step 14. + let root_element = document.GetDocumentElement().expect("no document element"); + for child in root_element.upcast::<Node>().children() { + output.AppendChild(&child).unwrap(); + } + } + + pub fn parse_xml_document( + document: &Document, + input: DOMString, + url: Url, + owner: Option<PipelineId>) { + let parser = ServoParser::new( + document, + owner, + Tokenizer::Xml(self::xml::Tokenizer::new(document, url)), + LastChunkState::NotReceived); + parser.parse_chunk(String::from(input)); + } + #[allow(unrooted_must_root)] fn new_inherited( document: &Document, @@ -214,51 +277,11 @@ impl ServoParser { } } -#[derive(HeapSizeOf)] +#[derive(HeapSizeOf, JSTraceable)] #[must_root] enum Tokenizer { - HTML(HtmlTokenizer), - XML( - #[ignore_heap_size_of = "Defined in xml5ever"] - XmlTokenizer<XmlTreeBuilder<JS<Node>, Sink>> - ), -} - -#[derive(HeapSizeOf)] -#[must_root] -struct HtmlTokenizer { - #[ignore_heap_size_of = "Defined in html5ever"] - inner: H5ETokenizer<HtmlTreeBuilder<JS<Node>, Sink>>, - #[ignore_heap_size_of = "Defined in html5ever"] - input_buffer: BufferQueue, -} - -impl HtmlTokenizer { - #[allow(unrooted_must_root)] - fn new(inner: H5ETokenizer<HtmlTreeBuilder<JS<Node>, Sink>>) -> Self { - HtmlTokenizer { - inner: inner, - input_buffer: BufferQueue::new(), - } - } - - fn feed(&mut self, input: String) { - self.input_buffer.push_back(input.into()); - self.run(); - } - - fn run(&mut self) { - self.inner.feed(&mut self.input_buffer); - } - - fn end(&mut self) { - assert!(self.input_buffer.is_empty()); - self.inner.end(); - } - - fn set_plaintext_state(&mut self) { - self.inner.set_plaintext_state(); - } + Html(self::html::Tokenizer), + Xml(self::xml::Tokenizer), } #[derive(JSTraceable, HeapSizeOf)] @@ -271,70 +294,36 @@ struct Sink { impl Tokenizer { fn feed(&mut self, input: String) { match *self { - Tokenizer::HTML(ref mut tokenizer) => tokenizer.feed(input), - Tokenizer::XML(ref mut tokenizer) => tokenizer.feed(input.into()), + Tokenizer::Html(ref mut tokenizer) => tokenizer.feed(input), + Tokenizer::Xml(ref mut tokenizer) => tokenizer.feed(input.into()), } } fn run(&mut self) { match *self { - Tokenizer::HTML(ref mut tokenizer) => tokenizer.run(), - Tokenizer::XML(ref mut tokenizer) => tokenizer.run(), + Tokenizer::Html(ref mut tokenizer) => tokenizer.run(), + Tokenizer::Xml(ref mut tokenizer) => tokenizer.run(), } } fn end(&mut self) { match *self { - Tokenizer::HTML(ref mut tokenizer) => tokenizer.end(), - Tokenizer::XML(ref mut tokenizer) => tokenizer.end(), + Tokenizer::Html(ref mut tokenizer) => tokenizer.end(), + Tokenizer::Xml(ref mut tokenizer) => tokenizer.end(), } } fn set_plaintext_state(&mut self) { match *self { - Tokenizer::HTML(ref mut tokenizer) => tokenizer.set_plaintext_state(), - Tokenizer::XML(_) => { /* todo */ }, + Tokenizer::Html(ref mut tokenizer) => tokenizer.set_plaintext_state(), + Tokenizer::Xml(_) => unimplemented!(), } } fn profiler_category(&self) -> ProfilerCategory { match *self { - Tokenizer::HTML(_) => ProfilerCategory::ScriptParseHTML, - Tokenizer::XML(_) => ProfilerCategory::ScriptParseXML, - } - } -} - -impl JSTraceable for Tokenizer { - fn trace(&self, trc: *mut JSTracer) { - struct Tracer(*mut JSTracer); - let tracer = Tracer(trc); - - match *self { - Tokenizer::HTML(ref tokenizer) => { - impl HtmlTracer for Tracer { - type Handle = JS<Node>; - #[allow(unrooted_must_root)] - fn trace_handle(&self, node: &JS<Node>) { - node.trace(self.0); - } - } - let tree_builder = tokenizer.inner.sink(); - tree_builder.trace_handles(&tracer); - tree_builder.sink().trace(trc); - }, - Tokenizer::XML(ref tokenizer) => { - impl XmlTracer for Tracer { - type Handle = JS<Node>; - #[allow(unrooted_must_root)] - fn trace_handle(&self, node: JS<Node>) { - node.trace(self.0); - } - } - let tree_builder = tokenizer.sink(); - tree_builder.trace_handles(&tracer); - tree_builder.sink().trace(trc); - } + Tokenizer::Html(_) => ProfilerCategory::ScriptParseHTML, + Tokenizer::Xml(_) => ProfilerCategory::ScriptParseXML, } } } @@ -492,3 +481,8 @@ impl FetchResponseListener for ParserContext { } impl PreInvoke for ParserContext {} + +pub struct FragmentContext<'a> { + pub context_elem: &'a Node, + pub form_elem: Option<&'a Node>, +} diff --git a/components/script/dom/servoparser/xml.rs b/components/script/dom/servoparser/xml.rs index 879bb9320d6..6f87d6a389c 100644 --- a/components/script/dom/servoparser/xml.rs +++ b/components/script/dom/servoparser/xml.rs @@ -8,6 +8,7 @@ use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods; use dom::bindings::inheritance::Castable; use dom::bindings::js::{JS, Root}; use dom::bindings::str::DOMString; +use dom::bindings::trace::JSTraceable; use dom::comment::Comment; use dom::document::Document; use dom::documenttype::DocumentType; @@ -16,15 +17,69 @@ use dom::htmlscriptelement::HTMLScriptElement; use dom::node::Node; use dom::processinginstruction::ProcessingInstruction; use dom::text::Text; -use html5ever; use html5ever_atoms::{Prefix, QualName}; -use msg::constellation_msg::PipelineId; +use js::jsapi::JSTracer; use std::borrow::Cow; -use super::{LastChunkState, ServoParser, Sink, Tokenizer}; +use super::Sink; use url::Url; use xml5ever::tendril::StrTendril; use xml5ever::tokenizer::{Attribute, QName, XmlTokenizer}; -use xml5ever::tree_builder::{NextParserState, NodeOrText, TreeSink, XmlTreeBuilder}; +use xml5ever::tree_builder::{NextParserState, NodeOrText}; +use xml5ever::tree_builder::{Tracer as XmlTracer, TreeSink, XmlTreeBuilder}; + +#[derive(HeapSizeOf, JSTraceable)] +#[must_root] +pub struct Tokenizer { + #[ignore_heap_size_of = "Defined in xml5ever"] + inner: XmlTokenizer<XmlTreeBuilder<JS<Node>, Sink>>, +} + +impl Tokenizer { + pub fn new(document: &Document, url: Url) -> Self { + let sink = Sink { + base_url: url, + document: JS::from_ref(document), + }; + + let tb = XmlTreeBuilder::new(sink); + let tok = XmlTokenizer::new(tb, Default::default()); + + Tokenizer { + inner: tok, + } + } + + pub fn feed(&mut self, input: String) { + self.inner.feed(input.into()) + } + + pub fn run(&mut self) { + self.inner.run() + } + + pub fn end(&mut self) { + self.inner.end() + } +} + +impl JSTraceable for XmlTokenizer<XmlTreeBuilder<JS<Node>, Sink>> { + fn trace(&self, trc: *mut JSTracer) { + struct Tracer(*mut JSTracer); + let tracer = Tracer(trc); + + impl XmlTracer for Tracer { + type Handle = JS<Node>; + #[allow(unrooted_must_root)] + fn trace_handle(&self, node: JS<Node>) { + node.trace(self.0); + } + } + + let tree_builder = self.sink(); + tree_builder.trace_handles(&tracer); + tree_builder.sink().trace(trc); + } +} impl<'a> TreeSink for Sink { type Handle = JS<Node>; @@ -113,35 +168,10 @@ impl<'a> TreeSink for Sink { let script = node.downcast::<HTMLScriptElement>(); if let Some(script) = script { return match script.prepare() { - html5ever::tree_builder::NextParserState::Continue => NextParserState::Continue, - html5ever::tree_builder::NextParserState::Suspend => NextParserState::Suspend + true => NextParserState::Continue, + false => NextParserState::Suspend, }; } NextParserState::Continue } } - - -pub enum ParseContext { - Owner(Option<PipelineId>) -} - - -pub fn parse_xml(document: &Document, - input: DOMString, - url: Url, - context: ParseContext) { - let parser = match context { - ParseContext::Owner(owner) => { - let tb = XmlTreeBuilder::new(Sink { - base_url: url, - document: JS::from_ref(document), - }); - let tok = XmlTokenizer::new(tb, Default::default()); - - ServoParser::new( - document, owner, Tokenizer::XML(tok), LastChunkState::NotReceived) - } - }; - parser.parse_chunk(String::from(input)); -} diff --git a/components/script/dom/webidls/CSSStyleDeclaration.webidl b/components/script/dom/webidls/CSSStyleDeclaration.webidl index c64ecd119c7..3424e5593a6 100644 --- a/components/script/dom/webidls/CSSStyleDeclaration.webidl +++ b/components/script/dom/webidls/CSSStyleDeclaration.webidl @@ -103,6 +103,39 @@ partial interface CSSStyleDeclaration { [SetterThrows, TreatNullAs=EmptyString] attribute DOMString borderTopWidth; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString border-top-width; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString border-block-start-color; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString borderBlockStartColor; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString border-block-start-width; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString borderBlockStartWidth; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString border-block-start-style; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString borderBlockStartStyle; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString border-block-end-color; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString borderBlockEndColor; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString border-block-end-width; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString borderBlockEndWidth; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString border-block-end-style; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString borderBlockEndStyle; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString border-inline-start-color; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString borderInlineStartColor; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString border-inline-start-width; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString borderInlineStartWidth; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString border-inline-start-style; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString borderInlineStartStyle; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString border-inline-end-color; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString borderInlineEndColor; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString border-inline-end-width; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString borderInlineEndWidth; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString border-inline-end-style; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString borderInlineEndStyle; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString border-block-start; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString borderBlockStart; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString border-block-end; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString borderBlockEnd; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString border-inline-start; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString borderInlineStart; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString border-inline-end; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString borderInlineEnd; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString content; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString color; @@ -242,6 +275,14 @@ partial interface CSSStyleDeclaration { [SetterThrows, TreatNullAs=EmptyString] attribute DOMString margin-right; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString marginTop; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString margin-top; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString margin-block-start; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString marginBlockStart; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString margin-block-end; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString marginBlockEnd; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString margin-inline-start; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString marginInlineStart; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString margin-inline-end; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString marginInlineEnd; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString padding; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString paddingBottom; @@ -252,6 +293,14 @@ partial interface CSSStyleDeclaration { [SetterThrows, TreatNullAs=EmptyString] attribute DOMString padding-right; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString paddingTop; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString padding-top; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString padding-block-start; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString paddingBlockStart; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString padding-block-end; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString paddingBlockEnd; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString padding-inline-start; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString paddingInlineStart; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString padding-inline-end; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString paddingInlineEnd; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString outline; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString outlineColor; @@ -284,6 +333,18 @@ partial interface CSSStyleDeclaration { [SetterThrows, TreatNullAs=EmptyString] attribute DOMString min-width; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString maxWidth; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString max-width; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString block-size; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString blockSize; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString inline-size; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString inlineSize; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString max-block-size; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString maxBlockSize; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString max-inline-size; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString maxInlineSize; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString min-block-size; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString minBlockSize; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString min-inline-size; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString minInlineSize; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString zIndex; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString z-index; diff --git a/components/script/dom/webidls/EventSource.webidl b/components/script/dom/webidls/EventSource.webidl index b9cf82d6a3e..11c30e959d4 100644 --- a/components/script/dom/webidls/EventSource.webidl +++ b/components/script/dom/webidls/EventSource.webidl @@ -7,8 +7,7 @@ */ [Constructor(DOMString url, optional EventSourceInit eventSourceInitDict), - Exposed=(Window,Worker), - Pref="dom.eventsource.enabled"] + Exposed=(Window,Worker)] interface EventSource : EventTarget { readonly attribute DOMString url; readonly attribute boolean withCredentials; diff --git a/components/script/dom/webidls/Navigator.webidl b/components/script/dom/webidls/Navigator.webidl index 0f32e3bdf1e..84d5a7c3ccb 100644 --- a/components/script/dom/webidls/Navigator.webidl +++ b/components/script/dom/webidls/Navigator.webidl @@ -7,7 +7,6 @@ interface Navigator { // objects implementing this interface also implement the interfaces given below }; Navigator implements NavigatorID; -Navigator implements NavigatorBluetooth; Navigator implements NavigatorLanguage; //Navigator implements NavigatorOnLine; //Navigator implements NavigatorContentUtils; @@ -27,9 +26,9 @@ interface NavigatorID { readonly attribute DOMString userAgent; }; -[NoInterfaceObject] -interface NavigatorBluetooth { - readonly attribute Bluetooth bluetooth; +// https://webbluetoothcg.github.io/web-bluetooth/#navigator-extensions +partial interface Navigator { + readonly attribute Bluetooth bluetooth; }; // https://w3c.github.io/ServiceWorker/#navigator-service-worker diff --git a/components/script/dom/websocket.rs b/components/script/dom/websocket.rs index dd49f62d247..1764db20764 100644 --- a/components/script/dom/websocket.rs +++ b/components/script/dom/websocket.rs @@ -33,14 +33,16 @@ use net_traits::CoreResourceMsg::{SetCookiesForUrl, WebsocketConnect}; use net_traits::MessageData; use net_traits::hosts::replace_hosts; use net_traits::unwrap_websocket_protocol; -use script_runtime::{CommonScriptMsg, ScriptChan}; +use script_runtime::CommonScriptMsg; use script_runtime::ScriptThreadEventCategory::WebSocketEvent; -use script_thread::Runnable; +use script_thread::{Runnable, RunnableWrapper}; use std::ascii::AsciiExt; use std::borrow::ToOwned; use std::cell::Cell; use std::ptr; use std::thread; +use task_source::TaskSource; +use task_source::networking::NetworkingTaskSource; use websocket::client::request::Url; use websocket::header::{Headers, WebSocketProtocol}; use websocket::ws::util::url::parse_url; @@ -141,7 +143,8 @@ mod close_code { } pub fn close_the_websocket_connection(address: Trusted<WebSocket>, - sender: Box<ScriptChan>, + task_source: &NetworkingTaskSource, + wrapper: &RunnableWrapper, code: Option<u16>, reason: String) { let close_task = box CloseTask { @@ -150,17 +153,19 @@ pub fn close_the_websocket_connection(address: Trusted<WebSocket>, code: code, reason: Some(reason), }; - sender.send(CommonScriptMsg::RunnableMsg(WebSocketEvent, close_task)).unwrap(); + task_source.queue_with_wrapper(close_task, &wrapper).unwrap(); } -pub fn fail_the_websocket_connection(address: Trusted<WebSocket>, sender: Box<ScriptChan>) { +pub fn fail_the_websocket_connection(address: Trusted<WebSocket>, + task_source: &NetworkingTaskSource, + wrapper: &RunnableWrapper) { let close_task = box CloseTask { address: address, failed: true, code: Some(close_code::ABNORMAL), reason: None, }; - sender.send(CommonScriptMsg::RunnableMsg(WebSocketEvent, close_task)).unwrap(); + task_source.queue_with_wrapper(close_task, &wrapper).unwrap(); } #[dom_struct] @@ -268,7 +273,8 @@ impl WebSocket { *ws.sender.borrow_mut() = Some(dom_action_sender); let moved_address = address.clone(); - let sender = global.networking_task_source(); + let task_source = global.networking_task_source(); + let wrapper = global.get_runnable_wrapper(); thread::spawn(move || { while let Ok(event) = dom_event_receiver.recv() { match event { @@ -278,20 +284,22 @@ impl WebSocket { headers: headers, protocols: protocols, }; - sender.send(CommonScriptMsg::RunnableMsg(WebSocketEvent, open_thread)).unwrap(); + task_source.queue_with_wrapper(open_thread, &wrapper).unwrap(); }, WebSocketNetworkEvent::MessageReceived(message) => { let message_thread = box MessageReceivedTask { address: moved_address.clone(), message: message, }; - sender.send(CommonScriptMsg::RunnableMsg(WebSocketEvent, message_thread)).unwrap(); + task_source.queue_with_wrapper(message_thread, &wrapper).unwrap(); }, WebSocketNetworkEvent::Fail => { - fail_the_websocket_connection(moved_address.clone(), sender.clone()); + fail_the_websocket_connection(moved_address.clone(), + &task_source, &wrapper); }, WebSocketNetworkEvent::Close(code, reason) => { - close_the_websocket_connection(moved_address.clone(), sender.clone(), code, reason); + close_the_websocket_connection(moved_address.clone(), + &task_source, &wrapper, code, reason); }, } } @@ -436,8 +444,8 @@ impl WebSocketMethods for WebSocket { self.ready_state.set(WebSocketRequestState::Closing); let address = Trusted::new(self); - let sender = self.global().networking_task_source(); - fail_the_websocket_connection(address, sender); + let task_source = self.global().networking_task_source(); + fail_the_websocket_connection(address, &task_source, &self.global().get_runnable_wrapper()); } WebSocketRequestState::Open => { self.ready_state.set(WebSocketRequestState::Closing); @@ -470,8 +478,8 @@ impl Runnable for ConnectionEstablishedTask { // Step 1: Protocols. if !self.protocols.is_empty() && self.headers.get::<WebSocketProtocol>().is_none() { - let sender = ws.global().networking_task_source(); - fail_the_websocket_connection(self.address, sender); + let task_source = ws.global().networking_task_source(); + fail_the_websocket_connection(self.address, &task_source, &ws.global().get_runnable_wrapper()); return; } diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index a78dcda8893..63485e45dfc 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -267,7 +267,7 @@ impl Window { self.user_interaction_task_source.clone() } - pub fn networking_task_source(&self) -> Box<ScriptChan + Send> { + pub fn networking_task_source(&self) -> NetworkingTaskSource { self.networking_task_source.clone() } diff --git a/components/script/dom/workerglobalscope.rs b/components/script/dom/workerglobalscope.rs index 2efa2920cb8..041ce8448de 100644 --- a/components/script/dom/workerglobalscope.rs +++ b/components/script/dom/workerglobalscope.rs @@ -41,6 +41,7 @@ use std::sync::Arc; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::mpsc::Receiver; use task_source::file_reading::FileReadingTaskSource; +use task_source::networking::NetworkingTaskSource; use timers::{IsInterval, TimerCallback}; use url::Url; @@ -361,6 +362,10 @@ impl WorkerGlobalScope { FileReadingTaskSource(self.script_chan()) } + pub fn networking_task_source(&self) -> NetworkingTaskSource { + NetworkingTaskSource(self.script_chan()) + } + pub fn new_script_pair(&self) -> (Box<ScriptChan + Send>, Box<ScriptPort + Send>) { let dedicated = self.downcast::<DedicatedWorkerGlobalScope>(); if let Some(dedicated) = dedicated { diff --git a/components/script/dom/xmlhttprequest.rs b/components/script/dom/xmlhttprequest.rs index c404c482308..57191d1d4e9 100644 --- a/components/script/dom/xmlhttprequest.rs +++ b/components/script/dom/xmlhttprequest.rs @@ -28,8 +28,7 @@ use dom::globalscope::GlobalScope; use dom::headers::is_forbidden_header_name; use dom::htmlformelement::{encode_multipart_form_data, generate_boundary}; use dom::progressevent::ProgressEvent; -use dom::servoparser::html::{ParseContext, parse_html}; -use dom::servoparser::xml::{self, parse_xml}; +use dom::servoparser::ServoParser; use dom::window::Window; use dom::workerglobalscope::WorkerGlobalScope; use dom::xmlhttprequesteventtarget::XMLHttpRequestEventTarget; @@ -49,13 +48,12 @@ use js::jsapi::{JSContext, JS_ParseJSON}; use js::jsapi::JS_ClearPendingException; use js::jsval::{JSVal, NullValue, UndefinedValue}; use msg::constellation_msg::PipelineId; -use net_traits::{CoreResourceThread, FetchMetadata, FilteredMetadata}; +use net_traits::{FetchMetadata, FilteredMetadata}; use net_traits::{FetchResponseListener, LoadOrigin, NetworkError, ReferrerPolicy}; use net_traits::CoreResourceMsg::Fetch; use net_traits::request::{CredentialsMode, Destination, RequestInit, RequestMode}; use net_traits::trim_http_whitespace; use network_listener::{NetworkListener, PreInvoke}; -use script_runtime::ScriptChan; use servo_atoms::Atom; use std::ascii::AsciiExt; use std::borrow::ToOwned; @@ -63,6 +61,7 @@ use std::cell::Cell; use std::default::Default; use std::str; use std::sync::{Arc, Mutex}; +use task_source::networking::NetworkingTaskSource; use time; use timers::{OneshotTimerCallback, OneshotTimerHandle}; use url::{Position, Url}; @@ -214,8 +213,8 @@ impl XMLHttpRequest { } fn initiate_async_xhr(context: Arc<Mutex<XHRContext>>, - script_chan: Box<ScriptChan + Send>, - core_resource_thread: CoreResourceThread, + task_source: NetworkingTaskSource, + global: &GlobalScope, init: RequestInit) { impl FetchResponseListener for XHRContext { fn process_request_body(&mut self) { @@ -262,13 +261,13 @@ impl XMLHttpRequest { let (action_sender, action_receiver) = ipc::channel().unwrap(); let listener = NetworkListener { context: context, - script_chan: script_chan, - wrapper: None, + task_source: task_source, + wrapper: Some(global.get_runnable_wrapper()) }; ROUTER.add_route(action_receiver.to_opaque(), box move |message| { listener.notify_fetch(message.to().unwrap()); }); - core_resource_thread.send(Fetch(init, action_sender)).unwrap(); + global.core_resource_thread().send(Fetch(init, action_sender)).unwrap(); } } @@ -590,7 +589,7 @@ impl XMLHttpRequestMethods for XMLHttpRequest { // https://github.com/whatwg/xhr/issues/71 destination: Destination::None, synchronous: self.sync.get(), - mode: RequestMode::CORSMode, + mode: RequestMode::CorsMode, use_cors_preflight: has_handlers, credentials_mode: credentials_mode, use_url_credentials: use_url_credentials, @@ -1199,10 +1198,11 @@ impl XMLHttpRequest { let decoded = charset.decode(&self.response.borrow(), DecoderTrap::Replace).unwrap(); let document = self.new_doc(IsHTMLDocument::HTMLDocument); // TODO: Disable scripting while parsing - parse_html(&document, - DOMString::from(decoded), - wr.get_url(), - ParseContext::Owner(Some(wr.pipeline_id()))); + ServoParser::parse_html_document( + &document, + DOMString::from(decoded), + wr.get_url(), + Some(wr.pipeline_id())); document } @@ -1212,10 +1212,11 @@ impl XMLHttpRequest { let decoded = charset.decode(&self.response.borrow(), DecoderTrap::Replace).unwrap(); let document = self.new_doc(IsHTMLDocument::NonHTMLDocument); // TODO: Disable scripting while parsing - parse_xml(&document, - DOMString::from(decoded), - wr.get_url(), - xml::ParseContext::Owner(Some(wr.pipeline_id()))); + ServoParser::parse_xml_document( + &document, + DOMString::from(decoded), + wr.get_url(), + Some(wr.pipeline_id())); document } @@ -1293,16 +1294,15 @@ impl XMLHttpRequest { sync_status: DOMRefCell::new(None), })); - let (script_chan, script_port) = if self.sync.get() { + let (task_source, script_port) = if self.sync.get() { let (tx, rx) = global.new_script_pair(); - (tx, Some(rx)) + (NetworkingTaskSource(tx), Some(rx)) } else { (global.networking_task_source(), None) }; - let core_resource_thread = global.core_resource_thread(); - XMLHttpRequest::initiate_async_xhr(context.clone(), script_chan, - core_resource_thread, init); + XMLHttpRequest::initiate_async_xhr(context.clone(), task_source, + global, init); if let Some(script_port) = script_port { loop { diff --git a/components/script/fetch.rs b/components/script/fetch.rs index ed1a66fc683..740099344bf 100644 --- a/components/script/fetch.rs +++ b/components/script/fetch.rs @@ -2,10 +2,10 @@ * 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::codegen::Bindings::RequestBinding::RequestInfo; use dom::bindings::codegen::Bindings::RequestBinding::RequestInit; use dom::bindings::codegen::Bindings::ResponseBinding::ResponseBinding::ResponseMethods; use dom::bindings::codegen::Bindings::ResponseBinding::ResponseType as DOMResponseType; -use dom::bindings::codegen::UnionTypes::RequestOrUSVString; use dom::bindings::error::Error; use dom::bindings::js::Root; use dom::bindings::refcounted::{Trusted, TrustedPromise}; @@ -62,12 +62,13 @@ fn request_init_from_request(request: NetTraitsRequest) -> NetTraitsRequestInit referrer_policy: request.referrer_policy.get(), pipeline_id: request.pipeline_id.get(), redirect_mode: request.redirect_mode.get(), + ..NetTraitsRequestInit::default() } } // https://fetch.spec.whatwg.org/#fetch-method #[allow(unrooted_must_root)] -pub fn Fetch(global: &GlobalScope, input: RequestOrUSVString, init: &RequestInit) -> Rc<Promise> { +pub fn Fetch(global: &GlobalScope, input: RequestInfo, init: &RequestInit) -> Rc<Promise> { let core_resource_thread = global.core_resource_thread(); // Step 1 @@ -96,8 +97,8 @@ pub fn Fetch(global: &GlobalScope, input: RequestOrUSVString, init: &RequestInit })); let listener = NetworkListener { context: fetch_context, - script_chan: global.networking_task_source(), - wrapper: None, + task_source: global.networking_task_source(), + wrapper: Some(global.get_runnable_wrapper()) }; ROUTER.add_route(action_receiver.to_opaque(), box move |message| { diff --git a/components/script/lib.rs b/components/script/lib.rs index 94e9683ffaf..ed3283868d5 100644 --- a/components/script/lib.rs +++ b/components/script/lib.rs @@ -48,6 +48,7 @@ extern crate heapsize; #[macro_use] extern crate heapsize_derive; extern crate html5ever; #[macro_use] extern crate html5ever_atoms; +#[macro_use] extern crate hyper; extern crate hyper_serde; extern crate image; @@ -163,15 +164,17 @@ fn perform_platform_specific_initialization() { #[cfg(not(target_os = "linux"))] fn perform_platform_specific_initialization() {} +pub fn init_service_workers(sw_senders: SWManagerSenders) { + // Spawn the service worker manager passing the constellation sender + ServiceWorkerManager::spawn_manager(sw_senders); +} + #[allow(unsafe_code)] -pub fn init(sw_senders: SWManagerSenders) { +pub fn init() { unsafe { proxyhandler::init(); } - // Spawn the service worker manager passing the constellation sender - ServiceWorkerManager::spawn_manager(sw_senders); - // Create the global vtables used by the (generated) DOM // bindings to implement JS proxies. RegisterBindings::RegisterProxyHandlers(); diff --git a/components/script/network_listener.rs b/components/script/network_listener.rs index cd0158409f1..5a96317fb18 100644 --- a/components/script/network_listener.rs +++ b/components/script/network_listener.rs @@ -4,16 +4,16 @@ use bluetooth_traits::{BluetoothResponseListener, BluetoothResponseResult}; use net_traits::{Action, FetchResponseListener, FetchResponseMsg}; -use script_runtime::{CommonScriptMsg, ScriptChan}; -use script_runtime::ScriptThreadEventCategory::NetworkEvent; use script_thread::{Runnable, RunnableWrapper}; use std::sync::{Arc, Mutex}; +use task_source::TaskSource; +use task_source::networking::NetworkingTaskSource; /// An off-thread sink for async network event runnables. All such events are forwarded to /// a target thread, where they are invoked on the provided context object. pub struct NetworkListener<Listener: PreInvoke + Send + 'static> { pub context: Arc<Mutex<Listener>>, - pub script_chan: Box<ScriptChan + Send>, + pub task_source: NetworkingTaskSource, pub wrapper: Option<RunnableWrapper>, } @@ -24,9 +24,9 @@ impl<Listener: PreInvoke + Send + 'static> NetworkListener<Listener> { action: action, }; let result = if let Some(ref wrapper) = self.wrapper { - self.script_chan.send(CommonScriptMsg::RunnableMsg(NetworkEvent, wrapper.wrap_runnable(runnable))) + self.task_source.queue_with_wrapper(runnable, wrapper) } else { - self.script_chan.send(CommonScriptMsg::RunnableMsg(NetworkEvent, runnable)) + self.task_source.queue_wrapperless(runnable) }; if let Err(err) = result { warn!("failed to deliver network data: {:?}", err); diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs index cccfbda842d..7dfe41dac20 100644 --- a/components/script/script_thread.rs +++ b/components/script/script_thread.rs @@ -51,8 +51,6 @@ use dom::node::{Node, NodeDamage, window_from_node}; use dom::serviceworker::TrustedServiceWorkerAddress; use dom::serviceworkerregistration::ServiceWorkerRegistration; use dom::servoparser::{ParserContext, ServoParser}; -use dom::servoparser::html::{ParseContext, parse_html}; -use dom::servoparser::xml::{self, parse_xml}; use dom::transitionevent::TransitionEvent; use dom::uievent::UIEvent; use dom::window::{ReflowReason, Window}; @@ -659,7 +657,7 @@ impl ScriptThread { chan: MainThreadScriptChan(chan.clone()), dom_manipulation_task_source: DOMManipulationTaskSource(chan.clone()), user_interaction_task_source: UserInteractionTaskSource(chan.clone()), - networking_task_source: NetworkingTaskSource(chan.clone()), + networking_task_source: NetworkingTaskSource(boxed_script_sender.clone()), history_traversal_task_source: HistoryTraversalTaskSource(chan), file_reading_task_source: FileReadingTaskSource(boxed_script_sender), @@ -1623,7 +1621,6 @@ impl ScriptThread { let MainThreadScriptChan(ref sender) = self.chan; let DOMManipulationTaskSource(ref dom_sender) = self.dom_manipulation_task_source; let UserInteractionTaskSource(ref user_sender) = self.user_interaction_task_source; - let NetworkingTaskSource(ref network_sender) = self.networking_task_source; let HistoryTraversalTaskSource(ref history_sender) = self.history_traversal_task_source; let (ipc_timer_event_chan, ipc_timer_event_port) = ipc::channel().unwrap(); @@ -1635,7 +1632,7 @@ impl ScriptThread { MainThreadScriptChan(sender.clone()), DOMManipulationTaskSource(dom_sender.clone()), UserInteractionTaskSource(user_sender.clone()), - NetworkingTaskSource(network_sender.clone()), + self.networking_task_source.clone(), HistoryTraversalTaskSource(history_sender.clone()), self.file_reading_task_source.clone(), self.image_cache_channel.clone(), @@ -1780,15 +1777,17 @@ impl ScriptThread { }; if is_xml { - parse_xml(&document, - parse_input, - final_url, - xml::ParseContext::Owner(Some(incomplete.pipeline_id))); + ServoParser::parse_xml_document( + &document, + parse_input, + final_url, + Some(incomplete.pipeline_id)); } else { - parse_html(&document, - parse_input, - final_url, - ParseContext::Owner(Some(incomplete.pipeline_id))); + ServoParser::parse_html_document( + &document, + parse_input, + final_url, + Some(incomplete.pipeline_id)); } if incomplete.is_frozen { @@ -2050,7 +2049,7 @@ impl ScriptThread { let (action_sender, action_receiver) = ipc::channel().unwrap(); let listener = NetworkListener { context: context, - script_chan: self.chan.clone(), + task_source: self.networking_task_source.clone(), wrapper: None, }; ROUTER.add_route(action_receiver.to_opaque(), box move |message| { diff --git a/components/script/task_source/networking.rs b/components/script/task_source/networking.rs index 4f85ac6c3e6..8306a4789bb 100644 --- a/components/script/task_source/networking.rs +++ b/components/script/task_source/networking.rs @@ -2,19 +2,32 @@ * 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 script_runtime::{CommonScriptMsg, ScriptChan}; -use script_thread::MainThreadScriptMsg; -use std::sync::mpsc::Sender; +use script_runtime::{CommonScriptMsg, ScriptChan, ScriptThreadEventCategory}; +use script_thread::{Runnable, RunnableWrapper}; +use task_source::TaskSource; #[derive(JSTraceable)] -pub struct NetworkingTaskSource(pub Sender<MainThreadScriptMsg>); +pub struct NetworkingTaskSource(pub Box<ScriptChan + Send + 'static>); -impl ScriptChan for NetworkingTaskSource { - fn send(&self, msg: CommonScriptMsg) -> Result<(), ()> { - self.0.send(MainThreadScriptMsg::Common(msg)).map_err(|_| ()) +impl Clone for NetworkingTaskSource { + fn clone(&self) -> NetworkingTaskSource { + NetworkingTaskSource(self.0.clone()) } +} + +impl TaskSource for NetworkingTaskSource { + fn queue_with_wrapper<T>(&self, + msg: Box<T>, + wrapper: &RunnableWrapper) + -> Result<(), ()> + where T: Runnable + Send + 'static { + self.0.send(CommonScriptMsg::RunnableMsg(ScriptThreadEventCategory::NetworkEvent, + wrapper.wrap_runnable(msg))) + } +} - fn clone(&self) -> Box<ScriptChan + Send> { - box NetworkingTaskSource((&self.0).clone()) +impl NetworkingTaskSource { + pub fn queue_wrapperless<T: Runnable + Send + 'static>(&self, msg: Box<T>) -> Result<(), ()> { + self.0.send(CommonScriptMsg::RunnableMsg(ScriptThreadEventCategory::NetworkEvent, msg)) } } diff --git a/components/script/textinput.rs b/components/script/textinput.rs index 25243d739ab..5f04b5b187e 100644 --- a/components/script/textinput.rs +++ b/components/script/textinput.rs @@ -544,7 +544,6 @@ impl<T: ClipboardProvider> TextInput<T> { self.adjust_vertical(28, maybe_select); KeyReaction::RedrawSelection } - (None, Key::Tab) => KeyReaction::TriggerDefaultAction, _ => KeyReaction::Nothing, } } diff --git a/components/script/timers.rs b/components/script/timers.rs index 34ac02b6589..6b67d6959e3 100644 --- a/components/script/timers.rs +++ b/components/script/timers.rs @@ -7,6 +7,7 @@ use dom::bindings::cell::DOMRefCell; use dom::bindings::codegen::Bindings::FunctionBinding::Function; use dom::bindings::reflector::Reflectable; use dom::bindings::str::DOMString; +use dom::eventsource::EventSourceTimeoutCallback; use dom::globalscope::GlobalScope; use dom::testbinding::TestBindingCallback; use dom::xmlhttprequest::XHRTimeoutCallback; @@ -67,6 +68,7 @@ struct OneshotTimer { #[derive(JSTraceable, HeapSizeOf)] pub enum OneshotTimerCallback { XhrTimeout(XHRTimeoutCallback), + EventSourceTimeout(EventSourceTimeoutCallback), JsTimer(JsTimerTask), TestBindingCallback(TestBindingCallback), } @@ -75,6 +77,7 @@ impl OneshotTimerCallback { fn invoke<T: Reflectable>(self, this: &T, js_timers: &JsTimers) { match self { OneshotTimerCallback::XhrTimeout(callback) => callback.invoke(), + OneshotTimerCallback::EventSourceTimeout(callback) => callback.invoke(), OneshotTimerCallback::JsTimer(task) => task.invoke(this, js_timers), OneshotTimerCallback::TestBindingCallback(callback) => callback.invoke(), } |