Skip to content

Commit

Permalink
Implement document focus context and hook it up to click events.
Browse files Browse the repository at this point in the history
  • Loading branch information
jdm authored and kmcallister committed Nov 4, 2014
1 parent 2062866 commit cf8b29f
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 7 deletions.
34 changes: 34 additions & 0 deletions components/script/dom/document.rs
Expand Up @@ -95,6 +95,10 @@ pub struct Document {
anchors: MutNullableJS<HTMLCollection>,
applets: MutNullableJS<HTMLCollection>,
ready_state: Cell<DocumentReadyState>,
/// The element that has most recently requested focus for itself.
possibly_focused: MutNullableJS<Element>,
/// The element that currently has the document focus context.
focused: MutNullableJS<Element>,
}

impl DocumentDerived for EventTarget {
Expand Down Expand Up @@ -177,6 +181,10 @@ pub trait DocumentHelpers<'a> {
fn load_anchor_href(self, href: DOMString);
fn find_fragment_node(self, fragid: DOMString) -> Option<Temporary<Element>>;
fn set_ready_state(self, state: DocumentReadyState);
fn get_focused_element(self) -> Option<Temporary<Element>>;
fn begin_focus_transaction(self);
fn request_focus(self, elem: JSRef<Element>);
fn commit_focus_transaction(self);
}

impl<'a> DocumentHelpers<'a> for JSRef<'a, Document> {
Expand Down Expand Up @@ -320,6 +328,30 @@ impl<'a> DocumentHelpers<'a> for JSRef<'a, Document> {
let target: JSRef<EventTarget> = EventTargetCast::from_ref(self);
let _ = target.DispatchEvent(*event);
}

/// Return the element that currently has focus.
// https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#events-focusevent-doc-focus
fn get_focused_element(self) -> Option<Temporary<Element>> {
self.focused.get()
}

/// Initiate a new round of checking for elements requesting focus. The last element to call
/// `request_focus` before `commit_focus_transaction` is called will receive focus.
fn begin_focus_transaction(self) {
self.possibly_focused.clear();
}

/// Request that the given element receive focus once the current transaction is complete.
fn request_focus(self, elem: JSRef<Element>) {
self.possibly_focused.assign(Some(elem))
}

/// Reassign the focus context to the element that last requested focus during this
/// transaction, or none if no elements requested it.
fn commit_focus_transaction(self) {
//TODO: dispatch blur, focus, focusout, and focusin events
self.focused.assign(self.possibly_focused.get());
}
}

#[deriving(PartialEq)]
Expand Down Expand Up @@ -383,6 +415,8 @@ impl Document {
anchors: Default::default(),
applets: Default::default(),
ready_state: Cell::new(ready_state),
possibly_focused: Default::default(),
focused: Default::default(),
}
}

Expand Down
3 changes: 3 additions & 0 deletions components/script/dom/htmlinputelement.rs
Expand Up @@ -406,6 +406,9 @@ impl<'a> VirtualMethods for JSRef<'a, HTMLInputElement> {
}
_ => {}
}

let doc = document_from_node(*self).root();
doc.request_focus(ElementCast::from_ref(*self));
}
}
}
Expand Down
20 changes: 13 additions & 7 deletions components/script/script_task.rs
Expand Up @@ -966,13 +966,17 @@ impl ScriptTask {
match *page.frame() {
Some(ref frame) => {
let window = frame.window.root();
let doc = window.Document().root();
doc.begin_focus_transaction();

let event =
Event::new(&global::Window(*window),
"click".to_string(),
Bubbles, Cancelable).root();
let eventtarget: JSRef<EventTarget> = EventTargetCast::from_ref(node);
let _ = eventtarget.dispatch_event_with_target(None, *event);

doc.commit_focus_transaction();
window.flush_layout();
}
None => {}
Expand Down Expand Up @@ -1067,11 +1071,13 @@ impl ScriptTask {
let frame = page.frame();
let window = frame.as_ref().unwrap().window.root();
let doc = window.Document().root();
let focused = doc.get_focused_element().root();
let body = doc.GetBody().root();

let target: JSRef<EventTarget> = match body {
Some(body) => EventTargetCast::from_ref(*body),
None => EventTargetCast::from_ref(*window),
let target: JSRef<EventTarget> = match (&focused, &body) {
(&Some(ref focused), _) => EventTargetCast::from_ref(**focused),
(&None, &Some(ref body)) => EventTargetCast::from_ref(**body),
(&None, &None) => EventTargetCast::from_ref(*window),
};

let ctrl = modifiers.contains(Control);
Expand All @@ -1095,10 +1101,10 @@ impl ScriptTask {
let _ = target.DispatchEvent(EventCast::from_ref(*event));

if state != Released && props.is_printable() {
let event = KeyboardEvent::new(*window, "keypress".to_string(), true, true, Some(*window),
0, props.key.clone(), props.code.clone(), props.location,
is_repeating, is_composing, ctrl, alt, shift, meta,
props.char_code, props.key_code).root();
let event = KeyboardEvent::new(*window, "keypress".to_string(), true, true, Some(*window),
0, props.key.clone(), props.code.clone(), props.location,
is_repeating, is_composing, ctrl, alt, shift, meta,
props.char_code, props.key_code).root();
let _ = target.DispatchEvent(EventCast::from_ref(*event));
}
}
Expand Down
7 changes: 7 additions & 0 deletions tests/html/test_focus.html
@@ -0,0 +1,7 @@
<body>
<input id="focused">
<script>
document.body.addEventListener('keydown', function() { alert("body"); }, false);
document.getElementById('focused').addEventListener('keydown', function() { alert("input"); }, false);
</script>
</body>

0 comments on commit cf8b29f

Please sign in to comment.