diff --git a/src/components/main/layout/box.rs b/src/components/main/layout/box.rs index 32d38b3116a..5a016d7e8ed 100644 --- a/src/components/main/layout/box.rs +++ b/src/components/main/layout/box.rs @@ -426,9 +426,7 @@ impl RenderBox { GenericRenderBoxClass(*) => Au(0), ImageRenderBoxClass(image_box) => { - // TODO: Consult the CSS `width` property as well as margins and borders. - // TODO: If the image isn't available, consult `width`. - Au::from_px(image_box.image.get_size().unwrap_or_default(Size2D(0, 0)).width) + self.image_width(image_box) } TextRenderBoxClass(text_box) => { @@ -449,7 +447,7 @@ impl RenderBox { GenericRenderBoxClass(*) => Au(0), ImageRenderBoxClass(image_box) => { - Au::from_px(image_box.image.get_size().unwrap_or_default(Size2D(0, 0)).width) + self.image_width(image_box) } TextRenderBoxClass(text_box) => { @@ -472,6 +470,58 @@ impl RenderBox { } } + // Calculate the width of an image, accounting for the width attribute + // TODO: This could probably go somewhere else + pub fn image_width(&self, image_box: @mut ImageRenderBox) -> Au { + let attr_width: Option = do self.with_base |base| { + do base.node.with_imm_element |elt| { + match elt.get_attr("width") { + Some(width) => { + FromStr::from_str(width) + } + None => { + None + } + } + } + }; + + // TODO: Consult margins and borders? + let px_width = if attr_width.is_some() { + attr_width.unwrap() + } else { + image_box.image.get_size().unwrap_or_default(Size2D(0, 0)).width + }; + + Au::from_px(px_width) + } + + // Calculate the height of an image, accounting for the height attribute + // TODO: This could probably go somewhere else + pub fn image_height(&self, image_box: @mut ImageRenderBox) -> Au { + let attr_height: Option = do self.with_base |base| { + do base.node.with_imm_element |elt| { + match elt.get_attr("height") { + Some(height) => { + FromStr::from_str(height) + } + None => { + None + } + } + } + }; + + // TODO: Consult margins and borders? + let px_height = if attr_height.is_some() { + attr_height.unwrap() + } else { + image_box.image.get_size().unwrap_or_default(Size2D(0, 0)).height + }; + + Au::from_px(px_height) + } + /// Returns the amount of left and right "fringe" used by this box. This is based on margins, /// borders, padding, and width. pub fn get_used_width(&self) -> (Au, Au) { diff --git a/src/components/main/layout/inline.rs b/src/components/main/layout/inline.rs index 96312846d84..33fe8ca335f 100644 --- a/src/components/main/layout/inline.rs +++ b/src/components/main/layout/inline.rs @@ -553,8 +553,7 @@ impl InlineFlowData { for &box in this.boxes.iter() { match box { ImageRenderBoxClass(image_box) => { - let size = image_box.image.get_size(); - let width = Au::from_px(size.unwrap_or_default(Size2D(0, 0)).width); + let width = box.image_width(image_box); image_box.base.position.size.width = width; } TextRenderBoxClass(_) => { @@ -684,8 +683,7 @@ impl InlineFlowData { let (top_from_base, bottom_from_base, ascent) = match cur_box { ImageRenderBoxClass(image_box) => { - let size = image_box.image.get_size(); - let mut height = Au::from_px(size.unwrap_or_default(Size2D(0, 0)).height); + let mut height = cur_box.image_height(image_box); // TODO: margin, border, padding's top and bottom should be calculated in advance, // since baseline of image is bottom margin edge. diff --git a/src/components/script/dom/bindings/codegen/Bindings.conf b/src/components/script/dom/bindings/codegen/Bindings.conf index 0ef1d9d502a..0bd49b5d1c7 100644 --- a/src/components/script/dom/bindings/codegen/Bindings.conf +++ b/src/components/script/dom/bindings/codegen/Bindings.conf @@ -547,12 +547,13 @@ def addExternalIface(iface, nativeType=None, headerFile=None, pointerType=None): domInterface['pointerType'] = pointerType DOMInterfaces[iface] = domInterface -def addHTMLElement(element, concrete=None): +def addHTMLElement(element, concrete=None, needsAbstract=[]): DOMInterfaces[element] = { 'nativeType': 'AbstractNode', 'pointerType': '', 'concreteType': concrete if concrete else element, - 'customTrace': 'trace' + 'customTrace': 'trace', + 'needsAbstract': needsAbstract } addHTMLElement('Comment') @@ -585,7 +586,7 @@ addHTMLElement('HTMLHeadingElement') addHTMLElement('HTMLHtmlElement') addHTMLElement('HTMLHRElement') addHTMLElement('HTMLIFrameElement') -addHTMLElement('HTMLImageElement') +addHTMLElement('HTMLImageElement', needsAbstract=['width', 'height']) addHTMLElement('HTMLInputElement') addHTMLElement('HTMLLabelElement') addHTMLElement('HTMLLegendElement') diff --git a/src/components/script/dom/bindings/codegen/CodegenRust.py b/src/components/script/dom/bindings/codegen/CodegenRust.py index 0e01fcef059..a26fff7d82a 100644 --- a/src/components/script/dom/bindings/codegen/CodegenRust.py +++ b/src/components/script/dom/bindings/codegen/CodegenRust.py @@ -3115,8 +3115,8 @@ class CGGetterCall(CGPerSignatureCall): A class to generate a native object getter call for a particular IDL getter. """ - def __init__(self, returnType, nativeMethodName, descriptor, attr): - CGPerSignatureCall.__init__(self, returnType, [], [], + def __init__(self, argsPre, returnType, nativeMethodName, descriptor, attr): + CGPerSignatureCall.__init__(self, returnType, argsPre, [], nativeMethodName, False, descriptor, attr, getter=True) @@ -3290,6 +3290,8 @@ class CGSpecializedGetter(CGAbstractExternMethod): def definition_body(self): name = self.attr.identifier.name nativeName = MakeNativeName(self.descriptor.binaryNames.get(name, name)) + extraPre = '' + argsPre = [] # resultOutParam does not depend on whether resultAlreadyAddRefed is set (_, resultOutParam) = getRetvalDeclarationForType(self.attr.type, self.descriptor, @@ -3297,11 +3299,16 @@ class CGSpecializedGetter(CGAbstractExternMethod): infallible = ('infallible' in self.descriptor.getExtendedAttributes(self.attr, getter=True)) + if name in self.descriptor.needsAbstract: + abstractName = re.sub(r'<\w+>', '', self.descriptor.nativeType) + extraPre = ' let abstract_this = %s::from_box(this);\n' % abstractName + argsPre = ['abstract_this'] if resultOutParam or self.attr.type.nullable() or not infallible: nativeName = "Get" + nativeName - return CGWrapper(CGIndenter(CGGetterCall(self.attr.type, nativeName, + return CGWrapper(CGIndenter(CGGetterCall(argsPre, self.attr.type, nativeName, self.descriptor, self.attr)), - pre=" let obj = (*obj.unnamed);\n" + + pre=extraPre + + " let obj = (*obj.unnamed);\n" + " let this = &mut (*this).payload;\n").define() class CGGenericSetter(CGAbstractBindingMethod): diff --git a/src/components/script/dom/htmlimageelement.rs b/src/components/script/dom/htmlimageelement.rs index f345d59a8b8..e90086103fc 100644 --- a/src/components/script/dom/htmlimageelement.rs +++ b/src/components/script/dom/htmlimageelement.rs @@ -2,9 +2,12 @@ * 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::utils::{DOMString, null_string, ErrorResult}; +use dom::bindings::utils::{DOMString, str, null_string, ErrorResult}; use dom::htmlelement::HTMLElement; +use dom::node::{ScriptView, AbstractNode}; use extra::url::Url; +use layout_interface::{ContentBoxQuery, ContentBoxResponse}; +use gfx::geometry::to_px; pub struct HTMLImageElement { parent: HTMLElement, @@ -47,18 +50,74 @@ impl HTMLImageElement { pub fn SetIsMap(&self, _is_map: bool, _rv: &mut ErrorResult) { } - pub fn Width(&self) -> u32 { - 0 + pub fn Width(&self, abstract_self: AbstractNode) -> u32 { + let node = &self.parent.parent.parent; + match node.owner_doc { + Some(doc) => { + match doc.with_base(|doc| doc.window) { + Some(win) => { + unsafe { + let page = win.page; + let (port, chan) = stream(); + match (*page).query_layout(ContentBoxQuery(abstract_self, chan), port) { + ContentBoxResponse(rect) => { + to_px(rect.size.width) as u32 + } + } + } + } + None => { + debug!("no window"); + 0 + } + } + } + None => { + debug!("no document"); + 0 + } + } } - pub fn SetWidth(&mut self, _width: u32, _rv: &mut ErrorResult) { + pub fn SetWidth(&mut self, width: u32, _rv: &mut ErrorResult) { + let node = &mut self.parent.parent; + node.set_attr(&str(~"width"), + &str(width.to_str())); } - pub fn Height(&self) -> u32 { - 0 + pub fn Height(&self, abstract_self: AbstractNode) -> u32 { + let node = &self.parent.parent.parent; + match node.owner_doc { + Some(doc) => { + match doc.with_base(|doc| doc.window) { + Some(win) => { + unsafe { + let page = win.page; + let (port, chan) = stream(); + match (*page).query_layout(ContentBoxQuery(abstract_self, chan), port) { + ContentBoxResponse(rect) => { + to_px(rect.size.height) as u32 + } + } + } + } + None => { + debug!("no window"); + 0 + } + } + } + None => { + debug!("no document"); + 0 + } + } } - pub fn SetHeight(&mut self, _height: u32, _rv: &mut ErrorResult) { + pub fn SetHeight(&mut self, height: u32, _rv: &mut ErrorResult) { + let node = &mut self.parent.parent; + node.set_attr(&str(~"height"), + &str(height.to_str())); } pub fn NaturalWidth(&self) -> u32 { @@ -114,4 +173,4 @@ impl HTMLImageElement { pub fn SetBorder(&mut self, _border: &DOMString, _rv: &mut ErrorResult) { } -} \ No newline at end of file +} diff --git a/src/components/script/dom/window.rs b/src/components/script/dom/window.rs index 00c4b63f539..8b36ce7ce95 100644 --- a/src/components/script/dom/window.rs +++ b/src/components/script/dom/window.rs @@ -9,7 +9,7 @@ use dom::document::AbstractDocument; use dom::node::{AbstractNode, ScriptView}; use dom::navigator::Navigator; -use layout_interface::ReflowForScriptQuery; +use layout_interface::ReflowForDisplay; use script_task::{ExitMsg, FireTimerMsg, Page, ScriptChan}; use servo_msg::compositor_msg::ScriptListener; @@ -168,7 +168,10 @@ impl Window { pub fn content_changed(&self) { unsafe { - (*self.page).reflow_all(ReflowForScriptQuery, self.script_chan.clone(), self.compositor); + // FIXME This should probably be ReflowForQuery, not Display. All queries currently + // currently rely on the display list, which means we can't destroy it by + // doing a query reflow. + (*self.page).reflow_all(ReflowForDisplay, self.script_chan.clone(), self.compositor); } } diff --git a/src/test/html/content/test.png b/src/test/html/content/test.png new file mode 100644 index 00000000000..353869b3c49 Binary files /dev/null and b/src/test/html/content/test.png differ diff --git a/src/test/html/content/test_img_width_height.html b/src/test/html/content/test_img_width_height.html new file mode 100644 index 00000000000..b61fa1aab06 --- /dev/null +++ b/src/test/html/content/test_img_width_height.html @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/src/test/html/content/test_img_width_height.js b/src/test/html/content/test_img_width_height.js new file mode 100644 index 00000000000..cb1a076113a --- /dev/null +++ b/src/test/html/content/test_img_width_height.js @@ -0,0 +1,21 @@ +// Testing get/set of image width/height properties + +var img = window.document.getElementsByTagName("img")[0]; + +function wait_for_img_load(f) { + if (img.width != 0) { + f(); + } else { + window.setTimeout(function() { wait_for_img_load(f) }, 1); + } +} + +wait_for_img_load(function() { + is(img.width, 500); + is(img.height, 378); + img.width = 200; + img.height = 100; + is(img.width, 200); + is(img.height, 100); + finish(); +});