Implement offsetParent/Top/Left/Width/Height.

This commit is contained in:
Glenn Watson 2015-07-27 15:20:27 +10:00
parent 1809748dc1
commit 9e5687e3e7
21 changed files with 325 additions and 24 deletions

View file

@ -12,7 +12,7 @@ use dom::bindings::codegen::Bindings::HTMLInputElementBinding::HTMLInputElementM
use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
use dom::bindings::codegen::InheritTypes::{ElementCast, HTMLFrameSetElementDerived};
use dom::bindings::codegen::InheritTypes::{EventTargetCast, HTMLInputElementCast, NodeCast};
use dom::bindings::codegen::InheritTypes::{HTMLElementDerived, HTMLBodyElementDerived};
use dom::bindings::codegen::InheritTypes::{HTMLElementDerived, HTMLBodyElementDerived, HTMLHtmlElementDerived};
use dom::bindings::js::{JS, MutNullableHeap, Root};
use dom::bindings::error::ErrorResult;
use dom::bindings::error::Error::Syntax;
@ -216,6 +216,63 @@ impl<'a> HTMLElementMethods for &'a HTMLElement {
// If `request_focus` is not called, focus will be set to None.
document.r().commit_focus_transaction(FocusType::Element);
}
// https://drafts.csswg.org/cssom-view/#extensions-to-the-htmlelement-interface
fn GetOffsetParent(self) -> Option<Root<Element>> {
if self.is_htmlbodyelement() || self.is_htmlhtmlelement() {
return None;
}
let node = NodeCast::from_ref(self);
let window = window_from_node(self);
let (element, _) = window.offset_parent_query(node.to_trusted_node_address());
element
}
// https://drafts.csswg.org/cssom-view/#extensions-to-the-htmlelement-interface
fn OffsetTop(self) -> i32 {
if self.is_htmlbodyelement() {
return 0;
}
let node = NodeCast::from_ref(self);
let window = window_from_node(self);
let (_, rect) = window.offset_parent_query(node.to_trusted_node_address());
rect.origin.y.to_nearest_px()
}
// https://drafts.csswg.org/cssom-view/#extensions-to-the-htmlelement-interface
fn OffsetLeft(self) -> i32 {
if self.is_htmlbodyelement() {
return 0;
}
let node = NodeCast::from_ref(self);
let window = window_from_node(self);
let (_, rect) = window.offset_parent_query(node.to_trusted_node_address());
rect.origin.x.to_nearest_px()
}
// https://drafts.csswg.org/cssom-view/#extensions-to-the-htmlelement-interface
fn OffsetWidth(self) -> i32 {
let node = NodeCast::from_ref(self);
let window = window_from_node(self);
let (_, rect) = window.offset_parent_query(node.to_trusted_node_address());
rect.size.width.to_nearest_px()
}
// https://drafts.csswg.org/cssom-view/#extensions-to-the-htmlelement-interface
fn OffsetHeight(self) -> i32 {
let node = NodeCast::from_ref(self);
let window = window_from_node(self);
let (_, rect) = window.offset_parent_query(node.to_trusted_node_address());
rect.size.height.to_nearest_px()
}
}
// https://html.spec.whatwg.org/#attr-data-*

View file

@ -45,5 +45,16 @@ interface HTMLElement : Element {
//readonly attribute boolean? commandDisabled;
//readonly attribute boolean? commandChecked;
};
// http://dev.w3.org/csswg/cssom-view/#extensions-to-the-htmlelement-interface
partial interface HTMLElement {
// CSSOM things are not [Pure] because they can flush
readonly attribute Element? offsetParent;
readonly attribute long offsetTop;
readonly attribute long offsetLeft;
readonly attribute long offsetWidth;
readonly attribute long offsetHeight;
};
HTMLElement implements GlobalEventHandlers;
HTMLElement implements ElementCSSInlineStyle;

View file

@ -8,7 +8,7 @@ use dom::bindings::codegen::Bindings::EventHandlerBinding::{OnErrorEventHandlerN
use dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods;
use dom::bindings::codegen::Bindings::FunctionBinding::Function;
use dom::bindings::codegen::Bindings::WindowBinding::{self, WindowMethods, FrameRequestCallback};
use dom::bindings::codegen::InheritTypes::{NodeCast, EventTargetCast};
use dom::bindings::codegen::InheritTypes::{NodeCast, ElementCast, EventTargetCast};
use dom::bindings::global::global_object_for_js_object;
use dom::bindings::error::{report_pending_exception, Fallible};
use dom::bindings::error::Error::InvalidCharacter;
@ -27,7 +27,7 @@ use dom::eventtarget::{EventTarget, EventTargetHelpers, EventTargetTypeId};
use dom::htmlelement::HTMLElement;
use dom::location::Location;
use dom::navigator::Navigator;
use dom::node::{window_from_node, TrustedNodeAddress, NodeHelpers};
use dom::node::{window_from_node, TrustedNodeAddress, NodeHelpers, from_untrusted_node_address};
use dom::performance::Performance;
use dom::screen::Screen;
use dom::storage::Storage;
@ -578,6 +578,7 @@ pub trait WindowHelpers {
fn client_rect_query(self, node_geometry_request: TrustedNodeAddress) -> Rect<i32>;
fn resolved_style_query(self, element: TrustedNodeAddress,
pseudo: Option<PseudoElement>, property: &Atom) -> Option<String>;
fn offset_parent_query(self, node: TrustedNodeAddress) -> (Option<Root<Element>>, Rect<Au>);
fn handle_reflow_complete_msg(self, reflow_id: u32);
fn set_fragment_name(self, fragment: Option<String>);
fn steal_fragment_name(self) -> Option<String>;
@ -827,6 +828,27 @@ impl<'a> WindowHelpers for &'a Window {
resolved
}
fn offset_parent_query(self, node: TrustedNodeAddress) -> (Option<Root<Element>>, Rect<Au>) {
self.reflow(ReflowGoal::ForScriptQuery,
ReflowQueryType::OffsetParentQuery(node),
ReflowReason::Query);
let response = self.layout_rpc.offset_parent();
let js_runtime = self.js_runtime.borrow();
let js_runtime = js_runtime.as_ref().unwrap();
let element = match response.node_address {
Some(parent_node_address) => {
let node = from_untrusted_node_address(js_runtime.rt(),
parent_node_address);
let element = ElementCast::to_ref(node.r()).unwrap();
Some(Root::from_ref(element))
}
None => {
None
}
};
(element, response.rect)
}
fn handle_reflow_complete_msg(self, reflow_id: u32) {
let last_reflow_id = self.last_reflow_id.get();
if last_reflow_id == reflow_id {
@ -1135,6 +1157,7 @@ fn debug_reflow_events(goal: &ReflowGoal, query_type: &ReflowQueryType, reason:
ReflowQueryType::ContentBoxesQuery(_n) => "\tContentBoxesQuery",
ReflowQueryType::NodeGeometryQuery(_n) => "\tNodeGeometryQuery",
ReflowQueryType::ResolvedStyleQuery(_, _, _) => "\tResolvedStyleQuery",
ReflowQueryType::OffsetParentQuery(_n) => "\tOffsetParentQuery",
});
debug_msg.push_str(match *reason {

View file

@ -104,6 +104,7 @@ pub trait LayoutRPC {
fn mouse_over(&self, node: TrustedNodeAddress, point: Point2D<f32>) -> Result<MouseOverResponse, ()>;
/// Query layout for the resolved value of a given CSS property
fn resolved_style(&self) -> ResolvedStyleResponse;
fn offset_parent(&self) -> OffsetParentResponse;
}
@ -116,6 +117,21 @@ pub struct HitTestResponse(pub UntrustedNodeAddress);
pub struct MouseOverResponse(pub Vec<UntrustedNodeAddress>);
pub struct ResolvedStyleResponse(pub Option<String>);
#[derive(Clone)]
pub struct OffsetParentResponse {
pub node_address: Option<UntrustedNodeAddress>,
pub rect: Rect<Au>,
}
impl OffsetParentResponse {
pub fn empty() -> OffsetParentResponse {
OffsetParentResponse {
node_address: None,
rect: Rect::zero(),
}
}
}
/// Why we're doing reflow.
#[derive(PartialEq, Copy, Clone, Debug)]
pub enum ReflowGoal {
@ -133,6 +149,7 @@ pub enum ReflowQueryType {
ContentBoxesQuery(TrustedNodeAddress),
NodeGeometryQuery(TrustedNodeAddress),
ResolvedStyleQuery(TrustedNodeAddress, Option<PseudoElement>, Atom),
OffsetParentQuery(TrustedNodeAddress),
}
/// Information needed for a reflow.