diff options
Diffstat (limited to 'src')
20 files changed, 451 insertions, 130 deletions
diff --git a/src/components/main/layout/box_.rs b/src/components/main/layout/box_.rs index 32f8d1dc001..125817264f1 100644 --- a/src/components/main/layout/box_.rs +++ b/src/components/main/layout/box_.rs @@ -267,16 +267,6 @@ impl UnscannedTextBoxInfo { } } -/// Represents the outcome of attempting to split a box. -pub enum SplitBoxResult { - CannotSplit, - // in general, when splitting the left or right side can - // be zero length, due to leading/trailing trimmable whitespace - SplitDidFit(Option<Box>, Option<Box>), - SplitDidNotFit(Option<Box>, Option<Box>) -} - - /// A box that represents a table column. #[deriving(Clone)] pub struct TableColumnBoxInfo { @@ -1097,11 +1087,15 @@ impl Box { } } - /// Split box which includes new-line character - pub fn split_by_new_line(&self) -> SplitBoxResult { + /// Split box which includes new-line character. + /// + /// A return value of `None` indicates that the box could not be split. + /// Otherwise the split boxes are returned. The right boxe is optional due + /// to the possibility of it being whitespace. + pub fn split_by_new_line(&self) -> Option<(Box, Option<Box>)> { match self.specific { GenericBox | IframeBox(_) | ImageBox(_) | TableBox | TableCellBox | - TableRowBox | TableWrapperBox => CannotSplit, + TableRowBox | TableWrapperBox => None, TableColumnBox(_) => fail!("Table column boxes do not need to split"), UnscannedTextBox(_) => fail!("Unscanned text boxes should have been scanned by now!"), ScannedTextBox(ref text_box_info) => { @@ -1118,7 +1112,7 @@ impl Box { let new_metrics = new_text_box_info.run.metrics_for_range(&left_range); let mut new_box = self.transform(new_metrics.bounding_box.size, ScannedTextBox(new_text_box_info)); new_box.new_line_pos = vec!(); - Some(new_box) + new_box }; // Right box is for right text of first founded new-line character. @@ -1132,16 +1126,20 @@ impl Box { None }; - SplitDidFit(left_box, right_box) + Some((left_box, right_box)) } } } /// Attempts to split this box so that its width is no more than `max_width`. - pub fn split_to_width(&self, max_width: Au, starts_line: bool) -> SplitBoxResult { + /// + /// A return value of `None` indicates that the box could not be split. + /// Otherwise the split boxes are returned. The left and right boxes are + /// optional due to the possibility of them being whitespace. + pub fn split_to_width(&self, max_width: Au, starts_line: bool) -> Option<(Option<Box>, Option<Box>)> { match self.specific { GenericBox | IframeBox(_) | ImageBox(_) | TableBox | TableCellBox | - TableRowBox | TableWrapperBox => CannotSplit, + TableRowBox | TableWrapperBox => None, TableColumnBox(_) => fail!("Table column boxes do not have width"), UnscannedTextBox(_) => fail!("Unscanned text boxes should have been scanned by now!"), ScannedTextBox(ref text_box_info) => { @@ -1214,28 +1212,30 @@ impl Box { } } - let left_box = if left_range.length() > CharIndex(0) { - let new_text_box_info = ScannedTextBoxInfo::new(text_box_info.run.clone(), left_range); - let width = new_text_box_info.run.advance_for_range(&left_range); - let height = self.border_box.size.height; - let size = Size2D(width, height); - Some(self.transform(size, ScannedTextBox(new_text_box_info))) - } else { - None - }; + let left_is_some = left_range.length() > CharIndex(0); - let right_box = right_range.map_or(None, |right_range: Range<CharIndex>| { - let new_text_box_info = ScannedTextBoxInfo::new(text_box_info.run.clone(), right_range); - let width = new_text_box_info.run.advance_for_range(&right_range); - let height = self.border_box.size.height; - let size = Size2D(width, height); - Some(self.transform(size, ScannedTextBox(new_text_box_info))) - }); - - if pieces_processed_count == 1 || left_box.is_none() { - SplitDidNotFit(left_box, right_box) + if (pieces_processed_count == 1 || !left_is_some) && !starts_line { + None } else { - SplitDidFit(left_box, right_box) + let left_box = if left_is_some { + let new_text_box_info = ScannedTextBoxInfo::new(text_box_info.run.clone(), left_range); + let width = new_text_box_info.run.advance_for_range(&left_range); + let height = self.border_box.size.height; + let size = Size2D(width, height); + Some(self.transform(size, ScannedTextBox(new_text_box_info))) + } else { + None + }; + + let right_box = right_range.map(|right_range| { + let new_text_box_info = ScannedTextBoxInfo::new(text_box_info.run.clone(), right_range); + let width = new_text_box_info.run.advance_for_range(&right_range); + let height = self.border_box.size.height; + let size = Size2D(width, height); + (self.transform(size, ScannedTextBox(new_text_box_info))) + }); + + Some((left_box, right_box)) } } } diff --git a/src/components/main/layout/floats.rs b/src/components/main/layout/floats.rs index 4f58da33e33..db8bc67101f 100644 --- a/src/components/main/layout/floats.rs +++ b/src/components/main/layout/floats.rs @@ -8,6 +8,7 @@ use geom::size::Size2D; use servo_util::cowarc::CowArc; use servo_util::geometry::{Au, max, min}; use std::i32; +use std::fmt; use style::computed_values::float; /// The kind of float: left or right. @@ -43,6 +44,12 @@ struct Float { kind: FloatKind, } +impl fmt::Show for Float { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f.buf, "bounds={} kind={:?}", self.bounds, self.kind) + } +} + /// Information about the floats next to a flow. /// /// FIXME(pcwalton): When we have fast `MutexArc`s, try removing `#[deriving(Clone)]` and wrap in a @@ -64,6 +71,12 @@ impl FloatList { } } +impl fmt::Show for FloatList { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f.buf, "max_top={} floats={:?}", self.max_top, self.floats) + } +} + /// Wraps a `FloatList` to avoid allocation in the common case of no floats. /// /// FIXME(pcwalton): When we have fast `MutexArc`s, try removing `CowArc` and use a mutex instead. @@ -114,6 +127,12 @@ pub struct PlacementInfo { pub kind: FloatKind } +impl fmt::Show for PlacementInfo { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f.buf, "size={} ceiling={} max_width={} kind={:?}", self.size, self.ceiling, self.max_width, self.kind) + } +} + fn range_intersect(top_1: Au, bottom_1: Au, top_2: Au, bottom_2: Au) -> (Au, Au) { (max(top_1, top_2), min(bottom_1, bottom_2)) } @@ -128,6 +147,19 @@ pub struct Floats { offset: Point2D<Au>, } +impl fmt::Show for Floats { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self.list.get() { + None => { + write!(f.buf, "[empty]") + } + Some(list) => { + write!(f.buf, "offset={} floats={}", self.offset, list) + } + } + } +} + impl Floats { /// Creates a new `Floats` object. pub fn new() -> Floats { diff --git a/src/components/main/layout/inline.rs b/src/components/main/layout/inline.rs index 5b0557c2c4d..58e21329f95 100644 --- a/src/components/main/layout/inline.rs +++ b/src/components/main/layout/inline.rs @@ -3,7 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use css::node_style::StyledNode; -use layout::box_::{Box, CannotSplit, SplitDidFit, SplitDidNotFit}; +use layout::box_::Box; use layout::context::LayoutContext; use layout::floats::{FloatLeft, Floats, PlacementInfo}; use layout::flow::{BaseFlow, FlowClass, Flow, InlineFlowClass}; @@ -425,19 +425,16 @@ impl LineboxScanner { } else { // In case of box includes new-line character match in_box.split_by_new_line() { - SplitDidFit(left, right) => { - match (left, right) { - (Some(left_box), Some(right_box)) => { - self.push_box_to_line(left_box); - self.work_list.push_front(right_box); - } - (Some(left_box), None) => { - self.push_box_to_line(left_box); - } - _ => error!("LineboxScanner: This split case makes no sense!"), - } - } - _ => {} + Some((left_box, Some(right_box))) => { + self.push_box_to_line(left_box); + self.work_list.push_front(right_box); + }, + Some((left_box, None)) => { + self.push_box_to_line(left_box); + }, + None => { + error!("LineboxScanner: This split case makes no sense!") + }, } false } @@ -495,46 +492,35 @@ impl LineboxScanner { } let available_width = green_zone.width - self.pending_line.bounds.size.width; - let split = in_box.split_to_width(available_width, line_is_empty); - let (left, right) = match (split, line_is_empty) { - (CannotSplit, _) => { - debug!("LineboxScanner: Tried to split unsplittable render box! {}", - in_box); - self.work_list.push_front(in_box); - return false - } - (SplitDidNotFit(_, _), false) => { - debug!("LineboxScanner: case=split box didn't fit, not appending and deferring \ - original box."); + match in_box.split_to_width(available_width, line_is_empty) { + None => { + debug!("LineboxScanner: Tried to split unsplittable render box! Deferring to next \ + line. {}", in_box); self.work_list.push_front(in_box); - return false - } - (SplitDidFit(left, right), _) => { - debug!("LineboxScanner: case=split box did fit; deferring remainder box."); - (left, right) - // Fall through to push boxes to the line. - } - (SplitDidNotFit(left, right), true) => { - // TODO(eatkinson, issue #224): Signal that horizontal overflow happened? - debug!("LineboxScanner: case=split box didn't fit and line {:u} is empty, so \ - overflowing and deferring remainder box.", - self.lines.len()); - (left, right) - // Fall though to push boxes to the line. - } - }; - - match (left, right) { - (Some(left_box), Some(right_box)) => { + false + }, + Some((Some(left_box), Some(right_box))) => { + debug!("LineboxScanner: Line break found! Pushing left box to line and deferring \ + right box to next line."); self.push_box_to_line(left_box); self.work_list.push_front(right_box); - } - (Some(left_box), None) => self.push_box_to_line(left_box), - (None, Some(right_box)) => self.push_box_to_line(right_box), - (None, None) => error!("LineboxScanner: This split case makes no sense!"), + true + }, + Some((Some(left_box), None)) => { + debug!("LineboxScanner: Pushing left box to line."); + self.push_box_to_line(left_box); + true + }, + Some((None, Some(right_box))) => { + debug!("LineboxScanner: Pushing right box to line."); + self.push_box_to_line(right_box); + true + }, + Some((None, None)) => { + error!("LineboxScanner: This split case makes no sense!"); + true + }, } - - true } // An unconditional push diff --git a/src/components/main/layout/model.rs b/src/components/main/layout/model.rs index 3fda7357fe7..b1a759286dc 100644 --- a/src/components/main/layout/model.rs +++ b/src/components/main/layout/model.rs @@ -12,6 +12,7 @@ use style::computed_values::{LPA_Auto, LPA_Length, LPA_Percentage, LP_Length, LP use style::ComputedValues; use servo_util::geometry::Au; use servo_util::geometry; +use std::fmt; /// A collapsible margin. See CSS 2.1 § 8.3.1. pub struct AdjoiningMargins { @@ -249,6 +250,12 @@ pub struct IntrinsicWidths { pub surround_width: Au, } +impl fmt::Show for IntrinsicWidths { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f.buf, "min={}, pref={}, surr={}", self.minimum_width, self.preferred_width, self.surround_width) + } +} + impl IntrinsicWidths { pub fn new() -> IntrinsicWidths { IntrinsicWidths { diff --git a/src/components/script/dom/bindings/codegen/Bindings.conf b/src/components/script/dom/bindings/codegen/Bindings.conf index dfb7180d65b..b2467b6d647 100644 --- a/src/components/script/dom/bindings/codegen/Bindings.conf +++ b/src/components/script/dom/bindings/codegen/Bindings.conf @@ -25,6 +25,7 @@ DOMInterfaces = { 'ClientRectList': {}, 'Comment': {}, 'Console': {}, +'CustomEvent': {}, 'Document': {}, 'DocumentFragment': {}, 'DocumentType': {}, diff --git a/src/components/script/dom/customevent.rs b/src/components/script/dom/customevent.rs new file mode 100644 index 00000000000..17b004817f6 --- /dev/null +++ b/src/components/script/dom/customevent.rs @@ -0,0 +1,86 @@ +/* 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::BindingDeclarations::CustomEventBinding; +use dom::bindings::codegen::InheritTypes::{EventCast, CustomEventDerived}; +use dom::bindings::js::{JSRef, Temporary}; +use dom::bindings::error::Fallible; +use dom::bindings::trace::Traceable; +use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object}; +use dom::event::{Event, EventMethods, EventTypeId, CustomEventTypeId}; +use dom::window::Window; +use js::jsapi::JSContext; +use js::jsval::{JSVal, NullValue}; +use servo_util::str::DOMString; + +#[deriving(Encodable)] +pub struct CustomEvent { + event: Event, + detail: Traceable<JSVal> +} + +impl CustomEventDerived for Event { + fn is_customevent(&self) -> bool { + self.type_id == CustomEventTypeId + } +} + +pub trait CustomEventMethods { + fn Detail(&self, _cx: *JSContext) -> JSVal; + fn InitCustomEvent(&mut self, _cx: *JSContext, + type_: DOMString, can_bubble: bool, + cancelable: bool, detail: JSVal); +} + +impl CustomEvent { + pub fn new_inherited(type_id: EventTypeId) -> CustomEvent { + CustomEvent { + event: Event::new_inherited(type_id), + detail: Traceable::new(NullValue()) + } + } + + pub fn new_uninitialized(window: &JSRef<Window>) -> Temporary<CustomEvent> { + reflect_dom_object(box CustomEvent::new_inherited(CustomEventTypeId), + window, + CustomEventBinding::Wrap) + } + pub fn new(window: &JSRef<Window>, type_: DOMString, bubbles: bool, cancelable: bool, detail: JSVal) -> Temporary<CustomEvent> { + let mut ev = CustomEvent::new_uninitialized(window).root(); + ev.InitCustomEvent(window.deref().get_cx(), type_, bubbles, cancelable, detail); + Temporary::from_rooted(&*ev) + } + pub fn Constructor(owner: &JSRef<Window>, + type_: DOMString, + init: &CustomEventBinding::CustomEventInit) -> Fallible<Temporary<CustomEvent>>{ + Ok(CustomEvent::new(owner, type_, init.parent.bubbles, init.parent.cancelable, init.detail)) + } +} + +impl<'a> CustomEventMethods for JSRef<'a, CustomEvent> { + fn Detail(&self, _cx: *JSContext) -> JSVal { + self.detail.deref().clone() + } + + fn InitCustomEvent(&mut self, + _cx: *JSContext, + type_: DOMString, + can_bubble: bool, + cancelable: bool, + detail: JSVal) { + self.detail = Traceable::new(detail); + let event: &mut JSRef<Event> = EventCast::from_mut_ref(self); + event.InitEvent(type_, can_bubble, cancelable); + } +} + +impl Reflectable for CustomEvent { + fn reflector<'a>(&'a self) -> &'a Reflector { + self.event.reflector() + } + + fn mut_reflector<'a>(&'a mut self) -> &'a mut Reflector { + self.event.mut_reflector() + } +} diff --git a/src/components/script/dom/document.rs b/src/components/script/dom/document.rs index 2ed144add2b..c554f5c5534 100644 --- a/src/components/script/dom/document.rs +++ b/src/components/script/dom/document.rs @@ -13,6 +13,7 @@ use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object}; use dom::bindings::error::{ErrorResult, Fallible, NotSupported, InvalidCharacter, HierarchyRequest, NamespaceError}; use dom::bindings::utils::{xml_name_type, InvalidXMLName, Name, QName}; use dom::comment::Comment; +use dom::customevent::CustomEvent; use dom::documentfragment::DocumentFragment; use dom::documenttype::DocumentType; use dom::domimplementation::DOMImplementation; @@ -83,6 +84,7 @@ pub trait DocumentHelpers { fn wait_until_safe_to_modify_dom(&self); fn unregister_named_element(&mut self, to_unregister: &JSRef<Element>, id: DOMString); fn register_named_element(&mut self, element: &JSRef<Element>, id: DOMString); + fn load_anchor_href(&self, href: DOMString); } impl<'a> DocumentHelpers for JSRef<'a, Document> { @@ -176,6 +178,11 @@ impl<'a> DocumentHelpers for JSRef<'a, Document> { elements.push_unrooted(element); self.idmap.insert(id, elements); } + + fn load_anchor_href(&self, href: DOMString) { + let mut window = self.window.root(); + window.load_url(href); + } } impl Document { @@ -534,6 +541,7 @@ impl<'a> DocumentMethods for JSRef<'a, Document> { // FIXME: Implement CustomEvent (http://dom.spec.whatwg.org/#customevent) "uievents" | "uievent" => Ok(EventCast::from_temporary(UIEvent::new_uninitialized(&*window))), "mouseevents" | "mouseevent" => Ok(EventCast::from_temporary(MouseEvent::new_uninitialized(&*window))), + "customevent" => Ok(EventCast::from_temporary(CustomEvent::new_uninitialized(&*window))), "htmlevents" | "events" | "event" => Ok(Event::new(&*window)), _ => Err(NotSupported) } diff --git a/src/components/script/dom/event.rs b/src/components/script/dom/event.rs index 450df543a38..9f7ab79dddb 100644 --- a/src/components/script/dom/event.rs +++ b/src/components/script/dom/event.rs @@ -34,11 +34,12 @@ pub enum EventPhase { #[deriving(Eq, Encodable)] pub enum EventTypeId { + CustomEventTypeId, HTMLEventTypeId, - UIEventTypeId, - MouseEventTypeId, KeyEventTypeId, - ProgressEventTypeId + MouseEventTypeId, + ProgressEventTypeId, + UIEventTypeId } #[deriving(Encodable)] diff --git a/src/components/script/dom/eventdispatcher.rs b/src/components/script/dom/eventdispatcher.rs index 518a00867e1..cc5eb66f577 100644 --- a/src/components/script/dom/eventdispatcher.rs +++ b/src/components/script/dom/eventdispatcher.rs @@ -4,10 +4,11 @@ use dom::bindings::callback::ReportExceptions; use dom::bindings::codegen::InheritTypes::{EventTargetCast, NodeCast, NodeDerived}; -use dom::bindings::js::{JSRef, OptionalSettable, Root}; +use dom::bindings::js::{JSRef, OptionalSettable, OptionalRootable, Root}; use dom::eventtarget::{Capturing, Bubbling, EventTarget}; use dom::event::{Event, PhaseAtTarget, PhaseNone, PhaseBubbling, PhaseCapturing, EventMethods}; use dom::node::{Node, NodeHelpers}; +use dom::virtualmethods::vtable_for; // See http://dom.spec.whatwg.org/#concept-event-dispatch for the full dispatch algorithm pub fn dispatch_event<'a, 'b>(target: &JSRef<'a, EventTarget>, @@ -115,6 +116,22 @@ pub fn dispatch_event<'a, 'b>(target: &JSRef<'a, EventTarget>, } } + /* default action */ + let target = event.GetTarget().root(); + match target { + Some(mut target) => { + let node: Option<&mut JSRef<Node>> = NodeCast::to_mut_ref(&mut *target); + match node { + Some(node) =>{ + let vtable = vtable_for(node); + vtable.handle_event(event); + } + None => {} + } + } + None => {} + } + // Root ordering restrictions mean we need to unroot the chain entries // in the same order they were rooted. while chain.len() > 0 { diff --git a/src/components/script/dom/htmlanchorelement.rs b/src/components/script/dom/htmlanchorelement.rs index fcc6f200203..b9de564237f 100644 --- a/src/components/script/dom/htmlanchorelement.rs +++ b/src/components/script/dom/htmlanchorelement.rs @@ -4,13 +4,18 @@ use dom::bindings::codegen::BindingDeclarations::HTMLAnchorElementBinding; use dom::bindings::codegen::InheritTypes::HTMLAnchorElementDerived; -use dom::bindings::js::{JSRef, Temporary}; +use dom::bindings::codegen::InheritTypes::{ElementCast, HTMLElementCast, NodeCast}; +use dom::bindings::js::{JSRef, Temporary, OptionalRootable}; use dom::bindings::error::ErrorResult; -use dom::document::Document; -use dom::element::HTMLAnchorElementTypeId; +use dom::document::{Document, DocumentHelpers}; +use dom::attr::AttrMethods; +use dom::element::{Element, AttributeHandlers, HTMLAnchorElementTypeId}; +use dom::event::{Event, EventMethods}; use dom::eventtarget::{EventTarget, NodeTargetTypeId}; use dom::htmlelement::HTMLElement; -use dom::node::{Node, ElementNodeTypeId}; +use dom::node::{Node, NodeHelpers, ElementNodeTypeId}; +use dom::virtualmethods::VirtualMethods; +use servo_util::namespace::Null; use servo_util::str::DOMString; #[deriving(Encodable)] @@ -171,3 +176,43 @@ impl<'a> HTMLAnchorElementMethods for JSRef<'a, HTMLAnchorElement> { Ok(()) } } + +trait PrivateHTMLAnchorElementHelpers { + fn handle_event_impl(&self, event: &JSRef<Event>); +} + +impl<'a> PrivateHTMLAnchorElementHelpers for JSRef<'a, HTMLAnchorElement> { + fn handle_event_impl(&self, event: &JSRef<Event>) { + if "click" == event.Type() && !event.DefaultPrevented() { + let element: &JSRef<Element> = ElementCast::from_ref(self); + let attr = element.get_attribute(Null, "href").root(); + match attr { + Some(ref href) => { + let value = href.Value(); + debug!("clicked on link to {:s}", value); + let node: &JSRef<Node> = NodeCast::from_ref(self); + let mut doc = node.owner_doc().root(); + doc.load_anchor_href(value); + } + None => () + } + } + } +} + +impl<'a> VirtualMethods for JSRef<'a, HTMLAnchorElement> { + fn super_type<'a>(&'a mut self) -> Option<&'a mut VirtualMethods:> { + let htmlelement: &mut JSRef<HTMLElement> = HTMLElementCast::from_mut_ref(self); + Some(htmlelement as &mut VirtualMethods:) + } + + fn handle_event(&mut self, event: &JSRef<Event>) { + match self.super_type() { + Some(s) => { + s.handle_event(event); + } + None => {} + } + self.handle_event_impl(event); + } +} diff --git a/src/components/script/dom/virtualmethods.rs b/src/components/script/dom/virtualmethods.rs index 2b0d9aabfe5..2c0dc358411 100644 --- a/src/components/script/dom/virtualmethods.rs +++ b/src/components/script/dom/virtualmethods.rs @@ -3,6 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use dom::bindings::codegen::InheritTypes::ElementCast; +use dom::bindings::codegen::InheritTypes::HTMLAnchorElementCast; use dom::bindings::codegen::InheritTypes::HTMLElementCast; use dom::bindings::codegen::InheritTypes::HTMLIFrameElementCast; use dom::bindings::codegen::InheritTypes::HTMLImageElementCast; @@ -10,8 +11,10 @@ use dom::bindings::codegen::InheritTypes::HTMLObjectElementCast; use dom::bindings::codegen::InheritTypes::HTMLStyleElementCast; use dom::bindings::js::JSRef; use dom::element::Element; -use dom::element::{ElementTypeId, HTMLImageElementTypeId}; +use dom::element::{ElementTypeId, HTMLAnchorElementTypeId, HTMLImageElementTypeId}; use dom::element::{HTMLIFrameElementTypeId, HTMLObjectElementTypeId, HTMLStyleElementTypeId}; +use dom::event::Event; +use dom::htmlanchorelement::HTMLAnchorElement; use dom::htmlelement::HTMLElement; use dom::htmliframeelement::HTMLIFrameElement; use dom::htmlimageelement::HTMLImageElement; @@ -68,6 +71,16 @@ pub trait VirtualMethods { _ => (), } } + + /// Called during event dispatch after the bubbling phase completes. + fn handle_event(&mut self, event: &JSRef<Event>) { + match self.super_type() { + Some(s) => { + s.handle_event(event); + } + _ => (), + } + } } /// Obtain a VirtualMethods instance for a given Node-derived object. Any @@ -76,6 +89,10 @@ pub trait VirtualMethods { /// interrupted. pub fn vtable_for<'a>(node: &'a mut JSRef<Node>) -> &'a mut VirtualMethods: { match node.type_id() { + ElementNodeTypeId(HTMLAnchorElementTypeId) => { + let element: &mut JSRef<HTMLAnchorElement> = HTMLAnchorElementCast::to_mut_ref(node).unwrap(); + element as &mut VirtualMethods: + } ElementNodeTypeId(HTMLImageElementTypeId) => { let element: &mut JSRef<HTMLImageElement> = HTMLImageElementCast::to_mut_ref(node).unwrap(); element as &mut VirtualMethods: diff --git a/src/components/script/dom/webidls/CustomEvent.webidl b/src/components/script/dom/webidls/CustomEvent.webidl new file mode 100644 index 00000000000..13e34ae9124 --- /dev/null +++ b/src/components/script/dom/webidls/CustomEvent.webidl @@ -0,0 +1,27 @@ +/* -*- 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/. + * + * For more information on this interface please see + * http://dom.spec.whatwg.org/#interface-customevent + * + * To the extent possible under law, the editors have waived + * all copyright and related or neighboring rights to this work. + * In addition, as of 1 May 2014, the editors have made this specification + * available under the Open Web Foundation Agreement Version 1.0, + * which is available at + * http://www.openwebfoundation.org/legal/the-owf-1-0-agreements/owfa-1-0. + */ + +[Constructor(DOMString type, optional CustomEventInit eventInitDict), + Exposed=Window,Worker] +interface CustomEvent : Event { + readonly attribute any detail; + + void initCustomEvent(DOMString type, boolean bubbles, boolean cancelable, any detail); +}; + +dictionary CustomEventInit : EventInit { + any detail = null; +}; diff --git a/src/components/script/dom/window.rs b/src/components/script/dom/window.rs index e620714a9c4..e8d4a121dcd 100644 --- a/src/components/script/dom/window.rs +++ b/src/components/script/dom/window.rs @@ -16,11 +16,12 @@ use dom::navigator::Navigator; use dom::performance::Performance; use layout_interface::{ReflowForDisplay, DocumentDamageLevel}; -use script_task::{ExitWindowMsg, FireTimerMsg, Page, ScriptChan}; +use script_task::{ExitWindowMsg, FireTimerMsg, Page, ScriptChan, TriggerLoadMsg, TriggerFragmentMsg}; use servo_msg::compositor_msg::ScriptListener; use servo_net::image_cache_task::ImageCacheTask; use servo_util::str::DOMString; use servo_util::task::{spawn_named}; +use servo_util::url::parse_url; use js::jsapi::JSContext; use js::jsapi::{JS_GC, JS_GetRuntime}; @@ -292,6 +293,7 @@ pub trait WindowHelpers { fn damage_and_reflow(&self, damage: DocumentDamageLevel); fn wait_until_safe_to_modify_dom(&self); fn init_browser_context(&mut self, doc: &JSRef<Document>); + fn load_url(&self, href: DOMString); } trait PrivateWindowHelpers { @@ -316,6 +318,19 @@ impl<'a> WindowHelpers for JSRef<'a, Window> { fn init_browser_context(&mut self, doc: &JSRef<Document>) { self.browser_context = Some(BrowserContext::new(doc)); } + + /// Commence a new URL load which will either replace this window or scroll to a fragment. + fn load_url(&self, href: DOMString) { + let base_url = Some(self.page().get_url()); + debug!("current page url is {:?}", base_url); + let url = parse_url(href, base_url); + let ScriptChan(ref script_chan) = self.script_chan; + if href.starts_with("#") { + script_chan.send(TriggerFragmentMsg(self.page.id, url)); + } else { + script_chan.send(TriggerLoadMsg(self.page.id, url)); + } + } } impl<'a> PrivateWindowHelpers for JSRef<'a, Window> { diff --git a/src/components/script/script.rs b/src/components/script/script.rs index a3dcae637ec..4e560c44691 100644 --- a/src/components/script/script.rs +++ b/src/components/script/script.rs @@ -69,6 +69,7 @@ pub mod dom { pub mod clientrectlist; pub mod comment; pub mod console; + pub mod customevent; pub mod document; pub mod documentfragment; pub mod documenttype; diff --git a/src/components/script/script_task.rs b/src/components/script/script_task.rs index 05c58db96e6..5846be72fa8 100644 --- a/src/components/script/script_task.rs +++ b/src/components/script/script_task.rs @@ -5,7 +5,6 @@ //! The script task is the task that owns the DOM in memory, runs JavaScript, and spawns parsing //! and layout tasks. -use dom::attr::AttrMethods; use dom::bindings::codegen::RegisterBindings; use dom::bindings::codegen::InheritTypes::{EventTargetCast, NodeCast, ElementCast, EventCast}; use dom::bindings::js::{JS, JSRef, RootCollection, Temporary, OptionalSettable}; @@ -51,9 +50,9 @@ use servo_msg::constellation_msg; use servo_net::image_cache_task::ImageCacheTask; use servo_net::resource_task::ResourceTask; use servo_util::geometry::to_frac_px; -use servo_util::url::parse_url; use servo_util::task::send_on_failure; use servo_util::namespace::Null; +use servo_util::str::DOMString; use std::cast; use std::cell::{Cell, RefCell, Ref, RefMut}; use std::comm::{channel, Sender, Receiver, Empty, Disconnected}; @@ -71,6 +70,10 @@ local_data_key!(pub StackRoots: *RootCollection) pub enum ScriptMsg { /// Loads a new URL on the specified pipeline. LoadMsg(PipelineId, Url), + /// Acts on a fragment URL load on the specified pipeline. + TriggerFragmentMsg(PipelineId, Url), + /// Begins a content-initiated load on the specified pipeline. + TriggerLoadMsg(PipelineId, Url), /// Gives a channel and ID to a layout task, as well as the ID of that layout's parent AttachLayoutMsg(NewLayoutInfo), /// Instructs the script task to send a navigate message to the constellation. @@ -440,7 +443,8 @@ impl Page { } } - fn find_fragment_node(&self, fragid: ~str) -> Option<Temporary<Element>> { + /// Attempt to find a named element in this page's document. + fn find_fragment_node(&self, fragid: DOMString) -> Option<Temporary<Element>> { let document = self.frame().get_ref().document.root(); match document.deref().GetElementById(fragid.to_owned()) { Some(node) => Some(node), @@ -774,6 +778,8 @@ impl ScriptTask { // TODO(tkuehn) need to handle auxiliary layouts for iframes AttachLayoutMsg(new_layout_info) => self.handle_new_layout(new_layout_info), LoadMsg(id, url) => self.load(id, url), + TriggerLoadMsg(id, url) => self.trigger_load(id, url), + TriggerFragmentMsg(id, url) => self.trigger_fragment(id, url), SendEventMsg(id, event) => self.handle_event(id, event), FireTimerMsg(id, timer_id) => self.handle_fire_timer_msg(id, timer_id), NavigateMsg(direction) => self.handle_navigate_msg(direction), @@ -1064,12 +1070,6 @@ impl ScriptTask { /// /// TODO: Actually perform DOM event dispatch. fn handle_event(&self, pipeline_id: PipelineId, event: Event_) { - fn get_page(page: &Rc<Page>, pipeline_id: PipelineId) -> Rc<Page> { - page.find(pipeline_id).expect("ScriptTask: received an event \ - message for a layout channel that is not associated with this script task.\ - This is a bug.") - } - match event { ResizeEvent(new_width, new_height) => { debug!("script got resize event: {:u}, {:u}", new_width, new_height); @@ -1130,12 +1130,20 @@ impl ScriptTask { node::from_untrusted_node_address( self.js_runtime.deref().ptr, node_address); - let maybe_node = temp_node.root().ancestors().find(|node| node.is_anchor_element()); + let maybe_node = temp_node.root().ancestors().find(|node| node.is_element()); match maybe_node { Some(node) => { debug!("clicked on {:s}", node.debug_str()); - let element: &JSRef<Element> = ElementCast::to_ref(&node).unwrap(); - self.load_url_from_element(&*page, element); + match *page.frame() { + Some(ref frame) => { + let window = frame.window.root(); + let mut event = Event::new(&*window).root(); + event.InitEvent("click".to_owned(), true, true); + let eventtarget: &JSRef<EventTarget> = EventTargetCast::from_ref(&node); + eventtarget.dispatch_event_with_target(None, &mut *event); + } + None => {} + } } None => {} } @@ -1213,27 +1221,24 @@ impl ScriptTask { } } - fn load_url_from_element(&self, page: &Page, element: &JSRef<Element>) { - // if the node's element is "a," load url from href attr - let attr = element.get_attribute(Null, "href"); - for href in attr.root().iter() { - debug!("ScriptTask: clicked on link to {:s}", href.Value()); - let click_frag = href.deref().value_ref().starts_with("#"); - let base_url = Some(page.get_url()); - debug!("ScriptTask: current url is {:?}", base_url); - let url = parse_url(href.deref().value_ref(), base_url); - - if click_frag { - match page.find_fragment_node(url.fragment.unwrap()).root() { - Some(node) => self.scroll_fragment_point(page.id, &*node), - None => {} - } - } else { - let ConstellationChan(ref chan) = self.constellation_chan; - chan.send(LoadUrlMsg(page.id, url)); - } - } + /// The entry point for content to notify that a new load has been requested + /// for the given pipeline. + fn trigger_load(&self, pipeline_id: PipelineId, url: Url) { + let ConstellationChan(ref const_chan) = self.constellation_chan; + const_chan.send(LoadUrlMsg(pipeline_id, url)); } + + /// The entry point for content to notify that a fragment url has been requested + /// for the given pipeline. + fn trigger_fragment(&self, pipeline_id: PipelineId, url: Url) { + let page = get_page(&*self.page.borrow(), pipeline_id); + match page.find_fragment_node(url.fragment.unwrap()).root() { + Some(node) => { + self.scroll_fragment_point(pipeline_id, &*node); + } + None => {} + } + } } /// Shuts down layout for the given page tree. @@ -1271,3 +1276,10 @@ fn shut_down_layout(page_tree: &Rc<Page>, rt: *JSRuntime) { chan.send(layout_interface::ExitNowMsg); } } + + +fn get_page(page: &Rc<Page>, pipeline_id: PipelineId) -> Rc<Page> { + page.find(pipeline_id).expect("ScriptTask: received an event \ + message for a layout channel that is not associated with this script task.\ + This is a bug.") +} diff --git a/src/components/util/geometry.rs b/src/components/util/geometry.rs index f1dd2d816ce..f63b7aa1cce 100644 --- a/src/components/util/geometry.rs +++ b/src/components/util/geometry.rs @@ -25,7 +25,7 @@ impl Default for Au { impl fmt::Show for Au { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let Au(n) = *self; - write!(f.buf, "Au({})", n) + write!(f.buf, "Au(au={} px={})", n, to_frac_px(*self)) }} impl Add<Au,Au> for Au { diff --git a/src/test/content/test_click_prevent.html b/src/test/content/test_click_prevent.html new file mode 100644 index 00000000000..1636080e0af --- /dev/null +++ b/src/test/content/test_click_prevent.html @@ -0,0 +1,18 @@ +<html> +<head> +<script src="./harness.js"></script> +<a id="foo" href="/nonexistent">test link</a> +<script> +var link = document.getElementById('foo'); +link.addEventListener('click', function(ev) { + ev.preventDefault(); +}); +var ev = new Event('click', {bubbles: true, cancelable: true}); +link.dispatchEvent(ev); +setTimeout(function() { + is(true, true, "load probably would have occurred by now"); + finish(); +}, 500); +</script> +</head> +</html> diff --git a/src/test/ref/basic.list b/src/test/ref/basic.list index 5911e544cf9..047eab56c16 100644 --- a/src/test/ref/basic.list +++ b/src/test/ref/basic.list @@ -73,3 +73,4 @@ == setattribute_id_restyle_a.html setattribute_id_restyle_b.html == pseudo_element_a.html pseudo_element_b.html == linebreak_simple_a.html linebreak_simple_b.html +== linebreak_inline_span_a.html linebreak_inline_span_b.html diff --git a/src/test/ref/linebreak_inline_span_a.html b/src/test/ref/linebreak_inline_span_a.html new file mode 100644 index 00000000000..ddaeabcb653 --- /dev/null +++ b/src/test/ref/linebreak_inline_span_a.html @@ -0,0 +1,24 @@ +<html> +<head> +<style type="text/css"> +* { + margin: 0px; + padding: 0px; +} +</style> +</head> +<body> +<blockquote> + The <span>most terrifying</span> fact about the universe <span>is not that + it is hostile but that it is indifferent;</span> but if we can come to terms + with this indifference and accept the<span> challenges</span> of <span>life + <span>within</span></span> the boundaries of death - <span>however</span> + mutable man may be able to make them - our existence as a species can have + genuine meaning and fulfillment. However vast the darkness, we must supply + our own light. + <footer> + <cite>Stanley Kubrick</cite> + </footer> +</blockquote> +</body> +</html> diff --git a/src/test/ref/linebreak_inline_span_b.html b/src/test/ref/linebreak_inline_span_b.html new file mode 100644 index 00000000000..ea1df5cc545 --- /dev/null +++ b/src/test/ref/linebreak_inline_span_b.html @@ -0,0 +1,23 @@ +<html> +<head> +<style type="text/css"> +* { + margin: 0px; + padding: 0px; +} +</style> +</head> +<body> +<blockquote> + The most terrifying fact about the universe is not that it is hostile but + that it is indifferent; but if we can come to terms with this indifference + and accept the challenges of life within the boundaries of death - however + mutable man may be able to make them - our existence as a species can have + genuine meaning and fulfillment. However vast the darkness, we must supply + our own light. + <footer> + <cite>Stanley Kubrick</cite> + </footer> +</blockquote> +</body> +</html> |