diff options
22 files changed, 531 insertions, 17 deletions
diff --git a/components/layout/block.rs b/components/layout/block.rs index 7de8f379560..e28e85d139a 100644 --- a/components/layout/block.rs +++ b/components/layout/block.rs @@ -678,6 +678,7 @@ impl BlockFlow { fn is_replaced_content(&self) -> bool { match self.fragment.specific { SpecificFragmentInfo::ScannedText(_) | + SpecificFragmentInfo::Svg(_) | SpecificFragmentInfo::Image(_) | SpecificFragmentInfo::Canvas(_) | SpecificFragmentInfo::InlineBlock(_) => true, diff --git a/components/layout/construct.rs b/components/layout/construct.rs index 8a463ded66a..ae77f4d7751 100644 --- a/components/layout/construct.rs +++ b/components/layout/construct.rs @@ -22,7 +22,7 @@ use floats::FloatKind; use flow::{self, AbsoluteDescendants, IS_ABSOLUTELY_POSITIONED, ImmutableFlowUtils}; use flow::{CAN_BE_FRAGMENTED, MutableFlowUtils, MutableOwnedFlowUtils}; use flow_ref::{self, FlowRef}; -use fragment::{CanvasFragmentInfo, ImageFragmentInfo, InlineAbsoluteFragmentInfo}; +use fragment::{CanvasFragmentInfo, ImageFragmentInfo, InlineAbsoluteFragmentInfo, SvgFragmentInfo}; use fragment::{Fragment, GeneratedContentInfo, IframeFragmentInfo}; use fragment::{InlineAbsoluteHypotheticalFragmentInfo, TableColumnFragmentInfo}; use fragment::{InlineBlockFragmentInfo, SpecificFragmentInfo, UnscannedTextFragmentInfo}; @@ -335,6 +335,10 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode> let data = node.canvas_data().unwrap(); SpecificFragmentInfo::Canvas(box CanvasFragmentInfo::new(node, data, self.style_context())) } + Some(LayoutNodeType::Element(LayoutElementType::SVGSVGElement)) => { + let data = node.svg_data().unwrap(); + SpecificFragmentInfo::Svg(box SvgFragmentInfo::new(node, data, self.style_context())) + } _ => { // This includes pseudo-elements. SpecificFragmentInfo::Generic @@ -1701,7 +1705,8 @@ impl<ConcreteThreadSafeLayoutNode> NodeUtils for ConcreteThreadSafeLayoutNode Some(LayoutNodeType::Document) | Some(LayoutNodeType::Element(LayoutElementType::HTMLImageElement)) | Some(LayoutNodeType::Element(LayoutElementType::HTMLIFrameElement)) | - Some(LayoutNodeType::Element(LayoutElementType::HTMLCanvasElement)) => true, + Some(LayoutNodeType::Element(LayoutElementType::HTMLCanvasElement)) | + Some(LayoutNodeType::Element(LayoutElementType::SVGSVGElement)) => true, Some(LayoutNodeType::Element(LayoutElementType::HTMLObjectElement)) => self.has_object_data(), Some(LayoutNodeType::Element(_)) => false, None => self.get_pseudo_element_type().is_replaced_content(), diff --git a/components/layout/display_list_builder.rs b/components/layout/display_list_builder.rs index 91b70ebed34..d163389f1bd 100644 --- a/components/layout/display_list_builder.rs +++ b/components/layout/display_list_builder.rs @@ -1239,7 +1239,8 @@ impl FragmentDisplayListBuilding for Fragment { SpecificFragmentInfo::MulticolColumn | SpecificFragmentInfo::InlineBlock(_) | SpecificFragmentInfo::InlineAbsoluteHypothetical(_) | - SpecificFragmentInfo::InlineAbsolute(_) => { + SpecificFragmentInfo::InlineAbsolute(_) | + SpecificFragmentInfo::Svg(_) => { if opts::get().show_debug_fragment_borders { self.build_debug_borders_around_fragment(state, stacking_relative_border_box, diff --git a/components/layout/fragment.rs b/components/layout/fragment.rs index a6f3e92f1be..910497daca2 100644 --- a/components/layout/fragment.rs +++ b/components/layout/fragment.rs @@ -30,6 +30,7 @@ use net_traits::image_cache_thread::{ImageOrMetadataAvailable, UsePlaceholder}; use range::*; use rustc_serialize::{Encodable, Encoder}; use script_layout_interface::HTMLCanvasData; +use script_layout_interface::SVGSVGData; use script_layout_interface::restyle_damage::{RECONSTRUCT_FLOW, RestyleDamage}; use script_layout_interface::wrapper_traits::{PseudoElementType, ThreadSafeLayoutElement, ThreadSafeLayoutNode}; use std::borrow::ToOwned; @@ -155,6 +156,7 @@ pub enum SpecificFragmentInfo { Iframe(IframeFragmentInfo), Image(Box<ImageFragmentInfo>), Canvas(Box<CanvasFragmentInfo>), + Svg(Box<SvgFragmentInfo>), /// A hypothetical box (see CSS 2.1 § 10.3.7) for an absolutely-positioned block that was /// declared with `display: inline;`. @@ -186,6 +188,7 @@ impl SpecificFragmentInfo { SpecificFragmentInfo::Iframe(_) | SpecificFragmentInfo::Image(_) | SpecificFragmentInfo::ScannedText(_) | + SpecificFragmentInfo::Svg(_) | SpecificFragmentInfo::Table | SpecificFragmentInfo::TableCell | SpecificFragmentInfo::TableColumn(_) | @@ -216,6 +219,7 @@ impl SpecificFragmentInfo { } SpecificFragmentInfo::InlineBlock(_) => "SpecificFragmentInfo::InlineBlock", SpecificFragmentInfo::ScannedText(_) => "SpecificFragmentInfo::ScannedText", + SpecificFragmentInfo::Svg(_) => "SpecificFragmentInfo::Svg", SpecificFragmentInfo::Table => "SpecificFragmentInfo::Table", SpecificFragmentInfo::TableCell => "SpecificFragmentInfo::TableCell", SpecificFragmentInfo::TableColumn(_) => "SpecificFragmentInfo::TableColumn", @@ -356,6 +360,44 @@ impl CanvasFragmentInfo { } } +#[derive(Clone)] +pub struct SvgFragmentInfo { + pub replaced_image_fragment_info: ReplacedImageFragmentInfo, + pub dom_width: Au, + pub dom_height: Au, +} + +impl SvgFragmentInfo { + pub fn new<N: ThreadSafeLayoutNode>(node: &N, + data: SVGSVGData, + ctx: &SharedStyleContext) + -> SvgFragmentInfo { + SvgFragmentInfo { + replaced_image_fragment_info: ReplacedImageFragmentInfo::new(node, ctx), + dom_width: Au::from_px(data.width as i32), + dom_height: Au::from_px(data.height as i32), + } + } + + /// Returns the original inline-size of the SVG element. + pub fn svg_inline_size(&self) -> Au { + if self.replaced_image_fragment_info.writing_mode_is_vertical { + self.dom_height + } else { + self.dom_width + } + } + + /// Returns the original block-size of the SVG element. + pub fn svg_block_size(&self) -> Au { + if self.replaced_image_fragment_info.writing_mode_is_vertical { + self.dom_width + } else { + self.dom_height + } + } +} + /// A fragment that represents a replaced content image and its accompanying borders, shadows, etc. #[derive(Clone)] @@ -1007,7 +1049,8 @@ impl Fragment { SpecificFragmentInfo::Iframe(_) | SpecificFragmentInfo::Image(_) | SpecificFragmentInfo::InlineAbsolute(_) | - SpecificFragmentInfo::Multicol => { + SpecificFragmentInfo::Multicol | + SpecificFragmentInfo::Svg(_) => { QuantitiesIncludedInIntrinsicInlineSizes::all() } SpecificFragmentInfo::Table | SpecificFragmentInfo::TableCell => { @@ -1509,6 +1552,26 @@ impl Fragment { preferred_inline_size: canvas_inline_size, }); } + SpecificFragmentInfo::Svg(ref mut svg_fragment_info) => { + let mut svg_inline_size = match self.style.content_inline_size() { + LengthOrPercentageOrAuto::Auto | + LengthOrPercentageOrAuto::Percentage(_) => { + svg_fragment_info.svg_inline_size() + } + LengthOrPercentageOrAuto::Length(length) => length, + LengthOrPercentageOrAuto::Calc(calc) => calc.length(), + }; + + svg_inline_size = max(model::specified(self.style.min_inline_size(), Au(0)), svg_inline_size); + if let Some(max) = model::specified_or_none(self.style.max_inline_size(), Au(0)) { + svg_inline_size = min(svg_inline_size, max) + } + + result.union_block(&IntrinsicISizes { + minimum_inline_size: svg_inline_size, + preferred_inline_size: svg_inline_size, + }); + } SpecificFragmentInfo::ScannedText(ref text_fragment_info) => { let range = &text_fragment_info.range; @@ -1596,6 +1659,9 @@ impl Fragment { SpecificFragmentInfo::Canvas(ref canvas_fragment_info) => { canvas_fragment_info.replaced_image_fragment_info.computed_inline_size() } + SpecificFragmentInfo::Svg(ref svg_fragment_info) => { + svg_fragment_info.replaced_image_fragment_info.computed_inline_size() + } SpecificFragmentInfo::Image(ref image_fragment_info) => { image_fragment_info.replaced_image_fragment_info.computed_inline_size() } @@ -1885,7 +1951,8 @@ impl Fragment { SpecificFragmentInfo::InlineBlock(_) | SpecificFragmentInfo::InlineAbsoluteHypothetical(_) | SpecificFragmentInfo::InlineAbsolute(_) | - SpecificFragmentInfo::ScannedText(_) => {} + SpecificFragmentInfo::ScannedText(_) | + SpecificFragmentInfo::Svg(_) => {} }; let style = &*self.style; @@ -1945,6 +2012,18 @@ impl Fragment { fragment_inline_size, fragment_block_size); } + SpecificFragmentInfo::Svg(ref mut svg_fragment_info) => { + let fragment_inline_size = svg_fragment_info.svg_inline_size(); + let fragment_block_size = svg_fragment_info.svg_block_size(); + self.border_box.size.inline = + svg_fragment_info.replaced_image_fragment_info + .calculate_replaced_inline_size(style, + noncontent_inline_size, + container_inline_size, + container_block_size, + fragment_inline_size, + fragment_block_size); + } SpecificFragmentInfo::Iframe(ref iframe_fragment_info) => { self.border_box.size.inline = iframe_fragment_info.calculate_replaced_inline_size(style, @@ -1981,7 +2060,8 @@ impl Fragment { SpecificFragmentInfo::InlineBlock(_) | SpecificFragmentInfo::InlineAbsoluteHypothetical(_) | SpecificFragmentInfo::InlineAbsolute(_) | - SpecificFragmentInfo::ScannedText(_) => {} + SpecificFragmentInfo::ScannedText(_) | + SpecificFragmentInfo::Svg(_) => {} } let style = &*self.style; @@ -2010,6 +2090,17 @@ impl Fragment { fragment_inline_size, fragment_block_size); } + SpecificFragmentInfo::Svg(ref mut svg_fragment_info) => { + let fragment_inline_size = svg_fragment_info.svg_inline_size(); + let fragment_block_size = svg_fragment_info.svg_block_size(); + self.border_box.size.block = + svg_fragment_info.replaced_image_fragment_info + .calculate_replaced_block_size(style, + noncontent_block_size, + containing_block_block_size, + fragment_inline_size, + fragment_block_size); + } SpecificFragmentInfo::ScannedText(ref info) => { // Scanned text fragments' content block-sizes are calculated by the text run // scanner during flow construction. @@ -2065,6 +2156,16 @@ impl Fragment { ascent: computed_block_size + self.border_padding.block_start, } } + SpecificFragmentInfo::Svg(ref svg_fragment_info) => { + let computed_block_size = svg_fragment_info.replaced_image_fragment_info + .computed_block_size(); + InlineMetrics { + block_size_above_baseline: computed_block_size + + self.border_padding.block_start, + depth_below_baseline: self.border_padding.block_end, + ascent: computed_block_size + self.border_padding.block_start, + } + } SpecificFragmentInfo::ScannedText(ref info) => { // Fragments with no glyphs don't contribute any inline metrics. // TODO: Filter out these fragments during flow construction? @@ -2213,6 +2314,7 @@ impl Fragment { SpecificFragmentInfo::Iframe(_) | SpecificFragmentInfo::Image(_) | SpecificFragmentInfo::ScannedText(_) | + SpecificFragmentInfo::Svg(_) | SpecificFragmentInfo::Table | SpecificFragmentInfo::TableCell | SpecificFragmentInfo::TableColumn(_) | @@ -2699,6 +2801,7 @@ impl Fragment { SpecificFragmentInfo::Iframe(_) | SpecificFragmentInfo::Image(_) | SpecificFragmentInfo::ScannedText(_) | + SpecificFragmentInfo::Svg(_) | SpecificFragmentInfo::UnscannedText(_) => true } } diff --git a/components/script/dom/create.rs b/components/script/dom/create.rs index 6cdaf2b0e78..98a2e5ad4ed 100644 --- a/components/script/dom/create.rs +++ b/components/script/dom/create.rs @@ -76,21 +76,44 @@ use dom::htmltrackelement::HTMLTrackElement; use dom::htmlulistelement::HTMLUListElement; use dom::htmlunknownelement::HTMLUnknownElement; use dom::htmlvideoelement::HTMLVideoElement; +use dom::svgsvgelement::SVGSVGElement; use string_cache::{Atom, QualName}; +use util::prefs::PREFS; -pub fn create_element(name: QualName, - prefix: Option<Atom>, - document: &Document, - creator: ElementCreator) +fn create_svg_element(name: QualName, + prefix: Option<DOMString>, + document: &Document) -> Root<Element> { - // FIXME(ajeffrey): Convert directly from Atom to DOMString. + assert!(name.ns == ns!(svg)); - let prefix = prefix.map(|p| DOMString::from(&*p)); + macro_rules! make( + ($ctor:ident) => ({ + let obj = $ctor::new(name.local, prefix, document); + Root::upcast(obj) + }); + ($ctor:ident, $($arg:expr),+) => ({ + let obj = $ctor::new(name.local, prefix, document, $($arg),+); + Root::upcast(obj) + }) + ); - if name.ns != ns!(html) { + if !PREFS.get("dom.svg.enabled").as_boolean().unwrap_or(false) { return Element::new(name.local, name.ns, prefix, document); } + match name.local { + atom!("svg") => make!(SVGSVGElement), + _ => Element::new(name.local, name.ns, prefix, document), + } +} + +fn create_html_element(name: QualName, + prefix: Option<DOMString>, + document: &Document, + creator: ElementCreator) + -> Root<Element> { + assert!(name.ns == ns!(html)); + macro_rules! make( ($ctor:ident) => ({ let obj = $ctor::new(name.local, prefix, document); @@ -249,3 +272,19 @@ pub fn create_element(name: QualName, _ => make!(HTMLUnknownElement), } } + +pub fn create_element(name: QualName, + prefix: Option<Atom>, + document: &Document, + creator: ElementCreator) + -> Root<Element> { + // FIXME(ajeffrey): Convert directly from Atom to DOMString. + + let prefix = prefix.map(|p| DOMString::from(&*p)); + + match name.ns { + ns!(html) => create_html_element(name, prefix, document, creator), + ns!(svg) => create_svg_element(name, prefix, document), + _ => Element::new(name.local, name.ns, prefix, document) + } +} diff --git a/components/script/dom/mod.rs b/components/script/dom/mod.rs index 2335f8a0a25..ace4f979be6 100644 --- a/components/script/dom/mod.rs +++ b/components/script/dom/mod.rs @@ -392,6 +392,9 @@ pub mod storage; pub mod storageevent; pub mod stylesheet; pub mod stylesheetlist; +pub mod svgelement; +pub mod svggraphicselement; +pub mod svgsvgelement; pub mod testbinding; pub mod testbindingiterable; pub mod testbindingpairiterable; diff --git a/components/script/dom/node.rs b/components/script/dom/node.rs index c18613ac6a2..b52a8aec965 100644 --- a/components/script/dom/node.rs +++ b/components/script/dom/node.rs @@ -22,6 +22,7 @@ use dom::bindings::conversions::{self, DerivedFrom}; use dom::bindings::error::{Error, ErrorResult, Fallible}; use dom::bindings::inheritance::{Castable, CharacterDataTypeId, ElementTypeId}; use dom::bindings::inheritance::{EventTargetTypeId, HTMLElementTypeId, NodeTypeId}; +use dom::bindings::inheritance::{SVGElementTypeId, SVGGraphicsElementTypeId}; use dom::bindings::js::{JS, LayoutJS, MutNullableHeap}; use dom::bindings::js::Root; use dom::bindings::js::RootedReference; @@ -36,7 +37,7 @@ use dom::element::{Element, ElementCreator}; use dom::eventtarget::EventTarget; use dom::globalscope::GlobalScope; use dom::htmlbodyelement::HTMLBodyElement; -use dom::htmlcanvaselement::LayoutHTMLCanvasElementHelpers; +use dom::htmlcanvaselement::{HTMLCanvasElement, LayoutHTMLCanvasElementHelpers}; use dom::htmlcollection::HTMLCollection; use dom::htmlelement::HTMLElement; use dom::htmliframeelement::{HTMLIFrameElement, HTMLIFrameElementLayoutMethods}; @@ -46,6 +47,7 @@ use dom::htmltextareaelement::{HTMLTextAreaElement, LayoutHTMLTextAreaElementHel use dom::nodelist::NodeList; use dom::processinginstruction::ProcessingInstruction; use dom::range::WeakRangeVec; +use dom::svgsvgelement::{SVGSVGElement, LayoutSVGSVGElementHelpers}; use dom::text::Text; use dom::virtualmethods::{VirtualMethods, vtable_for}; use dom::window::Window; @@ -59,7 +61,7 @@ use libc::{self, c_void, uintptr_t}; use msg::constellation_msg::PipelineId; use parse::html::parse_html_fragment; use ref_slice::ref_slice; -use script_layout_interface::{HTMLCanvasData, OpaqueStyleAndLayoutData}; +use script_layout_interface::{HTMLCanvasData, OpaqueStyleAndLayoutData, SVGSVGData}; use script_layout_interface::{LayoutElementType, LayoutNodeType, TrustedNodeAddress}; use script_layout_interface::message::Msg; use script_traits::UntrustedNodeAddress; @@ -955,6 +957,7 @@ pub trait LayoutNodeHelpers { fn selection(&self) -> Option<Range<usize>>; fn image_url(&self) -> Option<Url>; fn canvas_data(&self) -> Option<HTMLCanvasData>; + fn svg_data(&self) -> Option<SVGSVGData>; fn iframe_pipeline_id(&self) -> PipelineId; fn opaque(&self) -> OpaqueNode; } @@ -1088,10 +1091,15 @@ impl LayoutNodeHelpers for LayoutJS<Node> { } fn canvas_data(&self) -> Option<HTMLCanvasData> { - self.downcast() + self.downcast::<HTMLCanvasElement>() .map(|canvas| canvas.data()) } + fn svg_data(&self) -> Option<SVGSVGData> { + self.downcast::<SVGSVGElement>() + .map(|svg| svg.data()) + } + fn iframe_pipeline_id(&self) -> PipelineId { let iframe_element = self.downcast::<HTMLIFrameElement>() .expect("not an iframe element!"); @@ -2689,6 +2697,8 @@ impl Into<LayoutElementType> for ElementTypeId { LayoutElementType::HTMLTableSectionElement, ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTextAreaElement) => LayoutElementType::HTMLTextAreaElement, + ElementTypeId::SVGElement(SVGElementTypeId::SVGGraphicsElement(SVGGraphicsElementTypeId::SVGSVGElement)) => + LayoutElementType::SVGSVGElement, _ => LayoutElementType::Element, } } diff --git a/components/script/dom/svgelement.rs b/components/script/dom/svgelement.rs new file mode 100644 index 00000000000..95fd42c40b6 --- /dev/null +++ b/components/script/dom/svgelement.rs @@ -0,0 +1,48 @@ +/* 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::SVGElementBinding; +use dom::bindings::inheritance::Castable; +use dom::bindings::js::Root; +use dom::bindings::str::DOMString; +use dom::document::Document; +use dom::element::Element; +use dom::node::Node; +use dom::virtualmethods::VirtualMethods; +use string_cache::Atom; +use style::element_state::ElementState; + +#[dom_struct] +pub struct SVGElement { + element: Element, +} + +impl SVGElement { + pub fn new_inherited(tag_name: Atom, prefix: Option<DOMString>, + document: &Document) -> SVGElement { + SVGElement::new_inherited_with_state(ElementState::empty(), tag_name, prefix, document) + } + + pub fn new_inherited_with_state(state: ElementState, tag_name: Atom, + prefix: Option<DOMString>, document: &Document) + -> SVGElement { + SVGElement { + element: + Element::new_inherited_with_state(state, tag_name, ns!(svg), prefix, document), + } + } + + #[allow(unrooted_must_root)] + pub fn new(local_name: Atom, prefix: Option<DOMString>, document: &Document) -> Root<SVGElement> { + Node::reflect_node(box SVGElement::new_inherited(local_name, prefix, document), + document, + SVGElementBinding::Wrap) + } +} + +impl VirtualMethods for SVGElement { + fn super_type(&self) -> Option<&VirtualMethods> { + Some(self.upcast::<Element>() as &VirtualMethods) + } +} diff --git a/components/script/dom/svggraphicselement.rs b/components/script/dom/svggraphicselement.rs new file mode 100644 index 00000000000..bd7c8d581f7 --- /dev/null +++ b/components/script/dom/svggraphicselement.rs @@ -0,0 +1,48 @@ +/* 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::SVGGraphicsElementBinding; +use dom::bindings::inheritance::Castable; +use dom::bindings::js::Root; +use dom::bindings::str::DOMString; +use dom::document::Document; +use dom::node::Node; +use dom::svgelement::SVGElement; +use dom::virtualmethods::VirtualMethods; +use string_cache::Atom; +use style::element_state::ElementState; + +#[dom_struct] +pub struct SVGGraphicsElement { + svgelement: SVGElement, +} + +impl SVGGraphicsElement { + pub fn new_inherited(tag_name: Atom, prefix: Option<DOMString>, + document: &Document) -> SVGGraphicsElement { + SVGGraphicsElement::new_inherited_with_state(ElementState::empty(), tag_name, prefix, document) + } + + pub fn new_inherited_with_state(state: ElementState, tag_name: Atom, + prefix: Option<DOMString>, document: &Document) + -> SVGGraphicsElement { + SVGGraphicsElement { + svgelement: + SVGElement::new_inherited_with_state(state, tag_name, prefix, document), + } + } + + #[allow(unrooted_must_root)] + pub fn new(local_name: Atom, prefix: Option<DOMString>, document: &Document) -> Root<SVGGraphicsElement> { + Node::reflect_node(box SVGGraphicsElement::new_inherited(local_name, prefix, document), + document, + SVGGraphicsElementBinding::Wrap) + } +} + +impl VirtualMethods for SVGGraphicsElement { + fn super_type(&self) -> Option<&VirtualMethods> { + Some(self.upcast::<SVGElement>() as &VirtualMethods) + } +} diff --git a/components/script/dom/svgsvgelement.rs b/components/script/dom/svgsvgelement.rs new file mode 100644 index 00000000000..11c1e35b9e9 --- /dev/null +++ b/components/script/dom/svgsvgelement.rs @@ -0,0 +1,83 @@ +/* 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::attr::Attr; +use dom::bindings::codegen::Bindings::SVGSVGElementBinding; +use dom::bindings::inheritance::Castable; +use dom::bindings::js::{LayoutJS, Root}; +use dom::bindings::str::DOMString; +use dom::document::Document; +use dom::element::{AttributeMutation, Element, RawLayoutElementHelpers}; +use dom::node::Node; +use dom::svggraphicselement::SVGGraphicsElement; +use dom::virtualmethods::VirtualMethods; +use script_layout_interface::SVGSVGData; +use string_cache::Atom; +use style::attr::AttrValue; + +const DEFAULT_WIDTH: u32 = 300; +const DEFAULT_HEIGHT: u32 = 150; + +#[dom_struct] +pub struct SVGSVGElement { + svggraphicselement: SVGGraphicsElement +} + +impl SVGSVGElement { + fn new_inherited(local_name: Atom, + prefix: Option<DOMString>, + document: &Document) -> SVGSVGElement { + SVGSVGElement { + svggraphicselement: + SVGGraphicsElement::new_inherited(local_name, prefix, document) + } + } + + #[allow(unrooted_must_root)] + pub fn new(local_name: Atom, + prefix: Option<DOMString>, + document: &Document) -> Root<SVGSVGElement> { + Node::reflect_node(box SVGSVGElement::new_inherited(local_name, prefix, document), + document, + SVGSVGElementBinding::Wrap) + } +} + +pub trait LayoutSVGSVGElementHelpers { + fn data(&self) -> SVGSVGData; +} + +impl LayoutSVGSVGElementHelpers for LayoutJS<SVGSVGElement> { + #[allow(unsafe_code)] + fn data(&self) -> SVGSVGData { + unsafe { + let SVG = &*self.unsafe_get(); + + let width_attr = SVG.upcast::<Element>().get_attr_for_layout(&ns!(), &atom!("width")); + let height_attr = SVG.upcast::<Element>().get_attr_for_layout(&ns!(), &atom!("height")); + SVGSVGData { + width: width_attr.map_or(DEFAULT_WIDTH, |val| val.as_uint()), + height: height_attr.map_or(DEFAULT_HEIGHT, |val| val.as_uint()), + } + } + } +} + +impl VirtualMethods for SVGSVGElement { + fn super_type(&self) -> Option<&VirtualMethods> { + Some(self.upcast::<SVGGraphicsElement>() as &VirtualMethods) + } + + fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) { + self.super_type().unwrap().attribute_mutated(attr, mutation); + } + + fn parse_plain_attribute(&self, name: &Atom, value: DOMString) -> AttrValue { + match name { + &atom!("width") => AttrValue::from_u32(value.into(), DEFAULT_WIDTH), + &atom!("height") => AttrValue::from_u32(value.into(), DEFAULT_HEIGHT), + _ => self.super_type().unwrap().parse_plain_attribute(name, value), + } + } +} diff --git a/components/script/dom/virtualmethods.rs b/components/script/dom/virtualmethods.rs index d4cf1609568..c8a8a7436a4 100644 --- a/components/script/dom/virtualmethods.rs +++ b/components/script/dom/virtualmethods.rs @@ -7,6 +7,8 @@ use dom::bindings::inheritance::Castable; use dom::bindings::inheritance::ElementTypeId; use dom::bindings::inheritance::HTMLElementTypeId; use dom::bindings::inheritance::NodeTypeId; +use dom::bindings::inheritance::SVGElementTypeId; +use dom::bindings::inheritance::SVGGraphicsElementTypeId; use dom::bindings::str::DOMString; use dom::document::Document; use dom::element::{AttributeMutation, Element}; @@ -47,6 +49,7 @@ use dom::htmltemplateelement::HTMLTemplateElement; use dom::htmltextareaelement::HTMLTextAreaElement; use dom::htmltitleelement::HTMLTitleElement; use dom::node::{ChildrenMutation, CloneChildrenFlag, Node, UnbindContext}; +use dom::svgsvgelement::SVGSVGElement; use string_cache::Atom; use style::attr::AttrValue; @@ -231,6 +234,11 @@ pub fn vtable_for(node: &Node) -> &VirtualMethods { NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTitleElement)) => { node.downcast::<HTMLTitleElement>().unwrap() as &VirtualMethods } + NodeTypeId::Element(ElementTypeId::SVGElement(SVGElementTypeId::SVGGraphicsElement( + SVGGraphicsElementTypeId::SVGSVGElement + ))) => { + node.downcast::<SVGSVGElement>().unwrap() as &VirtualMethods + } NodeTypeId::Element(ElementTypeId::Element) => { node.downcast::<Element>().unwrap() as &VirtualMethods } diff --git a/components/script/dom/webidls/SVGElement.webidl b/components/script/dom/webidls/SVGElement.webidl new file mode 100644 index 00000000000..02f673a420e --- /dev/null +++ b/components/script/dom/webidls/SVGElement.webidl @@ -0,0 +1,22 @@ +/* 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://svgwg.org/svg2-draft/types.html#InterfaceSVGElement +[Pref="dom.svg.enabled"] +interface SVGElement : Element { + + //[SameObject] readonly attribute SVGAnimatedString className; + + //[SameObject] readonly attribute DOMStringMap dataset; + + //readonly attribute SVGSVGElement? ownerSVGElement; + //readonly attribute SVGElement? viewportElement; + + //attribute long tabIndex; + //void focus(); + //void blur(); +}; + +//SVGElement implements GlobalEventHandlers; +//SVGElement implements SVGElementInstance; diff --git a/components/script/dom/webidls/SVGGraphicsElement.webidl b/components/script/dom/webidls/SVGGraphicsElement.webidl new file mode 100644 index 00000000000..d8f90e639ea --- /dev/null +++ b/components/script/dom/webidls/SVGGraphicsElement.webidl @@ -0,0 +1,22 @@ +/* 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://svgwg.org/svg2-draft/types.html#InterfaceSVGGraphicsElement +[Pref="dom.svg.enabled"] +//dictionary SVGBoundingBoxOptions { +// boolean fill = true; +// boolean stroke = false; +// boolean markers = false; +// boolean clipped = false; +//}; + +interface SVGGraphicsElement : SVGElement { + //[SameObject] readonly attribute SVGAnimatedTransformList transform; + + //DOMRect getBBox(optional SVGBoundingBoxOptions options); + //DOMMatrix? getCTM(); + //DOMMatrix? getScreenCTM(); +}; + +//SVGGraphicsElement implements SVGTests; diff --git a/components/script/dom/webidls/SVGSVGElement.webidl b/components/script/dom/webidls/SVGSVGElement.webidl new file mode 100644 index 00000000000..bed2c03a74b --- /dev/null +++ b/components/script/dom/webidls/SVGSVGElement.webidl @@ -0,0 +1,45 @@ +/* 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://svgwg.org/svg2-draft/struct.html#InterfaceSVGSVGElement +[Pref="dom.svg.enabled"] +interface SVGSVGElement : SVGGraphicsElement { + + //[SameObject] readonly attribute SVGAnimatedLength x; + //[SameObject] readonly attribute SVGAnimatedLength y; + //[SameObject] readonly attribute SVGAnimatedLength width; + //[SameObject] readonly attribute SVGAnimatedLength height; + + //attribute float currentScale; + //[SameObject] readonly attribute DOMPointReadOnly currentTranslate; + + //NodeList getIntersectionList(DOMRectReadOnly rect, SVGElement? referenceElement); + //NodeList getEnclosureList(DOMRectReadOnly rect, SVGElement? referenceElement); + //boolean checkIntersection(SVGElement element, DOMRectReadOnly rect); + //boolean checkEnclosure(SVGElement element, DOMRectReadOnly rect); + + //void deselectAll(); + + //SVGNumber createSVGNumber(); + //SVGLength createSVGLength(); + //SVGAngle createSVGAngle(); + //DOMPoint createSVGPoint(); + //DOMMatrix createSVGMatrix(); + //DOMRect createSVGRect(); + //SVGTransform createSVGTransform(); + //SVGTransform createSVGTransformFromMatrix(DOMMatrixReadOnly matrix); + + //Element getElementById(DOMString elementId); + + // Deprecated methods that have no effect when called, + // but which are kept for compatibility reasons. + //unsigned long suspendRedraw(unsigned long maxWaitMilliseconds); + //void unsuspendRedraw(unsigned long suspendHandleID); + //void unsuspendRedrawAll(); + //void forceRedraw(); +}; + +//SVGSVGElement implements SVGFitToViewBox; +//SVGSVGElement implements SVGZoomAndPan; +//SVGSVGElement implements WindowEventHandlers; diff --git a/components/script/layout_wrapper.rs b/components/script/layout_wrapper.rs index 19b00c422e5..91c2a29285d 100644 --- a/components/script/layout_wrapper.rs +++ b/components/script/layout_wrapper.rs @@ -43,7 +43,7 @@ use gfx_traits::ByteIndex; use msg::constellation_msg::PipelineId; use parking_lot::RwLock; use range::Range; -use script_layout_interface::{HTMLCanvasData, LayoutNodeType, TrustedNodeAddress}; +use script_layout_interface::{HTMLCanvasData, LayoutNodeType, SVGSVGData, TrustedNodeAddress}; use script_layout_interface::{OpaqueStyleAndLayoutData, PartialPersistentLayoutData}; use script_layout_interface::restyle_damage::RestyleDamage; use script_layout_interface::wrapper_traits::{DangerousThreadSafeLayoutNode, LayoutNode, PseudoElementType}; @@ -861,6 +861,11 @@ impl<'ln> ThreadSafeLayoutNode for ServoThreadSafeLayoutNode<'ln> { this.canvas_data() } + fn svg_data(&self) -> Option<SVGSVGData> { + let this = unsafe { self.get_jsmanaged() }; + this.svg_data() + } + fn iframe_pipeline_id(&self) -> PipelineId { let this = unsafe { self.get_jsmanaged() }; this.iframe_pipeline_id() diff --git a/components/script_layout_interface/lib.rs b/components/script_layout_interface/lib.rs index cad2e012c7c..18c4805a485 100644 --- a/components/script_layout_interface/lib.rs +++ b/components/script_layout_interface/lib.rs @@ -95,6 +95,7 @@ pub enum LayoutElementType { HTMLTableRowElement, HTMLTableSectionElement, HTMLTextAreaElement, + SVGSVGElement, } pub struct HTMLCanvasData { @@ -103,6 +104,11 @@ pub struct HTMLCanvasData { pub height: u32, } +pub struct SVGSVGData { + pub width: u32, + pub height: u32, +} + /// The address of a node known to be valid. These are sent from script to layout. #[derive(Clone, PartialEq, Eq, Copy)] pub struct TrustedNodeAddress(pub *const c_void); diff --git a/components/script_layout_interface/wrapper_traits.rs b/components/script_layout_interface/wrapper_traits.rs index c02d76a2f4e..c98afa3a72e 100644 --- a/components/script_layout_interface/wrapper_traits.rs +++ b/components/script_layout_interface/wrapper_traits.rs @@ -5,6 +5,7 @@ use HTMLCanvasData; use LayoutNodeType; use OpaqueStyleAndLayoutData; +use SVGSVGData; use gfx_traits::{ByteIndex, LayerId, LayerType}; use msg::constellation_msg::PipelineId; use range::Range; @@ -362,6 +363,8 @@ pub trait ThreadSafeLayoutNode: Clone + Copy + NodeInfo + PartialEq + Sized { fn canvas_data(&self) -> Option<HTMLCanvasData>; + fn svg_data(&self) -> Option<SVGSVGData>; + /// If this node is an iframe element, returns its pipeline ID. If this node is /// not an iframe element, fails. fn iframe_pipeline_id(&self) -> PipelineId; diff --git a/python/tidy/servo_tidy/tidy.py b/python/tidy/servo_tidy/tidy.py index 91de7229d43..17a473ea92a 100644 --- a/python/tidy/servo_tidy/tidy.py +++ b/python/tidy/servo_tidy/tidy.py @@ -70,6 +70,7 @@ WEBIDL_STANDARDS = [ "//w3c.github.io", "//heycam.github.io/webidl", "//webbluetoothcg.github.io/web-bluetooth/", + "//svgwg.org/svg2-draft", # Not a URL "// This interface is entirely internal to Servo, and should not be" + " accessible to\n// web pages." diff --git a/tests/wpt/mozilla/meta/MANIFEST.json b/tests/wpt/mozilla/meta/MANIFEST.json index 613cad0de9c..2045a615f5d 100644 --- a/tests/wpt/mozilla/meta/MANIFEST.json +++ b/tests/wpt/mozilla/meta/MANIFEST.json @@ -6294,6 +6294,18 @@ "url": "/_mozilla/mozilla/sslfail.html" } ], + "mozilla/svg/svg.html": [ + { + "path": "mozilla/svg/svg.html", + "references": [ + [ + "/_mozilla/mozilla/svg/svg_ref.html", + "==" + ] + ], + "url": "/_mozilla/mozilla/svg/svg.html" + } + ], "mozilla/table_valign_bottom.html": [ { "path": "mozilla/table_valign_bottom.html", @@ -20040,6 +20052,18 @@ "url": "/_mozilla/mozilla/sslfail.html" } ], + "mozilla/svg/svg.html": [ + { + "path": "mozilla/svg/svg.html", + "references": [ + [ + "/_mozilla/mozilla/svg/svg_ref.html", + "==" + ] + ], + "url": "/_mozilla/mozilla/svg/svg.html" + } + ], "mozilla/table_valign_bottom.html": [ { "path": "mozilla/table_valign_bottom.html", diff --git a/tests/wpt/mozilla/meta/mozilla/svg/svg.html.ini b/tests/wpt/mozilla/meta/mozilla/svg/svg.html.ini new file mode 100644 index 00000000000..baf8742fd8f --- /dev/null +++ b/tests/wpt/mozilla/meta/mozilla/svg/svg.html.ini @@ -0,0 +1,3 @@ +[svg.html] + type: reftest + prefs: [dom.svg.enabled:true] diff --git a/tests/wpt/mozilla/tests/mozilla/svg/svg.html b/tests/wpt/mozilla/tests/mozilla/svg/svg.html new file mode 100644 index 00000000000..d32cd8d6d95 --- /dev/null +++ b/tests/wpt/mozilla/tests/mozilla/svg/svg.html @@ -0,0 +1,15 @@ +<!DOCTYPE html> +<html> + <head> + <title>SVG embedded inline</title> + <link rel="match" href="svg_ref.html"> + </head> + <body> + top<br> + a + <svg width="100px" height="100px"> + </svg> + b<br> + bottom + </body> +</html> diff --git a/tests/wpt/mozilla/tests/mozilla/svg/svg_ref.html b/tests/wpt/mozilla/tests/mozilla/svg/svg_ref.html new file mode 100644 index 00000000000..5ea92e454f1 --- /dev/null +++ b/tests/wpt/mozilla/tests/mozilla/svg/svg_ref.html @@ -0,0 +1,19 @@ +<!DOCTYPE html> +<html> + <head> + <title>SVG embedded inline</title> + <style> + img { + width: 100px; + height: 100px; + } + </style> + </head> + <body> + top<br> + a + <img> + b<br> + bottom + </body> +</html> |