aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/components/main/layout/box_.rs74
-rw-r--r--src/components/main/layout/floats.rs32
-rw-r--r--src/components/main/layout/inline.rs86
-rw-r--r--src/components/main/layout/model.rs7
-rw-r--r--src/components/script/dom/bindings/codegen/Bindings.conf1
-rw-r--r--src/components/script/dom/customevent.rs86
-rw-r--r--src/components/script/dom/document.rs8
-rw-r--r--src/components/script/dom/event.rs7
-rw-r--r--src/components/script/dom/eventdispatcher.rs19
-rw-r--r--src/components/script/dom/htmlanchorelement.rs53
-rw-r--r--src/components/script/dom/virtualmethods.rs19
-rw-r--r--src/components/script/dom/webidls/CustomEvent.webidl27
-rw-r--r--src/components/script/dom/window.rs17
-rw-r--r--src/components/script/script.rs1
-rw-r--r--src/components/script/script_task.rs76
-rw-r--r--src/components/util/geometry.rs2
-rw-r--r--src/test/content/test_click_prevent.html18
-rw-r--r--src/test/ref/basic.list1
-rw-r--r--src/test/ref/linebreak_inline_span_a.html24
-rw-r--r--src/test/ref/linebreak_inline_span_b.html23
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>