diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index 793bfad38b2..b5a39073dad 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -96,6 +96,10 @@ pub struct Document { anchors: MutNullableJS, applets: MutNullableJS, ready_state: Cell, + /// The element that has most recently requested focus for itself. + possibly_focused: MutNullableJS, + /// The element that currently has the document focus context. + focused: MutNullableJS, } impl DocumentDerived for EventTarget { @@ -178,6 +182,10 @@ pub trait DocumentHelpers<'a> { fn load_anchor_href(self, href: DOMString); fn find_fragment_node(self, fragid: DOMString) -> Option>; fn set_ready_state(self, state: DocumentReadyState); + fn get_focused_element(self) -> Option>; + fn begin_focus_transaction(self); + fn request_focus(self, elem: JSRef); + fn commit_focus_transaction(self); } impl<'a> DocumentHelpers<'a> for JSRef<'a, Document> { @@ -327,6 +335,30 @@ impl<'a> DocumentHelpers<'a> for JSRef<'a, Document> { let target: JSRef = 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> { + 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) { + 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)] @@ -390,6 +422,8 @@ impl Document { anchors: Default::default(), applets: Default::default(), ready_state: Cell::new(ready_state), + possibly_focused: Default::default(), + focused: Default::default(), } } diff --git a/components/script/dom/htmlinputelement.rs b/components/script/dom/htmlinputelement.rs index f12e75db38d..44e8b21ab18 100644 --- a/components/script/dom/htmlinputelement.rs +++ b/components/script/dom/htmlinputelement.rs @@ -415,6 +415,9 @@ impl<'a> VirtualMethods for JSRef<'a, HTMLInputElement> { } _ => {} } + + let doc = document_from_node(*self).root(); + doc.request_focus(ElementCast::from_ref(*self)); } } } diff --git a/components/script/script_task.rs b/components/script/script_task.rs index ff499fb474b..684ae327ad2 100644 --- a/components/script/script_task.rs +++ b/components/script/script_task.rs @@ -928,11 +928,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 = match body { - Some(body) => EventTargetCast::from_ref(*body), - None => EventTargetCast::from_ref(*window), + let target: JSRef = 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); @@ -956,10 +958,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)); } } @@ -1065,6 +1067,9 @@ 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(), @@ -1072,6 +1077,7 @@ impl ScriptTask { let eventtarget: JSRef = EventTargetCast::from_ref(node); let _ = eventtarget.dispatch_event_with_target(None, *event); + doc.commit_focus_transaction(); window.flush_layout(); } None => {} diff --git a/tests/html/test_focus.html b/tests/html/test_focus.html new file mode 100644 index 00000000000..967030b2a78 --- /dev/null +++ b/tests/html/test_focus.html @@ -0,0 +1,7 @@ + + + +