From 13f9a66632644ea91711ce1ab5cfa92ffee88484 Mon Sep 17 00:00:00 2001 From: Youngmin Yoo Date: Mon, 10 Feb 2014 11:26:39 +0900 Subject: [PATCH 1/4] Impl Basic support for object element --- src/components/main/layout/construct.rs | 66 +++++++++++++++---- src/components/main/layout/layout_task.rs | 7 +- src/components/script/dom/element.rs | 5 ++ src/components/script/dom/node.rs | 15 ++++- .../script/html/hubbub_html_parser.rs | 1 - src/components/util/url.rs | 7 ++ 6 files changed, 83 insertions(+), 18 deletions(-) diff --git a/src/components/main/layout/construct.rs b/src/components/main/layout/construct.rs index 293ba995da3..98d33db8d26 100644 --- a/src/components/main/layout/construct.rs +++ b/src/components/main/layout/construct.rs @@ -34,14 +34,19 @@ use layout::util::{LayoutDataAccess, OpaqueNode}; use layout::wrapper::{PostorderNodeMutTraversal, TLayoutNode, ThreadSafeLayoutNode}; use gfx::font_context::FontContext; -use script::dom::element::{HTMLIframeElementTypeId, HTMLImageElementTypeId}; +use script::dom::element::{HTMLIframeElementTypeId, HTMLImageElementTypeId, HTMLObjectElementTypeId}; use script::dom::node::{CommentNodeTypeId, DoctypeNodeTypeId, DocumentFragmentNodeTypeId}; use script::dom::node::{DocumentNodeTypeId, ElementNodeTypeId, ProcessingInstructionNodeTypeId}; use script::dom::node::{TextNodeTypeId}; use style::computed_values::{display, position, float, white_space}; use style::ComputedValues; +use servo_util::namespace; +use servo_util::url::parse_url; +use servo_util::url::is_image_data; +use extra::url::Url; use extra::arc::Arc; + use std::cell::RefCell; use std::util; use std::num::Zero; @@ -220,16 +225,20 @@ pub struct FlowConstructor<'a> { /// The font context. font_context: ~FontContext, + + /// The URL of the page. + url: &'a Url, } impl<'fc> FlowConstructor<'fc> { /// Creates a new flow constructor. - pub fn init<'a>(layout_context: &'a mut LayoutContext) -> FlowConstructor<'a> { + pub fn init<'a>(layout_context: &'a mut LayoutContext, url: &'a Url) -> FlowConstructor<'a> { let font_context = ~FontContext::new(layout_context.font_context_info.clone()); FlowConstructor { layout_context: layout_context, next_flow_id: RefCell::new(0), font_context: font_context, + url: url, } } @@ -241,14 +250,13 @@ impl<'fc> FlowConstructor<'fc> { } /// Builds the `ImageBoxInfo` for the given image. This is out of line to guide inlining. - fn build_box_info_for_image(&mut self, node: ThreadSafeLayoutNode) -> Option { - // FIXME(pcwalton): Don't copy URLs. - match node.image_url() { - None => None, + fn build_box_info_for_image(&mut self, node: ThreadSafeLayoutNode, url: Option) -> SpecificBoxInfo { + match url { + None => GenericBox, Some(url) => { // FIXME(pcwalton): The fact that image boxes store the cache within them makes // little sense to me. - Some(ImageBoxInfo::new(&node, url, self.layout_context.image_cache.clone())) + ImageBox(ImageBoxInfo::new(&node, url, self.layout_context.image_cache.clone())) } } } @@ -257,13 +265,11 @@ impl<'fc> FlowConstructor<'fc> { pub fn build_specific_box_info_for_node(&mut self, node: ThreadSafeLayoutNode) -> SpecificBoxInfo { match node.type_id() { - ElementNodeTypeId(HTMLImageElementTypeId) => { - match self.build_box_info_for_image(node) { - None => GenericBox, - Some(image_box_info) => ImageBox(image_box_info), - } - } + ElementNodeTypeId(HTMLImageElementTypeId) => self.build_box_info_for_image(node, node.image_url()), ElementNodeTypeId(HTMLIframeElementTypeId) => IframeBox(IframeBoxInfo::new(&node)), + ElementNodeTypeId(HTMLObjectElementTypeId) => { + self.build_box_info_for_image(node, node.get_object_data(self.url)) + } TextNodeTypeId => UnscannedTextBox(UnscannedTextBoxInfo::new(&node)), _ => GenericBox, } @@ -709,6 +715,7 @@ impl<'ln> NodeUtils for ThreadSafeLayoutNode<'ln> { DocumentFragmentNodeTypeId | DocumentNodeTypeId(_) | ElementNodeTypeId(HTMLImageElementTypeId) => true, + ElementNodeTypeId(HTMLObjectElementTypeId) => self.has_object_data(), ElementNodeTypeId(_) => false, } } @@ -761,6 +768,39 @@ impl<'ln> NodeUtils for ThreadSafeLayoutNode<'ln> { } } +/// Methods for interacting with HTMLObjectElement nodes +trait ObjectElement { + /// Returns None if this node is not matching attributes. + fn get_type_and_data(self) -> (Option<&'static str>, Option<&'static str>); + + /// Returns true if this node has object data that is correct uri. + fn has_object_data(self) -> bool; + + /// Returns the "data" attribute value parsed as a URL + fn get_object_data(self, base_url: &Url) -> Option; +} + +impl<'ln> ObjectElement for ThreadSafeLayoutNode<'ln> { + fn get_type_and_data(self) -> (Option<&'static str>, Option<&'static str>) { + (self.with_element(|e| { e.get_attr(&namespace::Null, "type") } ), + self.with_element(|e| { e.get_attr(&namespace::Null, "data") } )) + } + + fn has_object_data(self) -> bool { + match self.get_type_and_data() { + (None, Some(uri)) => is_image_data(uri), + _ => false + } + } + + fn get_object_data(self, base_url: &Url) -> Option { + match self.get_type_and_data() { + (None, Some(uri)) if is_image_data(uri) => Some(parse_url(uri, Some(base_url.clone()))), + _ => None + } + } +} + /// Strips ignorable whitespace from the start of a list of boxes. fn strip_ignorable_whitespace_from_start(opt_boxes: &mut Option<~[Box]>) { match util::replace(opt_boxes, None) { diff --git a/src/components/main/layout/layout_task.rs b/src/components/main/layout/layout_task.rs index 2d79208b6ed..64482cd9803 100644 --- a/src/components/main/layout/layout_task.rs +++ b/src/components/main/layout/layout_task.rs @@ -22,6 +22,7 @@ use layout::parallel; use layout::util::{LayoutDataAccess, OpaqueNode, LayoutDataWrapper}; use layout::wrapper::{DomLeafSet, LayoutNode, TLayoutNode, ThreadSafeLayoutNode}; +use extra::url::Url; use extra::arc::{Arc, MutexArc}; use geom::rect::Rect; use geom::size::Size2D; @@ -427,9 +428,9 @@ impl LayoutTask { /// is intertwined with selector matching, making it difficult to compare directly. It is /// marked `#[inline(never)]` to aid benchmarking in sampling profilers. #[inline(never)] - fn construct_flow_tree(&self, layout_context: &mut LayoutContext, node: LayoutNode) -> ~Flow { + fn construct_flow_tree(&self, layout_context: &mut LayoutContext, node: LayoutNode, url: &Url) -> ~Flow { let node = ThreadSafeLayoutNode::new(node); - node.traverse_postorder_mut(&mut FlowConstructor::init(layout_context)); + node.traverse_postorder_mut(&mut FlowConstructor::init(layout_context, url)); let mut layout_data_ref = node.mutate_layout_data(); let result = match *layout_data_ref.get() { @@ -594,7 +595,7 @@ impl LayoutTask { // Construct the flow tree. profile(time::LayoutTreeBuilderCategory, self.profiler_chan.clone(), - || self.construct_flow_tree(&mut layout_ctx, *node)) + || self.construct_flow_tree(&mut layout_ctx, *node, &data.url)) }); // Verification of the flow tree, which ensures that all nodes were either marked as leaves diff --git a/src/components/script/dom/element.rs b/src/components/script/dom/element.rs index 2eb0553f2f6..12361d7ec85 100644 --- a/src/components/script/dom/element.rs +++ b/src/components/script/dom/element.rs @@ -248,6 +248,11 @@ impl Element { iframe.AfterSetAttr(local_name.clone(), value.clone()); }); } + ElementNodeTypeId(HTMLObjectElementTypeId) => { + abstract_self.with_mut_object_element(|object| { + object.AfterSetAttr(local_name.clone(), value.clone()); + }); + } _ => () } diff --git a/src/components/script/dom/node.rs b/src/components/script/dom/node.rs index 87e2bddaa37..3f0649fbdac 100644 --- a/src/components/script/dom/node.rs +++ b/src/components/script/dom/node.rs @@ -10,11 +10,12 @@ use dom::bindings::utils; use dom::characterdata::CharacterData; use dom::document::{AbstractDocument, DocumentTypeId}; use dom::documenttype::DocumentType; -use dom::element::{Element, ElementTypeId, HTMLImageElementTypeId, HTMLIframeElementTypeId}; +use dom::element::{Element, ElementTypeId, HTMLImageElementTypeId, HTMLIframeElementTypeId, HTMLObjectElementTypeId}; use dom::element::{HTMLAnchorElementTypeId, HTMLStyleElementTypeId}; use dom::eventtarget::{AbstractEventTarget, EventTarget, NodeTypeId}; use dom::htmliframeelement::HTMLIFrameElement; use dom::htmlimageelement::HTMLImageElement; +use dom::htmlobjectelement::HTMLObjectElement; use dom::nodelist::{NodeList}; use dom::text::Text; use dom::processinginstruction::ProcessingInstruction; @@ -488,6 +489,18 @@ impl<'a> AbstractNode { self.type_id() == ElementNodeTypeId(HTMLIframeElementTypeId) } + #[inline] + pub fn is_object_element(self) -> bool { + self.type_id() == ElementNodeTypeId(HTMLObjectElementTypeId) + } + + pub fn with_mut_object_element(self, f: |&mut HTMLObjectElement| -> R) -> R { + if !self.is_object_element() { + fail!(~"node is not an image element"); + } + self.transmute_mut(f) + } + pub fn with_mut_iframe_element(self, f: |&mut HTMLIFrameElement| -> R) -> R { if !self.is_iframe_element() { fail!(~"node is not an iframe element"); diff --git a/src/components/script/html/hubbub_html_parser.rs b/src/components/script/html/hubbub_html_parser.rs index 9b66e4ecb1a..d388a77b0e3 100644 --- a/src/components/script/html/hubbub_html_parser.rs +++ b/src/components/script/html/hubbub_html_parser.rs @@ -392,7 +392,6 @@ pub fn parse_html(cx: *JSContext, image_element.update_image(image_cache_task.clone(), Some(url2.clone())); }); } - _ => {} } diff --git a/src/components/util/url.rs b/src/components/util/url.rs index 6ef650a64cd..004d72e48b8 100644 --- a/src/components/util/url.rs +++ b/src/components/util/url.rs @@ -196,3 +196,10 @@ pub fn url_map() -> UrlMap { HashMap::new() } + +pub fn is_image_data(uri: &str) -> bool { + static types: &'static [&'static str] = &[&"data:image/png", &"data:image/gif", &"data:image/jpeg"]; + types.iter().any(|&type_| uri.starts_with(type_)) +} + + From 5ea247253e6115e9b2af7fed31da379bf9c15cfa Mon Sep 17 00:00:00 2001 From: Youngmin Yoo Date: Mon, 10 Feb 2014 15:59:27 +0900 Subject: [PATCH 2/4] Support object element data. --- .../script/dom/htmlobjectelement.rs | 40 ++++++++++++++++++- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/src/components/script/dom/htmlobjectelement.rs b/src/components/script/dom/htmlobjectelement.rs index aeb6ad754e9..74f40db40dd 100644 --- a/src/components/script/dom/htmlobjectelement.rs +++ b/src/components/script/dom/htmlobjectelement.rs @@ -12,14 +12,21 @@ use dom::validitystate::ValidityState; use dom::windowproxy::WindowProxy; use servo_util::str::DOMString; +use extra::url::Url; +use servo_net::image_cache_task; +use servo_net::image_cache_task::ImageCacheTask; +use servo_util::url::parse_url; +use servo_util::namespace::Null; +use servo_util::url::is_image_data; + pub struct HTMLObjectElement { - htmlelement: HTMLElement + htmlelement: HTMLElement, } impl HTMLObjectElement { pub fn new_inherited(localName: DOMString, document: AbstractDocument) -> HTMLObjectElement { HTMLObjectElement { - htmlelement: HTMLElement::new_inherited(HTMLObjectElementTypeId, localName, document) + htmlelement: HTMLElement::new_inherited(HTMLObjectElementTypeId, localName, document), } } @@ -30,6 +37,35 @@ impl HTMLObjectElement { } impl HTMLObjectElement { + + // Makes the local `data` member match the status of the `data` attribute and starts + /// prefetching the image. This method must be called after `data` is changed. + pub fn process_data_url(&mut self, image_cache: ImageCacheTask, url: Option) { + let elem = &mut self.htmlelement.element; + + // TODO: support other values + match (elem.get_attribute(Null, "type").map(|x| x.Value()), + elem.get_attribute(Null, "data").map(|x| x.Value())) { + (None, Some(uri)) => { + if is_image_data(uri) { + let data_url = parse_url(uri, url); + // Issue #84 + image_cache.send(image_cache_task::Prefetch(data_url)); + } + } + _ => { } + } + } + + pub fn AfterSetAttr(&mut self, name: DOMString, _value: DOMString) { + if "data" == name { + let document = self.htmlelement.element.node.owner_doc(); + let window = document.document().window; + let url = window.page.url.as_ref().map(|&(ref url, _)| url.clone()); + self.process_data_url(window.image_cache_task.clone(), url); + } + } + pub fn Data(&self) -> DOMString { ~"" } From 80cc71be8f58ddfd7137b4257e1c71195add6357 Mon Sep 17 00:00:00 2001 From: Youngmin Yoo Date: Tue, 11 Feb 2014 14:02:09 +0900 Subject: [PATCH 3/4] Add object element test file --- src/test/html/object_element.html | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 src/test/html/object_element.html diff --git a/src/test/html/object_element.html b/src/test/html/object_element.html new file mode 100644 index 00000000000..501e0c049f9 --- /dev/null +++ b/src/test/html/object_element.html @@ -0,0 +1,15 @@ + + + + + + + + + + ERROR + + + + + From ed0c15a93aa9598b3f2f0bc49b8560f640960c44 Mon Sep 17 00:00:00 2001 From: Youngmin Yoo Date: Tue, 11 Feb 2014 14:06:14 +0900 Subject: [PATCH 4/4] Add a reftest for object element --- src/test/ref/basic.list | 1 + src/test/ref/object_element_a.html | 15 +++++++++++++++ src/test/ref/object_element_b.html | 9 +++++++++ 3 files changed, 25 insertions(+) create mode 100644 src/test/ref/object_element_a.html create mode 100644 src/test/ref/object_element_b.html diff --git a/src/test/ref/basic.list b/src/test/ref/basic.list index 97e2770f93f..e3e7997a5ae 100644 --- a/src/test/ref/basic.list +++ b/src/test/ref/basic.list @@ -33,3 +33,4 @@ == background_external_stylesheet.html background_ref.html == block_image.html 500x300_green.html # == simple_iframe.html simple_iframe_ref.html -- disabled due to iframe crashiness +== object_element_a.html object_element_b.html diff --git a/src/test/ref/object_element_a.html b/src/test/ref/object_element_a.html new file mode 100644 index 00000000000..501e0c049f9 --- /dev/null +++ b/src/test/ref/object_element_a.html @@ -0,0 +1,15 @@ + + + + + + + + + + ERROR + + + + + diff --git a/src/test/ref/object_element_b.html b/src/test/ref/object_element_b.html new file mode 100644 index 00000000000..ae6d8ab3648 --- /dev/null +++ b/src/test/ref/object_element_b.html @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file