From c5b864919bdcc1c7cdb396bf16481a048e9d0972 Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Thu, 29 May 2014 12:19:28 -0400 Subject: [PATCH] servo: Merge #2505 - Enable inline event handlers for XHR, and add most progressevent calls (from Manishearth:events-events-events); r=jdm events events events! Blocks #2282 Source-Repo: https://github.com/servo/servo Source-Revision: 468752fd4d05c736da0b085b46e162cc18870464 --- .../src/components/script/dom/bindings/str.rs | 15 ++- .../script/dom/webidls/XMLHttpRequest.webidl | 2 +- .../webidls/XMLHttpRequestEventTarget.webidl | 2 - .../components/script/dom/xmlhttprequest.rs | 107 ++++++++++++++++-- .../script/dom/xmlhttprequesteventtarget.rs | 91 ++++++++++++++- 5 files changed, 196 insertions(+), 21 deletions(-) diff --git a/servo/src/components/script/dom/bindings/str.rs b/servo/src/components/script/dom/bindings/str.rs index a662470aeabc..e7d84d21c07c 100644 --- a/servo/src/components/script/dom/bindings/str.rs +++ b/servo/src/components/script/dom/bindings/str.rs @@ -22,6 +22,11 @@ impl ByteString { vector.as_slice() } + pub fn len(&self) -> uint { + let ByteString(ref vector) = *self; + vector.len() + } + pub fn eq_ignore_case(&self, other: &ByteString) -> bool { // XXXManishearth make this more efficient self.to_lower() == other.to_lower() @@ -60,7 +65,7 @@ impl ByteString { Other, CR, LF, - SP_HT // SP or HT + SPHT // SP or HT } let ByteString(ref vec) = *self; let mut prev = Other; // The previous character @@ -68,7 +73,7 @@ impl ByteString { // http://tools.ietf.org/html/rfc2616#section-2.2 match x { 13 => { // CR - if prev == Other || prev == SP_HT { + if prev == Other || prev == SPHT { prev = CR; true } else { @@ -84,8 +89,8 @@ impl ByteString { } }, 32 | 9 => { // SP | HT - if prev == LF || prev == SP_HT { - prev = SP_HT; + if prev == LF || prev == SPHT { + prev = SPHT; true } else { false @@ -93,7 +98,7 @@ impl ByteString { }, 0..31 | 127 => false, // CTLs x if x > 127 => false, // non ASCII - _ if prev == Other || prev == SP_HT => { + _ if prev == Other || prev == SPHT => { prev = Other; true }, diff --git a/servo/src/components/script/dom/webidls/XMLHttpRequest.webidl b/servo/src/components/script/dom/webidls/XMLHttpRequest.webidl index 43a542c4ef0b..ae9e1c9ef8f8 100644 --- a/servo/src/components/script/dom/webidls/XMLHttpRequest.webidl +++ b/servo/src/components/script/dom/webidls/XMLHttpRequest.webidl @@ -26,7 +26,7 @@ enum XMLHttpRequestResponseType { Exposed=Window,Worker] interface XMLHttpRequest : XMLHttpRequestEventTarget { // event handler - // attribute EventHandler onreadystatechange; + attribute EventHandler onreadystatechange; // states const unsigned short UNSENT = 0; diff --git a/servo/src/components/script/dom/webidls/XMLHttpRequestEventTarget.webidl b/servo/src/components/script/dom/webidls/XMLHttpRequestEventTarget.webidl index 5a7bbc652b49..0a7d044f1216 100644 --- a/servo/src/components/script/dom/webidls/XMLHttpRequestEventTarget.webidl +++ b/servo/src/components/script/dom/webidls/XMLHttpRequestEventTarget.webidl @@ -16,7 +16,6 @@ /* https://github.com/mozilla/servo/issues/1223: [NoInterfaceObject] */ interface XMLHttpRequestEventTarget : EventTarget { // event handlers - /* Needs EventHandler: https://github.com/mozilla/servo/issues/1238 attribute EventHandler onloadstart; attribute EventHandler onprogress; attribute EventHandler onabort; @@ -24,5 +23,4 @@ interface XMLHttpRequestEventTarget : EventTarget { attribute EventHandler onload; attribute EventHandler ontimeout; attribute EventHandler onloadend; - */ }; \ No newline at end of file diff --git a/servo/src/components/script/dom/xmlhttprequest.rs b/servo/src/components/script/dom/xmlhttprequest.rs index 608c64e6ef9e..f718ea106bef 100644 --- a/servo/src/components/script/dom/xmlhttprequest.rs +++ b/servo/src/components/script/dom/xmlhttprequest.rs @@ -3,21 +3,23 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use dom::bindings::codegen::BindingDeclarations::XMLHttpRequestBinding; +use dom::bindings::codegen::EventHandlerBinding::EventHandlerNonNull; use dom::bindings::str::ByteString; use dom::bindings::codegen::BindingDeclarations::XMLHttpRequestBinding::XMLHttpRequestResponseType; use dom::bindings::codegen::BindingDeclarations::XMLHttpRequestBinding::XMLHttpRequestResponseTypeValues::{_empty, Text}; -use dom::bindings::codegen::InheritTypes::{EventTargetCast, XMLHttpRequestDerived}; +use dom::bindings::codegen::InheritTypes::{EventCast, EventTargetCast, XMLHttpRequestDerived}; use dom::bindings::error::{ErrorResult, InvalidState, Network, Syntax, Security}; use dom::document::Document; use dom::event::{Event, EventMethods}; use dom::eventtarget::{EventTarget, EventTargetHelpers, XMLHttpRequestTargetTypeId}; use dom::bindings::conversions::ToJSValConvertible; use dom::bindings::error::Fallible; -use dom::bindings::js::{JS, JSRef, Temporary, OptionalSettable}; +use dom::bindings::js::{JS, JSRef, Temporary, OptionalSettable, OptionalRootedRootable}; use dom::bindings::trace::Untraceable; use js::jsapi::{JS_AddObjectRoot, JS_RemoveObjectRoot, JSContext}; use js::jsval::{JSVal, NullValue}; use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object}; +use dom::progressevent::ProgressEvent; use dom::window::Window; use dom::xmlhttprequesteventtarget::XMLHttpRequestEventTarget; use dom::xmlhttprequestupload::XMLHttpRequestUpload; @@ -215,6 +217,8 @@ impl XMLHttpRequest { } pub trait XMLHttpRequestMethods<'a> { + fn GetOnreadystatechange(&self) -> Option; + fn SetOnreadystatechange(&mut self, listener: Option); fn ReadyState(&self) -> u16; fn Open(&mut self, _method: ByteString, _url: DOMString) -> ErrorResult; fn Open_(&mut self, _method: ByteString, _url: DOMString, _async: bool, @@ -241,9 +245,20 @@ pub trait XMLHttpRequestMethods<'a> { } impl<'a> XMLHttpRequestMethods<'a> for JSRef<'a, XMLHttpRequest> { + fn GetOnreadystatechange(&self) -> Option { + let eventtarget: &JSRef = EventTargetCast::from_ref(self); + eventtarget.get_event_handler_common("readystatechange") + } + + fn SetOnreadystatechange(&mut self, listener: Option) { + let eventtarget: &mut JSRef = EventTargetCast::from_mut_ref(self); + eventtarget.set_event_handler_common("readystatechange", listener) + } + fn ReadyState(&self) -> u16 { self.ready_state as u16 } + fn Open(&mut self, method: ByteString, url: DOMString) -> ErrorResult { let maybe_method: Option = method.as_str().and_then(|s| { FromStr::from_str(s.to_ascii_upper()) // rust-http tests against the uppercase versions @@ -383,12 +398,29 @@ impl<'a> XMLHttpRequestMethods<'a> for JSRef<'a, XMLHttpRequest> { }; // Step 6 - self.upload_complete = false; self.upload_events = false; - // XXXManishearth handle upload events + // Step 7 + self.upload_complete = match data { + None => true, + Some (ref s) if s.len() == 0 => true, + _ => false + }; + if !self.sync { + // Step 8 + let upload_target = &*self.upload.root().unwrap(); + let event_target: &JSRef = EventTargetCast::from_ref(upload_target); + if event_target.handlers.iter().len() > 0 { + self.upload_events = true; + } + + // Step 9 + self.send_flag = true; + self.dispatch_response_progress_event("loadstart".to_owned()); + if !self.upload_complete { + self.dispatch_upload_progress_event("loadstart".to_owned(), Some(0)); + } + } - // Step 9 - self.send_flag = true; let mut global = self.global.root(); let resource_task = global.page().resource_task.deref().clone(); let mut load_data = LoadData::new((*self.request_url).clone()); @@ -506,6 +538,9 @@ trait PrivateXMLHttpRequestHelpers { fn change_ready_state(&mut self, XMLHttpRequestState); fn process_partial_response(&mut self, progress: XHRProgress); fn insert_trusted_header(&mut self, name: ~str, value: ~str); + fn dispatch_progress_event(&self, upload: bool, type_: DOMString, loaded: u64, total: Option); + fn dispatch_upload_progress_event(&self, type_: DOMString, partial_load: Option); + fn dispatch_response_progress_event(&self, type_: DOMString); } impl<'a> PrivateXMLHttpRequestHelpers for JSRef<'a, XMLHttpRequest> { @@ -537,6 +572,12 @@ impl<'a> PrivateXMLHttpRequestHelpers for JSRef<'a, XMLHttpRequest> { fn process_partial_response(&mut self, progress: XHRProgress) { match progress { HeadersReceivedMsg(headers) => { + // XXXManishearth Find a way to track partial progress of the send (onprogresss for XHRUpload) + self.upload_complete = true; + self.dispatch_upload_progress_event("progress".to_owned(), None); + self.dispatch_upload_progress_event("load".to_owned(), None); + self.dispatch_upload_progress_event("loadend".to_owned(), None); + match headers { Some(ref h) => *self.response_headers = h.clone(), None => {} @@ -545,23 +586,36 @@ impl<'a> PrivateXMLHttpRequestHelpers for JSRef<'a, XMLHttpRequest> { }, LoadingMsg(partial_response) => { self.response = partial_response; + self.dispatch_response_progress_event("progress".to_owned()); if self.ready_state == HeadersReceived { self.change_ready_state(Loading); } }, DoneMsg => { + let len = self.response.len() as u64; + self.dispatch_response_progress_event("progress".to_owned()); + self.dispatch_response_progress_event("load".to_owned()); + self.dispatch_response_progress_event("loadend".to_owned()); self.send_flag = false; self.change_ready_state(XHRDone); }, ErroredMsg => { - self.send_flag = false; + self.send_flag = false; // XXXManishearth set response to NetworkError - if !self.upload_complete { - self.upload_complete = true; - // XXXManishearth handle upload progress - } - // XXXManishearth fire some progress events + // XXXManishearth also handle terminated requests (timeout/abort/fatal) self.change_ready_state(XHRDone); + if !self.sync { + if !self.upload_complete { + self.upload_complete = true; + self.dispatch_upload_progress_event("progress".to_owned(), None); + self.dispatch_upload_progress_event("load".to_owned(), None); + self.dispatch_upload_progress_event("loadend".to_owned(), None); + } + self.dispatch_response_progress_event("progress".to_owned()); + self.dispatch_response_progress_event("load".to_owned()); + self.dispatch_response_progress_event("loadend".to_owned()); + } + }, ReleaseMsg => { self.release(); @@ -580,4 +634,33 @@ impl<'a> PrivateXMLHttpRequestHelpers for JSRef<'a, XMLHttpRequest> { &mut HeaderValueByteIterator::new(&mut reader)); collection.insert(maybe_header.unwrap()); } + + fn dispatch_progress_event(&self, upload: bool, type_: DOMString, loaded: u64, total: Option) { + let win = &*self.global.root(); + let upload_target = &*self.upload.root().unwrap(); + let mut progressevent = ProgressEvent::new(win, type_, false, false, + total.is_some(), loaded, + total.unwrap_or(0)).root(); + let target: &JSRef = if upload { + EventTargetCast::from_ref(upload_target) + } else { + EventTargetCast::from_ref(self) + }; + let event: &mut JSRef = EventCast::from_mut_ref(&mut *progressevent); + target.dispatch_event_with_target(None, &mut *event).ok(); + } + + fn dispatch_upload_progress_event(&self, type_: DOMString, partial_load: Option) { + // If partial_load is None, loading has completed and we can just use the value from the request body + + let total = self.request_body.len() as u64; + self.dispatch_progress_event(true, type_, partial_load.unwrap_or(total), Some(total)); + } + + fn dispatch_response_progress_event(&self, type_: DOMString) { + let win = &*self.global.root(); + let len = self.response.len() as u64; + let total = self.response_headers.deref().content_length.map(|x| {x as u64}); + self.dispatch_progress_event(false, type_, len, total); + } } diff --git a/servo/src/components/script/dom/xmlhttprequesteventtarget.rs b/servo/src/components/script/dom/xmlhttprequesteventtarget.rs index 68d67fab89b5..7126c2e0b6c1 100644 --- a/servo/src/components/script/dom/xmlhttprequesteventtarget.rs +++ b/servo/src/components/script/dom/xmlhttprequesteventtarget.rs @@ -3,8 +3,11 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use dom::bindings::codegen::InheritTypes::XMLHttpRequestEventTargetDerived; +use dom::bindings::codegen::EventHandlerBinding::EventHandlerNonNull; +use dom::bindings::codegen::InheritTypes::EventTargetCast; +use dom::bindings::js::JSRef; use dom::bindings::utils::{Reflectable, Reflector}; -use dom::eventtarget::{EventTarget, XMLHttpRequestTargetTypeId}; +use dom::eventtarget::{EventTarget, EventTargetHelpers, XMLHttpRequestTargetTypeId}; use dom::xmlhttprequest::XMLHttpRequestId; #[deriving(Encodable)] @@ -40,4 +43,90 @@ impl Reflectable for XMLHttpRequestEventTarget { } pub trait XMLHttpRequestEventTargetMethods { + fn GetOnloadstart(&self) -> Option; + fn SetOnloadstart(&mut self, listener: Option); + fn GetOnprogress(&self) -> Option; + fn SetOnprogress(&mut self, listener: Option); + fn GetOnabort(&self) -> Option; + fn SetOnabort(&mut self, listener: Option); + fn GetOnerror(&self) -> Option; + fn SetOnerror(&mut self, listener: Option); + fn GetOnload(&self) -> Option; + fn SetOnload(&mut self, listener: Option); + fn GetOntimeout(&self) -> Option; + fn SetOntimeout(&mut self, listener: Option); + fn GetOnloadend(&self) -> Option; + fn SetOnloadend(&mut self, listener: Option); +} + +impl<'a> XMLHttpRequestEventTargetMethods for JSRef<'a, XMLHttpRequestEventTarget> { + fn GetOnloadstart(&self) -> Option { + let eventtarget: &JSRef = EventTargetCast::from_ref(self); + eventtarget.get_event_handler_common("loadstart") + } + + fn SetOnloadstart(&mut self, listener: Option) { + let eventtarget: &mut JSRef = EventTargetCast::from_mut_ref(self); + eventtarget.set_event_handler_common("loadstart", listener) + } + + fn GetOnprogress(&self) -> Option { + let eventtarget: &JSRef = EventTargetCast::from_ref(self); + eventtarget.get_event_handler_common("progress") + } + + fn SetOnprogress(&mut self, listener: Option) { + let eventtarget: &mut JSRef = EventTargetCast::from_mut_ref(self); + eventtarget.set_event_handler_common("progress", listener) + } + + fn GetOnabort(&self) -> Option { + let eventtarget: &JSRef = EventTargetCast::from_ref(self); + eventtarget.get_event_handler_common("abort") + } + + fn SetOnabort(&mut self, listener: Option) { + let eventtarget: &mut JSRef = EventTargetCast::from_mut_ref(self); + eventtarget.set_event_handler_common("abort", listener) + } + + fn GetOnerror(&self) -> Option { + let eventtarget: &JSRef = EventTargetCast::from_ref(self); + eventtarget.get_event_handler_common("error") + } + + fn SetOnerror(&mut self, listener: Option) { + let eventtarget: &mut JSRef = EventTargetCast::from_mut_ref(self); + eventtarget.set_event_handler_common("error", listener) + } + + fn GetOnload(&self) -> Option { + let eventtarget: &JSRef = EventTargetCast::from_ref(self); + eventtarget.get_event_handler_common("load") + } + + fn SetOnload(&mut self, listener: Option) { + let eventtarget: &mut JSRef = EventTargetCast::from_mut_ref(self); + eventtarget.set_event_handler_common("load", listener) + } + + fn GetOntimeout(&self) -> Option { + let eventtarget: &JSRef = EventTargetCast::from_ref(self); + eventtarget.get_event_handler_common("timeout") + } + + fn SetOntimeout(&mut self, listener: Option) { + let eventtarget: &mut JSRef = EventTargetCast::from_mut_ref(self); + eventtarget.set_event_handler_common("timeout", listener) + } + + fn GetOnloadend(&self) -> Option { + let eventtarget: &JSRef = EventTargetCast::from_ref(self); + eventtarget.get_event_handler_common("loadend") + } + + fn SetOnloadend(&mut self, listener: Option) { + let eventtarget: &mut JSRef = EventTargetCast::from_mut_ref(self); + eventtarget.set_event_handler_common("loadend", listener) + } } \ No newline at end of file