From 2de05450df4be9f49ff183903d2706a6655dc9d6 Mon Sep 17 00:00:00 2001 From: Tim Cuthbertson Date: Tue, 7 Apr 2015 16:24:04 +1000 Subject: [PATCH 01/32] wpt: Update "running the tests manually" instructions --- tests/wpt/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/wpt/README.md b/tests/wpt/README.md index 4637efe7058..20cb96beda2 100644 --- a/tests/wpt/README.md +++ b/tests/wpt/README.md @@ -64,7 +64,7 @@ first adding the following to the system's hosts file: 127.0.0.1 xn--n8j6ds53lwwkrqhv28a.web-platform.test 127.0.0.1 xn--lve-6lad.web-platform.test -and then running `python serve.py` from `tests/wpt/web-platform-tests`. +and then running `python serve` from `tests/wpt/web-platform-tests`. Then navigate Servo to `http://web-platform.test:8000/path/to/test`. Updating test expectations From dd7ec693a5f3d1ac81bfbdfe20c99c3ff8c5532c Mon Sep 17 00:00:00 2001 From: Ms2ger Date: Tue, 7 Apr 2015 17:53:12 +0200 Subject: [PATCH 02/32] Remove some unnecessary transmute calls. --- ports/cef/wrappers.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/ports/cef/wrappers.rs b/ports/cef/wrappers.rs index 3bfd1c27464..d955aac62b0 100644 --- a/ports/cef/wrappers.rs +++ b/ports/cef/wrappers.rs @@ -61,7 +61,7 @@ macro_rules! cef_pointer_wrapper( rust_object } unsafe fn to_rust(c_object: *const $ty) -> &'a $ty { - mem::transmute::<*const $ty,&'a $ty>(c_object) + &*c_object } } impl<'a> CefWrap<*mut $ty> for &'a mut $ty { @@ -69,7 +69,7 @@ macro_rules! cef_pointer_wrapper( rust_object } unsafe fn to_rust(c_object: *mut $ty) -> &'a mut $ty { - mem::transmute::<*mut $ty,&'a mut $ty>(c_object) + &mut *c_object } } cef_noop_wrapper!(*const $ty); @@ -214,8 +214,7 @@ impl<'a> CefWrap<*mut cef_string_t> for &'a mut [u16] { panic!("unimplemented CEF type conversion: &'a str") } unsafe fn to_rust(_: *mut cef_string_t) -> &'a mut [u16] { - mem::transmute::<(int,int),_>(panic!("unimplemented CEF type conversion: *mut \ - cef_string_t")) + panic!("unimplemented CEF type conversion: *mut cef_string_t") } } From b5d41aa8a5171a65a060df62abb5847a98ab334a Mon Sep 17 00:00:00 2001 From: Ms2ger Date: Tue, 7 Apr 2015 17:54:16 +0200 Subject: [PATCH 03/32] Use boxed::into_raw where it makes sense. --- ports/cef/wrappers.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/ports/cef/wrappers.rs b/ports/cef/wrappers.rs index d955aac62b0..f1da9106df2 100644 --- a/ports/cef/wrappers.rs +++ b/ports/cef/wrappers.rs @@ -32,6 +32,7 @@ use types::{cef_window_info_t, cef_xml_encoding_type_t, cef_xml_node_type_t}; use unicode::str::Utf16Encoder; use libc::{self, c_char, c_int, c_ushort, c_void}; +use std::boxed; use std::collections::HashMap; use std::mem; use std::ptr; @@ -187,14 +188,11 @@ impl<'a> CefWrap<*const cef_string_t> for &'a [u16] { // FIXME(pcwalton): This leaks!! We should instead have the caller pass some scratch // stack space to create the object in. What a botch. - let boxed_string = box cef_string_utf16 { + boxed::into_raw(box cef_string_utf16 { str: ptr, length: buffer.len() as u64, dtor: Some(free_boxed_utf16_string as extern "C" fn(*mut c_ushort)), - }; - let result: *const cef_string_utf16 = &*boxed_string; - mem::forget(boxed_string); - result + }) as *const _ } } unsafe fn to_rust(cef_string: *const cef_string_t) -> &'a [u16] { From cba3b6806e664942846cfef7204161addcc4718e Mon Sep 17 00:00:00 2001 From: Ms2ger Date: Tue, 7 Apr 2015 17:54:36 +0200 Subject: [PATCH 04/32] Use raw::Slice where it makes sense. --- ports/cef/wrappers.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/ports/cef/wrappers.rs b/ports/cef/wrappers.rs index f1da9106df2..d8929352c84 100644 --- a/ports/cef/wrappers.rs +++ b/ports/cef/wrappers.rs @@ -36,6 +36,7 @@ use std::boxed; use std::collections::HashMap; use std::mem; use std::ptr; +use std::raw; pub trait CefWrap { fn to_c(rust_object: Self) -> CObject; @@ -196,8 +197,10 @@ impl<'a> CefWrap<*const cef_string_t> for &'a [u16] { } } unsafe fn to_rust(cef_string: *const cef_string_t) -> &'a [u16] { - let (ptr, len): (*mut c_ushort, uint) = ((*cef_string).str, (*cef_string).length as uint); - mem::transmute((ptr, len)) + mem::transmute(raw::Slice { + data: (*cef_string).str, + len: (*cef_string).length as usize, + }) } } From 1fa1950ab1d185d5d310e413a779563b03585d5b Mon Sep 17 00:00:00 2001 From: Ms2ger Date: Tue, 7 Apr 2015 17:55:49 +0200 Subject: [PATCH 05/32] Allocate only as much as necessary. --- ports/cef/wrappers.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/cef/wrappers.rs b/ports/cef/wrappers.rs index d8929352c84..9964eac6728 100644 --- a/ports/cef/wrappers.rs +++ b/ports/cef/wrappers.rs @@ -246,7 +246,7 @@ impl<'a> CefWrap for String { let boxed_string; unsafe { let buffer = libc::malloc((mem::size_of::() as u64) * - ((utf16_chars.len() + 1) as u64 + 1)) as *mut u16; + ((utf16_chars.len() + 1) as u64)) as *mut u16; for (i, ch) in utf16_chars.iter().enumerate() { *buffer.offset(i as int) = *ch } From 6d1cde22830f538b2c36428dfbc19d0839f13d2b Mon Sep 17 00:00:00 2001 From: Ms2ger Date: Tue, 7 Apr 2015 17:56:01 +0200 Subject: [PATCH 06/32] Stop leaking the Vec. --- ports/cef/wrappers.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/ports/cef/wrappers.rs b/ports/cef/wrappers.rs index 9964eac6728..29a04e38678 100644 --- a/ports/cef/wrappers.rs +++ b/ports/cef/wrappers.rs @@ -257,7 +257,6 @@ impl<'a> CefWrap for String { ptr::write(&mut (*boxed_string).str, buffer); ptr::write(&mut (*boxed_string).length, utf16_chars.len() as u64); ptr::write(&mut (*boxed_string).dtor, Some(free_utf16_buffer as extern "C" fn(*mut c_ushort))); - mem::forget(utf16_chars); } boxed_string } From 9ddaf82d47d6a49643885f8bca31d332fd419de9 Mon Sep 17 00:00:00 2001 From: Ms2ger Date: Tue, 7 Apr 2015 17:56:46 +0200 Subject: [PATCH 07/32] Use size_t for the arguments to malloc. --- ports/cef/wrappers.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ports/cef/wrappers.rs b/ports/cef/wrappers.rs index 29a04e38678..efc8ac8aaae 100644 --- a/ports/cef/wrappers.rs +++ b/ports/cef/wrappers.rs @@ -245,14 +245,14 @@ impl<'a> CefWrap for String { let boxed_string; unsafe { - let buffer = libc::malloc((mem::size_of::() as u64) * - ((utf16_chars.len() + 1) as u64)) as *mut u16; + let buffer = libc::malloc((mem::size_of::() as libc::size_t) * + ((utf16_chars.len() + 1) as libc::size_t)) as *mut u16; for (i, ch) in utf16_chars.iter().enumerate() { *buffer.offset(i as int) = *ch } *buffer.offset(utf16_chars.len() as int) = 0; - boxed_string = libc::malloc(mem::size_of::() as u64) as + boxed_string = libc::malloc(mem::size_of::() as libc::size_t) as *mut cef_string_utf16; ptr::write(&mut (*boxed_string).str, buffer); ptr::write(&mut (*boxed_string).length, utf16_chars.len() as u64); From cdb3efec879d67d2bdfe3aa1b2f98950a995d784 Mon Sep 17 00:00:00 2001 From: Anthony Ramine Date: Tue, 7 Apr 2015 20:27:30 +0200 Subject: [PATCH 08/32] Use a Temporary iterator in Node::collect_text_contents() --- components/script/dom/htmlscriptelement.rs | 3 +-- components/script/dom/node.rs | 8 +++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/components/script/dom/htmlscriptelement.rs b/components/script/dom/htmlscriptelement.rs index 7b4f7a8aea0..49cf5f257ed 100644 --- a/components/script/dom/htmlscriptelement.rs +++ b/components/script/dom/htmlscriptelement.rs @@ -524,8 +524,7 @@ impl<'a> HTMLScriptElementMethods for JSRef<'a, HTMLScriptElement> { // http://www.whatwg.org/html/#dom-script-text fn Text(self) -> DOMString { - let node: JSRef = NodeCast::from_ref(self); - Node::collect_text_contents(node.children().map(|c| c.root()).map(|c| c.get_unsound_ref_forever())) + Node::collect_text_contents(NodeCast::from_ref(self).children()) } // http://www.whatwg.org/html/#dom-script-text diff --git a/components/script/dom/node.rs b/components/script/dom/node.rs index 95374e4f360..822fe4ff4e6 100644 --- a/components/script/dom/node.rs +++ b/components/script/dom/node.rs @@ -1681,10 +1681,11 @@ impl Node { Temporary::from_rooted(copy.r()) } - pub fn collect_text_contents<'a, T: Iterator>>(iterator: T) -> String { + pub fn collect_text_contents>>(iterator: T) -> String { let mut content = String::new(); for node in iterator { - let text: Option> = TextCast::to_ref(node); + let node = node.root(); + let text = TextCast::to_ref(node.r()); match text { Some(text) => content.push_str(text.characterdata().data().as_slice()), None => (), @@ -1832,7 +1833,8 @@ impl<'a> NodeMethods for JSRef<'a, Node> { match self.type_id { NodeTypeId::DocumentFragment | NodeTypeId::Element(..) => { - let content = Node::collect_text_contents(self.traverse_preorder()); + let content = Node::collect_text_contents( + self.traverse_preorder().map(Temporary::from_rooted)); Some(content) } NodeTypeId::Comment | From 7e07dcc7ee964e89cbb263ebca6db71fac22cf05 Mon Sep 17 00:00:00 2001 From: Anthony Ramine Date: Tue, 7 Apr 2015 21:20:57 +0200 Subject: [PATCH 09/32] Add missing NewObject annotation in Document --- components/script/dom/webidls/Document.webidl | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/components/script/dom/webidls/Document.webidl b/components/script/dom/webidls/Document.webidl index 984f79eb528..dea9b9c38f6 100644 --- a/components/script/dom/webidls/Document.webidl +++ b/components/script/dom/webidls/Document.webidl @@ -27,27 +27,31 @@ interface Document : Node { HTMLCollection getElementsByClassName(DOMString classNames); Element? getElementById(DOMString elementId); - [Throws] + [NewObject, Throws] Element createElement(DOMString localName); - [Throws] + [NewObject, Throws] Element createElementNS(DOMString? namespace, DOMString qualifiedName); + [NewObject] DocumentFragment createDocumentFragment(); + [NewObject] Text createTextNode(DOMString data); + [NewObject] Comment createComment(DOMString data); - [Throws] + [NewObject, Throws] ProcessingInstruction createProcessingInstruction(DOMString target, DOMString data); - [Throws] + [NewObject, Throws] Attr createAttribute(DOMString localName); - [Throws] + [NewObject, Throws] Node importNode(Node node, optional boolean deep = false); [Throws] Node adoptNode(Node node); - [Throws] + [NewObject, Throws] Event createEvent(DOMString interface_); + [NewObject] Range createRange(); // NodeFilter.SHOW_ALL = 0xFFFFFFFF From 9f5f7f4b8e81c3b4630039d6b285f7de77bb0876 Mon Sep 17 00:00:00 2001 From: Dave Hodder Date: Tue, 7 Apr 2015 21:05:01 +0100 Subject: [PATCH 10/32] Use canonical GitHub URL for Rust --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bbc4e2a3da5..56c6ee6844d 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # The Servo Parallel Browser Project Servo is a prototype web browser engine written in the -[Rust](https://github.com/mozilla/rust) language. It is currently developed on +[Rust](https://github.com/rust-lang/rust) language. It is currently developed on 64bit OS X, 64bit Linux, Android, and Gonk (Firefox OS). Servo welcomes contribution from everyone. See From c86bc2eb8b69b6a4d8c80aca6b36a7dc041eddd9 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Fri, 3 Apr 2015 17:18:22 -0700 Subject: [PATCH 11/32] layout: Don't take collapsible top margin into account when determining the float ceiling. As the float ceiling is relative to the border box, not the margin box, of the parent flow, top margin must not be included. This exposed a pre-existing bug whereby margins are discarded if a block contains only floats and no content, affecting the tests `float_intrinsic_height.html` and `margins_inside_floats_a.html`. As a workaround, some invisible content has been added to the bodies of both tests. --- components/layout/block.rs | 3 ++- components/layout/model.rs | 19 +++++++++++---- tests/ref/basic.list | 1 + tests/ref/float_intrinsic_height.html | 2 +- tests/ref/float_under_top_margin_a.html | 29 +++++++++++++++++++++++ tests/ref/float_under_top_margin_ref.html | 29 +++++++++++++++++++++++ tests/ref/margins_inside_floats_a.html | 2 +- 7 files changed, 77 insertions(+), 8 deletions(-) create mode 100644 tests/ref/float_under_top_margin_a.html create mode 100644 tests/ref/float_under_top_margin_ref.html diff --git a/components/layout/block.rs b/components/layout/block.rs index 24eac4f48ea..236bfd321ea 100644 --- a/components/layout/block.rs +++ b/components/layout/block.rs @@ -1990,7 +1990,8 @@ pub struct ISizeConstraintSolution { } impl ISizeConstraintSolution { - pub fn new(inline_size: Au, margin_inline_start: Au, margin_inline_end: Au) -> ISizeConstraintSolution { + pub fn new(inline_size: Au, margin_inline_start: Au, margin_inline_end: Au) + -> ISizeConstraintSolution { ISizeConstraintSolution { inline_start: Au(0), inline_end: Au(0), diff --git a/components/layout/model.rs b/components/layout/model.rs index 64ba9a001d8..2479ab7cb47 100644 --- a/components/layout/model.rs +++ b/components/layout/model.rs @@ -174,7 +174,12 @@ impl MarginCollapseInfo { pub fn current_float_ceiling(&mut self) -> Au { match self.state { - MarginCollapseState::AccumulatingCollapsibleTopMargin => self.block_start_margin.collapse(), + MarginCollapseState::AccumulatingCollapsibleTopMargin => { + // We do not include the top margin in the float ceiling, because the float flow + // needs to be positioned relative to our *border box*, not our margin box. See + // `tests/ref/float_under_top_margin_a.html`. + Au(0) + } MarginCollapseState::AccumulatingMarginIn => self.margin_in.collapse(), } } @@ -182,18 +187,22 @@ impl MarginCollapseInfo { /// Adds the child's potentially collapsible block-start margin to the current margin state and /// advances the Y offset by the appropriate amount to handle that margin. Returns the amount /// that should be added to the Y offset during block layout. - pub fn advance_block_start_margin(&mut self, child_collapsible_margins: &CollapsibleMargins) -> Au { + pub fn advance_block_start_margin(&mut self, child_collapsible_margins: &CollapsibleMargins) + -> Au { match (self.state, *child_collapsible_margins) { - (MarginCollapseState::AccumulatingCollapsibleTopMargin, CollapsibleMargins::None(block_start, _)) => { + (MarginCollapseState::AccumulatingCollapsibleTopMargin, + CollapsibleMargins::None(block_start, _)) => { self.state = MarginCollapseState::AccumulatingMarginIn; block_start } - (MarginCollapseState::AccumulatingCollapsibleTopMargin, CollapsibleMargins::Collapse(block_start, _)) => { + (MarginCollapseState::AccumulatingCollapsibleTopMargin, + CollapsibleMargins::Collapse(block_start, _)) => { self.block_start_margin.union(block_start); self.state = MarginCollapseState::AccumulatingMarginIn; Au(0) } - (MarginCollapseState::AccumulatingMarginIn, CollapsibleMargins::None(block_start, _)) => { + (MarginCollapseState::AccumulatingMarginIn, + CollapsibleMargins::None(block_start, _)) => { let previous_margin_value = self.margin_in.collapse(); self.margin_in = AdjoiningMargins::new(); previous_margin_value + block_start diff --git a/tests/ref/basic.list b/tests/ref/basic.list index 1d814a10e87..f7e535872ef 100644 --- a/tests/ref/basic.list +++ b/tests/ref/basic.list @@ -100,6 +100,7 @@ flaky_cpu == append_style_a.html append_style_b.html == float_intrinsic_width_a.html float_intrinsic_width_ref.html == float_right_intrinsic_width_a.html float_right_intrinsic_width_ref.html == float_table_a.html float_table_ref.html +== float_under_top_margin_a.html float_under_top_margin_ref.html == floated_generated_content_a.html floated_generated_content_b.html == floated_list_item_a.html floated_list_item_ref.html == floated_table_with_margin_a.html floated_table_with_margin_ref.html diff --git a/tests/ref/float_intrinsic_height.html b/tests/ref/float_intrinsic_height.html index b6d9e7e302a..f349085d23c 100644 --- a/tests/ref/float_intrinsic_height.html +++ b/tests/ref/float_intrinsic_height.html @@ -19,6 +19,6 @@
-
+   diff --git a/tests/ref/float_under_top_margin_a.html b/tests/ref/float_under_top_margin_a.html new file mode 100644 index 00000000000..5cf1fdbdbd4 --- /dev/null +++ b/tests/ref/float_under_top_margin_a.html @@ -0,0 +1,29 @@ + + + + + + +
+ + + diff --git a/tests/ref/float_under_top_margin_ref.html b/tests/ref/float_under_top_margin_ref.html new file mode 100644 index 00000000000..904cbd5c66d --- /dev/null +++ b/tests/ref/float_under_top_margin_ref.html @@ -0,0 +1,29 @@ + + + + + + +
+ + + diff --git a/tests/ref/margins_inside_floats_a.html b/tests/ref/margins_inside_floats_a.html index 139f12677df..9ad6aa576ed 100644 --- a/tests/ref/margins_inside_floats_a.html +++ b/tests/ref/margins_inside_floats_a.html @@ -4,7 +4,7 @@
Must be this tall
to write multi-threaded code.
-
+  From abc01d598afca71f419053b3e886769d874003b0 Mon Sep 17 00:00:00 2001 From: Anthony Ramine Date: Tue, 7 Apr 2015 23:20:56 +0200 Subject: [PATCH 12/32] Add script::dom::utils::validate_qualified_name() --- components/script/dom/bindings/utils.rs | 17 ++++++++++++++++- components/script/dom/document.rs | 16 +++------------- components/script/dom/domimplementation.rs | 21 ++++++--------------- components/script/dom/element.rs | 14 ++++---------- 4 files changed, 29 insertions(+), 39 deletions(-) diff --git a/components/script/dom/bindings/utils.rs b/components/script/dom/bindings/utils.rs index 7faca76b6c6..97f6eaf9392 100644 --- a/components/script/dom/bindings/utils.rs +++ b/components/script/dom/bindings/utils.rs @@ -7,7 +7,7 @@ use dom::bindings::codegen::PrototypeList; use dom::bindings::codegen::PrototypeList::MAX_PROTO_CHAIN_LENGTH; use dom::bindings::conversions::{native_from_reflector_jsmanaged, is_dom_class}; -use dom::bindings::error::throw_type_error; +use dom::bindings::error::{Error, ErrorResult, throw_type_error}; use dom::bindings::global::GlobalRef; use dom::bindings::js::{Temporary, Root}; use dom::browsercontext; @@ -604,6 +604,21 @@ pub unsafe fn delete_property_by_id(cx: *mut JSContext, object: *mut JSObject, return true; } +/// Validate a qualified name. See https://dom.spec.whatwg.org/#validate for details. +pub fn validate_qualified_name(qualified_name: &str) -> ErrorResult { + match xml_name_type(qualified_name) { + XMLName::InvalidXMLName => { + // Step 1. + return Err(Error::InvalidCharacter); + }, + XMLName::Name => { + // Step 2. + return Err(Error::Namespace); + }, + XMLName::QName => Ok(()) + } +} + /// Results of `xml_name_type`. #[derive(PartialEq)] #[allow(missing_docs)] diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index 62fdfbeb109..a280507d4ff 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -27,8 +27,8 @@ use dom::bindings::js::{MutNullableJS, JS, JSRef, LayoutJS, Temporary, Temporary use dom::bindings::js::{OptionalRootable, RootedReference}; use dom::bindings::refcounted::Trusted; use dom::bindings::utils::reflect_dom_object; -use dom::bindings::utils::xml_name_type; -use dom::bindings::utils::XMLName::{QName, Name, InvalidXMLName}; +use dom::bindings::utils::{xml_name_type, validate_qualified_name}; +use dom::bindings::utils::XMLName::InvalidXMLName; use dom::comment::Comment; use dom::customevent::CustomEvent; use dom::documentfragment::DocumentFragment; @@ -976,17 +976,7 @@ impl<'a> DocumentMethods for JSRef<'a, Document> { namespace: Option, qualified_name: DOMString) -> Fallible> { let ns = namespace::from_domstring(namespace); - match xml_name_type(&qualified_name) { - InvalidXMLName => { - debug!("Not a valid element name"); - return Err(InvalidCharacter); - }, - Name => { - debug!("Not a valid qualified element name"); - return Err(Namespace); - }, - QName => {} - } + try!(validate_qualified_name(&qualified_name)); let (prefix_from_qname, local_name_from_qname) = get_attribute_parts(&qualified_name); match (&ns, prefix_from_qname, local_name_from_qname) { diff --git a/components/script/dom/domimplementation.rs b/components/script/dom/domimplementation.rs index 7e2a3dba03a..e5cac62f4e7 100644 --- a/components/script/dom/domimplementation.rs +++ b/components/script/dom/domimplementation.rs @@ -8,12 +8,10 @@ use dom::bindings::codegen::Bindings::DOMImplementationBinding::DOMImplementatio use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods; use dom::bindings::codegen::InheritTypes::NodeCast; use dom::bindings::error::Fallible; -use dom::bindings::error::Error::{InvalidCharacter, Namespace}; use dom::bindings::global::GlobalRef; use dom::bindings::js::{JS, JSRef, Root, Temporary, OptionalRootable}; use dom::bindings::utils::{Reflector, reflect_dom_object}; -use dom::bindings::utils::xml_name_type; -use dom::bindings::utils::XMLName::{QName, Name, InvalidXMLName}; +use dom::bindings::utils::validate_qualified_name; use dom::document::{Document, DocumentHelpers, IsHTMLDocument}; use dom::document::DocumentSource; use dom::documenttype::DocumentType; @@ -52,18 +50,11 @@ impl DOMImplementation { // http://dom.spec.whatwg.org/#domimplementation impl<'a> DOMImplementationMethods for JSRef<'a, DOMImplementation> { // http://dom.spec.whatwg.org/#dom-domimplementation-createdocumenttype - fn CreateDocumentType(self, qname: DOMString, pubid: DOMString, sysid: DOMString) -> Fallible> { - match xml_name_type(&qname) { - // Step 1. - InvalidXMLName => Err(InvalidCharacter), - // Step 2. - Name => Err(Namespace), - // Step 3. - QName => { - let document = self.document.root(); - Ok(DocumentType::new(qname, Some(pubid), Some(sysid), document.r())) - } - } + fn CreateDocumentType(self, qualified_name: DOMString, pubid: DOMString, sysid: DOMString) + -> Fallible> { + try!(validate_qualified_name(&qualified_name)); + let document = self.document.root(); + Ok(DocumentType::new(qualified_name, Some(pubid), Some(sysid), document.r())) } // http://dom.spec.whatwg.org/#dom-domimplementation-createdocument diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs index c3bf678a48d..0ebf4d02bea 100644 --- a/components/script/dom/element.rs +++ b/components/script/dom/element.rs @@ -30,8 +30,8 @@ use dom::bindings::error::Error::NoModificationAllowed; use dom::bindings::js::{MutNullableJS, JS, JSRef, LayoutJS, Temporary, TemporaryPushable}; use dom::bindings::js::{OptionalRootable, RootedReference}; use dom::bindings::trace::RootedVec; -use dom::bindings::utils::xml_name_type; -use dom::bindings::utils::XMLName::{QName, Name, InvalidXMLName}; +use dom::bindings::utils::{xml_name_type, validate_qualified_name}; +use dom::bindings::utils::XMLName::InvalidXMLName; use dom::create::create_element; use dom::domrect::DOMRect; use dom::domrectlist::DOMRectList; @@ -1037,14 +1037,8 @@ impl<'a> ElementMethods for JSRef<'a, Element> { // Step 1. let namespace = namespace::from_domstring(namespace_url); - let name_type = xml_name_type(&name); - match name_type { - // Step 2. - InvalidXMLName => return Err(InvalidCharacter), - // Step 3. - Name => return Err(Error::Namespace), - QName => {} - } + // Steps 2-3. + try!(validate_qualified_name(&name)); // Step 4. let (prefix, local_name) = get_attribute_parts(&name); From 7b4f6126c87077318355604c279bc0cfed5041e6 Mon Sep 17 00:00:00 2001 From: Anthony Ramine Date: Wed, 8 Apr 2015 01:29:27 +0200 Subject: [PATCH 13/32] Add script::dom::utils::validate_and_extract() Accidentally fixes bugs about Document::createElementNS() where the implementation of "validate and extract" used to check whether the local name extracted from the qualified name was "xmlns" instead of the qualified name itself. --- components/script/dom/bindings/utils.rs | 53 ++++++++++++++- components/script/dom/document.rs | 41 +++--------- components/script/dom/element.rs | 64 ++----------------- .../DOMImplementation-createDocument.html.ini | 35 ---------- .../nodes/Document-createElementNS.html.ini | 35 ---------- 5 files changed, 67 insertions(+), 161 deletions(-) delete mode 100644 tests/wpt/metadata/dom/nodes/DOMImplementation-createDocument.html.ini delete mode 100644 tests/wpt/metadata/dom/nodes/Document-createElementNS.html.ini diff --git a/components/script/dom/bindings/utils.rs b/components/script/dom/bindings/utils.rs index 97f6eaf9392..c8a4e6a9da7 100644 --- a/components/script/dom/bindings/utils.rs +++ b/components/script/dom/bindings/utils.rs @@ -7,14 +7,17 @@ use dom::bindings::codegen::PrototypeList; use dom::bindings::codegen::PrototypeList::MAX_PROTO_CHAIN_LENGTH; use dom::bindings::conversions::{native_from_reflector_jsmanaged, is_dom_class}; -use dom::bindings::error::{Error, ErrorResult, throw_type_error}; +use dom::bindings::error::{Error, ErrorResult, Fallible, throw_type_error}; use dom::bindings::global::GlobalRef; use dom::bindings::js::{Temporary, Root}; use dom::browsercontext; use dom::window; +use util::namespace; +use util::str::DOMString; use libc; use libc::c_uint; +use std::borrow::ToOwned; use std::boxed; use std::cell::Cell; use std::ffi::CString; @@ -43,6 +46,7 @@ use js::rust::with_compartment; use js::{JSPROP_ENUMERATE, JSPROP_READONLY, JSPROP_PERMANENT}; use js::JSFUN_CONSTRUCTOR; use js; +use string_cache::{Atom, Namespace}; /// Proxy handler for a WindowProxy. pub struct WindowProxyHandler(pub *const libc::c_void); @@ -619,6 +623,53 @@ pub fn validate_qualified_name(qualified_name: &str) -> ErrorResult { } } +/// Validate a namespace and qualified name and extract their parts. +/// See https://dom.spec.whatwg.org/#validate-and-extract for details. +pub fn validate_and_extract(namespace: Option, qualified_name: &str) + -> Fallible<(Namespace, Option, Atom)> { + // Step 1. + let namespace = namespace::from_domstring(namespace); + + // Step 2. + try!(validate_qualified_name(qualified_name)); + + let (prefix, local_name) = if qualified_name.contains(":") { + // Step 5. + let mut parts = qualified_name.splitn(1, ':'); + let prefix = parts.next().unwrap(); + debug_assert!(!prefix.is_empty()); + let local_name = parts.next().unwrap(); + debug_assert!(!local_name.contains(":")); + (Some(prefix), local_name) + } else { + (None, qualified_name) + }; + + match (namespace, prefix) { + (ns!(""), Some(_)) => { + // Step 6. + Err(Error::Namespace) + }, + (ref ns, Some("xml")) if ns != &ns!(XML) => { + // Step 7. + Err(Error::Namespace) + }, + (ref ns, p) if ns != &ns!(XMLNS) && + (qualified_name == "xmlns" || p == Some("xmlns")) => { + // Step 8. + Err(Error::Namespace) + }, + (ns!(XMLNS), p) if qualified_name != "xmlns" && p != Some("xmlns") => { + // Step 9. + Err(Error::Namespace) + }, + (ns, p) => { + // Step 10. + Ok((ns, p.map(|s| s.to_owned()), Atom::from_slice(local_name))) + } + } +} + /// Results of `xml_name_type`. #[derive(PartialEq)] #[allow(missing_docs)] diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index a280507d4ff..3e490fe8ba4 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -21,20 +21,20 @@ use dom::bindings::codegen::InheritTypes::{HTMLFormElementDerived, HTMLImageElem use dom::bindings::codegen::InheritTypes::{HTMLScriptElementDerived}; use dom::bindings::error::{ErrorResult, Fallible}; use dom::bindings::error::Error::{NotSupported, InvalidCharacter, Security}; -use dom::bindings::error::Error::{HierarchyRequest, Namespace}; +use dom::bindings::error::Error::HierarchyRequest; use dom::bindings::global::GlobalRef; use dom::bindings::js::{MutNullableJS, JS, JSRef, LayoutJS, Temporary, TemporaryPushable}; use dom::bindings::js::{OptionalRootable, RootedReference}; use dom::bindings::refcounted::Trusted; use dom::bindings::utils::reflect_dom_object; -use dom::bindings::utils::{xml_name_type, validate_qualified_name}; +use dom::bindings::utils::{xml_name_type, validate_and_extract}; use dom::bindings::utils::XMLName::InvalidXMLName; use dom::comment::Comment; use dom::customevent::CustomEvent; use dom::documentfragment::DocumentFragment; use dom::documenttype::DocumentType; use dom::domimplementation::DOMImplementation; -use dom::element::{Element, ElementCreator, AttributeHandlers, get_attribute_parts}; +use dom::element::{Element, ElementCreator, AttributeHandlers}; use dom::element::{ElementTypeId, ActivationElementHelpers}; use dom::event::{Event, EventBubbles, EventCancelable, EventHelpers}; use dom::eventtarget::{EventTarget, EventTargetTypeId, EventTargetHelpers}; @@ -67,7 +67,7 @@ use net_traits::CookieSource::NonHTTP; use net_traits::ControlMsg::{SetCookiesForUrl, GetCookiesForUrl}; use script_task::Runnable; use script_traits::{MouseButton, UntrustedNodeAddress}; -use util::{opts, namespace}; +use util::opts; use util::str::{DOMString, split_html_space_chars}; use layout_interface::{ReflowGoal, ReflowQueryType}; @@ -975,35 +975,10 @@ impl<'a> DocumentMethods for JSRef<'a, Document> { fn CreateElementNS(self, namespace: Option, qualified_name: DOMString) -> Fallible> { - let ns = namespace::from_domstring(namespace); - try!(validate_qualified_name(&qualified_name)); - - let (prefix_from_qname, local_name_from_qname) = get_attribute_parts(&qualified_name); - match (&ns, prefix_from_qname, local_name_from_qname) { - // throw if prefix is not null and namespace is null - (&ns!(""), Some(_), _) => { - debug!("Namespace can't be null with a non-null prefix"); - return Err(Namespace); - }, - // throw if prefix is "xml" and namespace is not the XML namespace - (_, Some(ref prefix), _) if "xml" == *prefix && ns != ns!(XML) => { - debug!("Namespace must be the xml namespace if the prefix is 'xml'"); - return Err(Namespace); - }, - // throw if namespace is the XMLNS namespace and neither qualifiedName nor prefix is - // "xmlns" - (&ns!(XMLNS), Some(ref prefix), _) if "xmlns" == *prefix => {}, - (&ns!(XMLNS), _, "xmlns") => {}, - (&ns!(XMLNS), _, _) => { - debug!("The prefix or the qualified name must be 'xmlns' if namespace is the XMLNS namespace "); - return Err(Namespace); - }, - _ => {} - } - - let name = QualName::new(ns, Atom::from_slice(local_name_from_qname)); - Ok(Element::create(name, prefix_from_qname.map(|s| s.to_owned()), self, - ElementCreator::ScriptCreated)) + let (namespace, prefix, local_name) = + try!(validate_and_extract(namespace, &qualified_name)); + let name = QualName::new(namespace, local_name); + Ok(Element::create(name, prefix, self, ElementCreator::ScriptCreated)) } // http://dom.spec.whatwg.org/#dom-document-createattribute diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs index 0ebf4d02bea..afdd42f6bf6 100644 --- a/components/script/dom/element.rs +++ b/components/script/dom/element.rs @@ -24,13 +24,12 @@ use dom::bindings::codegen::InheritTypes::{HTMLTableRowElementDerived, HTMLTextA use dom::bindings::codegen::InheritTypes::{HTMLTableSectionElementDerived, NodeCast}; use dom::bindings::codegen::InheritTypes::HTMLAnchorElementCast; use dom::bindings::error::{ErrorResult, Fallible}; -use dom::bindings::error::Error; use dom::bindings::error::Error::{InvalidCharacter, Syntax}; use dom::bindings::error::Error::NoModificationAllowed; use dom::bindings::js::{MutNullableJS, JS, JSRef, LayoutJS, Temporary, TemporaryPushable}; use dom::bindings::js::{OptionalRootable, RootedReference}; use dom::bindings::trace::RootedVec; -use dom::bindings::utils::{xml_name_type, validate_qualified_name}; +use dom::bindings::utils::{xml_name_type, validate_and_extract}; use dom::bindings::utils::XMLName::InvalidXMLName; use dom::create::create_element; use dom::domrect::DOMRect; @@ -1031,52 +1030,14 @@ impl<'a> ElementMethods for JSRef<'a, Element> { // http://dom.spec.whatwg.org/#dom-element-setattributens fn SetAttributeNS(self, - namespace_url: Option, - name: DOMString, + namespace: Option, + qualified_name: DOMString, value: DOMString) -> ErrorResult { - // Step 1. - let namespace = namespace::from_domstring(namespace_url); - - // Steps 2-3. - try!(validate_qualified_name(&name)); - - // Step 4. - let (prefix, local_name) = get_attribute_parts(&name); - - if let Some(ref prefix_str) = prefix { - // Step 5. - if namespace == ns!("") { - return Err(Error::Namespace); - } - - // Step 6. - if "xml" == *prefix_str && namespace != ns!(XML) { - return Err(Error::Namespace); - } - - // Step 7b. - if "xmlns" == *prefix_str && namespace != ns!(XMLNS) { - return Err(Error::Namespace); - } - } - - let name = Atom::from_slice(&name); - let local_name = Atom::from_slice(local_name); - let xmlns = atom!("xmlns"); - - // Step 7a. - if xmlns == name && namespace != ns!(XMLNS) { - return Err(Error::Namespace); - } - - // Step 8. - if namespace == ns!(XMLNS) && xmlns != name && Some("xmlns") != prefix { - return Err(Error::Namespace); - } - - // Step 9. + let (namespace, prefix, local_name) = + try!(validate_and_extract(namespace, &qualified_name)); + let qualified_name = Atom::from_slice(&qualified_name); let value = self.parse_attribute(&namespace, &local_name, value); - self.do_set_attribute(local_name.clone(), value, name, + self.do_set_attribute(local_name.clone(), value, qualified_name, namespace.clone(), prefix.map(|s| s.to_owned()), |attr| { *attr.local_name() == local_name && @@ -1266,17 +1227,6 @@ impl<'a> ElementMethods for JSRef<'a, Element> { } } -pub fn get_attribute_parts<'a>(name: &'a str) -> (Option<&'a str>, &'a str) { - //FIXME: Throw for XML-invalid names - //FIXME: Throw for XMLNS-invalid names - if name.contains(":") { - let mut parts = name.splitn(1, ':'); - (Some(parts.next().unwrap()), parts.next().unwrap()) - } else { - (None, name) - } -} - impl<'a> VirtualMethods for JSRef<'a, Element> { fn super_type<'b>(&'b self) -> Option<&'b VirtualMethods> { let node: &JSRef = NodeCast::from_borrowed_ref(self); diff --git a/tests/wpt/metadata/dom/nodes/DOMImplementation-createDocument.html.ini b/tests/wpt/metadata/dom/nodes/DOMImplementation-createDocument.html.ini deleted file mode 100644 index 8d02570c391..00000000000 --- a/tests/wpt/metadata/dom/nodes/DOMImplementation-createDocument.html.ini +++ /dev/null @@ -1,35 +0,0 @@ -[DOMImplementation-createDocument.html] - type: testharness - [createDocument test 23: null,"xmlns",null,"NAMESPACE_ERR"] - expected: FAIL - - [createDocument test 41: undefined,"xmlns",null,"NAMESPACE_ERR"] - expected: FAIL - - [createDocument test 64: "http://example.com/","xmlns",null,"NAMESPACE_ERR"] - expected: FAIL - - [createDocument test 69: "http://example.com/","xmlns:foo",null,"NAMESPACE_ERR"] - expected: FAIL - - [createDocument test 108: "/","xmlns",null,"NAMESPACE_ERR"] - expected: FAIL - - [createDocument test 111: "/","xmlns:foo",null,"NAMESPACE_ERR"] - expected: FAIL - - [createDocument test 121: "http://www.w3.org/XML/1998/namespace","xmlns",null,"NAMESPACE_ERR"] - expected: FAIL - - [createDocument test 124: "http://www.w3.org/XML/1998/namespace","xmlns:foo",null,"NAMESPACE_ERR"] - expected: FAIL - - [createDocument test 141: "http://www.w3.org/2000/xmlns/","foo:xmlns",null,"NAMESPACE_ERR"] - expected: FAIL - - [createDocument test 150: "foo:","xmlns",null,"NAMESPACE_ERR"] - expected: FAIL - - [createDocument test 153: "foo:","xmlns:foo",null,"NAMESPACE_ERR"] - expected: FAIL - diff --git a/tests/wpt/metadata/dom/nodes/Document-createElementNS.html.ini b/tests/wpt/metadata/dom/nodes/Document-createElementNS.html.ini deleted file mode 100644 index 69ab187bf8d..00000000000 --- a/tests/wpt/metadata/dom/nodes/Document-createElementNS.html.ini +++ /dev/null @@ -1,35 +0,0 @@ -[Document-createElementNS.html] - type: testharness - [createElementNS test 23: null,"xmlns","NAMESPACE_ERR"] - expected: FAIL - - [createElementNS test 41: undefined,"xmlns","NAMESPACE_ERR"] - expected: FAIL - - [createElementNS test 64: "http://example.com/","xmlns","NAMESPACE_ERR"] - expected: FAIL - - [createElementNS test 69: "http://example.com/","xmlns:foo","NAMESPACE_ERR"] - expected: FAIL - - [createElementNS test 108: "/","xmlns","NAMESPACE_ERR"] - expected: FAIL - - [createElementNS test 111: "/","xmlns:foo","NAMESPACE_ERR"] - expected: FAIL - - [createElementNS test 121: "http://www.w3.org/XML/1998/namespace","xmlns","NAMESPACE_ERR"] - expected: FAIL - - [createElementNS test 124: "http://www.w3.org/XML/1998/namespace","xmlns:foo","NAMESPACE_ERR"] - expected: FAIL - - [createElementNS test 141: "http://www.w3.org/2000/xmlns/","foo:xmlns","NAMESPACE_ERR"] - expected: FAIL - - [createElementNS test 150: "foo:","xmlns","NAMESPACE_ERR"] - expected: FAIL - - [createElementNS test 153: "foo:","xmlns:foo","NAMESPACE_ERR"] - expected: FAIL - From 2353bc4798f074f6b4d249c98c39cc74822a5f2a Mon Sep 17 00:00:00 2001 From: Anthony Ramine Date: Wed, 8 Apr 2015 02:00:18 +0200 Subject: [PATCH 14/32] Implement Document::CreateAttributeNS() --- components/script/dom/document.rs | 12 ++++++++++++ components/script/dom/webidls/Document.webidl | 8 +++++--- tests/wpt/metadata/dom/interfaces.html.ini | 9 --------- tests/wpt/metadata/html/dom/interfaces.html.ini | 6 ------ 4 files changed, 17 insertions(+), 18 deletions(-) diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index 3e490fe8ba4..eccaeabde5e 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -997,6 +997,18 @@ impl<'a> DocumentMethods for JSRef<'a, Document> { Ok(Attr::new(window.r(), name, value, l_name, ns!(""), None, None)) } + // http://dom.spec.whatwg.org/#dom-document-createattributens + fn CreateAttributeNS(self, namespace: Option, qualified_name: DOMString) + -> Fallible> { + let (namespace, prefix, local_name) = + try!(validate_and_extract(namespace, &qualified_name)); + let window = self.window.root(); + let value = AttrValue::String("".to_owned()); + let qualified_name = Atom::from_slice(&qualified_name); + Ok(Attr::new(window.r(), local_name, value, qualified_name, + namespace, prefix, None)) + } + // http://dom.spec.whatwg.org/#dom-document-createdocumentfragment fn CreateDocumentFragment(self) -> Temporary { DocumentFragment::new(self) diff --git a/components/script/dom/webidls/Document.webidl b/components/script/dom/webidls/Document.webidl index dea9b9c38f6..c57b09fff1b 100644 --- a/components/script/dom/webidls/Document.webidl +++ b/components/script/dom/webidls/Document.webidl @@ -40,14 +40,16 @@ interface Document : Node { [NewObject, Throws] ProcessingInstruction createProcessingInstruction(DOMString target, DOMString data); - [NewObject, Throws] - Attr createAttribute(DOMString localName); - [NewObject, Throws] Node importNode(Node node, optional boolean deep = false); [Throws] Node adoptNode(Node node); + [NewObject, Throws] + Attr createAttribute(DOMString localName); + [NewObject, Throws] + Attr createAttributeNS(DOMString? namespace, DOMString localName); + [NewObject, Throws] Event createEvent(DOMString interface_); diff --git a/tests/wpt/metadata/dom/interfaces.html.ini b/tests/wpt/metadata/dom/interfaces.html.ini index c3913ed9046..2c1b57f5f92 100644 --- a/tests/wpt/metadata/dom/interfaces.html.ini +++ b/tests/wpt/metadata/dom/interfaces.html.ini @@ -90,9 +90,6 @@ [Document interface: operation importNode(Node,boolean)] expected: FAIL - [Document interface: operation createAttributeNS(DOMString,DOMString)] - expected: FAIL - [Document interface: operation createNodeIterator(Node,unsigned long,NodeFilter)] expected: FAIL @@ -141,9 +138,6 @@ [Document interface: xmlDoc must inherit property "origin" with the proper type (3)] expected: FAIL - [Document interface: calling createAttributeNS(DOMString,DOMString) on xmlDoc with too few arguments must throw TypeError] - expected: FAIL - [Document interface: calling createNodeIterator(Node,unsigned long,NodeFilter) on xmlDoc with too few arguments must throw TypeError] expected: FAIL @@ -1020,9 +1014,6 @@ [DOMTokenList interface object length] expected: FAIL - [Document interface: xmlDoc must inherit property "createAttributeNS" with the proper type (22)] - expected: FAIL - [Document interface: xmlDoc must inherit property "createNodeIterator" with the proper type (25)] expected: FAIL diff --git a/tests/wpt/metadata/html/dom/interfaces.html.ini b/tests/wpt/metadata/html/dom/interfaces.html.ini index 6bb217f0051..f91c0e52aeb 100644 --- a/tests/wpt/metadata/html/dom/interfaces.html.ini +++ b/tests/wpt/metadata/html/dom/interfaces.html.ini @@ -1035,12 +1035,6 @@ [Document interface: document.implementation.createDocument(null, "", null) must inherit property "origin" with the proper type (3)] expected: FAIL - [Document interface: document.implementation.createDocument(null, "", null) must inherit property "createAttributeNS" with the proper type (22)] - expected: FAIL - - [Document interface: calling createAttributeNS(DOMString,DOMString) on document.implementation.createDocument(null, "", null) with too few arguments must throw TypeError] - expected: FAIL - [Document interface: document.implementation.createDocument(null, "", null) must inherit property "createNodeIterator" with the proper type (25)] expected: FAIL From c842e499b37eff29b8cba3744ad561ddaa81b88c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1ty=C3=A1s=20Mustoha?= Date: Wed, 8 Apr 2015 12:11:36 +0200 Subject: [PATCH 15/32] Fix int-related warnings in net/mime_classifier. --- components/net/mime_classifier.rs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/components/net/mime_classifier.rs b/components/net/mime_classifier.rs index 773436d92d9..6501abdb6bb 100644 --- a/components/net/mime_classifier.rs +++ b/components/net/mime_classifier.rs @@ -111,7 +111,7 @@ impl MIMEClassifier { self.binary_or_plaintext.classify(data) } fn is_xml(tp: &str, sub_tp: &str) -> bool { - let suffix = &sub_tp[(max((sub_tp.len() as int) - ("+xml".len() as int), 0i) as uint)..]; + let suffix = &sub_tp[(max(sub_tp.len() as isize - "+xml".len() as isize, 0) as usize)..]; match (tp, sub_tp, suffix) { (_, _, "+xml") | ("application", "xml",_) | ("text", "xml",_) => {true} _ => {false} @@ -170,13 +170,13 @@ struct ByteMatcher { } impl ByteMatcher { - fn matches(&self, data: &Vec) -> Option { + fn matches(&self, data: &Vec) -> Option { if data.len() < self.pattern.len() { return None; } //TODO replace with iterators if I ever figure them out... - let mut i = 0u; + let mut i: usize = 0; let max_i = data.len()-self.pattern.len(); loop { @@ -184,12 +184,12 @@ impl ByteMatcher { break; } - i=i + 1; + i = i + 1; if i > max_i { return None; } } - for j in range(0u,self.pattern.len()) { + for j in 0..self.pattern.len() { if (data[i] & self.mask[j]) != (self.pattern[j] & self.mask[j]) { return None; } @@ -231,7 +231,7 @@ impl Mp4Matcher { return false; } let box_size = ((data[0] as u32) << 3 | (data[1] as u32) << 2 | - (data[2] as u32) << 1 | (data[3] as u32)) as uint; + (data[2] as u32) << 1 | (data[3] as u32)) as usize; if (data.len() < box_size) || (box_size % 4 != 0) { return false; } @@ -239,14 +239,14 @@ impl Mp4Matcher { let ftyp = [0x66, 0x74, 0x79, 0x70]; let mp4 = [0x6D, 0x70, 0x34]; - for i in range(4u,8u) { + for i in 4..8 { if data[i] != ftyp[i - 4] { return false; } } let mut all_match = true; - for i in range(8u,11u) { - if data[i]!=mp4[i - 8u] { + for i in 8..11 { + if data[i]!=mp4[i - 8] { all_match = false; break; } @@ -255,11 +255,11 @@ impl Mp4Matcher { return true; } - let mut bytes_read = 16u; + let mut bytes_read: usize = 16; while bytes_read < box_size { all_match = true; - for i in range(0u,3u) { + for i in 0..3 { if mp4[i] != data[i + bytes_read] { all_match = false; break; From 4e5ab246545b474c5fc6dc705826e1c97bd05bd0 Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Sat, 14 Feb 2015 17:17:44 +0800 Subject: [PATCH 16/32] Implement TextDecoder (fixes #4769). --- components/script/dom/mod.rs | 1 + components/script/dom/textdecoder.rs | 65 ++ .../script/dom/webidls/TextDecoder.webidl | 19 + .../wpt/metadata/encoding/api-basics.html.ini | 3 - .../wpt/metadata/encoding/idlharness.html.ini | 27 - .../encoding/textdecoder-fatal.html.ini | 107 ---- .../encoding/textdecoder-labels.html.ini | 600 ------------------ .../textdecoder-utf16-surrogates.html.ini | 15 - .../textencoder-constructor-non-utf.html.ini | 6 - 9 files changed, 85 insertions(+), 758 deletions(-) create mode 100644 components/script/dom/textdecoder.rs create mode 100644 components/script/dom/webidls/TextDecoder.webidl delete mode 100644 tests/wpt/metadata/encoding/textdecoder-fatal.html.ini diff --git a/components/script/dom/mod.rs b/components/script/dom/mod.rs index d1dd72a30b6..6c7204f7765 100644 --- a/components/script/dom/mod.rs +++ b/components/script/dom/mod.rs @@ -315,6 +315,7 @@ pub mod servohtmlparser; pub mod storage; pub mod storageevent; pub mod text; +pub mod textdecoder; pub mod textencoder; pub mod treewalker; pub mod uievent; diff --git a/components/script/dom/textdecoder.rs b/components/script/dom/textdecoder.rs new file mode 100644 index 00000000000..7aa6b4f92bc --- /dev/null +++ b/components/script/dom/textdecoder.rs @@ -0,0 +1,65 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * 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::TextDecoderBinding; +use dom::bindings::codegen::Bindings::TextDecoderBinding::TextDecoderMethods; +use dom::bindings::error::{Error, Fallible}; +use dom::bindings::global::GlobalRef; +use dom::bindings::js::{JSRef, Temporary}; +use dom::bindings::trace::JSTraceable; +use dom::bindings::utils::{Reflector, reflect_dom_object}; + +use util::str::DOMString; + +use encoding::Encoding; +use encoding::types::EncodingRef; +use encoding::label::encoding_from_whatwg_label; + +use std::borrow::ToOwned; + +#[dom_struct] +pub struct TextDecoder { + reflector_: Reflector, + encoding: EncodingRef, + fatal: bool, +} + +impl TextDecoder { + fn new_inherited(encoding: EncodingRef, fatal: bool) -> TextDecoder { + TextDecoder { + reflector_: Reflector::new(), + encoding: encoding, + fatal: fatal, + } + } + + pub fn new(global: GlobalRef, encoding: EncodingRef, fatal: bool) -> Temporary { + reflect_dom_object(box TextDecoder::new_inherited(encoding, fatal), + global, + TextDecoderBinding::Wrap) + } + + /// https://encoding.spec.whatwg.org/#dom-textdecoder + pub fn Constructor(global: GlobalRef, + label: DOMString, + options: &TextDecoderBinding::TextDecoderOptions) + -> Fallible> { + let encoding = match encoding_from_whatwg_label(&label) { + Some(enc) => enc, + // FIXME: Should throw a RangeError as per spec + None => return Err(Error::Syntax), + }; + Ok(TextDecoder::new(global, encoding, options.fatal)) + } +} + +impl<'a> TextDecoderMethods for JSRef<'a, TextDecoder> { + fn Encoding(self) -> DOMString { + self.encoding.whatwg_name().unwrap().to_owned() + } + + fn Fatal(self) -> bool { + self.fatal + } +} diff --git a/components/script/dom/webidls/TextDecoder.webidl b/components/script/dom/webidls/TextDecoder.webidl new file mode 100644 index 00000000000..6450c2ef0b6 --- /dev/null +++ b/components/script/dom/webidls/TextDecoder.webidl @@ -0,0 +1,19 @@ +/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * 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/. */ + +// https://encoding.spec.whatwg.org/#interface-textdecoder +dictionary TextDecoderOptions { + boolean fatal = false; + //boolean ignoreBOM = false; +}; + +[Constructor(optional DOMString label = "utf-8", optional TextDecoderOptions options)/*, + Exposed=Window,Worker*/] +interface TextDecoder { + readonly attribute DOMString encoding; + readonly attribute boolean fatal; + //readonly attribute boolean ignoreBOM; + //USVString decode(optional BufferSource input, optional TextDecodeOptions options); +}; diff --git a/tests/wpt/metadata/encoding/api-basics.html.ini b/tests/wpt/metadata/encoding/api-basics.html.ini index 03947330276..0045f9e5794 100644 --- a/tests/wpt/metadata/encoding/api-basics.html.ini +++ b/tests/wpt/metadata/encoding/api-basics.html.ini @@ -1,8 +1,5 @@ [api-basics.html] type: testharness - [Default encodings] - expected: FAIL - [Default inputs] expected: FAIL diff --git a/tests/wpt/metadata/encoding/idlharness.html.ini b/tests/wpt/metadata/encoding/idlharness.html.ini index 2c00e08d92e..f7c414c2d63 100644 --- a/tests/wpt/metadata/encoding/idlharness.html.ini +++ b/tests/wpt/metadata/encoding/idlharness.html.ini @@ -1,41 +1,14 @@ [idlharness.html] type: testharness - [TextDecoder interface: existence and properties of interface object] - expected: FAIL - [TextDecoder interface object length] expected: FAIL - [TextDecoder interface: existence and properties of interface prototype object] - expected: FAIL - - [TextDecoder interface: existence and properties of interface prototype object\'s "constructor" property] - expected: FAIL - - [TextDecoder interface: attribute encoding] - expected: FAIL - - [TextDecoder interface: attribute fatal] - expected: FAIL - [TextDecoder interface: attribute ignoreBOM] expected: FAIL [TextDecoder interface: operation decode(BufferSource,TextDecodeOptions)] expected: FAIL - [TextDecoder must be primary interface of new TextDecoder()] - expected: FAIL - - [Stringification of new TextDecoder()] - expected: FAIL - - [TextDecoder interface: new TextDecoder() must inherit property "encoding" with the proper type (0)] - expected: FAIL - - [TextDecoder interface: new TextDecoder() must inherit property "fatal" with the proper type (1)] - expected: FAIL - [TextDecoder interface: new TextDecoder() must inherit property "ignoreBOM" with the proper type (2)] expected: FAIL diff --git a/tests/wpt/metadata/encoding/textdecoder-fatal.html.ini b/tests/wpt/metadata/encoding/textdecoder-fatal.html.ini deleted file mode 100644 index 3f7dc6f45f6..00000000000 --- a/tests/wpt/metadata/encoding/textdecoder-fatal.html.ini +++ /dev/null @@ -1,107 +0,0 @@ -[textdecoder-fatal.html] - type: testharness - [Fatal flag: utf-8 - invalid code] - expected: FAIL - - [Fatal flag: utf-8 - ends early] - expected: FAIL - - [Fatal flag: utf-8 - ends early 2] - expected: FAIL - - [Fatal flag: utf-8 - invalid trail] - expected: FAIL - - [Fatal flag: utf-8 - invalid trail 2] - expected: FAIL - - [Fatal flag: utf-8 - invalid trail 3] - expected: FAIL - - [Fatal flag: utf-8 - invalid trail 4] - expected: FAIL - - [Fatal flag: utf-8 - invalid trail 5] - expected: FAIL - - [Fatal flag: utf-8 - invalid trail 6] - expected: FAIL - - [Fatal flag: utf-8 - > 0x10FFFF] - expected: FAIL - - [Fatal flag: utf-8 - obsolete lead byte] - expected: FAIL - - [Fatal flag: utf-8 - overlong U+0000 - 2 bytes] - expected: FAIL - - [Fatal flag: utf-8 - overlong U+0000 - 3 bytes] - expected: FAIL - - [Fatal flag: utf-8 - overlong U+0000 - 4 bytes] - expected: FAIL - - [Fatal flag: utf-8 - overlong U+0000 - 5 bytes] - expected: FAIL - - [Fatal flag: utf-8 - overlong U+0000 - 6 bytes] - expected: FAIL - - [Fatal flag: utf-8 - overlong U+007F - 2 bytes] - expected: FAIL - - [Fatal flag: utf-8 - overlong U+007F - 3 bytes] - expected: FAIL - - [Fatal flag: utf-8 - overlong U+007F - 4 bytes] - expected: FAIL - - [Fatal flag: utf-8 - overlong U+007F - 5 bytes] - expected: FAIL - - [Fatal flag: utf-8 - overlong U+007F - 6 bytes] - expected: FAIL - - [Fatal flag: utf-8 - overlong U+07FF - 3 bytes] - expected: FAIL - - [Fatal flag: utf-8 - overlong U+07FF - 4 bytes] - expected: FAIL - - [Fatal flag: utf-8 - overlong U+07FF - 5 bytes] - expected: FAIL - - [Fatal flag: utf-8 - overlong U+07FF - 6 bytes] - expected: FAIL - - [Fatal flag: utf-8 - overlong U+FFFF - 4 bytes] - expected: FAIL - - [Fatal flag: utf-8 - overlong U+FFFF - 5 bytes] - expected: FAIL - - [Fatal flag: utf-8 - overlong U+FFFF - 6 bytes] - expected: FAIL - - [Fatal flag: utf-8 - overlong U+10FFFF - 5 bytes] - expected: FAIL - - [Fatal flag: utf-8 - overlong U+10FFFF - 6 bytes] - expected: FAIL - - [Fatal flag: utf-8 - lead surrogate] - expected: FAIL - - [Fatal flag: utf-8 - trail surrogate] - expected: FAIL - - [Fatal flag: utf-8 - surrogate pair] - expected: FAIL - - [Fatal flag: utf-16le - truncated code unit] - expected: FAIL - - [The fatal attribute of TextDecoder] - expected: FAIL - diff --git a/tests/wpt/metadata/encoding/textdecoder-labels.html.ini b/tests/wpt/metadata/encoding/textdecoder-labels.html.ini index 61506476753..4f3fd8edbbf 100644 --- a/tests/wpt/metadata/encoding/textdecoder-labels.html.ini +++ b/tests/wpt/metadata/encoding/textdecoder-labels.html.ini @@ -1,515 +1,5 @@ [textdecoder-labels.html] type: testharness - [name=utf-8 label=unicode-1-1-utf-8] - expected: FAIL - - [name=utf-8 label=utf-8] - expected: FAIL - - [name=utf-8 label=utf8] - expected: FAIL - - [name=ibm866 label=866] - expected: FAIL - - [name=ibm866 label=cp866] - expected: FAIL - - [name=ibm866 label=csibm866] - expected: FAIL - - [name=ibm866 label=ibm866] - expected: FAIL - - [name=iso-8859-2 label=csisolatin2] - expected: FAIL - - [name=iso-8859-2 label=iso-8859-2] - expected: FAIL - - [name=iso-8859-2 label=iso-ir-101] - expected: FAIL - - [name=iso-8859-2 label=iso8859-2] - expected: FAIL - - [name=iso-8859-2 label=iso88592] - expected: FAIL - - [name=iso-8859-2 label=iso_8859-2] - expected: FAIL - - [name=iso-8859-2 label=iso_8859-2:1987] - expected: FAIL - - [name=iso-8859-2 label=l2] - expected: FAIL - - [name=iso-8859-2 label=latin2] - expected: FAIL - - [name=iso-8859-3 label=csisolatin3] - expected: FAIL - - [name=iso-8859-3 label=iso-8859-3] - expected: FAIL - - [name=iso-8859-3 label=iso-ir-109] - expected: FAIL - - [name=iso-8859-3 label=iso8859-3] - expected: FAIL - - [name=iso-8859-3 label=iso88593] - expected: FAIL - - [name=iso-8859-3 label=iso_8859-3] - expected: FAIL - - [name=iso-8859-3 label=iso_8859-3:1988] - expected: FAIL - - [name=iso-8859-3 label=l3] - expected: FAIL - - [name=iso-8859-3 label=latin3] - expected: FAIL - - [name=iso-8859-4 label=csisolatin4] - expected: FAIL - - [name=iso-8859-4 label=iso-8859-4] - expected: FAIL - - [name=iso-8859-4 label=iso-ir-110] - expected: FAIL - - [name=iso-8859-4 label=iso8859-4] - expected: FAIL - - [name=iso-8859-4 label=iso88594] - expected: FAIL - - [name=iso-8859-4 label=iso_8859-4] - expected: FAIL - - [name=iso-8859-4 label=iso_8859-4:1988] - expected: FAIL - - [name=iso-8859-4 label=l4] - expected: FAIL - - [name=iso-8859-4 label=latin4] - expected: FAIL - - [name=iso-8859-5 label=csisolatincyrillic] - expected: FAIL - - [name=iso-8859-5 label=cyrillic] - expected: FAIL - - [name=iso-8859-5 label=iso-8859-5] - expected: FAIL - - [name=iso-8859-5 label=iso-ir-144] - expected: FAIL - - [name=iso-8859-5 label=iso8859-5] - expected: FAIL - - [name=iso-8859-5 label=iso88595] - expected: FAIL - - [name=iso-8859-5 label=iso_8859-5] - expected: FAIL - - [name=iso-8859-5 label=iso_8859-5:1988] - expected: FAIL - - [name=iso-8859-6 label=arabic] - expected: FAIL - - [name=iso-8859-6 label=asmo-708] - expected: FAIL - - [name=iso-8859-6 label=csiso88596e] - expected: FAIL - - [name=iso-8859-6 label=csiso88596i] - expected: FAIL - - [name=iso-8859-6 label=csisolatinarabic] - expected: FAIL - - [name=iso-8859-6 label=ecma-114] - expected: FAIL - - [name=iso-8859-6 label=iso-8859-6] - expected: FAIL - - [name=iso-8859-6 label=iso-8859-6-e] - expected: FAIL - - [name=iso-8859-6 label=iso-8859-6-i] - expected: FAIL - - [name=iso-8859-6 label=iso-ir-127] - expected: FAIL - - [name=iso-8859-6 label=iso8859-6] - expected: FAIL - - [name=iso-8859-6 label=iso88596] - expected: FAIL - - [name=iso-8859-6 label=iso_8859-6] - expected: FAIL - - [name=iso-8859-6 label=iso_8859-6:1987] - expected: FAIL - - [name=iso-8859-7 label=csisolatingreek] - expected: FAIL - - [name=iso-8859-7 label=ecma-118] - expected: FAIL - - [name=iso-8859-7 label=elot_928] - expected: FAIL - - [name=iso-8859-7 label=greek] - expected: FAIL - - [name=iso-8859-7 label=greek8] - expected: FAIL - - [name=iso-8859-7 label=iso-8859-7] - expected: FAIL - - [name=iso-8859-7 label=iso-ir-126] - expected: FAIL - - [name=iso-8859-7 label=iso8859-7] - expected: FAIL - - [name=iso-8859-7 label=iso88597] - expected: FAIL - - [name=iso-8859-7 label=iso_8859-7] - expected: FAIL - - [name=iso-8859-7 label=iso_8859-7:1987] - expected: FAIL - - [name=iso-8859-7 label=sun_eu_greek] - expected: FAIL - - [name=iso-8859-8 label=csiso88598e] - expected: FAIL - - [name=iso-8859-8 label=csisolatinhebrew] - expected: FAIL - - [name=iso-8859-8 label=hebrew] - expected: FAIL - - [name=iso-8859-8 label=iso-8859-8] - expected: FAIL - - [name=iso-8859-8 label=iso-8859-8-e] - expected: FAIL - - [name=iso-8859-8 label=iso-ir-138] - expected: FAIL - - [name=iso-8859-8 label=iso8859-8] - expected: FAIL - - [name=iso-8859-8 label=iso88598] - expected: FAIL - - [name=iso-8859-8 label=iso_8859-8] - expected: FAIL - - [name=iso-8859-8 label=iso_8859-8:1988] - expected: FAIL - - [name=iso-8859-8 label=visual] - expected: FAIL - - [name=iso-8859-8-i label=csiso88598i] - expected: FAIL - - [name=iso-8859-8-i label=iso-8859-8-i] - expected: FAIL - - [name=iso-8859-8-i label=logical] - expected: FAIL - - [name=iso-8859-10 label=csisolatin6] - expected: FAIL - - [name=iso-8859-10 label=iso-8859-10] - expected: FAIL - - [name=iso-8859-10 label=iso-ir-157] - expected: FAIL - - [name=iso-8859-10 label=iso8859-10] - expected: FAIL - - [name=iso-8859-10 label=iso885910] - expected: FAIL - - [name=iso-8859-10 label=l6] - expected: FAIL - - [name=iso-8859-10 label=latin6] - expected: FAIL - - [name=iso-8859-13 label=iso-8859-13] - expected: FAIL - - [name=iso-8859-13 label=iso8859-13] - expected: FAIL - - [name=iso-8859-13 label=iso885913] - expected: FAIL - - [name=iso-8859-14 label=iso-8859-14] - expected: FAIL - - [name=iso-8859-14 label=iso8859-14] - expected: FAIL - - [name=iso-8859-14 label=iso885914] - expected: FAIL - - [name=iso-8859-15 label=csisolatin9] - expected: FAIL - - [name=iso-8859-15 label=iso-8859-15] - expected: FAIL - - [name=iso-8859-15 label=iso8859-15] - expected: FAIL - - [name=iso-8859-15 label=iso885915] - expected: FAIL - - [name=iso-8859-15 label=iso_8859-15] - expected: FAIL - - [name=iso-8859-15 label=l9] - expected: FAIL - - [name=iso-8859-16 label=iso-8859-16] - expected: FAIL - - [name=koi8-r label=cskoi8r] - expected: FAIL - - [name=koi8-r label=koi] - expected: FAIL - - [name=koi8-r label=koi8] - expected: FAIL - - [name=koi8-r label=koi8-r] - expected: FAIL - - [name=koi8-r label=koi8_r] - expected: FAIL - - [name=koi8-u label=koi8-u] - expected: FAIL - - [name=macintosh label=csmacintosh] - expected: FAIL - - [name=macintosh label=mac] - expected: FAIL - - [name=macintosh label=macintosh] - expected: FAIL - - [name=macintosh label=x-mac-roman] - expected: FAIL - - [name=windows-874 label=dos-874] - expected: FAIL - - [name=windows-874 label=iso-8859-11] - expected: FAIL - - [name=windows-874 label=iso8859-11] - expected: FAIL - - [name=windows-874 label=iso885911] - expected: FAIL - - [name=windows-874 label=tis-620] - expected: FAIL - - [name=windows-874 label=windows-874] - expected: FAIL - - [name=windows-1250 label=cp1250] - expected: FAIL - - [name=windows-1250 label=windows-1250] - expected: FAIL - - [name=windows-1250 label=x-cp1250] - expected: FAIL - - [name=windows-1251 label=cp1251] - expected: FAIL - - [name=windows-1251 label=windows-1251] - expected: FAIL - - [name=windows-1251 label=x-cp1251] - expected: FAIL - - [name=windows-1252 label=ansi_x3.4-1968] - expected: FAIL - - [name=windows-1252 label=ascii] - expected: FAIL - - [name=windows-1252 label=cp1252] - expected: FAIL - - [name=windows-1252 label=cp819] - expected: FAIL - - [name=windows-1252 label=csisolatin1] - expected: FAIL - - [name=windows-1252 label=ibm819] - expected: FAIL - - [name=windows-1252 label=iso-8859-1] - expected: FAIL - - [name=windows-1252 label=iso-ir-100] - expected: FAIL - - [name=windows-1252 label=iso8859-1] - expected: FAIL - - [name=windows-1252 label=iso88591] - expected: FAIL - - [name=windows-1252 label=iso_8859-1] - expected: FAIL - - [name=windows-1252 label=iso_8859-1:1987] - expected: FAIL - - [name=windows-1252 label=l1] - expected: FAIL - - [name=windows-1252 label=latin1] - expected: FAIL - - [name=windows-1252 label=us-ascii] - expected: FAIL - - [name=windows-1252 label=windows-1252] - expected: FAIL - - [name=windows-1252 label=x-cp1252] - expected: FAIL - - [name=windows-1253 label=cp1253] - expected: FAIL - - [name=windows-1253 label=windows-1253] - expected: FAIL - - [name=windows-1253 label=x-cp1253] - expected: FAIL - - [name=windows-1254 label=cp1254] - expected: FAIL - - [name=windows-1254 label=csisolatin5] - expected: FAIL - - [name=windows-1254 label=iso-8859-9] - expected: FAIL - - [name=windows-1254 label=iso-ir-148] - expected: FAIL - - [name=windows-1254 label=iso8859-9] - expected: FAIL - - [name=windows-1254 label=iso88599] - expected: FAIL - - [name=windows-1254 label=iso_8859-9] - expected: FAIL - - [name=windows-1254 label=iso_8859-9:1989] - expected: FAIL - - [name=windows-1254 label=l5] - expected: FAIL - - [name=windows-1254 label=latin5] - expected: FAIL - - [name=windows-1254 label=windows-1254] - expected: FAIL - - [name=windows-1254 label=x-cp1254] - expected: FAIL - - [name=windows-1255 label=cp1255] - expected: FAIL - - [name=windows-1255 label=windows-1255] - expected: FAIL - - [name=windows-1255 label=x-cp1255] - expected: FAIL - - [name=windows-1256 label=cp1256] - expected: FAIL - - [name=windows-1256 label=windows-1256] - expected: FAIL - - [name=windows-1256 label=x-cp1256] - expected: FAIL - - [name=windows-1257 label=cp1257] - expected: FAIL - - [name=windows-1257 label=windows-1257] - expected: FAIL - - [name=windows-1257 label=x-cp1257] - expected: FAIL - - [name=windows-1258 label=cp1258] - expected: FAIL - - [name=windows-1258 label=windows-1258] - expected: FAIL - - [name=windows-1258 label=x-cp1258] - expected: FAIL - - [name=x-mac-cyrillic label=x-mac-cyrillic] - expected: FAIL - - [name=x-mac-cyrillic label=x-mac-ukrainian] - expected: FAIL - [name=gbk label=chinese] expected: FAIL @@ -537,99 +27,9 @@ [name=gbk label=x-gbk] expected: FAIL - [name=gb18030 label=gb18030] - expected: FAIL - - [name=big5 label=big5] - expected: FAIL - - [name=big5 label=big5-hkscs] - expected: FAIL - - [name=big5 label=cn-big5] - expected: FAIL - - [name=big5 label=csbig5] - expected: FAIL - - [name=big5 label=x-x-big5] - expected: FAIL - - [name=euc-jp label=cseucpkdfmtjapanese] - expected: FAIL - - [name=euc-jp label=euc-jp] - expected: FAIL - - [name=euc-jp label=x-euc-jp] - expected: FAIL - - [name=iso-2022-jp label=csiso2022jp] - expected: FAIL - - [name=iso-2022-jp label=iso-2022-jp] - expected: FAIL - - [name=shift_jis label=csshiftjis] - expected: FAIL - - [name=shift_jis label=ms_kanji] - expected: FAIL - - [name=shift_jis label=shift-jis] - expected: FAIL - - [name=shift_jis label=shift_jis] - expected: FAIL - - [name=shift_jis label=sjis] - expected: FAIL - - [name=shift_jis label=windows-31j] - expected: FAIL - - [name=shift_jis label=x-sjis] - expected: FAIL - - [name=euc-kr label=cseuckr] - expected: FAIL - - [name=euc-kr label=csksc56011987] - expected: FAIL - - [name=euc-kr label=euc-kr] - expected: FAIL - - [name=euc-kr label=iso-ir-149] - expected: FAIL - - [name=euc-kr label=korean] - expected: FAIL - - [name=euc-kr label=ks_c_5601-1987] - expected: FAIL - - [name=euc-kr label=ks_c_5601-1989] - expected: FAIL - - [name=euc-kr label=ksc5601] - expected: FAIL - - [name=euc-kr label=ksc_5601] - expected: FAIL - - [name=euc-kr label=windows-949] - expected: FAIL - - [name=utf-16be label=utf-16be] - expected: FAIL - [name=utf-16le label=utf-16] expected: FAIL [name=utf-16le label=utf-16le] expected: FAIL - [name=x-user-defined label=x-user-defined] - expected: FAIL - diff --git a/tests/wpt/metadata/encoding/textdecoder-utf16-surrogates.html.ini b/tests/wpt/metadata/encoding/textdecoder-utf16-surrogates.html.ini index 0630043c8c7..9c32d21843c 100644 --- a/tests/wpt/metadata/encoding/textdecoder-utf16-surrogates.html.ini +++ b/tests/wpt/metadata/encoding/textdecoder-utf16-surrogates.html.ini @@ -3,30 +3,15 @@ [utf-16le - lone surrogate lead] expected: FAIL - [utf-16le - lone surrogate lead (fatal flag set)] - expected: FAIL - [utf-16le - lone surrogate trail] expected: FAIL - [utf-16le - lone surrogate trail (fatal flag set)] - expected: FAIL - [utf-16le - unmatched surrogate lead] expected: FAIL - [utf-16le - unmatched surrogate lead (fatal flag set)] - expected: FAIL - [utf-16le - unmatched surrogate trail] expected: FAIL - [utf-16le - unmatched surrogate trail (fatal flag set)] - expected: FAIL - [utf-16le - swapped surrogate pair] expected: FAIL - [utf-16le - swapped surrogate pair (fatal flag set)] - expected: FAIL - diff --git a/tests/wpt/metadata/encoding/textencoder-constructor-non-utf.html.ini b/tests/wpt/metadata/encoding/textencoder-constructor-non-utf.html.ini index 1af7e7f02b9..4d846b4443c 100644 --- a/tests/wpt/metadata/encoding/textencoder-constructor-non-utf.html.ini +++ b/tests/wpt/metadata/encoding/textencoder-constructor-non-utf.html.ini @@ -1,8 +1,5 @@ [textencoder-constructor-non-utf.html] type: testharness - [UTF encodings are supported for encode and decode: utf-8] - expected: FAIL - [Non-UTF encodings supported only for decode, not encode: ibm866] expected: FAIL @@ -108,9 +105,6 @@ [Non-UTF encodings supported only for decode, not encode: euc-kr] expected: FAIL - [UTF encodings are supported for encode and decode: utf-16be] - expected: FAIL - [UTF encodings are supported for encode and decode: utf-16le] expected: FAIL From c0d4e27d794636541dff7db91a1f3d2397602d1e Mon Sep 17 00:00:00 2001 From: Ms2ger Date: Wed, 8 Apr 2015 16:50:51 +0200 Subject: [PATCH 17/32] Update js. --- components/servo/Cargo.lock | 4 ++-- ports/cef/Cargo.lock | 4 ++-- ports/gonk/Cargo.lock | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/components/servo/Cargo.lock b/components/servo/Cargo.lock index 83800fc7709..d61b2571292 100644 --- a/components/servo/Cargo.lock +++ b/components/servo/Cargo.lock @@ -479,7 +479,7 @@ dependencies = [ [[package]] name = "js" version = "0.1.0" -source = "git+https://github.com/servo/rust-mozjs#879b256e6bc5b38f792b68da130eb7a70633769b" +source = "git+https://github.com/servo/rust-mozjs#9512c3c770774ed73a2fdcc635eee178cbd02ab1" dependencies = [ "libc 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "mozjs_sys 0.0.0 (git+https://github.com/servo/mozjs)", @@ -617,7 +617,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "mozjs_sys" version = "0.0.0" -source = "git+https://github.com/servo/mozjs#40e0680008cb129ddf9ccf40c6b0095e14d1cd97" +source = "git+https://github.com/servo/mozjs#19edb950930f03f0ad305ffbd9548b92fdb0a250" [[package]] name = "msg" diff --git a/ports/cef/Cargo.lock b/ports/cef/Cargo.lock index 7cd06499343..3fcab77d28b 100644 --- a/ports/cef/Cargo.lock +++ b/ports/cef/Cargo.lock @@ -482,7 +482,7 @@ dependencies = [ [[package]] name = "js" version = "0.1.0" -source = "git+https://github.com/servo/rust-mozjs#879b256e6bc5b38f792b68da130eb7a70633769b" +source = "git+https://github.com/servo/rust-mozjs#9512c3c770774ed73a2fdcc635eee178cbd02ab1" dependencies = [ "libc 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "mozjs_sys 0.0.0 (git+https://github.com/servo/mozjs)", @@ -620,7 +620,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "mozjs_sys" version = "0.0.0" -source = "git+https://github.com/servo/mozjs#40e0680008cb129ddf9ccf40c6b0095e14d1cd97" +source = "git+https://github.com/servo/mozjs#19edb950930f03f0ad305ffbd9548b92fdb0a250" [[package]] name = "msg" diff --git a/ports/gonk/Cargo.lock b/ports/gonk/Cargo.lock index ddc5afd7988..7de597c61f9 100644 --- a/ports/gonk/Cargo.lock +++ b/ports/gonk/Cargo.lock @@ -415,7 +415,7 @@ dependencies = [ [[package]] name = "js" version = "0.1.0" -source = "git+https://github.com/servo/rust-mozjs#879b256e6bc5b38f792b68da130eb7a70633769b" +source = "git+https://github.com/servo/rust-mozjs#9512c3c770774ed73a2fdcc635eee178cbd02ab1" dependencies = [ "libc 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "mozjs_sys 0.0.0 (git+https://github.com/servo/mozjs)", @@ -545,7 +545,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "mozjs_sys" version = "0.0.0" -source = "git+https://github.com/servo/mozjs#40e0680008cb129ddf9ccf40c6b0095e14d1cd97" +source = "git+https://github.com/servo/mozjs#19edb950930f03f0ad305ffbd9548b92fdb0a250" [[package]] name = "msg" From e2929403eff482b83f044218919f40919015358d Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Wed, 8 Apr 2015 14:26:42 +0200 Subject: [PATCH 18/32] Implement TextDecoder#decode. --- components/script/dom/textdecoder.rs | 36 ++++++++++++- .../script/dom/webidls/TextDecoder.webidl | 2 + .../encoding/api-surrogates-utf8.html.ini | 20 -------- .../wpt/metadata/encoding/idlharness.html.ini | 6 --- .../encoding/iso-2022-jp-decoder.html.ini | 51 ------------------- .../encoding/textdecoder-streaming.html.ini | 27 ---------- .../textdecoder-utf16-surrogates.html.ini | 17 ------- .../textencoder-utf16-surrogates.html.ini | 20 -------- 8 files changed, 37 insertions(+), 142 deletions(-) delete mode 100644 tests/wpt/metadata/encoding/api-surrogates-utf8.html.ini delete mode 100644 tests/wpt/metadata/encoding/textdecoder-utf16-surrogates.html.ini delete mode 100644 tests/wpt/metadata/encoding/textencoder-utf16-surrogates.html.ini diff --git a/components/script/dom/textdecoder.rs b/components/script/dom/textdecoder.rs index 7aa6b4f92bc..714200f9a8f 100644 --- a/components/script/dom/textdecoder.rs +++ b/components/script/dom/textdecoder.rs @@ -7,16 +7,21 @@ use dom::bindings::codegen::Bindings::TextDecoderBinding::TextDecoderMethods; use dom::bindings::error::{Error, Fallible}; use dom::bindings::global::GlobalRef; use dom::bindings::js::{JSRef, Temporary}; +use dom::bindings::str::USVString; use dom::bindings::trace::JSTraceable; use dom::bindings::utils::{Reflector, reflect_dom_object}; use util::str::DOMString; use encoding::Encoding; -use encoding::types::EncodingRef; +use encoding::types::{EncodingRef, DecoderTrap}; use encoding::label::encoding_from_whatwg_label; +use js::jsapi::{JSContext, JSObject}; +use js::jsfriendapi::bindgen::JS_GetObjectAsArrayBufferView; use std::borrow::ToOwned; +use std::ptr; +use std::slice; #[dom_struct] pub struct TextDecoder { @@ -62,4 +67,33 @@ impl<'a> TextDecoderMethods for JSRef<'a, TextDecoder> { fn Fatal(self) -> bool { self.fatal } + + #[allow(unsafe_code)] + fn Decode(self, cx: *mut JSContext, input: Option<*mut JSObject>) + -> Fallible { + let input = match input { + Some(input) => input, + None => return Ok(USVString("".to_owned())), + }; + + let mut length = 0; + let mut data = ptr::null_mut(); + if unsafe { JS_GetObjectAsArrayBufferView(cx, input, &mut length, &mut data).is_null() } { + return Err(Error::Type("Argument to TextDecoder.decode is not an ArrayBufferView".to_owned())); + } + + let buffer = unsafe { + slice::from_raw_parts(data as *const _, length as usize) + }; + let trap = if self.fatal { + DecoderTrap::Strict + } else { + DecoderTrap::Replace + }; + match self.encoding.decode(buffer, trap) { + Ok(s) => Ok(USVString(s)), + Err(_) => Err(Error::Type("Decoding failed".to_owned())), + } + } + } diff --git a/components/script/dom/webidls/TextDecoder.webidl b/components/script/dom/webidls/TextDecoder.webidl index 6450c2ef0b6..4ec66f07d7a 100644 --- a/components/script/dom/webidls/TextDecoder.webidl +++ b/components/script/dom/webidls/TextDecoder.webidl @@ -16,4 +16,6 @@ interface TextDecoder { readonly attribute boolean fatal; //readonly attribute boolean ignoreBOM; //USVString decode(optional BufferSource input, optional TextDecodeOptions options); + [Throws] + USVString decode(optional object input); }; diff --git a/tests/wpt/metadata/encoding/api-surrogates-utf8.html.ini b/tests/wpt/metadata/encoding/api-surrogates-utf8.html.ini deleted file mode 100644 index 76324a435ae..00000000000 --- a/tests/wpt/metadata/encoding/api-surrogates-utf8.html.ini +++ /dev/null @@ -1,20 +0,0 @@ -[api-surrogates-utf8.html] - type: testharness - [Invalid surrogates encoded into UTF-8: Sanity check] - expected: FAIL - - [Invalid surrogates encoded into UTF-8: Surrogate half (low)] - expected: FAIL - - [Invalid surrogates encoded into UTF-8: Surrogate half (high)] - expected: FAIL - - [Invalid surrogates encoded into UTF-8: Surrogate half (low), in a string] - expected: FAIL - - [Invalid surrogates encoded into UTF-8: Surrogate half (high), in a string] - expected: FAIL - - [Invalid surrogates encoded into UTF-8: Wrong order] - expected: FAIL - diff --git a/tests/wpt/metadata/encoding/idlharness.html.ini b/tests/wpt/metadata/encoding/idlharness.html.ini index f7c414c2d63..28ad8fba868 100644 --- a/tests/wpt/metadata/encoding/idlharness.html.ini +++ b/tests/wpt/metadata/encoding/idlharness.html.ini @@ -12,12 +12,6 @@ [TextDecoder interface: new TextDecoder() must inherit property "ignoreBOM" with the proper type (2)] expected: FAIL - [TextDecoder interface: new TextDecoder() must inherit property "decode" with the proper type (3)] - expected: FAIL - - [TextDecoder interface: calling decode(BufferSource,TextDecodeOptions) on new TextDecoder() with too few arguments must throw TypeError] - expected: FAIL - [TextEncoder interface object length] expected: FAIL diff --git a/tests/wpt/metadata/encoding/iso-2022-jp-decoder.html.ini b/tests/wpt/metadata/encoding/iso-2022-jp-decoder.html.ini index 4651cfad0d9..ace8b904e2b 100644 --- a/tests/wpt/metadata/encoding/iso-2022-jp-decoder.html.ini +++ b/tests/wpt/metadata/encoding/iso-2022-jp-decoder.html.ini @@ -3,21 +3,12 @@ [iso-2022-jp decoder: Error ESC] expected: FAIL - [iso-2022-jp decoder: Error ESC, character] - expected: FAIL - [iso-2022-jp decoder: ASCII ESC, character] expected: FAIL [iso-2022-jp decoder: Double ASCII ESC, character] expected: FAIL - [iso-2022-jp decoder: character, ASCII ESC, character] - expected: FAIL - - [iso-2022-jp decoder: characters] - expected: FAIL - [iso-2022-jp decoder: SO / SI] expected: FAIL @@ -27,12 +18,6 @@ [iso-2022-jp decoder: Roman ESC, SO / SI] expected: FAIL - [iso-2022-jp decoder: Roman ESC, error ESC, Katakana ESC] - expected: FAIL - - [iso-2022-jp decoder: Katakana ESC, character] - expected: FAIL - [iso-2022-jp decoder: Katakana ESC, multibyte ESC, character] expected: FAIL @@ -42,18 +27,6 @@ [iso-2022-jp decoder: Katakana ESC, error ESC #2, character] expected: FAIL - [iso-2022-jp decoder: Katakana ESC, character, Katakana ESC, character] - expected: FAIL - - [iso-2022-jp decoder: Katakana ESC, SO / SI] - expected: FAIL - - [iso-2022-jp decoder: Multibyte ESC, character] - expected: FAIL - - [iso-2022-jp decoder: Multibyte ESC #2, character] - expected: FAIL - [iso-2022-jp decoder: Multibyte ESC, error ESC, character] expected: FAIL @@ -75,30 +48,6 @@ [iso-2022-jp decoder: Multibyte ESC, lead error byte] expected: FAIL - [iso-2022-jp decoder: Multibyte ESC, trail error byte] - expected: FAIL - - [iso-2022-jp decoder: character, error ESC] - expected: FAIL - [iso-2022-jp decoder: character, error ESC #2] expected: FAIL - [iso-2022-jp decoder: character, error ESC #3] - expected: FAIL - - [iso-2022-jp decoder: character, ASCII ESC] - expected: FAIL - - [iso-2022-jp decoder: character, Roman ESC] - expected: FAIL - - [iso-2022-jp decoder: character, Katakana ESC] - expected: FAIL - - [iso-2022-jp decoder: character, Multibyte ESC] - expected: FAIL - - [iso-2022-jp decoder: character, Multibyte ESC #2] - expected: FAIL - diff --git a/tests/wpt/metadata/encoding/textdecoder-streaming.html.ini b/tests/wpt/metadata/encoding/textdecoder-streaming.html.ini index 8f34fafe58b..401997d8036 100644 --- a/tests/wpt/metadata/encoding/textdecoder-streaming.html.ini +++ b/tests/wpt/metadata/encoding/textdecoder-streaming.html.ini @@ -1,47 +1,20 @@ [textdecoder-streaming.html] type: testharness - [Streaming decode: utf-8, 1 byte window] - expected: FAIL - - [Streaming decode: utf-8, 2 byte window] - expected: FAIL - - [Streaming decode: utf-8, 3 byte window] - expected: FAIL - - [Streaming decode: utf-8, 4 byte window] - expected: FAIL - - [Streaming decode: utf-8, 5 byte window] - expected: FAIL - [Streaming decode: utf-16le, 1 byte window] expected: FAIL - [Streaming decode: utf-16le, 2 byte window] - expected: FAIL - [Streaming decode: utf-16le, 3 byte window] expected: FAIL - [Streaming decode: utf-16le, 4 byte window] - expected: FAIL - [Streaming decode: utf-16le, 5 byte window] expected: FAIL [Streaming decode: utf-16be, 1 byte window] expected: FAIL - [Streaming decode: utf-16be, 2 byte window] - expected: FAIL - [Streaming decode: utf-16be, 3 byte window] expected: FAIL - [Streaming decode: utf-16be, 4 byte window] - expected: FAIL - [Streaming decode: utf-16be, 5 byte window] expected: FAIL diff --git a/tests/wpt/metadata/encoding/textdecoder-utf16-surrogates.html.ini b/tests/wpt/metadata/encoding/textdecoder-utf16-surrogates.html.ini deleted file mode 100644 index 9c32d21843c..00000000000 --- a/tests/wpt/metadata/encoding/textdecoder-utf16-surrogates.html.ini +++ /dev/null @@ -1,17 +0,0 @@ -[textdecoder-utf16-surrogates.html] - type: testharness - [utf-16le - lone surrogate lead] - expected: FAIL - - [utf-16le - lone surrogate trail] - expected: FAIL - - [utf-16le - unmatched surrogate lead] - expected: FAIL - - [utf-16le - unmatched surrogate trail] - expected: FAIL - - [utf-16le - swapped surrogate pair] - expected: FAIL - diff --git a/tests/wpt/metadata/encoding/textencoder-utf16-surrogates.html.ini b/tests/wpt/metadata/encoding/textencoder-utf16-surrogates.html.ini deleted file mode 100644 index 45d49ee6a37..00000000000 --- a/tests/wpt/metadata/encoding/textencoder-utf16-surrogates.html.ini +++ /dev/null @@ -1,20 +0,0 @@ -[textencoder-utf16-surrogates.html] - type: testharness - [USVString handling: lone surrogate lead] - expected: FAIL - - [USVString handling: lone surrogate trail] - expected: FAIL - - [USVString handling: unmatched surrogate lead] - expected: FAIL - - [USVString handling: unmatched surrogate trail] - expected: FAIL - - [USVString handling: swapped surrogate pair] - expected: FAIL - - [USVString handling: properly encoded MUSICAL SYMBOL G CLEF (U+1D11E)] - expected: FAIL - From 6ae74b6673d70ed89276ee72a04a31f9bb0ca08b Mon Sep 17 00:00:00 2001 From: Ms2ger Date: Wed, 8 Apr 2015 19:00:34 +0200 Subject: [PATCH 19/32] Pass the buffer to create_face_from_buffer. It's better to hold on to the abstraction as long as possible. --- components/gfx/platform/freetype/font.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/components/gfx/platform/freetype/font.rs b/components/gfx/platform/freetype/font.rs index 181c3569bab..60587acb297 100644 --- a/components/gfx/platform/freetype/font.rs +++ b/components/gfx/platform/freetype/font.rs @@ -76,8 +76,7 @@ impl FontHandleMethods for FontHandle { let ft_ctx: FT_Library = fctx.ctx.ctx; if ft_ctx.is_null() { return Err(()); } - let bytes = &template.bytes; - let face_result = create_face_from_buffer(ft_ctx, bytes.as_ptr(), bytes.len(), pt_size); + let face_result = create_face_from_buffer(ft_ctx, &template.bytes, pt_size); // TODO: this could be more simply written as result::chain // and moving buf into the struct ctor, but cant' move out of @@ -94,12 +93,12 @@ impl FontHandleMethods for FontHandle { Err(()) => Err(()) }; - fn create_face_from_buffer(lib: FT_Library, cbuf: *const u8, cbuflen: uint, pt_size: Option) + fn create_face_from_buffer(lib: FT_Library, buffer: &[u8], pt_size: Option) -> Result { unsafe { let mut face: FT_Face = ptr::null_mut(); let face_index = 0 as FT_Long; - let result = FT_New_Memory_Face(lib, cbuf, cbuflen as FT_Long, + let result = FT_New_Memory_Face(lib, buffer.as_ptr(), buffer.len() as FT_Long, face_index, &mut face); if !result.succeeded() || face.is_null() { From a8c7864c689e66036e2757cae173bdef53a8986a Mon Sep 17 00:00:00 2001 From: Ms2ger Date: Wed, 8 Apr 2015 19:00:58 +0200 Subject: [PATCH 20/32] Stop using int in font_list. --- components/gfx/platform/freetype/font_list.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/gfx/platform/freetype/font_list.rs b/components/gfx/platform/freetype/font_list.rs index 3970efa92ec..9f939fa9391 100644 --- a/components/gfx/platform/freetype/font_list.rs +++ b/components/gfx/platform/freetype/font_list.rs @@ -36,7 +36,7 @@ pub fn get_available_families(mut callback: F) where F: FnMut(String) { unsafe { let config = FcConfigGetCurrent(); let fontSet = FcConfigGetFonts(config, FcSetSystem); - for i in 0..((*fontSet).nfont as int) { + for i in 0..((*fontSet).nfont as isize) { let font = (*fontSet).fonts.offset(i); let mut family: *mut FcChar8 = ptr::null_mut(); let mut v: c_int = 0; @@ -74,7 +74,7 @@ pub fn get_variations_for_family(family_name: &str, mut callback: F) debug!("found {} variations", (*matches).nfont); - for i in 0..((*matches).nfont as int) { + for i in 0..((*matches).nfont as isize) { let font = (*matches).fonts.offset(i); let mut file: *mut FcChar8 = ptr::null_mut(); let file = if FcPatternGetString(*font, FC_FILE.as_ptr() as *mut c_char, 0, &mut file) == FcResultMatch { From f8dec0fe583f47388cfcb7a85fd965f577b8bbc2 Mon Sep 17 00:00:00 2001 From: Ms2ger Date: Tue, 24 Mar 2015 00:07:18 +0100 Subject: [PATCH 21/32] Adjust the assertions in throw_dom_exception. --- components/script/dom/bindings/error.rs | 11 ++++++++--- ...WorkerGlobalScope_importScripts_NetworkErr.htm.ini | 3 +-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/components/script/dom/bindings/error.rs b/components/script/dom/bindings/error.rs index feba3941bf3..426bff64d95 100644 --- a/components/script/dom/bindings/error.rs +++ b/components/script/dom/bindings/error.rs @@ -71,10 +71,9 @@ pub type Fallible = Result; /// return `()`. pub type ErrorResult = Fallible<()>; -/// Set a pending DOM exception for the given `result` on `cx`. +/// Set a pending exception for the given `result` on `cx`. pub fn throw_dom_exception(cx: *mut JSContext, global: GlobalRef, result: Error) { - assert!(unsafe { JS_IsExceptionPending(cx) } == 0); let code = match result { Error::IndexSize => DOMErrorName::IndexSizeError, Error::NotFound => DOMErrorName::NotFoundError, @@ -93,11 +92,17 @@ pub fn throw_dom_exception(cx: *mut JSContext, global: GlobalRef, Error::DataClone => DOMErrorName::DataCloneError, Error::NoModificationAllowed => DOMErrorName::NoModificationAllowedError, Error::Type(message) => { + assert!(unsafe { JS_IsExceptionPending(cx) } == 0); throw_type_error(cx, &message); return; } - Error::JSFailed => panic!(), + Error::JSFailed => { + assert!(unsafe { JS_IsExceptionPending(cx) } == 1); + return; + } }; + + assert!(unsafe { JS_IsExceptionPending(cx) } == 0); let exception = DOMException::new(global, code).root(); let thrown = exception.to_jsval(cx); unsafe { diff --git a/tests/wpt/metadata/workers/WorkerGlobalScope_importScripts_NetworkErr.htm.ini b/tests/wpt/metadata/workers/WorkerGlobalScope_importScripts_NetworkErr.htm.ini index 6e4caece23b..ddec84b7387 100644 --- a/tests/wpt/metadata/workers/WorkerGlobalScope_importScripts_NetworkErr.htm.ini +++ b/tests/wpt/metadata/workers/WorkerGlobalScope_importScripts_NetworkErr.htm.ini @@ -1,6 +1,5 @@ [WorkerGlobalScope_importScripts_NetworkErr.htm] type: testharness - expected: TIMEOUT [ importScripts() with non-existent script file ] - expected: TIMEOUT + expected: FAIL From 6d6146816031a04d9e4597ad85c77266c6dd1b8f Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Mon, 30 Mar 2015 14:09:37 -0700 Subject: [PATCH 22/32] layout: Simplify and improve the correctness of whitespace stripping in text layout, and unify the inline layout paths for pre- and normally-formatted text. This fixes a lot of "jumpiness" and removes the `new_line_pos` stuff. Closes #2260. --- components/gfx/text/text_run.rs | 4 +- components/gfx/text/util.rs | 17 +- components/layout/fragment.rs | 369 ++++++------------ components/layout/inline.rs | 356 +++++++++-------- components/layout/text.rs | 78 +++- components/util/str.rs | 7 +- tests/ref/basic.list | 2 + tests/ref/incremental_inline_layout_a.html | 32 ++ tests/ref/incremental_inline_layout_ref.html | 28 ++ .../line_breaking_whitespace_collapse_a.html | 19 + ...line_breaking_whitespace_collapse_ref.html | 19 + tests/unit/gfx/text_util.rs | 15 +- 12 files changed, 485 insertions(+), 461 deletions(-) create mode 100644 tests/ref/incremental_inline_layout_a.html create mode 100644 tests/ref/incremental_inline_layout_ref.html create mode 100644 tests/ref/line_breaking_whitespace_collapse_a.html create mode 100644 tests/ref/line_breaking_whitespace_collapse_ref.html diff --git a/components/gfx/text/text_run.rs b/components/gfx/text/text_run.rs index 93c4c6c50b9..4e963bf1b28 100644 --- a/components/gfx/text/text_run.rs +++ b/components/gfx/text/text_run.rs @@ -16,6 +16,7 @@ use text::glyph::{CharIndex, GlyphStore}; /// A single "paragraph" of text in one font size and style. #[derive(Clone)] pub struct TextRun { + /// The UTF-8 string represented by this text run. pub text: Arc, pub font_template: Arc, pub actual_pt_size: Au, @@ -310,7 +311,8 @@ impl<'a> TextRun { self.font_metrics.descent) } - pub fn metrics_for_slice(&self, glyphs: &GlyphStore, slice_range: &Range) -> RunMetrics { + pub fn metrics_for_slice(&self, glyphs: &GlyphStore, slice_range: &Range) + -> RunMetrics { RunMetrics::new(glyphs.advance_for_char_range(slice_range), self.font_metrics.ascent, self.font_metrics.descent) diff --git a/components/gfx/text/util.rs b/components/gfx/text/util.rs index 890069b401c..95654af9ce5 100644 --- a/components/gfx/text/util.rs +++ b/components/gfx/text/util.rs @@ -2,8 +2,6 @@ * 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 text::glyph::CharIndex; - #[derive(PartialEq, Eq, Copy)] pub enum CompressionMode { CompressNone, @@ -25,12 +23,10 @@ pub enum CompressionMode { pub fn transform_text(text: &str, mode: CompressionMode, incoming_whitespace: bool, - output_text: &mut String, - new_line_pos: &mut Vec) + output_text: &mut String) -> bool { let out_whitespace = match mode { CompressionMode::CompressNone | CompressionMode::DiscardNewline => { - let mut new_line_index = CharIndex(0); for ch in text.chars() { if is_discardable_char(ch, mode) { // TODO: record skipped char @@ -38,15 +34,6 @@ pub fn transform_text(text: &str, // TODO: record kept char if ch == '\t' { // TODO: set "has tab" flag - } else if ch == '\n' { - // Save new-line's position for line-break - // This value is relative(not absolute) - new_line_pos.push(new_line_index); - new_line_index = CharIndex(0); - } - - if ch != '\n' { - new_line_index = new_line_index + CharIndex(1); } output_text.push(ch); } @@ -124,6 +111,6 @@ pub fn fixed_to_rounded_int(before: isize, f: i32) -> isize { if f > 0i32 { ((half + f) >> before) as isize } else { - -((half - f) >> before) as isize + -((half - f) >> before as usize) as isize } } diff --git a/components/layout/fragment.rs b/components/layout/fragment.rs index 4c3d3bdc4c6..b766b27752e 100644 --- a/components/layout/fragment.rs +++ b/components/layout/fragment.rs @@ -27,16 +27,11 @@ use geom::{Point2D, Rect, Size2D}; use gfx::display_list::{BLUR_INFLATION_FACTOR, OpaqueNode}; use gfx::text::glyph::CharIndex; use gfx::text::text_run::{TextRun, TextRunSlice}; -use script_traits::UntrustedNodeAddress; -use rustc_serialize::{Encodable, Encoder}; use msg::constellation_msg::{ConstellationChan, Msg, PipelineId, SubpageId}; use net_traits::image::holder::ImageHolder; use net_traits::local_image_cache::LocalImageCache; -use util::geometry::{self, Au, ZERO_POINT}; -use util::logical_geometry::{LogicalRect, LogicalSize, LogicalMargin, WritingMode}; -use util::range::*; -use util::smallvec::SmallVec; -use util::str::is_whitespace; +use rustc_serialize::{Encodable, Encoder}; +use script_traits::UntrustedNodeAddress; use std::borrow::ToOwned; use std::cmp::{max, min}; use std::collections::LinkedList; @@ -56,6 +51,12 @@ use style::values::computed::{LengthOrPercentage, LengthOrPercentageOrAuto}; use style::values::computed::{LengthOrPercentageOrNone}; use text::TextRunScanner; use url::Url; +use util::geometry::{self, Au, ZERO_POINT}; +use util::logical_geometry::{LogicalRect, LogicalSize, LogicalMargin, WritingMode}; +use util::range::*; +use util::smallvec::SmallVec; +use util::str::is_whitespace; +use util; /// Fragments (`struct Fragment`) are the leaves of the layout tree. They cannot position /// themselves. In general, fragments do not have a simple correspondence with CSS fragments in the @@ -580,36 +581,27 @@ pub struct ScannedTextFragmentInfo { /// The text run that this represents. pub run: Arc>, + /// The intrinsic size of the text fragment. + pub content_size: LogicalSize, + /// The range within the above text run that this represents. pub range: Range, - /// The positions of newlines within this scanned text fragment. - /// - /// FIXME(#2260, pcwalton): Can't this go somewhere else, like in the text run or something? - /// Or can we just remove it? - pub new_line_pos: Vec, - - /// The new_line_pos is eaten during line breaking. If we need to re-merge - /// fragments, it will have to be restored. - pub original_new_line_pos: Option>, - - /// The intrinsic size of the text fragment. - pub content_size: LogicalSize, + /// The endpoint of the above range, including whitespace that was stripped out. This exists + /// so that we can restore the range to its original value (before line breaking occurred) when + /// performing incremental reflow. + pub range_end_including_stripped_whitespace: CharIndex, } impl ScannedTextFragmentInfo { /// Creates the information specific to a scanned text fragment from a range and a text run. - pub fn new(run: Arc>, - range: Range, - new_line_positions: Vec, - content_size: LogicalSize) + pub fn new(run: Arc>, range: Range, content_size: LogicalSize) -> ScannedTextFragmentInfo { ScannedTextFragmentInfo { run: run, range: range, - new_line_pos: new_line_positions, - original_new_line_pos: None, content_size: content_size, + range_end_including_stripped_whitespace: range.end(), } } } @@ -769,32 +761,6 @@ impl Fragment { self.margin = LogicalMargin::zero(self.style.writing_mode); } - /// Saves the new_line_pos vector into a `SpecificFragmentInfo::ScannedText`. This will fail - /// if called on any other type of fragment. - pub fn save_new_line_pos(&mut self) { - match &mut self.specific { - &mut SpecificFragmentInfo::ScannedText(ref mut info) => { - if !info.new_line_pos.is_empty() { - info.original_new_line_pos = Some(info.new_line_pos.clone()); - } - } - _ => {} - } - } - - pub fn restore_new_line_pos(&mut self) { - match &mut self.specific { - &mut SpecificFragmentInfo::ScannedText(ref mut info) => { - match info.original_new_line_pos.take() { - None => {} - Some(new_line_pos) => info.new_line_pos = new_line_pos, - } - return - } - _ => {} - } - } - /// Returns a debug ID of this fragment. This ID should not be considered stable across /// multiple layouts or fragment manipulations. pub fn debug_id(&self) -> u16 { @@ -823,14 +789,12 @@ impl Fragment { } /// Transforms this fragment using the given `SplitInfo`, preserving all the other data. - pub fn transform_with_split_info(&self, - split: &SplitInfo, - text_run: Arc>) + pub fn transform_with_split_info(&self, split: &SplitInfo, text_run: Arc>) -> Fragment { let size = LogicalSize::new(self.style.writing_mode, split.inline_size, self.border_box.size.block); - let info = box ScannedTextFragmentInfo::new(text_run, split.range, Vec::new(), size); + let info = box ScannedTextFragmentInfo::new(text_run, split.range, size); self.transform(size, SpecificFragmentInfo::ScannedText(info)) } @@ -857,7 +821,6 @@ impl Fragment { style: Arc, first_frag: bool, last_frag: bool) { - if self.inline_context.is_none() { self.inline_context = Some(InlineFragmentContext::new()); } @@ -1179,25 +1142,11 @@ impl Fragment { } } - /// Returns true if this element can be split. This is true for text fragments. + /// Returns true if this element can be split. This is true for text fragments, unless + /// `white-space: pre` is set. pub fn can_split(&self) -> bool { - self.is_scanned_text_fragment() - } - - /// Returns the newline positions of this fragment, if it's a scanned text fragment. - pub fn newline_positions(&self) -> Option<&Vec> { - match self.specific { - SpecificFragmentInfo::ScannedText(ref info) => Some(&info.new_line_pos), - _ => None, - } - } - - /// Returns the newline positions of this fragment, if it's a scanned text fragment. - pub fn newline_positions_mut(&mut self) -> Option<&mut Vec> { - match self.specific { - SpecificFragmentInfo::ScannedText(ref mut info) => Some(&mut info.new_line_pos), - _ => None, - } + self.is_scanned_text_fragment() && + self.style.get_inheritedtext().white_space != white_space::T::pre } /// Returns true if and only if this fragment is a generated content fragment. @@ -1359,64 +1308,6 @@ impl Fragment { self.border_box - self.border_padding } - /// Find the split of a fragment that includes a new-line character. - /// - /// A return value of `None` indicates that the fragment is not splittable. - /// Otherwise the split information is returned. The right information is - /// optional due to the possibility of it being whitespace. - // - // TODO(bjz): The text run should be removed in the future, but it is currently needed for - // the current method of fragment splitting in the `inline::try_append_*` functions. - pub fn find_split_info_by_new_line(&self) - -> Option<(SplitInfo, Option, Arc> /* TODO(bjz): remove */)> { - match self.specific { - SpecificFragmentInfo::Canvas(_) | - SpecificFragmentInfo::Generic | - SpecificFragmentInfo::GeneratedContent(_) | - SpecificFragmentInfo::Iframe(_) | - SpecificFragmentInfo::Image(_) | - SpecificFragmentInfo::Table | - SpecificFragmentInfo::TableCell | - SpecificFragmentInfo::TableRow | - SpecificFragmentInfo::TableWrapper => { - None - } - SpecificFragmentInfo::TableColumn(_) => { - panic!("Table column fragments do not need to split") - } - SpecificFragmentInfo::UnscannedText(_) => { - panic!("Unscanned text fragments should have been scanned by now!") - } - SpecificFragmentInfo::InlineBlock(_) | - SpecificFragmentInfo::InlineAbsoluteHypothetical(_) => { - panic!("Inline blocks or inline absolute hypothetical fragments do not get split") - } - SpecificFragmentInfo::ScannedText(ref text_fragment_info) => { - let mut new_line_pos = text_fragment_info.new_line_pos.clone(); - let cur_new_line_pos = new_line_pos.remove(0); - - let inline_start_range = Range::new(text_fragment_info.range.begin(), - cur_new_line_pos); - let inline_end_range = Range::new( - text_fragment_info.range.begin() + cur_new_line_pos + CharIndex(1), - text_fragment_info.range.length() - (cur_new_line_pos + CharIndex(1))); - - // Left fragment is for inline-start text of first founded new-line character. - let inline_start_fragment = SplitInfo::new(inline_start_range, - &**text_fragment_info); - - // Right fragment is for inline-end text of first founded new-line character. - let inline_end_fragment = if inline_end_range.length() > CharIndex(0) { - Some(SplitInfo::new(inline_end_range, &**text_fragment_info)) - } else { - None - }; - - Some((inline_start_fragment, inline_end_fragment, text_fragment_info.run.clone())) - } - } - } - /// Attempts to find the split positions of a text fragment so that its inline-size is no more /// than `max_inline_size`. /// @@ -1495,13 +1386,13 @@ impl Fragment { /// A helper method that uses the breaking strategy described by `slice_iterator` (at present, /// either natural word breaking or character breaking) to split this fragment. - fn calculate_split_position_using_breaking_strategy<'a,I>(&self, - slice_iterator: I, - max_inline_size: Au, - flags: SplitOptions) - -> Option - where I: Iterator> { + fn calculate_split_position_using_breaking_strategy<'a,I>( + &self, + slice_iterator: I, + max_inline_size: Au, + flags: SplitOptions) + -> Option + where I: Iterator> { let text_fragment_info = if let SpecificFragmentInfo::ScannedText(ref text_fragment_info) = self.specific { text_fragment_info @@ -1515,31 +1406,35 @@ impl Fragment { let mut inline_end_range = None; let mut overflowing = false; - debug!("calculate_split_position: splitting text fragment (strlen={}, range={:?}, \ - max_inline_size={:?})", + debug!("calculate_split_position_using_breaking_strategy: splitting text fragment \ + (strlen={}, range={:?}, max_inline_size={:?})", text_fragment_info.run.text.len(), text_fragment_info.range, max_inline_size); for slice in slice_iterator { - debug!("calculate_split_position: considering slice (offset={:?}, slice range={:?}, \ - remaining_inline_size={:?})", + debug!("calculate_split_position_using_breaking_strategy: considering slice \ + (offset={:?}, slice range={:?}, remaining_inline_size={:?})", slice.offset, slice.range, remaining_inline_size); + // Use the `remaining_inline_size` to find a split point if possible. If not, go around + // the loop again with the next slice. let metrics = text_fragment_info.run.metrics_for_slice(slice.glyphs, &slice.range); let advance = metrics.advance_width; // Have we found the split point? if advance <= remaining_inline_size || slice.glyphs.is_whitespace() { // Keep going; we haven't found the split point yet. - if flags.contains(STARTS_LINE) && pieces_processed_count == 0 && + if flags.contains(STARTS_LINE) && + pieces_processed_count == 0 && slice.glyphs.is_whitespace() { - debug!("calculate_split_position: skipping leading trimmable whitespace"); + debug!("calculate_split_position_using_breaking_strategy: skipping \ + leading trimmable whitespace"); inline_start_range.shift_by(slice.range.length()); } else { - debug!("split_to_inline_size: enlarging span"); + debug!("calculate_split_position_using_breaking_strategy: enlarging span"); remaining_inline_size = remaining_inline_size - advance; inline_start_range.extend_by(slice.range.length()); } @@ -1570,60 +1465,31 @@ impl Fragment { inline_end); } - break - } + // If we failed to find a suitable split point, we're on the verge of overflowing the + // line. + if inline_start_range.is_empty() || overflowing { + // If we've been instructed to retry at character boundaries (probably via + // `overflow-wrap: break-word`), do so. + if flags.contains(RETRY_AT_CHARACTER_BOUNDARIES) { + let character_breaking_strategy = + text_fragment_info.run + .character_slices_in_range(&text_fragment_info.range); + let mut flags = flags; + flags.remove(RETRY_AT_CHARACTER_BOUNDARIES); + return self.calculate_split_position_using_breaking_strategy( + character_breaking_strategy, + max_inline_size, + flags) + } - // If we failed to find a suitable split point, we're on the verge of overflowing the line. - if inline_start_range.is_empty() || overflowing { - // If we've been instructed to retry at character boundaries (probably via - // `overflow-wrap: break-word`), do so. - if flags.contains(RETRY_AT_CHARACTER_BOUNDARIES) { - let character_breaking_strategy = - text_fragment_info.run.character_slices_in_range(&text_fragment_info.range); - let mut flags = flags; - flags.remove(RETRY_AT_CHARACTER_BOUNDARIES); - return self.calculate_split_position_using_breaking_strategy( - character_breaking_strategy, - max_inline_size, - flags) - } - - // We aren't at the start of the line, so don't overflow. Let inline layout wrap to the - // next line instead. - if !flags.contains(STARTS_LINE) { - return None - } - } - - // Remove trailing whitespace from the inline-start split, if necessary. - // - // FIXME(pcwalton): Is there a more clever (i.e. faster) way to do this? - strip_trailing_whitespace(&**text_fragment_info.run, &mut inline_start_range); - - // Remove leading whitespace from the inline-end split, if necessary. - // - // FIXME(pcwalton): Is there a more clever (i.e. faster) way to do this? - if let Some(ref mut inline_end_range) = inline_end_range { - let inline_end_fragment_text = - text_fragment_info.run.text.slice_chars(inline_end_range.begin().to_usize(), - inline_end_range.end().to_usize()); - let mut leading_whitespace_character_count = 0; - for ch in inline_end_fragment_text.chars() { - if ch.is_whitespace() { - leading_whitespace_character_count += 1 - } else { - break + // We aren't at the start of the line, so don't overflow. Let inline layout wrap to + // the next line instead. + if !flags.contains(STARTS_LINE) { + return None } } - inline_end_range.adjust_by(CharIndex(leading_whitespace_character_count), - -CharIndex(leading_whitespace_character_count)); - } - // Normalize our split so that the inline-end fragment will never be `Some` while the - // inline-start fragment is `None`. - if inline_start_range.is_empty() && inline_end_range.is_some() { - inline_start_range = inline_end_range.unwrap(); - inline_end_range = None + break } let inline_start = if !inline_start_range.is_empty() { @@ -1642,22 +1508,21 @@ impl Fragment { }) } - /// Attempts to strip trailing whitespace from this fragment by adjusting the text run range. - /// Returns true if any modifications were made. - pub fn strip_trailing_whitespace_if_necessary(&mut self) -> bool { - let text_fragment_info = - if let SpecificFragmentInfo::ScannedText(ref mut text_fragment_info) = self.specific { - text_fragment_info - } else { - return false - }; - - let run = text_fragment_info.run.clone(); - if strip_trailing_whitespace(&**run, &mut text_fragment_info.range) { - self.border_box.size.inline = run.advance_for_range(&text_fragment_info.range); - return true + /// The opposite of `calculate_split_position_using_breaking_strategy`: merges this fragment + /// with the next one. + pub fn merge_with(&mut self, next_fragment: Fragment) { + match (&mut self.specific, &next_fragment.specific) { + (&mut SpecificFragmentInfo::ScannedText(ref mut this_info), + &SpecificFragmentInfo::ScannedText(ref other_info)) => { + debug_assert!(util::arc_ptr_eq(&this_info.run, &other_info.run)); + this_info.range.extend_to(other_info.range_end_including_stripped_whitespace); + this_info.content_size.inline = + this_info.run.metrics_for_range(&this_info.range).advance_width; + self.border_box.size.inline = this_info.content_size.inline + + self.border_padding.inline_start_end(); + } + _ => panic!("Can only merge two scanned-text fragments!"), } - false } /// Returns true if this fragment is an unscanned text fragment that consists entirely of @@ -1669,7 +1534,7 @@ impl Fragment { } match self.specific { SpecificFragmentInfo::UnscannedText(ref text_fragment_info) => { - is_whitespace(text_fragment_info.text.as_slice()) + util::str::is_whitespace(text_fragment_info.text.as_slice()) } _ => false, } @@ -1890,11 +1755,14 @@ impl Fragment { /// Returns true if this fragment can merge with another adjacent fragment or false otherwise. pub fn can_merge_with_fragment(&self, other: &Fragment) -> bool { match (&self.specific, &other.specific) { - (&SpecificFragmentInfo::UnscannedText(_), &SpecificFragmentInfo::UnscannedText(_)) => { + (&SpecificFragmentInfo::UnscannedText(ref first_unscanned_text), + &SpecificFragmentInfo::UnscannedText(_)) => { // FIXME: Should probably use a whitelist of styles that can safely differ (#3165) + let length = first_unscanned_text.text.len(); self.style().get_font() == other.style().get_font() && self.text_decoration() == other.text_decoration() && - self.white_space() == other.white_space() + self.white_space() == other.white_space() && + (length == 0 || first_unscanned_text.text.char_at_reverse(length) != '\n') } _ => false, } @@ -2076,14 +1944,57 @@ impl Fragment { _ => {} } } + + pub fn requires_line_break_afterward_if_wrapping_on_newlines(&self) -> bool { + match self.specific { + SpecificFragmentInfo::ScannedText(ref scanned_text) => { + !scanned_text.range.is_empty() && + scanned_text.run.text.char_at_reverse(scanned_text.range + .end() + .get() as usize) == '\n' + } + _ => false, + } + } + + pub fn strip_leading_whitespace_if_necessary(&mut self) { + let mut scanned_text_fragment_info = match self.specific { + SpecificFragmentInfo::ScannedText(ref mut scanned_text_fragment_info) => { + scanned_text_fragment_info + } + _ => return, + }; + + if self.style.get_inheritedtext().white_space == white_space::T::pre { + return + } + + let mut leading_whitespace_character_count = 0; + { + let text = scanned_text_fragment_info.run.text.slice_chars( + scanned_text_fragment_info.range.begin().to_usize(), + scanned_text_fragment_info.range.end().to_usize()); + for character in text.chars() { + if util::str::char_is_whitespace(character) { + leading_whitespace_character_count += 1 + } else { + break + } + } + } + + scanned_text_fragment_info.range.adjust_by(CharIndex(leading_whitespace_character_count), + -CharIndex(leading_whitespace_character_count)); + } } impl fmt::Debug for Fragment { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { try!(write!(f, "({} {} ", self.debug_id(), self.specific.get_type())); - try!(write!(f, "bp {:?}", self.border_padding)); - try!(write!(f, " ")); - try!(write!(f, "m {:?}", self.margin)); + try!(write!(f, "bb {:?} bp {:?} m {:?}", + self.border_box, + self.border_padding, + self.margin)); write!(f, ")") } } @@ -2105,7 +2016,7 @@ bitflags! { const STARTS_LINE = 0x01, #[doc="True if we should attempt to split at character boundaries if this split fails. \ This is used to implement `overflow-wrap: break-word`."] - const RETRY_AT_CHARACTER_BOUNDARIES = 0x02 + const RETRY_AT_CHARACTER_BOUNDARIES = 0x02, } } @@ -2129,25 +2040,3 @@ pub enum CoordinateSystem { Own, } -/// Given a range and a text run, adjusts the range to eliminate trailing whitespace. Returns true -/// if any modifications were made. -fn strip_trailing_whitespace(text_run: &TextRun, range: &mut Range) -> bool { - // FIXME(pcwalton): Is there a more clever (i.e. faster) way to do this? - let text = text_run.text.slice_chars(range.begin().to_usize(), range.end().to_usize()); - let mut trailing_whitespace_character_count = 0; - for ch in text.chars().rev() { - if ch.is_whitespace() { - trailing_whitespace_character_count += 1 - } else { - break - } - } - - if trailing_whitespace_character_count == 0 { - return false - } - - range.extend_by(-CharIndex(trailing_whitespace_character_count)); - return true -} - diff --git a/components/layout/inline.rs b/components/layout/inline.rs index 2c2625225b2..1f9e59dba2e 100644 --- a/components/layout/inline.rs +++ b/components/layout/inline.rs @@ -8,11 +8,9 @@ use css::node_style::StyledNode; use context::LayoutContext; use display_list_builder::{FragmentDisplayListBuilding, InlineFlowDisplayListBuilding}; use floats::{FloatKind, Floats, PlacementInfo}; -use flow::{BaseFlow, FlowClass, Flow, MutableFlowUtils, ForceNonfloatedFlag}; +use flow::{self, BaseFlow, FlowClass, Flow, MutableFlowUtils, ForceNonfloatedFlag}; use flow::{IS_ABSOLUTELY_POSITIONED}; -use flow; -use fragment::{CoordinateSystem, Fragment, FragmentBorderBoxIterator, ScannedTextFragmentInfo}; -use fragment::{SpecificFragmentInfo, SplitInfo}; +use fragment::{CoordinateSystem, Fragment, FragmentBorderBoxIterator, SpecificFragmentInfo}; use incremental::{REFLOW, REFLOW_OUT_OF_FLOW, RESOLVE_GENERATED_CONTENT}; use layout_debug; use model::IntrinsicISizesContribution; @@ -23,20 +21,21 @@ use geom::{Point2D, Rect}; use gfx::font::FontMetrics; use gfx::font_context::FontContext; use gfx::text::glyph::CharIndex; -use util::arc_ptr_eq; -use util::geometry::{Au, ZERO_RECT}; -use util::logical_geometry::{LogicalRect, LogicalSize, WritingMode}; -use util::range::{Range, RangeIndex}; +use gfx::text::text_run::TextRun; use std::cmp::max; use std::fmt; use std::mem; use std::num::ToPrimitive; use std::ops::{Add, Sub, Mul, Div, Rem, Neg, Shl, Shr, Not, BitOr, BitAnd, BitXor}; +use std::sync::Arc; use std::u16; use style::computed_values::{overflow_x, text_align, text_justify, text_overflow, vertical_align}; use style::computed_values::{white_space}; use style::properties::ComputedValues; -use std::sync::Arc; +use util::geometry::{Au, MAX_AU, ZERO_RECT}; +use util::logical_geometry::{LogicalRect, LogicalSize, WritingMode}; +use util::range::{Range, RangeIndex}; +use util; // From gfxFontConstants.h in Firefox static FONT_SUBSCRIPT_OFFSET_RATIO: f64 = 0.20; @@ -160,7 +159,9 @@ int_range_index! { bitflags! { flags InlineReflowFlags: u8 { #[doc="The `white-space: nowrap` property from CSS 2.1 § 16.6 is in effect."] - const NO_WRAP_INLINE_REFLOW_FLAG = 0x01 + const NO_WRAP_INLINE_REFLOW_FLAG = 0x01, + #[doc="The `white-space: pre` property from CSS 2.1 § 16.6 is in effect."] + const WRAP_ON_NEWLINE_INLINE_REFLOW_FLAG = 0x02 } } @@ -191,7 +192,7 @@ impl LineBreaker { pending_line: Line { range: Range::empty(), bounds: LogicalRect::zero(float_context.writing_mode), - green_zone: LogicalSize::zero(float_context.writing_mode) + green_zone: LogicalSize::zero(float_context.writing_mode), }, floats: float_context, lines: Vec::new(), @@ -216,7 +217,7 @@ impl LineBreaker { self.cur_b, Au(0), Au(0)); - self.pending_line.green_zone = LogicalSize::zero(self.floats.writing_mode) + self.pending_line.green_zone = LogicalSize::zero(self.floats.writing_mode); } /// Reflows fragments for the given inline flow. @@ -226,23 +227,13 @@ impl LineBreaker { // Create our fragment iterator. debug!("LineBreaker: scanning for lines, {} fragments", flow.fragments.len()); let mut old_fragments = mem::replace(&mut flow.fragments, InlineFragments::new()); - let mut old_fragment_iter = old_fragments.fragments.into_iter(); + let old_fragment_iter = old_fragments.fragments.into_iter(); - // Set up our initial line state with the clean lines from a previous reflow. - // // TODO(pcwalton): This would likely be better as a list of dirty line indices. That way we // could resynchronize if we discover during reflow that all subsequent fragments must have // the same position as they had in the previous reflow. I don't know how common this case // really is in practice, but it's probably worth handling. - self.lines = mem::replace(&mut flow.lines, Vec::new()); - match self.lines.as_slice().last() { - None => {} - Some(last_line) => { - for _ in range(FragmentIndex(0), last_line.range.end()) { - self.new_fragments.push(old_fragment_iter.next().unwrap()) - } - } - } + self.lines = Vec::new(); // Do the reflow. self.reflow_fragments(old_fragment_iter, flow, layout_context); @@ -270,30 +261,14 @@ impl LineBreaker { // Set up our reflow flags. let flags = match fragment.style().get_inheritedtext().white_space { white_space::T::normal => InlineReflowFlags::empty(), - white_space::T::pre | white_space::T::nowrap => NO_WRAP_INLINE_REFLOW_FLAG, + white_space::T::nowrap => NO_WRAP_INLINE_REFLOW_FLAG, + white_space::T::pre => { + WRAP_ON_NEWLINE_INLINE_REFLOW_FLAG | NO_WRAP_INLINE_REFLOW_FLAG + } }; - // Try to append the fragment, and commit the line (so we can try again with the next - // line) if we couldn't. - match fragment.style().get_inheritedtext().white_space { - white_space::T::normal | white_space::T::nowrap => { - if !self.append_fragment_to_line_if_possible(fragment, - flow, - layout_context, - flags) { - self.flush_current_line() - } - } - white_space::T::pre => { - // FIXME(pcwalton): Surely we can unify - // `append_fragment_to_line_if_possible` and - // `try_append_to_line_by_new_line` by adding another bit in the reflow - // flags. - if !self.try_append_to_line_by_new_line(layout_context, fragment) { - self.flush_current_line() - } - } - } + // Try to append the fragment. + self.reflow_fragment(fragment, flow, layout_context, flags); } if !self.pending_line_is_empty() { @@ -301,37 +276,25 @@ impl LineBreaker { self.lines.len()); self.flush_current_line() } - - // Strip trailing whitespace from the last line if necessary. - if let Some(ref mut last_line) = self.lines.last_mut() { - if let Some(ref mut last_fragment) = self.new_fragments.last_mut() { - let previous_inline_size = last_line.bounds.size.inline - - last_fragment.border_box.size.inline; - if last_fragment.strip_trailing_whitespace_if_necessary() { - last_line.bounds.size.inline = previous_inline_size + - last_fragment.border_box.size.inline; - } - } - } } /// Acquires a new fragment to lay out from the work list or fragment list as appropriate. - /// Note that you probably don't want to call this method directly in order to be - /// incremental-reflow-safe; try `next_unbroken_fragment` instead. + /// If the fragment was at the end of an old line, undoes the line break for that fragment. + /// Note that you probably don't want to call this method directly in order to be incremental- + /// reflow-safe; try `next_unbroken_fragment` instead. fn next_fragment(&mut self, old_fragment_iter: &mut I) -> Option where I: Iterator { + let mut fragment; if self.work_list.is_empty() { - return match old_fragment_iter.next() { - None => None, - Some(fragment) => { - debug!("LineBreaker: working with fragment from flow: {:?}", fragment); - Some(fragment) - } + match old_fragment_iter.next() { + None => return None, + Some(this_fragment) => fragment = this_fragment, } + } else { + return self.work_list.pop_front() } - debug!("LineBreaker: working with fragment from work list: {:?}", self.work_list.front()); - self.work_list.pop_front() + Some(fragment) } /// Acquires a new fragment to lay out from the work list or fragment list, merging it with any @@ -346,10 +309,6 @@ impl LineBreaker { }; loop { - // FIXME(pcwalton): Yuck! I hate this `new_line_pos` stuff. Can we avoid having to do - // this? - result.restore_new_line_pos(); - let candidate = match self.next_fragment(old_fragment_iter) { None => return Some(result), Some(fragment) => fragment, @@ -357,31 +316,58 @@ impl LineBreaker { let need_to_merge = match (&mut result.specific, &candidate.specific) { (&mut SpecificFragmentInfo::ScannedText(ref mut result_info), - &SpecificFragmentInfo::ScannedText(ref candidate_info)) - if arc_ptr_eq(&result_info.run, &candidate_info.run) && - result_info.range.end() + CharIndex(1) == candidate_info.range.begin() => { - // We found a previously-broken fragment. Merge it up. - result_info.range.extend_by(candidate_info.range.length() + CharIndex(1)); - true + &SpecificFragmentInfo::ScannedText(ref candidate_info)) => { + util::arc_ptr_eq(&result_info.run, &candidate_info.run) && + inline_contexts_are_equal(&result.inline_context, + &candidate.inline_context) } _ => false, }; - if !need_to_merge { - self.work_list.push_front(candidate); - return Some(result) + + if need_to_merge { + result.merge_with(candidate); + continue } + + self.work_list.push_front(candidate); + return Some(result) } } /// Commits a line to the list. fn flush_current_line(&mut self) { debug!("LineBreaker: flushing line {}: {:?}", self.lines.len(), self.pending_line); + self.strip_trailing_whitespace_from_pending_line_if_necessary(); self.lines.push(self.pending_line); self.cur_b = self.pending_line.bounds.start.b + self.pending_line.bounds.size.block; self.reset_line(); } + /// Removes trailing whitespace from the pending line if necessary. This is done right before + /// flushing it. + fn strip_trailing_whitespace_from_pending_line_if_necessary(&mut self) { + if self.pending_line.range.is_empty() { + return + } + let last_fragment_index = self.pending_line.range.end() - FragmentIndex(1); + let mut fragment = &mut self.new_fragments[last_fragment_index.get() as usize]; + if let SpecificFragmentInfo::ScannedText(ref mut scanned_text_fragment_info) = + fragment.specific { + let scanned_text_fragment_info = &mut **scanned_text_fragment_info; + let mut range = &mut scanned_text_fragment_info.range; + strip_trailing_whitespace_if_necessary(&**scanned_text_fragment_info.run, range); + + let old_fragment_inline_size = fragment.border_box.size.inline; + scanned_text_fragment_info.content_size.inline = + scanned_text_fragment_info.run.metrics_for_range(range).advance_width; + fragment.border_box.size.inline = scanned_text_fragment_info.content_size.inline + + fragment.border_padding.inline_start_end(); + self.pending_line.bounds.size.inline = self.pending_line.bounds.size.inline - + (old_fragment_inline_size - fragment.border_box.size.inline) + } + } + // FIXME(eatkinson): this assumes that the tallest fragment in the line determines the line // block-size. This might not be the case with some weird text fonts. fn new_block_size_for_line(&self, new_fragment: &Fragment, layout_context: &LayoutContext) @@ -488,71 +474,16 @@ impl LineBreaker { false } - /// Tries to append the given fragment to the line for `pre`-formatted text, splitting it if - /// necessary. Returns true if we successfully pushed the fragment to the line or false if we - /// couldn't. - fn try_append_to_line_by_new_line(&mut self, - layout_context: &LayoutContext, - in_fragment: Fragment) - -> bool { - let should_push = match in_fragment.newline_positions() { - None => true, - Some(ref positions) => positions.is_empty(), - }; - if should_push { - debug!("LineBreaker: did not find a newline character; pushing the fragment to \ - the line without splitting"); - self.push_fragment_to_line(layout_context, in_fragment); - return true - } - - debug!("LineBreaker: Found a new-line character, so splitting the line."); - - let (inline_start, inline_end, run) = - in_fragment.find_split_info_by_new_line() - .expect("LineBreaker: this split case makes no sense!"); - let writing_mode = self.floats.writing_mode; - - let split_fragment = |split: SplitInfo| { - let size = LogicalSize::new(writing_mode, - split.inline_size, - in_fragment.border_box.size.block); - let info = box ScannedTextFragmentInfo::new(run.clone(), - split.range, - (*in_fragment.newline_positions() - .unwrap()).clone(), - size); - in_fragment.transform(size, SpecificFragmentInfo::ScannedText(info)) - }; - - debug!("LineBreaker: Pushing the fragment to the inline_start of the new-line character \ - to the line."); - let mut inline_start = split_fragment(inline_start); - inline_start.save_new_line_pos(); - *inline_start.newline_positions_mut().unwrap() = vec![]; - self.push_fragment_to_line(layout_context, inline_start); - - for inline_end in inline_end.into_iter() { - debug!("LineBreaker: Deferring the fragment to the inline_end of the new-line \ - character to the line."); - let mut inline_end = split_fragment(inline_end); - inline_end.newline_positions_mut().unwrap().remove(0); - self.work_list.push_front(inline_end); - } - - false - } - - /// Tries to append the given fragment to the line, splitting it if necessary. Returns true if - /// we successfully pushed the fragment to the line or false if we couldn't. - fn append_fragment_to_line_if_possible(&mut self, - fragment: Fragment, - flow: &InlineFlow, - layout_context: &LayoutContext, - flags: InlineReflowFlags) - -> bool { + /// Tries to append the given fragment to the line, splitting it if necessary. Commits the + /// current line if needed. + fn reflow_fragment(&mut self, + mut fragment: Fragment, + flow: &InlineFlow, + layout_context: &LayoutContext, + flags: InlineReflowFlags) { // Determine initial placement for the fragment if we need to. if self.pending_line_is_empty() { + fragment.strip_leading_whitespace_if_necessary(); let (line_bounds, _) = self.initial_line_placement(flow, &fragment, self.cur_b); self.pending_line.bounds.start = line_bounds.start; self.pending_line.green_zone = line_bounds.size; @@ -572,9 +503,22 @@ impl LineBreaker { let new_block_size = self.new_block_size_for_line(&fragment, layout_context); if new_block_size > green_zone.block { // Uh-oh. Float collision imminent. Enter the float collision avoider! - return self.avoid_floats(flow, fragment, new_block_size) + if !self.avoid_floats(flow, fragment, new_block_size) { + self.flush_current_line(); + } + return } + // If we must flush the line after finishing this fragment due to `white-space: pre`, + // detect that. + let line_flush_mode = + if flags.contains(WRAP_ON_NEWLINE_INLINE_REFLOW_FLAG) && + fragment.requires_line_break_afterward_if_wrapping_on_newlines() { + LineFlushMode::Flush + } else { + LineFlushMode::No + }; + // If we're not going to overflow the green zone vertically, we might still do so // horizontally. We'll try to place the whole fragment on this line and break somewhere if // it doesn't fit. @@ -583,23 +527,27 @@ impl LineBreaker { fragment.border_box.size.inline + indentation; if new_inline_size <= green_zone.inline { debug!("LineBreaker: fragment fits without splitting"); - self.push_fragment_to_line(layout_context, fragment); - return true + self.push_fragment_to_line(layout_context, fragment, line_flush_mode); + return } // If we can't split the fragment or aren't allowed to because of the wrapping mode, then // just overflow. if (!fragment.can_split() && self.pending_line_is_empty()) || - flags.contains(NO_WRAP_INLINE_REFLOW_FLAG) { + (flags.contains(NO_WRAP_INLINE_REFLOW_FLAG) && + !flags.contains(WRAP_ON_NEWLINE_INLINE_REFLOW_FLAG)) { debug!("LineBreaker: fragment can't split and line {} is empty, so overflowing", self.lines.len()); - self.push_fragment_to_line(layout_context, fragment); - return false + self.push_fragment_to_line(layout_context, fragment, LineFlushMode::No); + return } // Split it up! - let available_inline_size = green_zone.inline - self.pending_line.bounds.size.inline - - indentation; + let available_inline_size = if !flags.contains(NO_WRAP_INLINE_REFLOW_FLAG) { + green_zone.inline - self.pending_line.bounds.size.inline - indentation + } else { + MAX_AU + }; let inline_start_fragment; let inline_end_fragment; let split_result = match fragment.calculate_split_position(available_inline_size, @@ -607,7 +555,8 @@ impl LineBreaker { None => { debug!("LineBreaker: fragment was unsplittable; deferring to next line"); self.work_list.push_front(fragment); - return false + self.flush_current_line(); + return } Some(split_result) => split_result, }; @@ -623,23 +572,30 @@ impl LineBreaker { // the second fragment. If there's no second fragment, the next line will start off empty. match (inline_start_fragment, inline_end_fragment) { (Some(inline_start_fragment), Some(inline_end_fragment)) => { - self.push_fragment_to_line(layout_context, inline_start_fragment); - self.flush_current_line(); + self.push_fragment_to_line(layout_context, + inline_start_fragment, + LineFlushMode::Flush); self.work_list.push_front(inline_end_fragment) }, (Some(fragment), None) => { - self.push_fragment_to_line(layout_context, fragment); + self.push_fragment_to_line(layout_context, fragment, line_flush_mode); + } + (None, Some(fragment)) => { + // Yes, this can happen! + self.flush_current_line(); + self.work_list.push_front(fragment) } - (None, Some(_)) => debug_assert!(false, "un-normalized split result"), (None, None) => {} } - - true } /// Pushes a fragment to the current line unconditionally, possibly truncating it and placing - /// an ellipsis based on the value of `text-overflow`. - fn push_fragment_to_line(&mut self, layout_context: &LayoutContext, fragment: Fragment) { + /// an ellipsis based on the value of `text-overflow`. If `flush_line` is `Flush`, then flushes + /// the line afterward; + fn push_fragment_to_line(&mut self, + layout_context: &LayoutContext, + fragment: Fragment, + line_flush_mode: LineFlushMode) { let indentation = self.indentation_for_pending_fragment(); if self.pending_line_is_empty() { assert!(self.new_fragments.len() <= (u16::MAX as usize)); @@ -661,25 +617,27 @@ impl LineBreaker { if !need_ellipsis { self.push_fragment_to_line_ignoring_text_overflow(fragment); - return + } else { + let ellipsis = fragment.transform_into_ellipsis(layout_context); + if let Some(truncation_info) = + fragment.truncate_to_inline_size(available_inline_size - + ellipsis.border_box.size.inline) { + let fragment = fragment.transform_with_split_info(&truncation_info.split, + truncation_info.text_run); + self.push_fragment_to_line_ignoring_text_overflow(fragment); + } + self.push_fragment_to_line_ignoring_text_overflow(ellipsis); } - let ellipsis = fragment.transform_into_ellipsis(layout_context); - if let Some(truncation_info) = - fragment.truncate_to_inline_size(available_inline_size - - ellipsis.border_box.size.inline) { - let fragment = fragment.transform_with_split_info(&truncation_info.split, - truncation_info.text_run); - self.push_fragment_to_line_ignoring_text_overflow(fragment); + if line_flush_mode == LineFlushMode::Flush { + self.flush_current_line() } - self.push_fragment_to_line_ignoring_text_overflow(ellipsis); } /// Pushes a fragment to the current line unconditionally, without placing an ellipsis in the /// case of `text-overflow: ellipsis`. fn push_fragment_to_line_ignoring_text_overflow(&mut self, fragment: Fragment) { let indentation = self.indentation_for_pending_fragment(); - self.pending_line.range.extend_by(FragmentIndex(1)); self.pending_line.bounds.size.inline = self.pending_line.bounds.size.inline + fragment.border_box.size.inline + @@ -813,7 +771,7 @@ impl InlineFlow { /// /// The extra boolean is set if and only if `largest_block_size_for_top_fragments` and/or /// `largest_block_size_for_bottom_fragments` were updated. That is, if the box has a `top` or - /// `bottom` value for `vertical-align, true is returned. + /// `bottom` value for `vertical-align`, true is returned. fn distance_from_baseline(fragment: &Fragment, ascent: Au, parent_text_block_start: Au, @@ -1180,7 +1138,7 @@ impl Flow for InlineFlow { // Reset our state, so that we handle incremental reflow correctly. // // TODO(pcwalton): Do something smarter, like Gecko and WebKit? - self.lines = Vec::new(); + self.lines.clear(); // Determine how much indentation the first line wants. let mut indentation = if self.fragments.is_empty() { @@ -1431,6 +1389,30 @@ impl InlineFragmentContext { styles: vec!() } } + + fn ptr_eq(&self, other: &InlineFragmentContext) -> bool { + if self.styles.len() != other.styles.len() { + return false + } + for (this_style, other_style) in self.styles.iter().zip(other.styles.iter()) { + if !util::arc_ptr_eq(this_style, other_style) { + return false + } + } + true + } +} + +fn inline_contexts_are_equal(inline_context_a: &Option, + inline_context_b: &Option) + -> bool { + match (inline_context_a, inline_context_b) { + (&Some(ref inline_context_a), &Some(ref inline_context_b)) => { + inline_context_a.ptr_eq(inline_context_b) + } + (&None, &None) => true, + (&Some(_), &None) | (&None, &Some(_)) => false, + } } /// Block-size above the baseline, depth below the baseline, and ascent for a fragment. See CSS 2.1 @@ -1464,3 +1446,31 @@ impl InlineMetrics { } } } + +#[derive(Copy, Clone, PartialEq)] +enum LineFlushMode { + No, + Flush, +} + +/// Given a range and a text run, adjusts the range to eliminate trailing whitespace. +fn strip_trailing_whitespace_if_necessary(text_run: &TextRun, range: &mut Range) { + // FIXME(pcwalton): Is there a more clever (i.e. faster) way to do this? + debug!("stripping trailing whitespace: range={:?}, len={}", + range, + text_run.text.chars().count()); + let text = text_run.text.slice_chars(range.begin().to_usize(), range.end().to_usize()); + let mut trailing_whitespace_character_count = 0; + for ch in text.chars().rev() { + if util::str::char_is_whitespace(ch) { + trailing_whitespace_character_count += 1 + } else { + break + } + } + + if trailing_whitespace_character_count != 0 { + range.extend_by(CharIndex(-trailing_whitespace_character_count)); + } +} + diff --git a/components/layout/text.rs b/components/layout/text.rs index 6380a4fe6ce..0844eadb359 100644 --- a/components/layout/text.rs +++ b/components/layout/text.rs @@ -6,7 +6,7 @@ #![deny(unsafe_code)] -use fragment::{Fragment, SpecificFragmentInfo, ScannedTextFragmentInfo}; +use fragment::{Fragment, SpecificFragmentInfo, ScannedTextFragmentInfo, UnscannedTextFragmentInfo}; use inline::InlineFragments; use gfx::font::{DISABLE_KERNING_SHAPING_FLAG, FontMetrics, IGNORE_LIGATURES_SHAPING_FLAG}; @@ -15,18 +15,19 @@ use gfx::font_context::FontContext; use gfx::text::glyph::CharIndex; use gfx::text::text_run::TextRun; use gfx::text::util::{self, CompressionMode}; -use util::linked_list::split_off_head; -use util::geometry::Au; -use util::logical_geometry::{LogicalSize, WritingMode}; -use util::range::Range; -use util::smallvec::{SmallVec, SmallVec1}; +use std::borrow::ToOwned; use std::collections::LinkedList; use std::mem; +use std::sync::Arc; use style::computed_values::{line_height, text_orientation, text_rendering, text_transform}; use style::computed_values::{white_space}; use style::properties::ComputedValues; use style::properties::style_structs::Font as FontStyle; -use std::sync::Arc; +use util::geometry::Au; +use util::linked_list::split_off_head; +use util::logical_geometry::{LogicalSize, WritingMode}; +use util::range::Range; +use util::smallvec::{SmallVec, SmallVec1}; /// A stack-allocated object for scanning an inline flow into `TextRun`-containing `TextFragment`s. pub struct TextRunScanner { @@ -40,7 +41,9 @@ impl TextRunScanner { } } - pub fn scan_for_runs(&mut self, font_context: &mut FontContext, mut fragments: LinkedList) + pub fn scan_for_runs(&mut self, + font_context: &mut FontContext, + mut fragments: LinkedList) -> InlineFragments { debug!("TextRunScanner: scanning {} fragments for text runs...", fragments.len()); @@ -50,12 +53,14 @@ impl TextRunScanner { let mut last_whitespace = true; while !fragments.is_empty() { // Create a clump. + split_first_fragment_at_newline_if_necessary(&mut fragments); self.clump.append(&mut split_off_head(&mut fragments)); while !fragments.is_empty() && self.clump .back() .unwrap() .can_merge_with_fragment(fragments.front() .unwrap()) { + split_first_fragment_at_newline_if_necessary(&mut fragments); self.clump.append(&mut split_off_head(&mut fragments)); } @@ -101,7 +106,6 @@ impl TextRunScanner { // // Concatenate all of the transformed strings together, saving the new character indices. let mut new_ranges: SmallVec1> = SmallVec1::new(); - let mut new_line_positions: SmallVec1 = SmallVec1::new(); let mut char_total = CharIndex(0); let run = { let fontgroup; @@ -137,14 +141,11 @@ impl TextRunScanner { _ => panic!("Expected an unscanned text fragment!"), }; - let mut new_line_pos = Vec::new(); let old_length = CharIndex(run_text.chars().count() as isize); last_whitespace = util::transform_text(in_fragment.as_slice(), compression, last_whitespace, - &mut run_text, - &mut new_line_pos); - new_line_positions.push(NewLinePositions(new_line_pos)); + &mut run_text); let added_chars = CharIndex(run_text.chars().count() as isize) - old_length; new_ranges.push(Range::new(char_total, added_chars)); @@ -200,13 +201,8 @@ impl TextRunScanner { } let text_size = old_fragment.border_box.size; - let &mut NewLinePositions(ref mut new_line_positions) = - new_line_positions.get_mut(logical_offset); let mut new_text_fragment_info = - box ScannedTextFragmentInfo::new(run.clone(), - range, - mem::replace(new_line_positions, Vec::new()), - text_size); + box ScannedTextFragmentInfo::new(run.clone(), range, text_size); let new_metrics = new_text_fragment_info.run.metrics_for_range(&range); let bounding_box_size = bounding_box_for_run_metrics(&new_metrics, old_fragment.style.writing_mode); @@ -270,8 +266,6 @@ impl TextRunScanner { } } -struct NewLinePositions(Vec); - #[inline] fn bounding_box_for_run_metrics(metrics: &RunMetrics, writing_mode: WritingMode) -> LogicalSize { @@ -318,3 +312,45 @@ pub fn line_height_from_style(style: &ComputedValues, metrics: &FontMetrics) -> line_height::T::Length(l) => l } } + +fn split_first_fragment_at_newline_if_necessary(fragments: &mut LinkedList) { + if fragments.len() < 1 { + return + } + + let new_fragment = { + let mut first_fragment = fragments.front_mut().unwrap(); + let string_before; + { + let unscanned_text_fragment_info = match first_fragment.specific { + SpecificFragmentInfo::UnscannedText(ref mut unscanned_text_fragment_info) => { + unscanned_text_fragment_info + } + _ => return, + }; + + if first_fragment.style.get_inheritedtext().white_space != white_space::T::pre { + return + } + + let position = match unscanned_text_fragment_info.text.find('\n') { + Some(position) if position < unscanned_text_fragment_info.text.len() - 1 => { + position + } + Some(_) | None => return, + }; + + string_before = + box unscanned_text_fragment_info.text[..(position + 1)].to_owned(); + unscanned_text_fragment_info.text = + box unscanned_text_fragment_info.text[(position + 1)..].to_owned(); + } + first_fragment.transform(first_fragment.border_box.size, + SpecificFragmentInfo::UnscannedText(UnscannedTextFragmentInfo { + text: string_before, + })) + }; + + fragments.push_front(new_fragment); +} + diff --git a/components/util/str.rs b/components/util/str.rs index e763215633f..a400edb9572 100644 --- a/components/util/str.rs +++ b/components/util/str.rs @@ -39,7 +39,12 @@ pub fn null_str_as_empty_ref<'a>(s: &'a Option) -> &'a str { const WHITESPACE: &'static [char] = &[' ', '\t', '\x0a', '\x0c', '\x0d']; pub fn is_whitespace(s: &str) -> bool { - s.chars().all(|c| WHITESPACE.contains(&c)) + s.chars().all(char_is_whitespace) +} + +#[inline] +pub fn char_is_whitespace(c: char) -> bool { + WHITESPACE.contains(&c) } /// A "space character" according to: diff --git a/tests/ref/basic.list b/tests/ref/basic.list index f09177b5b2f..8d0a4a0e210 100644 --- a/tests/ref/basic.list +++ b/tests/ref/basic.list @@ -151,6 +151,7 @@ flaky_cpu == append_style_a.html append_style_b.html != img_simple.html img_simple_ref.html == img_size_a.html img_size_b.html == incremental_float_a.html incremental_float_ref.html +== incremental_inline_layout_a.html incremental_inline_layout_ref.html != inline_background_a.html inline_background_ref.html == inline_block_baseline_a.html inline_block_baseline_ref.html == inline_block_border_a.html inline_block_border_ref.html @@ -184,6 +185,7 @@ flaky_cpu == append_style_a.html append_style_b.html == legacy_td_bgcolor_attribute_a.html legacy_td_bgcolor_attribute_ref.html == legacy_td_width_attribute_a.html legacy_td_width_attribute_ref.html == letter_spacing_a.html letter_spacing_ref.html +== line_breaking_whitespace_collapse_a.html line_breaking_whitespace_collapse_ref.html == line_height_a.html line_height_ref.html != linear_gradients_corners_a.html linear_gradients_corners_ref.html == linear_gradients_lengths_a.html linear_gradients_lengths_ref.html diff --git a/tests/ref/incremental_inline_layout_a.html b/tests/ref/incremental_inline_layout_a.html new file mode 100644 index 00000000000..cbd2d932071 --- /dev/null +++ b/tests/ref/incremental_inline_layout_a.html @@ -0,0 +1,32 @@ + + + + + + +

+Incremental Inline Layout +Incremental Inline Layout +Incremental Inline Layout +Incremental Inline Layout +Incremental Inline Layout +Incremental Inline Layout +Incremental Inline Layout +Incremental Inline Layout +Incremental Inline Layout +Incremental Inline Layout +Incremental Inline Layout +Incremental Inline Layout +Incremental Inline Layout +

+ + + + diff --git a/tests/ref/incremental_inline_layout_ref.html b/tests/ref/incremental_inline_layout_ref.html new file mode 100644 index 00000000000..7c42ff75bc5 --- /dev/null +++ b/tests/ref/incremental_inline_layout_ref.html @@ -0,0 +1,28 @@ + + + + + + +

+Incremental Inline Layout +Incremental Inline Layout +Incremental Inline Layout +Incremental Inline Layout +Incremental Inline Layout +Incremental Inline Layout +Incremental Inline Layout +Incremental Inline Layout +Incremental Inline Layout +Incremental Inline Layout +Incremental Inline Layout +Incremental Inline Layout +Incremental Inline Layout +

+ + + diff --git a/tests/ref/line_breaking_whitespace_collapse_a.html b/tests/ref/line_breaking_whitespace_collapse_a.html new file mode 100644 index 00000000000..fdba44158de --- /dev/null +++ b/tests/ref/line_breaking_whitespace_collapse_a.html @@ -0,0 +1,19 @@ + + + + + + + +

xxxxx xxxxx xxxxx xxx xxxxx xxxxxxxxxxx

+ + diff --git a/tests/ref/line_breaking_whitespace_collapse_ref.html b/tests/ref/line_breaking_whitespace_collapse_ref.html new file mode 100644 index 00000000000..c36c0b4068d --- /dev/null +++ b/tests/ref/line_breaking_whitespace_collapse_ref.html @@ -0,0 +1,19 @@ + + + + + + + +

xxxxx xxxxx xxxxx xxx xxxxx
xxxxxxxxxxx

+ + diff --git a/tests/unit/gfx/text_util.rs b/tests/unit/gfx/text_util.rs index 6f63d18316f..f0253179145 100644 --- a/tests/unit/gfx/text_util.rs +++ b/tests/unit/gfx/text_util.rs @@ -18,9 +18,8 @@ fn test_transform_compress_none() { let mode = CompressionMode::CompressNone; for &test in test_strs.iter() { - let mut new_line_pos = vec![]; let mut trimmed_str = String::new(); - transform_text(test, mode, true, &mut trimmed_str, &mut new_line_pos); + transform_text(test, mode, true, &mut trimmed_str); assert_eq!(trimmed_str, test) } } @@ -52,9 +51,8 @@ fn test_transform_discard_newline() { let mode = CompressionMode::DiscardNewline; for &(test, oracle) in test_strs.iter() { - let mut new_line_pos = vec![]; let mut trimmed_str = String::new(); - transform_text(test, mode, true, &mut trimmed_str, &mut new_line_pos); + transform_text(test, mode, true, &mut trimmed_str); assert_eq!(trimmed_str, oracle) } } @@ -86,9 +84,8 @@ fn test_transform_compress_whitespace() { let mode = CompressionMode::CompressWhitespace; for &(test, oracle) in test_strs.iter() { - let mut new_line_pos = vec![]; let mut trimmed_str = String::new(); - transform_text(test, mode, true, &mut trimmed_str, &mut new_line_pos); + transform_text(test, mode, true, &mut trimmed_str); assert_eq!(&*trimmed_str, oracle) } } @@ -120,9 +117,8 @@ fn test_transform_compress_whitespace_newline() { let mode = CompressionMode::CompressWhitespaceNewline; for &(test, oracle) in test_strs.iter() { - let mut new_line_pos = vec![]; let mut trimmed_str = String::new(); - transform_text(test, mode, true, &mut trimmed_str, &mut new_line_pos); + transform_text(test, mode, true, &mut trimmed_str); assert_eq!(&*trimmed_str, oracle) } } @@ -157,9 +153,8 @@ fn test_transform_compress_whitespace_newline_no_incoming() { let mode = CompressionMode::CompressWhitespaceNewline; for &(test, oracle) in test_strs.iter() { - let mut new_line_pos = vec![]; let mut trimmed_str = String::new(); - transform_text(test, mode, false, &mut trimmed_str, &mut new_line_pos); + transform_text(test, mode, false, &mut trimmed_str); assert_eq!(trimmed_str, oracle) } } From b9244ec1c2c38ce19cd7f9fe113317837c673b2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1ty=C3=A1s=20Mustoha?= Date: Thu, 9 Apr 2015 11:47:19 +0200 Subject: [PATCH 23/32] Canvas: moved Rect validation to a common function. --- .../script/dom/canvasrenderingcontext2d.rs | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/components/script/dom/canvasrenderingcontext2d.rs b/components/script/dom/canvasrenderingcontext2d.rs index 8213e7afc87..9c7c7a58f2f 100644 --- a/components/script/dom/canvasrenderingcontext2d.rs +++ b/components/script/dom/canvasrenderingcontext2d.rs @@ -258,6 +258,18 @@ impl CanvasRenderingContext2D { _ => panic!("Image Cache: Unknown Result") } } + + fn create_drawable_rect(&self, x: f64, y: f64, w: f64, h: f64) -> Option> { + if !([x, y, w, h].iter().all(|val| val.is_finite())) { + return None; + } + + if w == 0.0 && h == 0.0 { + return None; + } + + Some(Rect(Point2D(x as f32, y as f32), Size2D(w as f32, h as f32))) + } } pub trait CanvasRenderingContext2DHelpers { @@ -358,33 +370,21 @@ impl<'a> CanvasRenderingContext2DMethods for JSRef<'a, CanvasRenderingContext2D> } fn FillRect(self, x: f64, y: f64, width: f64, height: f64) { - if !(x.is_finite() && y.is_finite() && - width.is_finite() && height.is_finite()) { - return; + if let Some(rect) = self.create_drawable_rect(x, y, width, height) { + self.renderer.send(CanvasMsg::FillRect(rect)).unwrap(); } - - let rect = Rect(Point2D(x as f32, y as f32), Size2D(width as f32, height as f32)); - self.renderer.send(CanvasMsg::FillRect(rect)).unwrap(); } fn ClearRect(self, x: f64, y: f64, width: f64, height: f64) { - if !(x.is_finite() && y.is_finite() && - width.is_finite() && height.is_finite()) { - return; + if let Some(rect) = self.create_drawable_rect(x, y, width, height) { + self.renderer.send(CanvasMsg::ClearRect(rect)).unwrap(); } - - let rect = Rect(Point2D(x as f32, y as f32), Size2D(width as f32, height as f32)); - self.renderer.send(CanvasMsg::ClearRect(rect)).unwrap(); } fn StrokeRect(self, x: f64, y: f64, width: f64, height: f64) { - if !(x.is_finite() && y.is_finite() && - width.is_finite() && height.is_finite()) { - return; + if let Some(rect) = self.create_drawable_rect(x, y, width, height) { + self.renderer.send(CanvasMsg::StrokeRect(rect)).unwrap(); } - - let rect = Rect(Point2D(x as f32, y as f32), Size2D(width as f32, height as f32)); - self.renderer.send(CanvasMsg::StrokeRect(rect)).unwrap(); } fn BeginPath(self) { From 93fa9e607a12f1b32b017bb94d0f01f5a0ea1607 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1ty=C3=A1s=20Mustoha?= Date: Thu, 9 Apr 2015 10:20:55 +0200 Subject: [PATCH 24/32] Canvas: added miterLimit support. --- components/canvas/canvas_paint_task.rs | 8 +++++++- components/script/dom/canvasrenderingcontext2d.rs | 15 +++++++++++++++ .../dom/webidls/CanvasRenderingContext2D.webidl | 2 +- .../2d.strokeRect.zero.4.html.ini | 5 +++++ .../2d.strokeRect.zero.5.html.ini | 5 ----- .../line-styles/2d.line.cap.closed.html.ini | 5 +++++ .../line-styles/2d.line.join.bevel.html.ini | 5 +++++ .../line-styles/2d.line.join.closed.html.ini | 5 ----- .../line-styles/2d.line.join.miter.html.ini | 5 ----- .../line-styles/2d.line.miter.acute.html.ini | 5 ----- .../line-styles/2d.line.miter.invalid.html.ini | 5 ----- .../line-styles/2d.line.miter.obtuse.html.ini | 5 ----- .../line-styles/2d.line.miter.valid.html.ini | 5 ----- .../line-styles/2d.line.miter.within.html.ini | 5 ----- tests/wpt/metadata/html/dom/interfaces.html.ini | 6 ------ 15 files changed, 38 insertions(+), 48 deletions(-) create mode 100644 tests/wpt/metadata/2dcontext/drawing-rectangles-to-the-canvas/2d.strokeRect.zero.4.html.ini delete mode 100644 tests/wpt/metadata/2dcontext/drawing-rectangles-to-the-canvas/2d.strokeRect.zero.5.html.ini create mode 100644 tests/wpt/metadata/2dcontext/line-styles/2d.line.cap.closed.html.ini create mode 100644 tests/wpt/metadata/2dcontext/line-styles/2d.line.join.bevel.html.ini delete mode 100644 tests/wpt/metadata/2dcontext/line-styles/2d.line.join.closed.html.ini delete mode 100644 tests/wpt/metadata/2dcontext/line-styles/2d.line.join.miter.html.ini delete mode 100644 tests/wpt/metadata/2dcontext/line-styles/2d.line.miter.acute.html.ini delete mode 100644 tests/wpt/metadata/2dcontext/line-styles/2d.line.miter.invalid.html.ini delete mode 100644 tests/wpt/metadata/2dcontext/line-styles/2d.line.miter.obtuse.html.ini delete mode 100644 tests/wpt/metadata/2dcontext/line-styles/2d.line.miter.valid.html.ini delete mode 100644 tests/wpt/metadata/2dcontext/line-styles/2d.line.miter.within.html.ini diff --git a/components/canvas/canvas_paint_task.rs b/components/canvas/canvas_paint_task.rs index 76af23fa44f..150240bc722 100644 --- a/components/canvas/canvas_paint_task.rs +++ b/components/canvas/canvas_paint_task.rs @@ -39,6 +39,7 @@ pub enum CanvasMsg { SetFillStyle(FillOrStrokeStyle), SetStrokeStyle(FillOrStrokeStyle), SetLineWidth(f32), + SetMiterLimit(f32), SetTransform(Matrix2D), SetGlobalAlpha(f32), Recreate(Size2D), @@ -202,7 +203,7 @@ impl<'a> CanvasPaintTask<'a> { draw_options: DrawOptions::new(1.0, 0), fill_style: Pattern::Color(ColorPattern::new(color::black())), stroke_style: Pattern::Color(ColorPattern::new(color::black())), - stroke_opts: StrokeOptions::new(1.0, JoinStyle::MiterOrBevel, CapStyle::Butt, 1.0, &[]), + stroke_opts: StrokeOptions::new(1.0, JoinStyle::MiterOrBevel, CapStyle::Butt, 10.0, &[]), path_builder: path_builder, transform: Matrix2D::identity(), } @@ -245,6 +246,7 @@ impl<'a> CanvasPaintTask<'a> { CanvasMsg::SetFillStyle(style) => painter.set_fill_style(style), CanvasMsg::SetStrokeStyle(style) => painter.set_stroke_style(style), CanvasMsg::SetLineWidth(width) => painter.set_line_width(width), + CanvasMsg::SetMiterLimit(limit) => painter.set_miter_limit(limit), CanvasMsg::SetTransform(ref matrix) => painter.set_transform(matrix), CanvasMsg::SetGlobalAlpha(alpha) => painter.set_global_alpha(alpha), CanvasMsg::Recreate(size) => painter.recreate(size), @@ -425,6 +427,10 @@ impl<'a> CanvasPaintTask<'a> { self.stroke_opts.line_width = width; } + fn set_miter_limit(&mut self, limit: f32) { + self.stroke_opts.miter_limit = limit; + } + fn set_transform(&mut self, transform: &Matrix2D) { self.transform = *transform; self.drawtarget.set_transform(transform) diff --git a/components/script/dom/canvasrenderingcontext2d.rs b/components/script/dom/canvasrenderingcontext2d.rs index 8213e7afc87..d079ce209c6 100644 --- a/components/script/dom/canvasrenderingcontext2d.rs +++ b/components/script/dom/canvasrenderingcontext2d.rs @@ -53,6 +53,7 @@ pub struct CanvasRenderingContext2D { image_smoothing_enabled: Cell, stroke_color: Cell, line_width: Cell, + miter_limit: Cell, fill_color: Cell, transform: Cell>, } @@ -75,6 +76,7 @@ impl CanvasRenderingContext2D { image_smoothing_enabled: Cell::new(true), stroke_color: Cell::new(black), line_width: Cell::new(1.0), + miter_limit: Cell::new(10.0), fill_color: Cell::new(black), transform: Cell::new(Matrix2D::identity()), } @@ -816,6 +818,19 @@ impl<'a> CanvasRenderingContext2DMethods for JSRef<'a, CanvasRenderingContext2D> self.line_width.set(width); self.renderer.send(CanvasMsg::SetLineWidth(width as f32)).unwrap() } + + fn MiterLimit(self) -> f64 { + self.miter_limit.get() + } + + fn SetMiterLimit(self, limit: f64) { + if !limit.is_finite() || limit <= 0.0 { + return; + } + + self.miter_limit.set(limit); + self.renderer.send(CanvasMsg::SetMiterLimit(limit as f32)).unwrap() + } } #[unsafe_destructor] diff --git a/components/script/dom/webidls/CanvasRenderingContext2D.webidl b/components/script/dom/webidls/CanvasRenderingContext2D.webidl index 6808e3c7d24..4c4b94374f2 100644 --- a/components/script/dom/webidls/CanvasRenderingContext2D.webidl +++ b/components/script/dom/webidls/CanvasRenderingContext2D.webidl @@ -136,7 +136,7 @@ interface CanvasDrawingStyles { attribute unrestricted double lineWidth; // (default 1) //attribute DOMString lineCap; // "butt", "round", "square" (default "butt") //attribute DOMString lineJoin; // "round", "bevel", "miter" (default "miter") - //attribute unrestricted double miterLimit; // (default 10) + attribute unrestricted double miterLimit; // (default 10) // dashed lines //void setLineDash(sequence segments); // default empty diff --git a/tests/wpt/metadata/2dcontext/drawing-rectangles-to-the-canvas/2d.strokeRect.zero.4.html.ini b/tests/wpt/metadata/2dcontext/drawing-rectangles-to-the-canvas/2d.strokeRect.zero.4.html.ini new file mode 100644 index 00000000000..31169ceaf88 --- /dev/null +++ b/tests/wpt/metadata/2dcontext/drawing-rectangles-to-the-canvas/2d.strokeRect.zero.4.html.ini @@ -0,0 +1,5 @@ +[2d.strokeRect.zero.4.html] + type: testharness + [strokeRect of Nx0 pixels draws a closed line with no caps] + expected: FAIL + diff --git a/tests/wpt/metadata/2dcontext/drawing-rectangles-to-the-canvas/2d.strokeRect.zero.5.html.ini b/tests/wpt/metadata/2dcontext/drawing-rectangles-to-the-canvas/2d.strokeRect.zero.5.html.ini deleted file mode 100644 index b08134d8578..00000000000 --- a/tests/wpt/metadata/2dcontext/drawing-rectangles-to-the-canvas/2d.strokeRect.zero.5.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[2d.strokeRect.zero.5.html] - type: testharness - [strokeRect of Nx0 pixels draws a closed line with joins] - expected: FAIL - diff --git a/tests/wpt/metadata/2dcontext/line-styles/2d.line.cap.closed.html.ini b/tests/wpt/metadata/2dcontext/line-styles/2d.line.cap.closed.html.ini new file mode 100644 index 00000000000..20856efda4e --- /dev/null +++ b/tests/wpt/metadata/2dcontext/line-styles/2d.line.cap.closed.html.ini @@ -0,0 +1,5 @@ +[2d.line.cap.closed.html] + type: testharness + [Line caps are not drawn at the corners of an unclosed rectangle] + expected: FAIL + diff --git a/tests/wpt/metadata/2dcontext/line-styles/2d.line.join.bevel.html.ini b/tests/wpt/metadata/2dcontext/line-styles/2d.line.join.bevel.html.ini new file mode 100644 index 00000000000..4f618507159 --- /dev/null +++ b/tests/wpt/metadata/2dcontext/line-styles/2d.line.join.bevel.html.ini @@ -0,0 +1,5 @@ +[2d.line.join.bevel.html] + type: testharness + [lineJoin \'bevel\' is rendered correctly] + expected: FAIL + diff --git a/tests/wpt/metadata/2dcontext/line-styles/2d.line.join.closed.html.ini b/tests/wpt/metadata/2dcontext/line-styles/2d.line.join.closed.html.ini deleted file mode 100644 index 15f8a96aea1..00000000000 --- a/tests/wpt/metadata/2dcontext/line-styles/2d.line.join.closed.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[2d.line.join.closed.html] - type: testharness - [Line joins are drawn at the corner of a closed rectangle] - expected: FAIL - diff --git a/tests/wpt/metadata/2dcontext/line-styles/2d.line.join.miter.html.ini b/tests/wpt/metadata/2dcontext/line-styles/2d.line.join.miter.html.ini deleted file mode 100644 index 9defd450bd7..00000000000 --- a/tests/wpt/metadata/2dcontext/line-styles/2d.line.join.miter.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[2d.line.join.miter.html] - type: testharness - [lineJoin \'miter\' is rendered correctly] - expected: FAIL - diff --git a/tests/wpt/metadata/2dcontext/line-styles/2d.line.miter.acute.html.ini b/tests/wpt/metadata/2dcontext/line-styles/2d.line.miter.acute.html.ini deleted file mode 100644 index 8a4bb47701b..00000000000 --- a/tests/wpt/metadata/2dcontext/line-styles/2d.line.miter.acute.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[2d.line.miter.acute.html] - type: testharness - [Miter joins are drawn correctly with acute angles] - expected: FAIL - diff --git a/tests/wpt/metadata/2dcontext/line-styles/2d.line.miter.invalid.html.ini b/tests/wpt/metadata/2dcontext/line-styles/2d.line.miter.invalid.html.ini deleted file mode 100644 index 90ea3c99711..00000000000 --- a/tests/wpt/metadata/2dcontext/line-styles/2d.line.miter.invalid.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[2d.line.miter.invalid.html] - type: testharness - [Setting miterLimit to invalid values is ignored] - expected: FAIL - diff --git a/tests/wpt/metadata/2dcontext/line-styles/2d.line.miter.obtuse.html.ini b/tests/wpt/metadata/2dcontext/line-styles/2d.line.miter.obtuse.html.ini deleted file mode 100644 index 8e0a2db94f8..00000000000 --- a/tests/wpt/metadata/2dcontext/line-styles/2d.line.miter.obtuse.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[2d.line.miter.obtuse.html] - type: testharness - [Miter joins are drawn correctly with obtuse angles] - expected: FAIL - diff --git a/tests/wpt/metadata/2dcontext/line-styles/2d.line.miter.valid.html.ini b/tests/wpt/metadata/2dcontext/line-styles/2d.line.miter.valid.html.ini deleted file mode 100644 index 1d0e60231c0..00000000000 --- a/tests/wpt/metadata/2dcontext/line-styles/2d.line.miter.valid.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[2d.line.miter.valid.html] - type: testharness - [Setting miterLimit to valid values works] - expected: FAIL - diff --git a/tests/wpt/metadata/2dcontext/line-styles/2d.line.miter.within.html.ini b/tests/wpt/metadata/2dcontext/line-styles/2d.line.miter.within.html.ini deleted file mode 100644 index 2a555ab524e..00000000000 --- a/tests/wpt/metadata/2dcontext/line-styles/2d.line.miter.within.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[2d.line.miter.within.html] - type: testharness - [Miter joins are drawn when the miter limit is not quite exceeded] - expected: FAIL - diff --git a/tests/wpt/metadata/html/dom/interfaces.html.ini b/tests/wpt/metadata/html/dom/interfaces.html.ini index 4c2f7bacd96..e36085a1f73 100644 --- a/tests/wpt/metadata/html/dom/interfaces.html.ini +++ b/tests/wpt/metadata/html/dom/interfaces.html.ini @@ -7005,9 +7005,6 @@ [CanvasRenderingContext2D interface: attribute lineJoin] expected: FAIL - [CanvasRenderingContext2D interface: attribute miterLimit] - expected: FAIL - [CanvasRenderingContext2D interface: operation setLineDash([object Object\])] expected: FAIL @@ -7215,9 +7212,6 @@ [CanvasRenderingContext2D interface: document.createElement("canvas").getContext("2d") must inherit property "lineJoin" with the proper type (61)] expected: FAIL - [CanvasRenderingContext2D interface: document.createElement("canvas").getContext("2d") must inherit property "miterLimit" with the proper type (62)] - expected: FAIL - [CanvasRenderingContext2D interface: document.createElement("canvas").getContext("2d") must inherit property "setLineDash" with the proper type (63)] expected: FAIL From 71008d816d071d840c80a63c2cd7fc26afaaff5b Mon Sep 17 00:00:00 2001 From: Ms2ger Date: Thu, 9 Apr 2015 13:39:50 +0200 Subject: [PATCH 25/32] Update web-platform-tests. --- .../2d.shadow.canvas.transparent.2.html.ini | 1 + .../2d.shadow.image.transparent.2.html.ini | 1 + tests/wpt/metadata/MANIFEST.json | 111 ++++++++++++------ .../getresponseheader-chunked-trailer.htm.ini | 5 - .../XMLHttpRequest/timeout-cors-async.htm.ini | 1 - .../CharacterData-substringData.html.ini | 3 + .../dom/nodes/Node-properties.html.ini | 1 + .../frameElement.sub.html.ini | 1 - .../disabledElement.html.ini | 1 + .../srcset/parse-a-srcset-attribute.html.ini | 3 + .../location/members.html.ini | 1 + .../IndexedDB/key_valid.html | 1 + tests/wpt/web-platform-tests/README.md | 12 +- .../XMLHttpRequest/resources/chunked.py | 2 +- .../media-src/media-src-7_1.html | 1 + .../media-src/media-src-7_1_2.html | 1 + .../media-src/media-src-7_2.html | 1 + .../media-src/media-src-7_2_2.html | 1 + .../media-src/media-src-7_3.html | 1 + .../media-src/media-src-7_3_2.html | 1 + .../object-src/object-src-2_1.html | 25 ++-- .../object-src/object-src-2_2.html | 25 ++-- .../support/checkReport.sub.js | 3 +- .../content-security-policy/support/report.py | 33 +++--- .../dom/nodes/CharacterData-appendData.html | 57 +++++++-- .../nodes/CharacterData-substringData.html | 108 +++++++++++++++++ .../dom/nodes/attributes.html | 9 ++ .../fetch/nosniff/image.html | 29 +++++ .../fetch/nosniff/importscripts.html | 14 +++ .../fetch/nosniff/importscripts.js | 17 +++ .../fetch/nosniff/parsing-nosniff.html | 28 +++++ .../fetch/nosniff/resources/css.py | 15 +++ .../fetch/nosniff/resources/image.py | 16 +++ .../fetch/nosniff/resources/js.py | 17 +++ .../nosniff/resources/nosniff-first.asis | 7 ++ .../fetch/nosniff/resources/nosniff-last.asis | 7 ++ .../fetch/nosniff/resources/nosniff-no-x.asis | 6 + .../resources/nosniff-quoted-single.asis | 6 + .../nosniff/resources/nosniff-quoted.asis | 6 + .../nosniff/resources/nosniff-uppercase.asis | 6 + .../fetch/nosniff/resources/worker.py | 16 +++ .../fetch/nosniff/script.html | 32 +++++ .../fetch/nosniff/stylesheet.html | 34 ++++++ .../fetch/nosniff/worker.html | 28 +++++ .../srcset/parse-a-srcset-attribute.html | 5 +- tests/wpt/web-platform-tests/manifest | 4 +- .../web-platform-tests/resources/docs/api.md | 36 ++++++ .../resources/testharness.js | 68 +++++++++++ tests/wpt/web-platform-tests/tools/.gitignore | 6 + .../tools/manifest/update.py | 26 +++- .../web-platform-tests/tools/manifest/vcs.py | 8 +- .../websockets/Close-undefined.htm | 4 +- .../websockets/Secure-Close-undefined.htm | 4 +- .../websockets/cookies/001.html | 6 +- .../websockets/cookies/002.html | 6 +- .../websockets/cookies/003.html | 27 ++--- .../websockets/cookies/004.html | 13 +- .../websockets/cookies/005.html | 12 +- .../websockets/cookies/006.html | 8 +- .../websockets/cookies/007.html | 8 +- .../interfaces/WebSocket/events/013.html | 6 +- .../websockets/websocket.js | 2 +- 62 files changed, 793 insertions(+), 150 deletions(-) delete mode 100644 tests/wpt/metadata/XMLHttpRequest/getresponseheader-chunked-trailer.htm.ini create mode 100644 tests/wpt/metadata/dom/nodes/CharacterData-substringData.html.ini create mode 100644 tests/wpt/web-platform-tests/dom/nodes/CharacterData-substringData.html create mode 100644 tests/wpt/web-platform-tests/fetch/nosniff/image.html create mode 100644 tests/wpt/web-platform-tests/fetch/nosniff/importscripts.html create mode 100644 tests/wpt/web-platform-tests/fetch/nosniff/importscripts.js create mode 100644 tests/wpt/web-platform-tests/fetch/nosniff/parsing-nosniff.html create mode 100644 tests/wpt/web-platform-tests/fetch/nosniff/resources/css.py create mode 100644 tests/wpt/web-platform-tests/fetch/nosniff/resources/image.py create mode 100644 tests/wpt/web-platform-tests/fetch/nosniff/resources/js.py create mode 100644 tests/wpt/web-platform-tests/fetch/nosniff/resources/nosniff-first.asis create mode 100644 tests/wpt/web-platform-tests/fetch/nosniff/resources/nosniff-last.asis create mode 100644 tests/wpt/web-platform-tests/fetch/nosniff/resources/nosniff-no-x.asis create mode 100644 tests/wpt/web-platform-tests/fetch/nosniff/resources/nosniff-quoted-single.asis create mode 100644 tests/wpt/web-platform-tests/fetch/nosniff/resources/nosniff-quoted.asis create mode 100644 tests/wpt/web-platform-tests/fetch/nosniff/resources/nosniff-uppercase.asis create mode 100644 tests/wpt/web-platform-tests/fetch/nosniff/resources/worker.py create mode 100644 tests/wpt/web-platform-tests/fetch/nosniff/script.html create mode 100644 tests/wpt/web-platform-tests/fetch/nosniff/stylesheet.html create mode 100644 tests/wpt/web-platform-tests/fetch/nosniff/worker.html create mode 100644 tests/wpt/web-platform-tests/tools/.gitignore diff --git a/tests/wpt/metadata/2dcontext/shadows/2d.shadow.canvas.transparent.2.html.ini b/tests/wpt/metadata/2dcontext/shadows/2d.shadow.canvas.transparent.2.html.ini index f0485ad1623..b3623a00589 100644 --- a/tests/wpt/metadata/2dcontext/shadows/2d.shadow.canvas.transparent.2.html.ini +++ b/tests/wpt/metadata/2dcontext/shadows/2d.shadow.canvas.transparent.2.html.ini @@ -2,3 +2,4 @@ type: testharness [Shadows are not drawn for transparent parts of canvases] expected: FAIL + diff --git a/tests/wpt/metadata/2dcontext/shadows/2d.shadow.image.transparent.2.html.ini b/tests/wpt/metadata/2dcontext/shadows/2d.shadow.image.transparent.2.html.ini index c4f70567f88..4b5b797c037 100644 --- a/tests/wpt/metadata/2dcontext/shadows/2d.shadow.image.transparent.2.html.ini +++ b/tests/wpt/metadata/2dcontext/shadows/2d.shadow.image.transparent.2.html.ini @@ -2,3 +2,4 @@ type: testharness [Shadows are not drawn for transparent parts of images] expected: FAIL + diff --git a/tests/wpt/metadata/MANIFEST.json b/tests/wpt/metadata/MANIFEST.json index 77a963fdaee..0c4dc0c1164 100644 --- a/tests/wpt/metadata/MANIFEST.json +++ b/tests/wpt/metadata/MANIFEST.json @@ -8955,10 +8955,6 @@ "path": "IndexedDB/key_invalid.htm", "url": "/IndexedDB/key_invalid.htm" }, - { - "path": "IndexedDB/key_valid.html", - "url": "/IndexedDB/key_valid.html" - }, { "path": "IndexedDB/keygenerator-constrainterror.htm", "url": "/IndexedDB/keygenerator-constrainterror.htm" @@ -9819,38 +9815,6 @@ "path": "content-security-policy/img-src/img-src-4_1.html", "url": "/content-security-policy/img-src/img-src-4_1.html" }, - { - "path": "content-security-policy/media-src/media-src-7_1.html", - "url": "/content-security-policy/media-src/media-src-7_1.html" - }, - { - "path": "content-security-policy/media-src/media-src-7_1_2.html", - "url": "/content-security-policy/media-src/media-src-7_1_2.html" - }, - { - "path": "content-security-policy/media-src/media-src-7_2.html", - "url": "/content-security-policy/media-src/media-src-7_2.html" - }, - { - "path": "content-security-policy/media-src/media-src-7_2_2.html", - "url": "/content-security-policy/media-src/media-src-7_2_2.html" - }, - { - "path": "content-security-policy/media-src/media-src-7_3.html", - "url": "/content-security-policy/media-src/media-src-7_3.html" - }, - { - "path": "content-security-policy/media-src/media-src-7_3_2.html", - "url": "/content-security-policy/media-src/media-src-7_3_2.html" - }, - { - "path": "content-security-policy/object-src/object-src-2_1.html", - "url": "/content-security-policy/object-src/object-src-2_1.html" - }, - { - "path": "content-security-policy/object-src/object-src-2_2.html", - "url": "/content-security-policy/object-src/object-src-2_2.html" - }, { "path": "content-security-policy/script-src/script-src-1_1.html", "url": "/content-security-policy/script-src/script-src-1_1.html" @@ -10311,6 +10275,10 @@ "path": "dom/nodes/CharacterData-replaceData.html", "url": "/dom/nodes/CharacterData-replaceData.html" }, + { + "path": "dom/nodes/CharacterData-substringData.html", + "url": "/dom/nodes/CharacterData-substringData.html" + }, { "path": "dom/nodes/Comment-constructor.html", "url": "/dom/nodes/Comment-constructor.html" @@ -11271,6 +11239,30 @@ "path": "ext-xhtml-pubid/the-xhtml-syntax/parsing-xhtml-documents/xhtml-pubid-1.html", "url": "/ext-xhtml-pubid/the-xhtml-syntax/parsing-xhtml-documents/xhtml-pubid-1.html" }, + { + "path": "fetch/nosniff/image.html", + "url": "/fetch/nosniff/image.html" + }, + { + "path": "fetch/nosniff/importscripts.html", + "url": "/fetch/nosniff/importscripts.html" + }, + { + "path": "fetch/nosniff/parsing-nosniff.html", + "url": "/fetch/nosniff/parsing-nosniff.html" + }, + { + "path": "fetch/nosniff/script.html", + "url": "/fetch/nosniff/script.html" + }, + { + "path": "fetch/nosniff/stylesheet.html", + "url": "/fetch/nosniff/stylesheet.html" + }, + { + "path": "fetch/nosniff/worker.html", + "url": "/fetch/nosniff/worker.html" + }, { "path": "gamepad/idlharness.html", "url": "/gamepad/idlharness.html" @@ -18798,6 +18790,11 @@ "timeout": "long", "url": "/IndexedDB/idbobjectstore_createIndex8-valid_keys.htm" }, + { + "path": "IndexedDB/key_valid.html", + "timeout": "long", + "url": "/IndexedDB/key_valid.html" + }, { "path": "IndexedDB/keypath_maxsize.htm", "timeout": "long", @@ -18823,6 +18820,46 @@ "timeout": "long", "url": "/ambient-light/AmbientLight_tests.html" }, + { + "path": "content-security-policy/media-src/media-src-7_1.html", + "timeout": "long", + "url": "/content-security-policy/media-src/media-src-7_1.html" + }, + { + "path": "content-security-policy/media-src/media-src-7_1_2.html", + "timeout": "long", + "url": "/content-security-policy/media-src/media-src-7_1_2.html" + }, + { + "path": "content-security-policy/media-src/media-src-7_2.html", + "timeout": "long", + "url": "/content-security-policy/media-src/media-src-7_2.html" + }, + { + "path": "content-security-policy/media-src/media-src-7_2_2.html", + "timeout": "long", + "url": "/content-security-policy/media-src/media-src-7_2_2.html" + }, + { + "path": "content-security-policy/media-src/media-src-7_3.html", + "timeout": "long", + "url": "/content-security-policy/media-src/media-src-7_3.html" + }, + { + "path": "content-security-policy/media-src/media-src-7_3_2.html", + "timeout": "long", + "url": "/content-security-policy/media-src/media-src-7_3_2.html" + }, + { + "path": "content-security-policy/object-src/object-src-2_1.html", + "timeout": "long", + "url": "/content-security-policy/object-src/object-src-2_1.html" + }, + { + "path": "content-security-policy/object-src/object-src-2_2.html", + "timeout": "long", + "url": "/content-security-policy/object-src/object-src-2_2.html" + }, { "path": "cors/status-async.htm", "timeout": "long", @@ -25034,7 +25071,7 @@ } ] }, - "rev": "0d318188757a9c996e20b82db201fd04de5aa255", + "rev": "2a9fd810bb18610b422dbc3998ab74aa1bffae95", "url_base": "/", "version": 2 } \ No newline at end of file diff --git a/tests/wpt/metadata/XMLHttpRequest/getresponseheader-chunked-trailer.htm.ini b/tests/wpt/metadata/XMLHttpRequest/getresponseheader-chunked-trailer.htm.ini deleted file mode 100644 index 0ee1ddfee54..00000000000 --- a/tests/wpt/metadata/XMLHttpRequest/getresponseheader-chunked-trailer.htm.ini +++ /dev/null @@ -1,5 +0,0 @@ -[getresponseheader-chunked-trailer.htm] - type: testharness - [XMLHttpRequest: getResponseHeader() and HTTP trailer] - expected: FAIL - diff --git a/tests/wpt/metadata/XMLHttpRequest/timeout-cors-async.htm.ini b/tests/wpt/metadata/XMLHttpRequest/timeout-cors-async.htm.ini index 4d37d487bfb..39ed4516c79 100644 --- a/tests/wpt/metadata/XMLHttpRequest/timeout-cors-async.htm.ini +++ b/tests/wpt/metadata/XMLHttpRequest/timeout-cors-async.htm.ini @@ -1,6 +1,5 @@ [timeout-cors-async.htm] type: testharness - expected: OK [XMLHttpRequest: timeout event and cross-origin request] expected: FAIL diff --git a/tests/wpt/metadata/dom/nodes/CharacterData-substringData.html.ini b/tests/wpt/metadata/dom/nodes/CharacterData-substringData.html.ini new file mode 100644 index 00000000000..236aa95dfa3 --- /dev/null +++ b/tests/wpt/metadata/dom/nodes/CharacterData-substringData.html.ini @@ -0,0 +1,3 @@ +[CharacterData-substringData.html] + type: testharness + expected: CRASH diff --git a/tests/wpt/metadata/dom/nodes/Node-properties.html.ini b/tests/wpt/metadata/dom/nodes/Node-properties.html.ini index 41bd3f2f43f..4f0913ec1a4 100644 --- a/tests/wpt/metadata/dom/nodes/Node-properties.html.ini +++ b/tests/wpt/metadata/dom/nodes/Node-properties.html.ini @@ -89,3 +89,4 @@ [paras[4\].nextElementSibling] expected: FAIL + diff --git a/tests/wpt/metadata/html/browsers/windows/nested-browsing-contexts/frameElement.sub.html.ini b/tests/wpt/metadata/html/browsers/windows/nested-browsing-contexts/frameElement.sub.html.ini index 974579d1009..922f8a7b076 100644 --- a/tests/wpt/metadata/html/browsers/windows/nested-browsing-contexts/frameElement.sub.html.ini +++ b/tests/wpt/metadata/html/browsers/windows/nested-browsing-contexts/frameElement.sub.html.ini @@ -1,6 +1,5 @@ [frameElement.sub.html] type: testharness - expected: OK [The window\'s frameElement attribute must return its container element if it is a nested browsing context] expected: FAIL diff --git a/tests/wpt/metadata/html/semantics/disabled-elements/disabledElement.html.ini b/tests/wpt/metadata/html/semantics/disabled-elements/disabledElement.html.ini index f381b21bccb..52c221a1e49 100644 --- a/tests/wpt/metadata/html/semantics/disabled-elements/disabledElement.html.ini +++ b/tests/wpt/metadata/html/semantics/disabled-elements/disabledElement.html.ini @@ -20,3 +20,4 @@ [A disabled should not be focusable] expected: FAIL + diff --git a/tests/wpt/metadata/html/semantics/embedded-content/the-img-element/srcset/parse-a-srcset-attribute.html.ini b/tests/wpt/metadata/html/semantics/embedded-content/the-img-element/srcset/parse-a-srcset-attribute.html.ini index af450694b73..0f0eaf8d6f4 100644 --- a/tests/wpt/metadata/html/semantics/embedded-content/the-img-element/srcset/parse-a-srcset-attribute.html.ini +++ b/tests/wpt/metadata/html/semantics/embedded-content/the-img-element/srcset/parse-a-srcset-attribute.html.ini @@ -705,3 +705,6 @@ ["data:,a 1w \\x011h" (leading U+0001)] expected: FAIL + ["data:,a 1w"] + expected: FAIL + diff --git a/tests/wpt/metadata/workers/interfaces/WorkerGlobalScope/location/members.html.ini b/tests/wpt/metadata/workers/interfaces/WorkerGlobalScope/location/members.html.ini index 71d4aa447ea..d708f6da6f9 100644 --- a/tests/wpt/metadata/workers/interfaces/WorkerGlobalScope/location/members.html.ini +++ b/tests/wpt/metadata/workers/interfaces/WorkerGlobalScope/location/members.html.ini @@ -2,3 +2,4 @@ type: testharness [members of WorkerLocation] expected: FAIL + diff --git a/tests/wpt/web-platform-tests/IndexedDB/key_valid.html b/tests/wpt/web-platform-tests/IndexedDB/key_valid.html index e695d14c4cd..f45d4a903e3 100644 --- a/tests/wpt/web-platform-tests/IndexedDB/key_valid.html +++ b/tests/wpt/web-platform-tests/IndexedDB/key_valid.html @@ -1,6 +1,7 @@ + Valid key diff --git a/tests/wpt/web-platform-tests/README.md b/tests/wpt/web-platform-tests/README.md index 0c1c7165ca5..dbf82da9785 100644 --- a/tests/wpt/web-platform-tests/README.md +++ b/tests/wpt/web-platform-tests/README.md @@ -1,8 +1,16 @@ The Web Platform Tests Project [![IRC chat](https://goo.gl/6nCIks)](http://irc.w3.org/?channels=testing) ============================== -These are test suites for 60+ Web-platform specifications, along -with test-infrastructure code for running the tests. +The Web Platform Tests Project is a W3C-coordinated attempt to build a +cross-browser testsuite for the Web-platform stack. Writing tests in a +way that allows them to be run in all browsers gives browser projects +confidence that they are shipping software that is compatible with other +implementations, and that later implementations will be compatible with +their implementations. This in turn gives Web authors/developers +confidence that they can actually rely on the Web platform to deliver on +the promise of working across browsers and devices without needing extra +layers of abstraction to paper over the gaps left by specification +editors and implementors. Running the Tests ================= diff --git a/tests/wpt/web-platform-tests/XMLHttpRequest/resources/chunked.py b/tests/wpt/web-platform-tests/XMLHttpRequest/resources/chunked.py index 7adabbfd7f4..7e8433bd869 100644 --- a/tests/wpt/web-platform-tests/XMLHttpRequest/resources/chunked.py +++ b/tests/wpt/web-platform-tests/XMLHttpRequest/resources/chunked.py @@ -10,7 +10,7 @@ def main(request, response): response.write_status_headers() for value in chunks: - response.writer.write("%d\r\n" % len(value)) + response.writer.write("%x\r\n" % len(value)) response.writer.write(value) response.writer.write("\r\n") response.writer.write("0\r\n") diff --git a/tests/wpt/web-platform-tests/content-security-policy/media-src/media-src-7_1.html b/tests/wpt/web-platform-tests/content-security-policy/media-src/media-src-7_1.html index d912b86bb8b..bf6d32cf83e 100644 --- a/tests/wpt/web-platform-tests/content-security-policy/media-src/media-src-7_1.html +++ b/tests/wpt/web-platform-tests/content-security-policy/media-src/media-src-7_1.html @@ -2,6 +2,7 @@ Video element src attribute must match src list - positive test + diff --git a/tests/wpt/web-platform-tests/content-security-policy/media-src/media-src-7_1_2.html b/tests/wpt/web-platform-tests/content-security-policy/media-src/media-src-7_1_2.html index c1b9f72f48d..61d4b142579 100644 --- a/tests/wpt/web-platform-tests/content-security-policy/media-src/media-src-7_1_2.html +++ b/tests/wpt/web-platform-tests/content-security-policy/media-src/media-src-7_1_2.html @@ -2,6 +2,7 @@ Video element src attribute must match src list - negative test + diff --git a/tests/wpt/web-platform-tests/content-security-policy/media-src/media-src-7_2.html b/tests/wpt/web-platform-tests/content-security-policy/media-src/media-src-7_2.html index 7509d7b051a..771e389c8f2 100644 --- a/tests/wpt/web-platform-tests/content-security-policy/media-src/media-src-7_2.html +++ b/tests/wpt/web-platform-tests/content-security-policy/media-src/media-src-7_2.html @@ -2,6 +2,7 @@ Audio element src attribute must match src list - positive test + diff --git a/tests/wpt/web-platform-tests/content-security-policy/media-src/media-src-7_2_2.html b/tests/wpt/web-platform-tests/content-security-policy/media-src/media-src-7_2_2.html index 573b5389e2a..9b613429620 100644 --- a/tests/wpt/web-platform-tests/content-security-policy/media-src/media-src-7_2_2.html +++ b/tests/wpt/web-platform-tests/content-security-policy/media-src/media-src-7_2_2.html @@ -2,6 +2,7 @@ Audio element src attribute must match src list - negative test + diff --git a/tests/wpt/web-platform-tests/content-security-policy/media-src/media-src-7_3.html b/tests/wpt/web-platform-tests/content-security-policy/media-src/media-src-7_3.html index 32187708239..c645d9bec83 100644 --- a/tests/wpt/web-platform-tests/content-security-policy/media-src/media-src-7_3.html +++ b/tests/wpt/web-platform-tests/content-security-policy/media-src/media-src-7_3.html @@ -2,6 +2,7 @@ Video track src attribute must match src list - positive test + diff --git a/tests/wpt/web-platform-tests/content-security-policy/media-src/media-src-7_3_2.html b/tests/wpt/web-platform-tests/content-security-policy/media-src/media-src-7_3_2.html index 7ead63f88e8..f4488bb4475 100644 --- a/tests/wpt/web-platform-tests/content-security-policy/media-src/media-src-7_3_2.html +++ b/tests/wpt/web-platform-tests/content-security-policy/media-src/media-src-7_3_2.html @@ -2,6 +2,7 @@ Video track src attribute must match src list - negative test + diff --git a/tests/wpt/web-platform-tests/content-security-policy/object-src/object-src-2_1.html b/tests/wpt/web-platform-tests/content-security-policy/object-src/object-src-2_1.html index aaf67bf740a..a6cc8d49df0 100644 --- a/tests/wpt/web-platform-tests/content-security-policy/object-src/object-src-2_1.html +++ b/tests/wpt/web-platform-tests/content-security-policy/object-src/object-src-2_1.html @@ -2,6 +2,7 @@ Objects loaded using data attribute of <object> tag are blocked unless their host is listed as an allowed source in the object-src directive + @@ -34,19 +35,19 @@ function object_loaded() { var elem = document.getElementById("flashObject"); - var is_loaded = false; - try { - - var pct_loaded = elem.PercentLoaded(); - is_loaded = true; - } catch (e) {} + var is_loaded = false; + try { + + var pct_loaded = elem.PercentLoaded(); + is_loaded = true; + } catch (e) {} - if (hasMimeType) { - test1.step(function() {assert_false(is_loaded, "External object loaded.")}); - } else { - test1.step(function() {assert_true(hasMimeType, "No Flash Player, cannot run test.")}); - } - test1.done(); + if (hasMimeType) { + test1.step(function() {assert_false(is_loaded, "External object loaded.")}); + } else { + test1.step(function() {assert_true(hasMimeType, "No Flash Player, cannot run test.")}); + } + test1.done(); } diff --git a/tests/wpt/web-platform-tests/content-security-policy/object-src/object-src-2_2.html b/tests/wpt/web-platform-tests/content-security-policy/object-src/object-src-2_2.html index 63b85604f5e..5ad75f99b80 100644 --- a/tests/wpt/web-platform-tests/content-security-policy/object-src/object-src-2_2.html +++ b/tests/wpt/web-platform-tests/content-security-policy/object-src/object-src-2_2.html @@ -2,6 +2,7 @@ Objects loaded using src attribute of <embed> tag are blocked unless their host is listed as an allowed source in the object-src directive + @@ -34,19 +35,19 @@ function object_loaded() { var elem = document.getElementById("flashObject"); - var is_loaded = false; - try { - - var pct_loaded = elem.PercentLoaded(); - is_loaded = true; - } catch (e) {} + var is_loaded = false; + try { + + var pct_loaded = elem.PercentLoaded(); + is_loaded = true; + } catch (e) {} - if (hasMimeType) { - test1.step(function() {assert_false(is_loaded, "External object loaded.")}); - } else { - test1.step(function() {assert_true(hasMimeType, "No Flash Player, cannot run test.")}); - } - test1.done(); + if (hasMimeType) { + test1.step(function() {assert_false(is_loaded, "External object loaded.")}); + } else { + test1.step(function() {assert_true(hasMimeType, "No Flash Player, cannot run test.")}); + } + test1.done(); } diff --git a/tests/wpt/web-platform-tests/content-security-policy/support/checkReport.sub.js b/tests/wpt/web-platform-tests/content-security-policy/support/checkReport.sub.js index dfaec7ac54d..023ce471910 100644 --- a/tests/wpt/web-platform-tests/content-security-policy/support/checkReport.sub.js +++ b/tests/wpt/web-platform-tests/content-security-policy/support/checkReport.sub.js @@ -31,7 +31,8 @@ } } - var reportLocation = location.protocol + "//" + location.host + "/content-security-policy/support/report.py?op=take&timeout=5&reportID=" + reportID; + var timeout = document.querySelector("meta[name=timeout][content=long]") ? 50 : 5; + var reportLocation = location.protocol + "//" + location.host + "/content-security-policy/support/report.py?op=take&timeout=" + timeout + "&reportID=" + reportID; var reportTest = async_test("Violation report status OK."); reportTest.step(function () { diff --git a/tests/wpt/web-platform-tests/content-security-policy/support/report.py b/tests/wpt/web-platform-tests/content-security-policy/support/report.py index f8c0c463c03..455d46c6d1d 100644 --- a/tests/wpt/web-platform-tests/content-security-policy/support/report.py +++ b/tests/wpt/web-platform-tests/content-security-policy/support/report.py @@ -2,25 +2,22 @@ import time import json def main(request, response): - op = request.GET.first("op"); key = request.GET.first("reportID") if op == "take": - timeout = float(request.GET.first("timeout")) - value = request.server.stash.take(key=key) - if value is not None: - return [("Content-Type", "application/json")], value - else: - time.sleep(timeout) - value = request.server.stash.take(key=key) - if value is not None: - return [("Content-Type", "application/json")], value - else: - return [("Content-Type", "application/json")], json.dumps({'error': 'No such report.' , 'guid' : key}) - else: - report = request.body - report.rstrip() - request.server.stash.take(key=key) - request.server.stash.put(key=key, value=report) - return [("Content-Type", "text/plain")], "Recorded report " + report \ No newline at end of file + timeout = float(request.GET.first("timeout")) + t0 = time.time() + while time.time() - t0 < timeout: + time.sleep(0.5) + value = request.server.stash.take(key=key) + if value is not None: + return [("Content-Type", "application/json")], value + + return [("Content-Type", "application/json")], json.dumps({'error': 'No such report.' , 'guid' : key}) + + report = request.body + report.rstrip() + request.server.stash.take(key=key) + request.server.stash.put(key=key, value=report) + return [("Content-Type", "text/plain")], "Recorded report " + report diff --git a/tests/wpt/web-platform-tests/dom/nodes/CharacterData-appendData.html b/tests/wpt/web-platform-tests/dom/nodes/CharacterData-appendData.html index 5e84e970551..464a119a03d 100644 --- a/tests/wpt/web-platform-tests/dom/nodes/CharacterData-appendData.html +++ b/tests/wpt/web-platform-tests/dom/nodes/CharacterData-appendData.html @@ -7,15 +7,56 @@
diff --git a/tests/wpt/web-platform-tests/dom/nodes/CharacterData-substringData.html b/tests/wpt/web-platform-tests/dom/nodes/CharacterData-substringData.html new file mode 100644 index 00000000000..1d7288ab3f1 --- /dev/null +++ b/tests/wpt/web-platform-tests/dom/nodes/CharacterData-substringData.html @@ -0,0 +1,108 @@ + + +CharacterData.substringData + + + + +
+ diff --git a/tests/wpt/web-platform-tests/dom/nodes/attributes.html b/tests/wpt/web-platform-tests/dom/nodes/attributes.html index f4a993a252f..34be42a0a0d 100644 --- a/tests/wpt/web-platform-tests/dom/nodes/attributes.html +++ b/tests/wpt/web-platform-tests/dom/nodes/attributes.html @@ -385,4 +385,13 @@ test(function() { el.removeAttributeNS(null, "pre:fix") assert_equals(el.attributes[0], unprefixed) }, "Attribute with prefix in local name") + +test(function() { + var el = document.createElement("div") + el.setAttribute("foo", "bar") + var attr = el.attributes[0] + assert_equals(attr.ownerElement, el) + el.removeAttribute("foo") + assert_equals(attr.ownerElement, null) +}, "Attribute loses its owner when removed") diff --git a/tests/wpt/web-platform-tests/fetch/nosniff/image.html b/tests/wpt/web-platform-tests/fetch/nosniff/image.html new file mode 100644 index 00000000000..e5869d94e60 --- /dev/null +++ b/tests/wpt/web-platform-tests/fetch/nosniff/image.html @@ -0,0 +1,29 @@ + + +
+ diff --git a/tests/wpt/web-platform-tests/fetch/nosniff/importscripts.html b/tests/wpt/web-platform-tests/fetch/nosniff/importscripts.html new file mode 100644 index 00000000000..920b6bdd409 --- /dev/null +++ b/tests/wpt/web-platform-tests/fetch/nosniff/importscripts.html @@ -0,0 +1,14 @@ + + +
+ diff --git a/tests/wpt/web-platform-tests/fetch/nosniff/importscripts.js b/tests/wpt/web-platform-tests/fetch/nosniff/importscripts.js new file mode 100644 index 00000000000..aeb6154870b --- /dev/null +++ b/tests/wpt/web-platform-tests/fetch/nosniff/importscripts.js @@ -0,0 +1,17 @@ +// Testing importScripts() +function log(w) { this.postMessage(w) } +function f() { log("FAIL") } +function p() { log("PASS") } + +["", "?type=", "?type=x", "?type=x/x"].forEach(function(urlpart) { + try { + importScripts("resources/js.py" + urlpart) + } catch(e) { + (e.name == "NetworkError") ? p() : log("FAIL (no NetworkError exception): " + urlpart) + } + +}) +importScripts("resources/js.py?type=text/javascript&outcome=p") +importScripts("resources/js.py?type=text/ecmascript&outcome=p") +importScripts("resources/js.py?type=text/ecmascript;blah&outcome=p") +log("END") diff --git a/tests/wpt/web-platform-tests/fetch/nosniff/parsing-nosniff.html b/tests/wpt/web-platform-tests/fetch/nosniff/parsing-nosniff.html new file mode 100644 index 00000000000..10c5cadfcaa --- /dev/null +++ b/tests/wpt/web-platform-tests/fetch/nosniff/parsing-nosniff.html @@ -0,0 +1,28 @@ + + +
+ diff --git a/tests/wpt/web-platform-tests/fetch/nosniff/resources/css.py b/tests/wpt/web-platform-tests/fetch/nosniff/resources/css.py new file mode 100644 index 00000000000..7c4c63b596a --- /dev/null +++ b/tests/wpt/web-platform-tests/fetch/nosniff/resources/css.py @@ -0,0 +1,15 @@ +def main(request, response): + outcome = request.GET.first("outcome", "f") + type = request.GET.first("type", None) + + content = "/* nothing to see here */" + + response.add_required_headers = False + response.writer.write_status(200) + response.writer.write_header("x-content-type-options", "nosniff") + response.writer.write_header("content-length", len(content)) + if(type != None): + response.writer.write_header("content-type", type) + response.writer.end_headers() + + response.writer.write(content) diff --git a/tests/wpt/web-platform-tests/fetch/nosniff/resources/image.py b/tests/wpt/web-platform-tests/fetch/nosniff/resources/image.py new file mode 100644 index 00000000000..32a07c197df --- /dev/null +++ b/tests/wpt/web-platform-tests/fetch/nosniff/resources/image.py @@ -0,0 +1,16 @@ +import os.path + +def main(request, response): + type = request.GET.first("type", None) + + body = open(os.path.join(os.path.dirname(__file__), "../../../images/blue96x96.png")).read() + + response.add_required_headers = False + response.writer.write_status(200) + response.writer.write_header("x-content-type-options", "nosniff") + response.writer.write_header("content-length", len(body)) + if(type != None): + response.writer.write_header("content-type", type) + response.writer.end_headers() + + response.writer.write(body) diff --git a/tests/wpt/web-platform-tests/fetch/nosniff/resources/js.py b/tests/wpt/web-platform-tests/fetch/nosniff/resources/js.py new file mode 100644 index 00000000000..0c06d9cd2e4 --- /dev/null +++ b/tests/wpt/web-platform-tests/fetch/nosniff/resources/js.py @@ -0,0 +1,17 @@ +def main(request, response): + outcome = request.GET.first("outcome", "f") + type = request.GET.first("type", "Content-Type missing") + + content = "// nothing to see here" + content += "\n" + content += "log('FAIL: " + type + "')" if (outcome == "f") else "p()" + + response.add_required_headers = False + response.writer.write_status(200) + response.writer.write_header("x-content-type-options", "nosniff") + response.writer.write_header("content-length", len(content)) + if(type != "Content-Type missing"): + response.writer.write_header("content-type", type) + response.writer.end_headers() + + response.writer.write(content) diff --git a/tests/wpt/web-platform-tests/fetch/nosniff/resources/nosniff-first.asis b/tests/wpt/web-platform-tests/fetch/nosniff/resources/nosniff-first.asis new file mode 100644 index 00000000000..bccc53eef8a --- /dev/null +++ b/tests/wpt/web-platform-tests/fetch/nosniff/resources/nosniff-first.asis @@ -0,0 +1,7 @@ +HTTP/1.1 200 YOU HAVE NO POWER HERE +Content-Length: 22 +Content-Type: x/x +X-Content-Type-options: nosniff +X-Content-Type-Options: no + +// nothing to see here diff --git a/tests/wpt/web-platform-tests/fetch/nosniff/resources/nosniff-last.asis b/tests/wpt/web-platform-tests/fetch/nosniff/resources/nosniff-last.asis new file mode 100644 index 00000000000..e3de0733e4f --- /dev/null +++ b/tests/wpt/web-platform-tests/fetch/nosniff/resources/nosniff-last.asis @@ -0,0 +1,7 @@ +HTTP/1.1 200 YOU HAVE NO POWER HERE +Content-Length: 22 +Content-Type: x/x +X-Content-Type-Options: no +X-Content-Type-options: nosniff + +// nothing to see here diff --git a/tests/wpt/web-platform-tests/fetch/nosniff/resources/nosniff-no-x.asis b/tests/wpt/web-platform-tests/fetch/nosniff/resources/nosniff-no-x.asis new file mode 100644 index 00000000000..329d0f72143 --- /dev/null +++ b/tests/wpt/web-platform-tests/fetch/nosniff/resources/nosniff-no-x.asis @@ -0,0 +1,6 @@ +HTTP/1.1 200 YOU HAVE NO POWER HERE +Content-Length: 22 +Content-Type: x/x +Content-Type-Options: nosniff + +// nothing to see here diff --git a/tests/wpt/web-platform-tests/fetch/nosniff/resources/nosniff-quoted-single.asis b/tests/wpt/web-platform-tests/fetch/nosniff/resources/nosniff-quoted-single.asis new file mode 100644 index 00000000000..501f18999d0 --- /dev/null +++ b/tests/wpt/web-platform-tests/fetch/nosniff/resources/nosniff-quoted-single.asis @@ -0,0 +1,6 @@ +HTTP/1.1 200 YOU HAVE NO POWER HERE +Content-Length: 22 +Content-Type: x/x +X-Content-Type-Options: 'NosniFF' + +// nothing to see here diff --git a/tests/wpt/web-platform-tests/fetch/nosniff/resources/nosniff-quoted.asis b/tests/wpt/web-platform-tests/fetch/nosniff/resources/nosniff-quoted.asis new file mode 100644 index 00000000000..c6de62b689e --- /dev/null +++ b/tests/wpt/web-platform-tests/fetch/nosniff/resources/nosniff-quoted.asis @@ -0,0 +1,6 @@ +HTTP/1.1 200 YOU HAVE NO POWER HERE +Content-Length: 22 +Content-Type: x/x +X-Content-Type-Options: "nosniFF" + +// nothing to see here diff --git a/tests/wpt/web-platform-tests/fetch/nosniff/resources/nosniff-uppercase.asis b/tests/wpt/web-platform-tests/fetch/nosniff/resources/nosniff-uppercase.asis new file mode 100644 index 00000000000..8097fddce10 --- /dev/null +++ b/tests/wpt/web-platform-tests/fetch/nosniff/resources/nosniff-uppercase.asis @@ -0,0 +1,6 @@ +HTTP/1.1 200 YOU HAVE NO POWER HERE +Content-Length: 22 +Content-Type: x/x +X-Content-Type-Options: NOSNIFF + +// nothing to see here diff --git a/tests/wpt/web-platform-tests/fetch/nosniff/resources/worker.py b/tests/wpt/web-platform-tests/fetch/nosniff/resources/worker.py new file mode 100644 index 00000000000..3903ba34947 --- /dev/null +++ b/tests/wpt/web-platform-tests/fetch/nosniff/resources/worker.py @@ -0,0 +1,16 @@ +def main(request, response): + type = request.GET.first("type", None) + + content = "// nothing to see here" + content += "\n" + content += "this.postMessage('hi')" + + response.add_required_headers = False + response.writer.write_status(200) + response.writer.write_header("x-content-type-options", "nosniff") + response.writer.write_header("content-length", len(content)) + if(type != None): + response.writer.write_header("content-type", type) + response.writer.end_headers() + + response.writer.write(content) diff --git a/tests/wpt/web-platform-tests/fetch/nosniff/script.html b/tests/wpt/web-platform-tests/fetch/nosniff/script.html new file mode 100644 index 00000000000..667f3c99a6c --- /dev/null +++ b/tests/wpt/web-platform-tests/fetch/nosniff/script.html @@ -0,0 +1,32 @@ + + +
+ diff --git a/tests/wpt/web-platform-tests/fetch/nosniff/stylesheet.html b/tests/wpt/web-platform-tests/fetch/nosniff/stylesheet.html new file mode 100644 index 00000000000..07de819f8af --- /dev/null +++ b/tests/wpt/web-platform-tests/fetch/nosniff/stylesheet.html @@ -0,0 +1,34 @@ + + + +
+ diff --git a/tests/wpt/web-platform-tests/fetch/nosniff/worker.html b/tests/wpt/web-platform-tests/fetch/nosniff/worker.html new file mode 100644 index 00000000000..466b2075e97 --- /dev/null +++ b/tests/wpt/web-platform-tests/fetch/nosniff/worker.html @@ -0,0 +1,28 @@ + + +
+ diff --git a/tests/wpt/web-platform-tests/html/semantics/embedded-content/the-img-element/srcset/parse-a-srcset-attribute.html b/tests/wpt/web-platform-tests/html/semantics/embedded-content/the-img-element/srcset/parse-a-srcset-attribute.html index 2ff9886e036..ce1e4cebe5f 100644 --- a/tests/wpt/web-platform-tests/html/semantics/embedded-content/the-img-element/srcset/parse-a-srcset-attribute.html +++ b/tests/wpt/web-platform-tests/html/semantics/embedded-content/the-img-element/srcset/parse-a-srcset-attribute.html @@ -58,12 +58,13 @@ - - + + + diff --git a/tests/wpt/web-platform-tests/manifest b/tests/wpt/web-platform-tests/manifest index f5b0c9bade7..edebae62695 100755 --- a/tests/wpt/web-platform-tests/manifest +++ b/tests/wpt/web-platform-tests/manifest @@ -1,6 +1,8 @@ #!/usr/bin/env python +import os import sys from tools.manifest import update -update.main() +update.main(default_tests_root= + os.path.abspath(os.path.dirname(__file__))) diff --git a/tests/wpt/web-platform-tests/resources/docs/api.md b/tests/wpt/web-platform-tests/resources/docs/api.md index 4598a9b15a9..78c00a957a1 100644 --- a/tests/wpt/web-platform-tests/resources/docs/api.md +++ b/tests/wpt/web-platform-tests/resources/docs/api.md @@ -147,6 +147,42 @@ with a TypeError: return promise_rejects(t, new TypeError(), bar); }, "Another example"); +`EventWatcher` is a constructor function that allows DOM events to be handled +using Promises, which can make it a lot easier to test a very specific series +of events, including ensuring that unexpected events are not fired at any point. + +Here's an example of how to use `EventWatcher`: + + var t = async_test("Event order on animation start"); + + var animation = watchedNode.getAnimations()[0]; + var eventWatcher = new EventWatcher(watchedNode, ['animationstart', + 'animationiteration', + 'animationend']); + + eventWatcher.wait_for(t, 'animationstart').then(t.step_func(function() { + assertExpectedStateAtStartOfAnimation(); + animation.currentTime = END_TIME; // skip to end + // We expect two animationiteration events then an animationend event on + // skipping to the end of the animation. + return eventWatcher.wait_for(['animationiteration', + 'animationiteration', + 'animationend']); + })).then(t.step_func(function() { + assertExpectedStateAtEndOfAnimation(); + test.done(); + })); + +`wait_for` either takes the name of a single event and returns a Promise that +will resolve after that event is fired at the watched node, or else it takes an +array of the names of a series of events and returns a Promise that will +resolve after that specific series of events has been fired at the watched node. + +`EventWatcher` will assert if an event occurs while there is no `wait_for`() +created Promise waiting to be fulfilled, or if the event is of a different type +to the type currently expected. This ensures that only the events that are +expected occur, in the correct order, and with the correct timing. + ## Single Page Tests ## Sometimes, particularly when dealing with asynchronous behaviour, diff --git a/tests/wpt/web-platform-tests/resources/testharness.js b/tests/wpt/web-platform-tests/resources/testharness.js index ab88e541fcb..748010306c1 100644 --- a/tests/wpt/web-platform-tests/resources/testharness.js +++ b/tests/wpt/web-platform-tests/resources/testharness.js @@ -470,6 +470,74 @@ policies and contribution forms [3]. }); } + /** + * This constructor helper allows DOM events to be handled using Promises, + * which can make it a lot easier to test a very specific series of events, + * including ensuring that unexpected events are not fired at any point. + */ + function EventWatcher(test, watchedNode, eventTypes) + { + if (typeof eventTypes == 'string') { + eventTypes = [eventTypes]; + } + + var waitingFor = null; + + var eventHandler = test.step_func(function(evt) { + assert_true(!!waitingFor, + 'Not expecting event, but got ' + evt.type + ' event'); + assert_equals(evt.type, waitingFor.types[0], + 'Expected ' + waitingFor.types[0] + ' event, but got ' + + evt.type + ' event instead'); + if (waitingFor.types.length > 1) { + // Pop first event from array + waitingFor.types.shift(); + return; + } + // We need to null out waitingFor before calling the resolve function + // since the Promise's resolve handlers may call wait_for() which will + // need to set waitingFor. + var resolveFunc = waitingFor.resolve; + waitingFor = null; + resolveFunc(evt); + }); + + for (var i = 0; i < eventTypes.length; i++) { + watchedNode.addEventListener(eventTypes[i], eventHandler); + } + + /** + * Returns a Promise that will resolve after the specified event or + * series of events has occured. + */ + this.wait_for = function(types) { + if (waitingFor) { + return Promise.reject('Already waiting for an event or events'); + } + if (typeof types == 'string') { + types = [types]; + } + return new Promise(function(resolve, reject) { + waitingFor = { + types: types, + resolve: resolve, + reject: reject + }; + }); + }; + + function stop_watching() { + for (var i = 0; i < eventTypes.length; i++) { + watchedNode.removeEventListener(eventTypes[i], eventHandler); + } + }; + + test.add_cleanup(stop_watching); + + return this; + } + expose(EventWatcher, 'EventWatcher'); + function setup(func_or_properties, maybe_properties) { var func = null; diff --git a/tests/wpt/web-platform-tests/tools/.gitignore b/tests/wpt/web-platform-tests/tools/.gitignore new file mode 100644 index 00000000000..7a8ba560c9e --- /dev/null +++ b/tests/wpt/web-platform-tests/tools/.gitignore @@ -0,0 +1,6 @@ +*# +*.py[co] +*.sw[po] +*~ +\#* + diff --git a/tests/wpt/web-platform-tests/tools/manifest/update.py b/tests/wpt/web-platform-tests/tools/manifest/update.py index 054e9ce7ab2..1c08c8850d4 100644 --- a/tests/wpt/web-platform-tests/tools/manifest/update.py +++ b/tests/wpt/web-platform-tests/tools/manifest/update.py @@ -2,6 +2,7 @@ import argparse import imp import os +import sys import manifest import vcs @@ -82,11 +83,32 @@ def create_parser(): return parser -def main(): +def find_top_repo(): + path = here + rv = None + while path != "/": + if vcs.is_git_repo(path): + rv = path + path = os.path.abspath(os.path.join(path, os.pardir)) + + return rv + +def main(default_tests_root=None): opts = create_parser().parse_args() if opts.tests_root is None: - opts.tests_root = vcs.get_repo_root() + tests_root = None + if default_tests_root is not None: + tests_root = default_tests_root + else: + tests_root = find_top_repo() + + if tests_root is None: + print >> sys.stderr, """No git repo found; could not determine test root. +Run again with --test-root""" + sys.exit(1) + + opts.tests_root = tests_root if opts.path is None: opts.path = os.path.join(opts.tests_root, "MANIFEST.json") diff --git a/tests/wpt/web-platform-tests/tools/manifest/vcs.py b/tests/wpt/web-platform-tests/tools/manifest/vcs.py index 7bf37949208..93bd445e1e8 100644 --- a/tests/wpt/web-platform-tests/tools/manifest/vcs.py +++ b/tests/wpt/web-platform-tests/tools/manifest/vcs.py @@ -13,9 +13,13 @@ def is_git_repo(tests_root): _repo_root = None -def get_repo_root(): +def get_repo_root(initial_dir=None): global _repo_root + + if initial_dir is None: + initial_dir = os.path.dirname(__file__) + if _repo_root is None: - git = get_git_func(os.path.dirname(__file__)) + git = get_git_func(initial_dir) _repo_root = git("rev-parse", "--show-toplevel").rstrip() return _repo_root diff --git a/tests/wpt/web-platform-tests/websockets/Close-undefined.htm b/tests/wpt/web-platform-tests/websockets/Close-undefined.htm index 4f67c067e74..3de1656561c 100644 --- a/tests/wpt/web-platform-tests/websockets/Close-undefined.htm +++ b/tests/wpt/web-platform-tests/websockets/Close-undefined.htm @@ -10,13 +10,13 @@
diff --git a/tests/wpt/web-platform-tests/websockets/Secure-Close-undefined.htm b/tests/wpt/web-platform-tests/websockets/Secure-Close-undefined.htm index ef4a874ea75..f6c77ba77ad 100644 --- a/tests/wpt/web-platform-tests/websockets/Secure-Close-undefined.htm +++ b/tests/wpt/web-platform-tests/websockets/Secure-Close-undefined.htm @@ -10,13 +10,13 @@
diff --git a/tests/wpt/web-platform-tests/websockets/cookies/001.html b/tests/wpt/web-platform-tests/websockets/cookies/001.html index b0701d0cd91..ed318456522 100644 --- a/tests/wpt/web-platform-tests/websockets/cookies/001.html +++ b/tests/wpt/web-platform-tests/websockets/cookies/001.html @@ -10,6 +10,10 @@ async_test(function(t) { if (window.WebSocket) { document.cookie = 'ws_test_'+cookie_id+'=test; Path=/'; } + t.add_cleanup(function() { + // remove cookie + document.cookie = 'ws_test_'+cookie_id+'=; Path=/; Expires=Sun, 06 Nov 1994 08:49:37 GMT'; + }); var ws = new WebSocket(SCHEME_DOMAIN_PORT+'/echo-cookie'); ws.onmessage = t.step_func(function(e) { assert_regexp_match(e.data, new RegExp('ws_test_'+cookie_id+'=test')); @@ -18,6 +22,4 @@ async_test(function(t) { }); ws.onerror = ws.onclose = t.step_func(function(e) {assert_unreached(e.type)}); }); -// remove cookie -document.cookie = 'ws_test_'+cookie_id+'=; Path=/; Expires=Sun, 06 Nov 1994 08:49:37 GMT'; diff --git a/tests/wpt/web-platform-tests/websockets/cookies/002.html b/tests/wpt/web-platform-tests/websockets/cookies/002.html index 1e6a1dbeab2..fd174f2aefb 100644 --- a/tests/wpt/web-platform-tests/websockets/cookies/002.html +++ b/tests/wpt/web-platform-tests/websockets/cookies/002.html @@ -7,11 +7,13 @@ diff --git a/tests/wpt/web-platform-tests/websockets/cookies/004.html b/tests/wpt/web-platform-tests/websockets/cookies/004.html index 894ef2eff89..db6b3c1c214 100644 --- a/tests/wpt/web-platform-tests/websockets/cookies/004.html +++ b/tests/wpt/web-platform-tests/websockets/cookies/004.html @@ -10,6 +10,11 @@ var cookie_id = ((new Date())-0) + '.' + Math.random(); var t = async_test(function(t) { var iframe = document.createElement('iframe'); + t.add_cleanup(function() { + // remove cookie + iframe.src = 'support/set-cookie.py?'+encodeURIComponent('ws_test_'+cookie_id+'=; Path=/; HttpOnly; Expires=Sun, 06 Nov 1994 08:49:37 GMT'); + iframe.onload = done; + }); var url = SCHEME_DOMAIN_PORT+'/set-cookie_http?'+cookie_id; var ws = new WebSocket(url); ws.onopen = t.step_func(function(e) { @@ -17,13 +22,7 @@ var t = async_test(function(t) { assert_false(new RegExp('ws_test_'+cookie_id+'=test').test(document.cookie)); t.done(); }); - ws.onclose = t.step_func(function(e) {assert_unreached()}); - - - add_result_callback(function() { - iframe.src = 'support/set-cookie.py?'+encodeURIComponent('ws_test_'+cookie_id+'=; Path=/; HttpOnly; Expires=Sun, 06 Nov 1994 08:49:37 GMT'); - iframe.onload = done; - }); + ws.onerror = ws.onclose = t.step_func(function(e) {assert_unreached(e.type)}); document.body.appendChild(iframe); }, null, {timeout:9900}) diff --git a/tests/wpt/web-platform-tests/websockets/cookies/005.html b/tests/wpt/web-platform-tests/websockets/cookies/005.html index e7aefd5978e..7b3d80f129c 100644 --- a/tests/wpt/web-platform-tests/websockets/cookies/005.html +++ b/tests/wpt/web-platform-tests/websockets/cookies/005.html @@ -11,6 +11,11 @@ var cookie_id = ((new Date())-0) + '.' + Math.random(); var t = async_test(function(t) { var iframe = document.createElement('iframe'); + t.add_cleanup(function() { + // remove cookie + iframe.src = 'support/set-cookie.py?'+encodeURIComponent('ws_test_'+cookie_id+'=; Path=/; HttpOnly; Expires=Sun, 06 Nov 1994 08:49:37 GMT'); + iframe.onload = done; + }); var ws = new WebSocket(SCHEME_DOMAIN_PORT+'/set-cookie_http?'+cookie_id); ws.onopen = t.step_func(function(e) { var ws2 = new WebSocket(SCHEME_DOMAIN_PORT+'/echo-cookie'); @@ -21,12 +26,7 @@ var t = async_test(function(t) { t.done(); }); }); - ws.onclose = t.step_func(function() {assert_unreached()}); - - add_result_callback(function() { - iframe.src = 'support/set-cookie.py?'+encodeURIComponent('ws_test_'+cookie_id+'=; Path=/; HttpOnly; Expires=Sun, 06 Nov 1994 08:49:37 GMT'); - iframe.onload = done; - }); + ws.onerror = ws.onclose = t.step_func(function(e) {assert_unreached(e.type)}); document.body.appendChild(iframe); }) diff --git a/tests/wpt/web-platform-tests/websockets/cookies/006.html b/tests/wpt/web-platform-tests/websockets/cookies/006.html index 05c848033b1..e14a90f7bdf 100644 --- a/tests/wpt/web-platform-tests/websockets/cookies/006.html +++ b/tests/wpt/web-platform-tests/websockets/cookies/006.html @@ -10,6 +10,10 @@ async_test(function(t) { if (window.WebSocket) { document.cookie = 'ws_test_'+cookie_id+'=test; Path=/; Secure'; } + t.add_cleanup(function() { + // remove cookie + document.cookie = 'ws_test_'+cookie_id+'=; Path=/; Secure; Expires=Sun, 06 Nov 1994 08:49:37 GMT'; + }); var ws = new WebSocket(SCHEME_DOMAIN_PORT+'/echo-cookie'); ws.onmessage = t.step_func(function(e) { ws.close(); @@ -20,8 +24,6 @@ async_test(function(t) { } t.done(); }) - ws.onclose = t.step_func(function() {assert_unreached()}); + ws.onerror = ws.onclose = t.step_func(function(e) {assert_unreached(e.type)}); }); -// remove cookie -document.cookie = 'ws_test_'+cookie_id+'; Path=/; Secure; Expires=Sun, 06 Nov 1994 08:49:37 GMT'; diff --git a/tests/wpt/web-platform-tests/websockets/cookies/007.html b/tests/wpt/web-platform-tests/websockets/cookies/007.html index efea696a1e9..eba3139645a 100644 --- a/tests/wpt/web-platform-tests/websockets/cookies/007.html +++ b/tests/wpt/web-platform-tests/websockets/cookies/007.html @@ -8,15 +8,17 @@ diff --git a/tests/wpt/web-platform-tests/websockets/websocket.js b/tests/wpt/web-platform-tests/websockets/websocket.js index a7c2e201c4c..196a1b5c6a2 100644 --- a/tests/wpt/web-platform-tests/websockets/websocket.js +++ b/tests/wpt/web-platform-tests/websockets/websocket.js @@ -2,7 +2,7 @@ var __SERVER__NAME = "{{host}}"; var __PORT = {{ports[ws][0]}}; var __SECURE__PORT = null; //{{ports[ws][0]}}; //Should be wss var __NEW__PORT = __PORT; //All ports are non-default for now -var __NEW_SECURE_PORT = __PORT; //All ports are non-default for now +var __NEW__SECURE__PORT = __PORT; //All ports are non-default for now var __PATH = "echo"; var __CONTROLPATH = "control"; var __PROTOCOL = "echo"; From 3d68a46fee3a33e76889f76350913128ae816ba9 Mon Sep 17 00:00:00 2001 From: Anthony Ramine Date: Wed, 8 Apr 2015 22:30:54 +0200 Subject: [PATCH 26/32] Implement NonDocumentTypeChildNode::*ElementSibling() --- components/script/dom/characterdata.rs | 18 ++++- components/script/dom/element.rs | 12 +++ components/script/dom/node.rs | 7 ++ .../script/dom/webidls/CharacterData.webidl | 1 + .../script/dom/webidls/ChildNode.webidl | 14 ++-- components/script/dom/webidls/Element.webidl | 1 + tests/wpt/metadata/dom/interfaces.html.ini | 36 --------- .../nodes/Element-nextElementSibling.html.ini | 5 -- .../Element-previousElementSibling.html.ini | 5 -- .../Element-siblingElement-null.html.ini | 5 -- .../dom/nodes/Node-properties.html.ini | 75 ------------------- .../wpt/metadata/html/dom/interfaces.html.ini | 6 -- 12 files changed, 44 insertions(+), 141 deletions(-) delete mode 100644 tests/wpt/metadata/dom/nodes/Element-nextElementSibling.html.ini delete mode 100644 tests/wpt/metadata/dom/nodes/Element-previousElementSibling.html.ini delete mode 100644 tests/wpt/metadata/dom/nodes/Element-siblingElement-null.html.ini diff --git a/components/script/dom/characterdata.rs b/components/script/dom/characterdata.rs index 1f2b18f1b99..2b730469582 100644 --- a/components/script/dom/characterdata.rs +++ b/components/script/dom/characterdata.rs @@ -6,11 +6,13 @@ use dom::bindings::cell::DOMRefCell; use dom::bindings::codegen::Bindings::CharacterDataBinding::CharacterDataMethods; -use dom::bindings::codegen::InheritTypes::{CharacterDataDerived, NodeCast}; +use dom::bindings::codegen::InheritTypes::{CharacterDataDerived, ElementCast}; +use dom::bindings::codegen::InheritTypes::NodeCast; use dom::bindings::error::{Fallible, ErrorResult}; use dom::bindings::error::Error::IndexSize; -use dom::bindings::js::JSRef; +use dom::bindings::js::{JSRef, Temporary}; use dom::document::Document; +use dom::element::Element; use dom::eventtarget::{EventTarget, EventTargetTypeId}; use dom::node::{Node, NodeHelpers, NodeTypeId}; @@ -127,5 +129,17 @@ impl<'a> CharacterDataMethods for JSRef<'a, CharacterData> { let node: JSRef = NodeCast::from_ref(self); node.remove_self(); } + + // https://dom.spec.whatwg.org/#dom-nondocumenttypechildnode-previouselementsibling + fn GetPreviousElementSibling(self) -> Option> { + NodeCast::from_ref(self).preceding_siblings() + .filter_map(ElementCast::to_temporary).next() + } + + // https://dom.spec.whatwg.org/#dom-nondocumenttypechildnode-nextelementsibling + fn GetNextElementSibling(self) -> Option> { + NodeCast::from_ref(self).following_siblings() + .filter_map(ElementCast::to_temporary).next() + } } diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs index 5b5aa57e3e5..418dd124158 100644 --- a/components/script/dom/element.rs +++ b/components/script/dom/element.rs @@ -1178,6 +1178,18 @@ impl<'a> ElementMethods for JSRef<'a, Element> { Ok(()) } + // https://dom.spec.whatwg.org/#dom-nondocumenttypechildnode-previouselementsibling + fn GetPreviousElementSibling(self) -> Option> { + NodeCast::from_ref(self).preceding_siblings() + .filter_map(ElementCast::to_temporary).next() + } + + // https://dom.spec.whatwg.org/#dom-nondocumenttypechildnode-nextelementsibling + fn GetNextElementSibling(self) -> Option> { + NodeCast::from_ref(self).following_siblings() + .filter_map(ElementCast::to_temporary).next() + } + // http://dom.spec.whatwg.org/#dom-parentnode-children fn Children(self) -> Temporary { let window = window_from_node(self).root(); diff --git a/components/script/dom/node.rs b/components/script/dom/node.rs index 55d60966b27..5368a012846 100644 --- a/components/script/dom/node.rs +++ b/components/script/dom/node.rs @@ -415,6 +415,7 @@ pub trait NodeHelpers<'a> { fn rev_children(self) -> ReverseChildrenIterator; fn child_elements(self) -> ChildElementIterator; fn following_siblings(self) -> NodeChildrenIterator; + fn preceding_siblings(self) -> ReverseChildrenIterator; fn is_in_doc(self) -> bool; fn is_inclusive_ancestor_of(self, parent: JSRef) -> bool; fn is_parent_of(self, child: JSRef) -> bool; @@ -764,6 +765,12 @@ impl<'a> NodeHelpers<'a> for JSRef<'a, Node> { } } + fn preceding_siblings(self) -> ReverseChildrenIterator { + ReverseChildrenIterator { + current: self.prev_sibling(), + } + } + fn is_parent_of(self, child: JSRef) -> bool { match child.parent_node() { Some(ref parent) if parent == &Temporary::from_rooted(self) => true, diff --git a/components/script/dom/webidls/CharacterData.webidl b/components/script/dom/webidls/CharacterData.webidl index d1b222bc168..39f304e0e39 100644 --- a/components/script/dom/webidls/CharacterData.webidl +++ b/components/script/dom/webidls/CharacterData.webidl @@ -26,3 +26,4 @@ interface CharacterData : Node { }; CharacterData implements ChildNode; +CharacterData implements NonDocumentTypeChildNode; diff --git a/components/script/dom/webidls/ChildNode.webidl b/components/script/dom/webidls/ChildNode.webidl index 16562fbafbf..fbcf8ea1208 100644 --- a/components/script/dom/webidls/ChildNode.webidl +++ b/components/script/dom/webidls/ChildNode.webidl @@ -16,10 +16,10 @@ interface ChildNode { void remove(); }; -// [NoInterfaceObject] -// interface NonDocumentTypeChildNode { -// [Pure] -// readonly attribute Element? previousElementSibling; -// [Pure] -// readonly attribute Element? nextElementSibling; -// }; +[NoInterfaceObject] +interface NonDocumentTypeChildNode { + [Pure] + readonly attribute Element? previousElementSibling; + [Pure] + readonly attribute Element? nextElementSibling; +}; diff --git a/components/script/dom/webidls/Element.webidl b/components/script/dom/webidls/Element.webidl index 8d08717cd31..1090ad91588 100644 --- a/components/script/dom/webidls/Element.webidl +++ b/components/script/dom/webidls/Element.webidl @@ -70,4 +70,5 @@ partial interface Element { }; Element implements ChildNode; +Element implements NonDocumentTypeChildNode; Element implements ParentNode; diff --git a/tests/wpt/metadata/dom/interfaces.html.ini b/tests/wpt/metadata/dom/interfaces.html.ini index 09001ad6270..e93857fa4ab 100644 --- a/tests/wpt/metadata/dom/interfaces.html.ini +++ b/tests/wpt/metadata/dom/interfaces.html.ini @@ -246,12 +246,6 @@ [Element interface: operation queryAll(DOMString)] expected: FAIL - [Element interface: attribute previousElementSibling] - expected: FAIL - - [Element interface: attribute nextElementSibling] - expected: FAIL - [Element interface: operation before([object Object\],[object Object\])] expected: FAIL @@ -315,12 +309,6 @@ [Element interface: calling queryAll(DOMString) on element with too few arguments must throw TypeError] expected: FAIL - [Element interface: element must inherit property "previousElementSibling" with the proper type (37)] - expected: FAIL - - [Element interface: element must inherit property "nextElementSibling" with the proper type (38)] - expected: FAIL - [Element interface: element must inherit property "before" with the proper type (39)] expected: FAIL @@ -339,12 +327,6 @@ [NamedNodeMap interface: operation setNamedItemNS(Attr)] expected: FAIL - [CharacterData interface: attribute previousElementSibling] - expected: FAIL - - [CharacterData interface: attribute nextElementSibling] - expected: FAIL - [CharacterData interface: operation before([object Object\],[object Object\])] expected: FAIL @@ -369,12 +351,6 @@ [Text interface: document.createTextNode("abc") must inherit property "wholeText" with the proper type (1)] expected: FAIL - [CharacterData interface: document.createTextNode("abc") must inherit property "previousElementSibling" with the proper type (7)] - expected: FAIL - - [CharacterData interface: document.createTextNode("abc") must inherit property "nextElementSibling" with the proper type (8)] - expected: FAIL - [CharacterData interface: document.createTextNode("abc") must inherit property "before" with the proper type (9)] expected: FAIL @@ -387,12 +363,6 @@ [CharacterData interface: calling after([object Object\],[object Object\]) on document.createTextNode("abc") with too few arguments must throw TypeError] expected: FAIL - [CharacterData interface: xmlDoc.createProcessingInstruction("abc", "def") must inherit property "previousElementSibling" with the proper type (7)] - expected: FAIL - - [CharacterData interface: xmlDoc.createProcessingInstruction("abc", "def") must inherit property "nextElementSibling" with the proper type (8)] - expected: FAIL - [CharacterData interface: xmlDoc.createProcessingInstruction("abc", "def") must inherit property "before" with the proper type (9)] expected: FAIL @@ -408,12 +378,6 @@ [Comment interface object length] expected: FAIL - [CharacterData interface: document.createComment("abc") must inherit property "previousElementSibling" with the proper type (7)] - expected: FAIL - - [CharacterData interface: document.createComment("abc") must inherit property "nextElementSibling" with the proper type (8)] - expected: FAIL - [CharacterData interface: document.createComment("abc") must inherit property "before" with the proper type (9)] expected: FAIL diff --git a/tests/wpt/metadata/dom/nodes/Element-nextElementSibling.html.ini b/tests/wpt/metadata/dom/nodes/Element-nextElementSibling.html.ini deleted file mode 100644 index 500ad7ddfd9..00000000000 --- a/tests/wpt/metadata/dom/nodes/Element-nextElementSibling.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[Element-nextElementSibling.html] - type: testharness - [nextElementSibling] - expected: FAIL - diff --git a/tests/wpt/metadata/dom/nodes/Element-previousElementSibling.html.ini b/tests/wpt/metadata/dom/nodes/Element-previousElementSibling.html.ini deleted file mode 100644 index 9fc5b61136c..00000000000 --- a/tests/wpt/metadata/dom/nodes/Element-previousElementSibling.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[Element-previousElementSibling.html] - type: testharness - [previousElementSibling] - expected: FAIL - diff --git a/tests/wpt/metadata/dom/nodes/Element-siblingElement-null.html.ini b/tests/wpt/metadata/dom/nodes/Element-siblingElement-null.html.ini deleted file mode 100644 index 77c3ddfa89e..00000000000 --- a/tests/wpt/metadata/dom/nodes/Element-siblingElement-null.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[Element-siblingElement-null.html] - type: testharness - [Null test] - expected: FAIL - diff --git a/tests/wpt/metadata/dom/nodes/Node-properties.html.ini b/tests/wpt/metadata/dom/nodes/Node-properties.html.ini index 4f0913ec1a4..a286ce6e019 100644 --- a/tests/wpt/metadata/dom/nodes/Node-properties.html.ini +++ b/tests/wpt/metadata/dom/nodes/Node-properties.html.ini @@ -1,50 +1,5 @@ [Node-properties.html] type: testharness - [testDiv.previousElementSibling] - expected: FAIL - - [detachedDiv.previousElementSibling] - expected: FAIL - - [detachedDiv.nextElementSibling] - expected: FAIL - - [detachedPara1.previousElementSibling] - expected: FAIL - - [detachedPara1.nextElementSibling] - expected: FAIL - - [detachedPara2.previousElementSibling] - expected: FAIL - - [detachedPara2.nextElementSibling] - expected: FAIL - - [foreignPara1.previousElementSibling] - expected: FAIL - - [foreignPara1.nextElementSibling] - expected: FAIL - - [foreignPara2.previousElementSibling] - expected: FAIL - - [foreignPara2.nextElementSibling] - expected: FAIL - - [xmlElement.previousElementSibling] - expected: FAIL - - [xmlElement.nextElementSibling] - expected: FAIL - - [detachedXmlElement.previousElementSibling] - expected: FAIL - - [detachedXmlElement.nextElementSibling] - expected: FAIL - [detachedTextNode.wholeText] expected: FAIL @@ -60,33 +15,3 @@ [detachedXmlTextNode.wholeText] expected: FAIL - [paras[0\].previousElementSibling] - expected: FAIL - - [paras[0\].nextElementSibling] - expected: FAIL - - [paras[1\].previousElementSibling] - expected: FAIL - - [paras[1\].nextElementSibling] - expected: FAIL - - [paras[2\].previousElementSibling] - expected: FAIL - - [paras[2\].nextElementSibling] - expected: FAIL - - [paras[3\].previousElementSibling] - expected: FAIL - - [paras[3\].nextElementSibling] - expected: FAIL - - [paras[4\].previousElementSibling] - expected: FAIL - - [paras[4\].nextElementSibling] - expected: FAIL - diff --git a/tests/wpt/metadata/html/dom/interfaces.html.ini b/tests/wpt/metadata/html/dom/interfaces.html.ini index 4c2f7bacd96..8fb6bc647dc 100644 --- a/tests/wpt/metadata/html/dom/interfaces.html.ini +++ b/tests/wpt/metadata/html/dom/interfaces.html.ini @@ -2214,12 +2214,6 @@ [Element interface: calling queryAll(DOMString) on document.createElement("noscript") with too few arguments must throw TypeError] expected: FAIL - [Element interface: document.createElement("noscript") must inherit property "previousElementSibling" with the proper type (37)] - expected: FAIL - - [Element interface: document.createElement("noscript") must inherit property "nextElementSibling" with the proper type (38)] - expected: FAIL - [Element interface: document.createElement("noscript") must inherit property "before" with the proper type (39)] expected: FAIL From 6eb6e386c18663424c9b94125622bc9dd722c966 Mon Sep 17 00:00:00 2001 From: Anthony Ramine Date: Thu, 9 Apr 2015 00:53:28 +0200 Subject: [PATCH 27/32] Add links to CharacterData's spec --- components/script/dom/characterdata.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/components/script/dom/characterdata.rs b/components/script/dom/characterdata.rs index 2b730469582..df173ff1787 100644 --- a/components/script/dom/characterdata.rs +++ b/components/script/dom/characterdata.rs @@ -70,42 +70,50 @@ impl CharacterData { } impl<'a> CharacterDataMethods for JSRef<'a, CharacterData> { + // https://dom.spec.whatwg.org/#dom-characterdata-data fn Data(self) -> DOMString { // FIXME(https://github.com/rust-lang/rust/issues/23338) let data = self.data.borrow(); data.clone() } + // https://dom.spec.whatwg.org/#dom-characterdata-data fn SetData(self, arg: DOMString) -> ErrorResult { *self.data.borrow_mut() = arg; Ok(()) } + // https://dom.spec.whatwg.org/#dom-characterdata-length fn Length(self) -> u32 { // FIXME(https://github.com/rust-lang/rust/issues/23338) let data = self.data.borrow(); data.chars().count() as u32 } + // https://dom.spec.whatwg.org/#dom-characterdata-substringdata fn SubstringData(self, offset: u32, count: u32) -> Fallible { // FIXME(https://github.com/rust-lang/rust/issues/23338) let data = self.data.borrow(); Ok(data.slice_chars(offset as usize, (offset + count) as usize).to_owned()) } + // https://dom.spec.whatwg.org/#dom-characterdata-appenddata fn AppendData(self, arg: DOMString) -> ErrorResult { self.data.borrow_mut().push_str(arg.as_slice()); Ok(()) } + // https://dom.spec.whatwg.org/#dom-characterdata-insertdata fn InsertData(self, offset: u32, arg: DOMString) -> ErrorResult { self.ReplaceData(offset, 0, arg) } + // https://dom.spec.whatwg.org/#dom-characterdata-deletedata fn DeleteData(self, offset: u32, count: u32) -> ErrorResult { self.ReplaceData(offset, count, "".to_owned()) } + // https://dom.spec.whatwg.org/#dom-characterdata-replacedata fn ReplaceData(self, offset: u32, count: u32, arg: DOMString) -> ErrorResult { let length = self.data.borrow().chars().count() as u32; if offset > length { From 14cd68221026a4e3a8c26200d36be982d9f72623 Mon Sep 17 00:00:00 2001 From: Anthony Ramine Date: Thu, 9 Apr 2015 01:02:32 +0200 Subject: [PATCH 28/32] Fix CharacterData annotations --- components/script/dom/characterdata.rs | 10 ++++------ components/script/dom/webidls/CharacterData.webidl | 3 +-- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/components/script/dom/characterdata.rs b/components/script/dom/characterdata.rs index df173ff1787..f10516c5278 100644 --- a/components/script/dom/characterdata.rs +++ b/components/script/dom/characterdata.rs @@ -78,9 +78,8 @@ impl<'a> CharacterDataMethods for JSRef<'a, CharacterData> { } // https://dom.spec.whatwg.org/#dom-characterdata-data - fn SetData(self, arg: DOMString) -> ErrorResult { - *self.data.borrow_mut() = arg; - Ok(()) + fn SetData(self, data: DOMString) { + *self.data.borrow_mut() = data; } // https://dom.spec.whatwg.org/#dom-characterdata-length @@ -98,9 +97,8 @@ impl<'a> CharacterDataMethods for JSRef<'a, CharacterData> { } // https://dom.spec.whatwg.org/#dom-characterdata-appenddata - fn AppendData(self, arg: DOMString) -> ErrorResult { - self.data.borrow_mut().push_str(arg.as_slice()); - Ok(()) + fn AppendData(self, data: DOMString) { + self.data.borrow_mut().push_str(&data); } // https://dom.spec.whatwg.org/#dom-characterdata-insertdata diff --git a/components/script/dom/webidls/CharacterData.webidl b/components/script/dom/webidls/CharacterData.webidl index 39f304e0e39..e758adb4d30 100644 --- a/components/script/dom/webidls/CharacterData.webidl +++ b/components/script/dom/webidls/CharacterData.webidl @@ -11,11 +11,10 @@ */ interface CharacterData : Node { - [TreatNullAs=EmptyString,SetterThrows] attribute DOMString data; + [TreatNullAs=EmptyString] attribute DOMString data; readonly attribute unsigned long length; [Throws] DOMString substringData(unsigned long offset, unsigned long count); - [Throws] void appendData(DOMString data); [Throws] void insertData(unsigned long offset, DOMString data); From c8f41b473cbe69be8bfd17fc02318b1b6520e76a Mon Sep 17 00:00:00 2001 From: Anthony Ramine Date: Thu, 9 Apr 2015 01:47:51 +0200 Subject: [PATCH 29/32] Remove CharacterData::set_data() --- components/script/dom/characterdata.rs | 5 ----- components/script/dom/node.rs | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/components/script/dom/characterdata.rs b/components/script/dom/characterdata.rs index f10516c5278..1796663acf0 100644 --- a/components/script/dom/characterdata.rs +++ b/components/script/dom/characterdata.rs @@ -56,11 +56,6 @@ impl CharacterData { self.data.borrow() } - #[inline] - pub fn set_data(&self, data: DOMString) { - *self.data.borrow_mut() = data; - } - #[inline] #[allow(unsafe_code)] pub unsafe fn data_for_layout<'a>(&'a self) -> &'a str { diff --git a/components/script/dom/node.rs b/components/script/dom/node.rs index 5368a012846..f6c7adc2668 100644 --- a/components/script/dom/node.rs +++ b/components/script/dom/node.rs @@ -1880,7 +1880,7 @@ impl<'a> NodeMethods for JSRef<'a, Node> { NodeTypeId::Text | NodeTypeId::ProcessingInstruction => { let characterdata: JSRef = CharacterDataCast::to_ref(self).unwrap(); - characterdata.set_data(value); + characterdata.SetData(value); // Notify the document that the content of this node is different let document = self.owner_doc().root(); From 45b490c977749618d200dbed77e7a098967d6864 Mon Sep 17 00:00:00 2001 From: Anthony Ramine Date: Thu, 9 Apr 2015 02:06:48 +0200 Subject: [PATCH 30/32] Generate from_layout_js() conversion methods --- components/script/dom/bindings/codegen/CodegenRust.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/components/script/dom/bindings/codegen/CodegenRust.py b/components/script/dom/bindings/codegen/CodegenRust.py index 650d1507b77..dd788146acd 100644 --- a/components/script/dom/bindings/codegen/CodegenRust.py +++ b/components/script/dom/bindings/codegen/CodegenRust.py @@ -5427,6 +5427,12 @@ impl ${name}Cast { unsafe { derived.transmute_borrowed() } } + #[inline(always)] + #[allow(unrooted_must_root)] + pub fn from_layout_js(derived: &LayoutJS) -> LayoutJS<${name}> { + unsafe { derived.transmute_copy() } + } + #[inline(always)] pub fn from_temporary(derived: Temporary) -> Temporary<${name}> { unsafe { derived.transmute() } From 8f73b452fb36cceed9d7a3aa253d937c338983b3 Mon Sep 17 00:00:00 2001 From: Anthony Ramine Date: Thu, 9 Apr 2015 02:07:15 +0200 Subject: [PATCH 31/32] Cleanup access to CharacterData nodes --- components/layout/wrapper.rs | 16 +++---- components/script/dom/characterdata.rs | 42 +++++++++++-------- components/script/dom/comment.rs | 5 --- components/script/dom/document.rs | 5 ++- components/script/dom/htmltitleelement.rs | 5 ++- components/script/dom/node.rs | 25 ++++++----- .../script/dom/processinginstruction.rs | 4 -- components/script/dom/text.rs | 5 --- components/script/parse/html.rs | 22 +++++----- 9 files changed, 60 insertions(+), 69 deletions(-) diff --git a/components/layout/wrapper.rs b/components/layout/wrapper.rs index b599e75d745..137a2e469b6 100644 --- a/components/layout/wrapper.rs +++ b/components/layout/wrapper.rs @@ -41,11 +41,12 @@ use opaque_node::OpaqueNodeMethods; use cssparser::RGBA; use gfx::display_list::OpaqueNode; -use script::dom::bindings::codegen::InheritTypes::{ElementCast, HTMLIFrameElementCast}; -use script::dom::bindings::codegen::InheritTypes::{HTMLCanvasElementCast, HTMLImageElementCast}; -use script::dom::bindings::codegen::InheritTypes::{HTMLInputElementCast, HTMLTextAreaElementCast}; -use script::dom::bindings::codegen::InheritTypes::{NodeCast, TextCast}; +use script::dom::bindings::codegen::InheritTypes::{CharacterDataCast, ElementCast}; +use script::dom::bindings::codegen::InheritTypes::{HTMLIFrameElementCast, HTMLCanvasElementCast}; +use script::dom::bindings::codegen::InheritTypes::{HTMLImageElementCast, HTMLInputElementCast}; +use script::dom::bindings::codegen::InheritTypes::{HTMLTextAreaElementCast, NodeCast, TextCast}; use script::dom::bindings::js::LayoutJS; +use script::dom::characterdata::LayoutCharacterDataHelpers; use script::dom::element::{Element, ElementTypeId}; use script::dom::element::{LayoutElementHelpers, RawLayoutElementHelpers}; use script::dom::htmlelement::HTMLElementTypeId; @@ -222,9 +223,8 @@ impl<'ln> TLayoutNode for LayoutNode<'ln> { let text: Option> = TextCast::to_layout_js(self.get_jsmanaged()); if let Some(text) = text { return vec![ - ContentItem::String((*text.unsafe_get()).characterdata() - .data_for_layout() - .to_owned()) + ContentItem::String( + CharacterDataCast::from_layout_js(&text).data_for_layout().to_owned()) ]; } let input: Option> = @@ -961,7 +961,7 @@ impl<'ln> ThreadSafeLayoutNode<'ln> { None => return false }; - if !is_whitespace((*text.unsafe_get()).characterdata().data_for_layout()) { + if !is_whitespace(CharacterDataCast::from_layout_js(&text).data_for_layout()) { return false } diff --git a/components/script/dom/characterdata.rs b/components/script/dom/characterdata.rs index 1796663acf0..82fcc459fec 100644 --- a/components/script/dom/characterdata.rs +++ b/components/script/dom/characterdata.rs @@ -10,7 +10,7 @@ use dom::bindings::codegen::InheritTypes::{CharacterDataDerived, ElementCast}; use dom::bindings::codegen::InheritTypes::NodeCast; use dom::bindings::error::{Fallible, ErrorResult}; use dom::bindings::error::Error::IndexSize; -use dom::bindings::js::{JSRef, Temporary}; +use dom::bindings::js::{JSRef, LayoutJS, Temporary}; use dom::document::Document; use dom::element::Element; use dom::eventtarget::{EventTarget, EventTargetTypeId}; @@ -45,23 +45,6 @@ impl CharacterData { data: DOMRefCell::new(data), } } - - #[inline] - pub fn node<'a>(&'a self) -> &'a Node { - &self.node - } - - #[inline] - pub fn data(&self) -> Ref { - self.data.borrow() - } - - #[inline] - #[allow(unsafe_code)] - pub unsafe fn data_for_layout<'a>(&'a self) -> &'a str { - self.data.borrow_for_layout().as_slice() - } - } impl<'a> CharacterDataMethods for JSRef<'a, CharacterData> { @@ -144,3 +127,26 @@ impl<'a> CharacterDataMethods for JSRef<'a, CharacterData> { } } +pub trait CharacterDataHelpers<'a> { + fn data(self) -> Ref<'a, DOMString>; +} + +impl<'a> CharacterDataHelpers<'a> for JSRef<'a, CharacterData> { + #[inline] + fn data(self) -> Ref<'a, DOMString> { + self.extended_deref().data.borrow() + } +} + +#[allow(unsafe_code)] +pub trait LayoutCharacterDataHelpers { + unsafe fn data_for_layout<'a>(&'a self) -> &'a str; +} + +#[allow(unsafe_code)] +impl LayoutCharacterDataHelpers for LayoutJS { + #[inline] + unsafe fn data_for_layout<'a>(&'a self) -> &'a str { + &(*self.unsafe_get()).data.borrow_for_layout() + } +} diff --git a/components/script/dom/comment.rs b/components/script/dom/comment.rs index 85cca18b2b2..743e935db01 100644 --- a/components/script/dom/comment.rs +++ b/components/script/dom/comment.rs @@ -42,10 +42,5 @@ impl Comment { let document = global.as_window().Document().root(); Ok(Comment::new(data, document.r())) } - - #[inline] - pub fn characterdata<'a>(&'a self) -> &'a CharacterData { - &self.characterdata - } } diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index 9812cf13f0b..ca4182164d8 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -18,7 +18,7 @@ use dom::bindings::codegen::InheritTypes::{EventTargetCast, HTMLAnchorElementCas use dom::bindings::codegen::InheritTypes::{HTMLAnchorElementDerived, HTMLAppletElementDerived}; use dom::bindings::codegen::InheritTypes::{HTMLAreaElementDerived, HTMLEmbedElementDerived}; use dom::bindings::codegen::InheritTypes::{HTMLFormElementDerived, HTMLImageElementDerived}; -use dom::bindings::codegen::InheritTypes::{HTMLScriptElementDerived}; +use dom::bindings::codegen::InheritTypes::{HTMLScriptElementDerived, CharacterDataCast}; use dom::bindings::error::{ErrorResult, Fallible}; use dom::bindings::error::Error::{NotSupported, InvalidCharacter, Security}; use dom::bindings::error::Error::HierarchyRequest; @@ -29,6 +29,7 @@ use dom::bindings::refcounted::Trusted; use dom::bindings::utils::reflect_dom_object; use dom::bindings::utils::{xml_name_type, validate_and_extract}; use dom::bindings::utils::XMLName::InvalidXMLName; +use dom::characterdata::CharacterDataHelpers; use dom::comment::Comment; use dom::customevent::CustomEvent; use dom::documentfragment::DocumentFragment; @@ -1125,7 +1126,7 @@ impl<'a> DocumentMethods for JSRef<'a, Document> { for child in title_element.children() { let child = child.root(); if let Some(text) = TextCast::to_ref(child.r()) { - title.push_str(&text.characterdata().data()); + title.push_str(&CharacterDataCast::from_ref(text).data()); } } } diff --git a/components/script/dom/htmltitleelement.rs b/components/script/dom/htmltitleelement.rs index df2592acef9..5fb9cd3b25f 100644 --- a/components/script/dom/htmltitleelement.rs +++ b/components/script/dom/htmltitleelement.rs @@ -6,8 +6,9 @@ use dom::bindings::codegen::Bindings::HTMLTitleElementBinding; use dom::bindings::codegen::Bindings::HTMLTitleElementBinding::HTMLTitleElementMethods; use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods; use dom::bindings::codegen::InheritTypes::{HTMLElementCast, HTMLTitleElementDerived, NodeCast}; -use dom::bindings::codegen::InheritTypes::{TextCast}; +use dom::bindings::codegen::InheritTypes::{CharacterDataCast, TextCast}; use dom::bindings::js::{JSRef, Temporary}; +use dom::characterdata::CharacterDataHelpers; use dom::document::{Document, DocumentHelpers}; use dom::eventtarget::{EventTarget, EventTargetTypeId}; use dom::element::ElementTypeId; @@ -51,7 +52,7 @@ impl<'a> HTMLTitleElementMethods for JSRef<'a, HTMLTitleElement> { let child = child.root(); let text: Option> = TextCast::to_ref(child.r()); match text { - Some(text) => content.push_str(text.characterdata().data().as_slice()), + Some(text) => content.push_str(&CharacterDataCast::from_ref(text).data()), None => (), } } diff --git a/components/script/dom/node.rs b/components/script/dom/node.rs index f6c7adc2668..89ec44718eb 100644 --- a/components/script/dom/node.rs +++ b/components/script/dom/node.rs @@ -13,12 +13,11 @@ use dom::bindings::codegen::Bindings::NamedNodeMapBinding::NamedNodeMapMethods; use dom::bindings::codegen::Bindings::NodeBinding::{NodeConstants, NodeMethods}; use dom::bindings::codegen::Bindings::NodeListBinding::NodeListMethods; use dom::bindings::codegen::Bindings::ProcessingInstructionBinding::ProcessingInstructionMethods; -use dom::bindings::codegen::InheritTypes::{CommentCast, DocumentCast, DocumentTypeCast}; -use dom::bindings::codegen::InheritTypes::{ElementCast, TextCast, NodeCast, ElementDerived}; -use dom::bindings::codegen::InheritTypes::{CharacterDataCast, NodeBase, NodeDerived}; -use dom::bindings::codegen::InheritTypes::{ProcessingInstructionCast, EventTargetCast}; +use dom::bindings::codegen::InheritTypes::{CharacterDataCast, DocumentCast, DocumentTypeCast}; +use dom::bindings::codegen::InheritTypes::{ElementCast, NodeCast, ElementDerived, EventTargetCast}; use dom::bindings::codegen::InheritTypes::{HTMLLegendElementDerived, HTMLFieldSetElementDerived}; -use dom::bindings::codegen::InheritTypes::HTMLOptGroupElementDerived; +use dom::bindings::codegen::InheritTypes::{HTMLOptGroupElementDerived, NodeBase, NodeDerived}; +use dom::bindings::codegen::InheritTypes::{ProcessingInstructionCast, TextCast}; use dom::bindings::conversions; use dom::bindings::error::Fallible; use dom::bindings::error::Error::{NotFound, HierarchyRequest, Syntax}; @@ -29,7 +28,7 @@ use dom::bindings::js::{ResultRootable, OptionalRootable, MutNullableJS}; use dom::bindings::trace::JSTraceable; use dom::bindings::trace::RootedVec; use dom::bindings::utils::{Reflectable, reflect_dom_object}; -use dom::characterdata::CharacterData; +use dom::characterdata::{CharacterData, CharacterDataHelpers}; use dom::comment::Comment; use dom::document::{Document, DocumentHelpers, IsHTMLDocument, DocumentSource}; use dom::documentfragment::DocumentFragment; @@ -1601,8 +1600,8 @@ impl Node { NodeCast::from_temporary(doc_fragment) }, NodeTypeId::Comment => { - let comment: JSRef = CommentCast::to_ref(node).unwrap(); - let comment = Comment::new(comment.characterdata().data().clone(), document.r()); + let cdata = CharacterDataCast::to_ref(node).unwrap(); + let comment = Comment::new(cdata.Data(), document.r()); NodeCast::from_temporary(comment) }, NodeTypeId::Document => { @@ -1629,14 +1628,14 @@ impl Node { NodeCast::from_temporary(element) }, NodeTypeId::Text => { - let text: JSRef = TextCast::to_ref(node).unwrap(); - let text = Text::new(text.characterdata().data().clone(), document.r()); + let cdata = CharacterDataCast::to_ref(node).unwrap(); + let text = Text::new(cdata.Data(), document.r()); NodeCast::from_temporary(text) }, NodeTypeId::ProcessingInstruction => { let pi: JSRef = ProcessingInstructionCast::to_ref(node).unwrap(); let pi = ProcessingInstruction::new(pi.target().clone(), - pi.characterdata().data().clone(), document.r()); + CharacterDataCast::from_ref(pi).Data(), document.r()); NodeCast::from_temporary(pi) }, }.root(); @@ -1696,7 +1695,7 @@ impl Node { let node = node.root(); let text = TextCast::to_ref(node.r()); match text { - Some(text) => content.push_str(text.characterdata().data().as_slice()), + Some(text) => content.push_str(&CharacterDataCast::from_ref(text).Data()), None => (), } } @@ -2123,7 +2122,7 @@ impl<'a> NodeMethods for JSRef<'a, Node> { let pi: JSRef = ProcessingInstructionCast::to_ref(node).unwrap(); let other_pi: JSRef = ProcessingInstructionCast::to_ref(other).unwrap(); (*pi.target() == *other_pi.target()) && - (*pi.characterdata().data() == *other_pi.characterdata().data()) + (*CharacterDataCast::from_ref(pi).data() == *CharacterDataCast::from_ref(other_pi).data()) } fn is_equal_characterdata(node: JSRef, other: JSRef) -> bool { let characterdata: JSRef = CharacterDataCast::to_ref(node).unwrap(); diff --git a/components/script/dom/processinginstruction.rs b/components/script/dom/processinginstruction.rs index 528c4cdd64a..c9d102b9862 100644 --- a/components/script/dom/processinginstruction.rs +++ b/components/script/dom/processinginstruction.rs @@ -38,10 +38,6 @@ impl ProcessingInstruction { document, ProcessingInstructionBinding::Wrap) } - pub fn characterdata<'a>(&'a self) -> &'a CharacterData { - &self.characterdata - } - pub fn target<'a>(&'a self) -> &'a DOMString { &self.target } diff --git a/components/script/dom/text.rs b/components/script/dom/text.rs index ea6c2791e96..c2e9c588665 100644 --- a/components/script/dom/text.rs +++ b/components/script/dom/text.rs @@ -42,10 +42,5 @@ impl Text { let document = global.as_window().Document().root(); Ok(Text::new(text, document.r())) } - - #[inline] - pub fn characterdata<'a>(&'a self) -> &'a CharacterData { - &self.characterdata - } } diff --git a/components/script/parse/html.rs b/components/script/parse/html.rs index 66a95f2e879..d3e6b981783 100644 --- a/components/script/parse/html.rs +++ b/components/script/parse/html.rs @@ -7,13 +7,14 @@ use dom::attr::AttrHelpers; use dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods; use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods; -use dom::bindings::codegen::InheritTypes::{NodeCast, ElementCast, HTMLScriptElementCast}; -use dom::bindings::codegen::InheritTypes::{DocumentTypeCast, TextCast, CommentCast}; +use dom::bindings::codegen::InheritTypes::{CharacterDataCast, DocumentTypeCast}; +use dom::bindings::codegen::InheritTypes::{ElementCast, HTMLScriptElementCast}; +use dom::bindings::codegen::InheritTypes::{HTMLFormElementDerived, NodeCast}; use dom::bindings::codegen::InheritTypes::ProcessingInstructionCast; -use dom::bindings::codegen::InheritTypes::HTMLFormElementDerived; use dom::bindings::js::{JS, JSRef, Temporary, OptionalRootable, Root}; use dom::bindings::js::RootedReference; use dom::bindings::trace::RootedVec; +use dom::characterdata::CharacterDataHelpers; use dom::comment::Comment; use dom::document::{Document, DocumentHelpers}; use dom::document::{DocumentSource, IsHTMLDocument}; @@ -234,22 +235,19 @@ impl<'a> Serializable for JSRef<'a, Node> { }, (IncludeNode, NodeTypeId::Text) => { - let text: JSRef = TextCast::to_ref(node).unwrap(); - let data = text.characterdata().data(); - serializer.write_text(data.as_slice()) + let cdata = CharacterDataCast::to_ref(node).unwrap(); + serializer.write_text(&cdata.data()) }, (IncludeNode, NodeTypeId::Comment) => { - let comment: JSRef = CommentCast::to_ref(node).unwrap(); - let data = comment.characterdata().data(); - serializer.write_comment(data.as_slice()) + let cdata = CharacterDataCast::to_ref(node).unwrap(); + serializer.write_comment(&cdata.data()) }, (IncludeNode, NodeTypeId::ProcessingInstruction) => { let pi: JSRef = ProcessingInstructionCast::to_ref(node).unwrap(); - let data = pi.characterdata().data(); - serializer.write_processing_instruction(pi.target().as_slice(), - data.as_slice()) + let data = CharacterDataCast::from_ref(pi).data(); + serializer.write_processing_instruction(&pi.target(), &data) }, (IncludeNode, NodeTypeId::DocumentFragment) => Ok(()), From 702cea6fc31241d6dd9d211db208ce7ff09cbda3 Mon Sep 17 00:00:00 2001 From: Anthony Ramine Date: Thu, 9 Apr 2015 02:40:47 +0200 Subject: [PATCH 32/32] Fix CharacterData::SubstringData() It was not following the spec and it could panic. --- components/script/dom/characterdata.rs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/components/script/dom/characterdata.rs b/components/script/dom/characterdata.rs index 82fcc459fec..172fda96f47 100644 --- a/components/script/dom/characterdata.rs +++ b/components/script/dom/characterdata.rs @@ -20,6 +20,7 @@ use util::str::DOMString; use std::borrow::ToOwned; use std::cell::Ref; +use std::cmp; #[dom_struct] pub struct CharacterData { @@ -69,9 +70,17 @@ impl<'a> CharacterDataMethods for JSRef<'a, CharacterData> { // https://dom.spec.whatwg.org/#dom-characterdata-substringdata fn SubstringData(self, offset: u32, count: u32) -> Fallible { - // FIXME(https://github.com/rust-lang/rust/issues/23338) let data = self.data.borrow(); - Ok(data.slice_chars(offset as usize, (offset + count) as usize).to_owned()) + // Step 1. + let len = data.chars().count(); + if len > offset as usize { + // Step 2. + return Err(IndexSize); + } + // Step 3. + let end = cmp::min((offset + count) as usize, len); + // Step 4. + Ok(data.slice_chars(offset as usize, end).to_owned()) } // https://dom.spec.whatwg.org/#dom-characterdata-appenddata