Initial spec-incompliant implementation of default click action for anchor elements.

This is cherry-picked from https://github.com/mozilla/servo/pull/1688:

  * Initial spec-incompliant implementation of default click action for anchor elements.
  * Add documentation; gut the new document URL loading method
    and move it all into the new Window method.
  * Add test for default event prevention.

Original developer: Josh Matthews <josh@joshmatthews.net>
This commit is contained in:
Tetsuharu OHZEKI 2014-05-14 08:31:50 +09:00
parent c753f3ee05
commit 46d31632e0
7 changed files with 169 additions and 39 deletions

View file

@ -83,6 +83,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 +177,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 {

View file

@ -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 {

View file

@ -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);
}
}

View file

@ -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:

View file

@ -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> {