diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index b5672319837..686f5ecf1c5 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -38,6 +38,7 @@ use dom::domimplementation::DOMImplementation; use dom::element::{Element, ElementCreator}; use dom::event::{Event, EventBubbles, EventCancelable}; use dom::eventtarget::EventTarget; +use dom::focusevent::FocusEvent; use dom::htmlanchorelement::HTMLAnchorElement; use dom::htmlappletelement::HTMLAppletElement; use dom::htmlareaelement::HTMLAreaElement; @@ -581,17 +582,21 @@ impl Document { /// Reassign the focus context to the element that last requested focus during this /// transaction, or none if no elements requested it. pub fn commit_focus_transaction(&self, focus_type: FocusType) { - // TODO: dispatch blur, focus, focusout, and focusin events if let Some(ref elem) = self.focused.get() { + let node = elem.upcast::(); elem.set_focus_state(false); + // FIXME: pass appropriate relatedTarget + self.fire_focus_event(FocusEventType::Blur, node, None); } self.focused.set(self.possibly_focused.get().r()); if let Some(ref elem) = self.focused.get() { elem.set_focus_state(true); - + let node = elem.upcast::(); + // FIXME: pass appropriate relatedTarget + self.fire_focus_event(FocusEventType::Focus, node, None); // Update the focus state for all elements in the focus chain. // https://html.spec.whatwg.org/multipage/#focus-chain if focus_type == FocusType::Element { @@ -1418,6 +1423,25 @@ impl Document { pub fn get_dom_complete(&self) -> u64 { self.dom_complete.get() } + + // https://html.spec.whatwg.org/multipage/#fire-a-focus-event + fn fire_focus_event(&self, focus_event_type: FocusEventType, node: &Node, relatedTarget: Option<&EventTarget>) { + let (event_name, does_bubble) = match focus_event_type { + FocusEventType::Focus => (DOMString::from("focus"), EventBubbles::DoesNotBubble), + FocusEventType::Blur => (DOMString::from("blur"), EventBubbles::DoesNotBubble), + }; + let event = FocusEvent::new(&self.window, + event_name, + does_bubble, + EventCancelable::NotCancelable, + Some(&self.window), + 0i32, + relatedTarget); + let event = event.upcast::(); + event.set_trusted(true); + let target = node.upcast(); + event.fire(target); + } } #[derive(PartialEq, HeapSizeOf)] @@ -2542,3 +2566,9 @@ pub enum FocusType { Element, // The first focus message - focus the element itself Parent, // Focusing a parent element (an iframe) } + +/// Focus events +pub enum FocusEventType { + Focus, // Element gained focus. Doesn't bubble. + Blur, // Element lost focus. Doesn't bubble. +} diff --git a/components/script/dom/focusevent.rs b/components/script/dom/focusevent.rs new file mode 100644 index 00000000000..5ad61305637 --- /dev/null +++ b/components/script/dom/focusevent.rs @@ -0,0 +1,84 @@ +/* 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::Bindings::FocusEventBinding; +use dom::bindings::codegen::Bindings::FocusEventBinding::FocusEventMethods; +use dom::bindings::codegen::Bindings::UIEventBinding::UIEventMethods; +use dom::bindings::error::Fallible; +use dom::bindings::global::GlobalRef; +use dom::bindings::inheritance::Castable; +use dom::bindings::js::{JS, MutNullableHeap, Root, RootedReference}; +use dom::bindings::reflector::reflect_dom_object; +use dom::event::{EventBubbles, EventCancelable}; +use dom::eventtarget::EventTarget; +use dom::uievent::UIEvent; +use dom::window::Window; +use std::default::Default; +use util::str::DOMString; + +#[dom_struct] +pub struct FocusEvent { + uievent: UIEvent, + related_target: MutNullableHeap>, +} + +impl FocusEvent { + fn new_inherited() -> FocusEvent { + FocusEvent { + uievent: UIEvent::new_inherited(), + related_target: Default::default(), + } + } + + pub fn new(window: &Window, + type_: DOMString, + can_bubble: EventBubbles, + cancelable: EventCancelable, + view: Option<&Window>, + detail: i32, + related_target: Option<&EventTarget>) -> Root { + let event = box FocusEvent::new_inherited(); + let ev = reflect_dom_object(event, GlobalRef::Window(window), FocusEventBinding::Wrap); + ev.upcast::().InitUIEvent(type_, + can_bubble == EventBubbles::Bubbles, + cancelable == EventCancelable::Cancelable, + view, detail); + ev.related_target.set(related_target); + ev + } + + pub fn Constructor(global: GlobalRef, + type_: DOMString, + init: &FocusEventBinding::FocusEventInit) -> Fallible> { + let bubbles = if init.parent.parent.bubbles { + EventBubbles::Bubbles + } else { + EventBubbles::DoesNotBubble + }; + let cancelable = if init.parent.parent.cancelable { + EventCancelable::Cancelable + } else { + EventCancelable::NotCancelable + }; + let event = FocusEvent::new(global.as_window(), type_, + bubbles, + cancelable, + init.parent.view.r(), + init.parent.detail, + init.relatedTarget.r()); + Ok(event) + } +} + +impl FocusEventMethods for FocusEvent { + // https://w3c.github.io/uievents/#widl-FocusEvent-relatedTarget + fn GetRelatedTarget(&self) -> Option> { + self.related_target.get() + } + + // https://dom.spec.whatwg.org/#dom-event-istrusted + fn IsTrusted(&self) -> bool { + self.uievent.IsTrusted() + } +} diff --git a/components/script/dom/mod.rs b/components/script/dom/mod.rs index 56693280f2f..3fdf140a0ef 100644 --- a/components/script/dom/mod.rs +++ b/components/script/dom/mod.rs @@ -253,6 +253,7 @@ pub mod eventtarget; pub mod file; pub mod filelist; pub mod filereader; +pub mod focusevent; pub mod formdata; pub mod htmlanchorelement; pub mod htmlappletelement; diff --git a/components/script/dom/webidls/FocusEvent.webidl b/components/script/dom/webidls/FocusEvent.webidl new file mode 100644 index 00000000000..0c6cf6e6bfb --- /dev/null +++ b/components/script/dom/webidls/FocusEvent.webidl @@ -0,0 +1,14 @@ +/* -*- 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/. */ + +// https://w3c.github.io/uievents/#interface-FocusEvent +[Constructor(DOMString typeArg, optional FocusEventInit focusEventInitDict)] +interface FocusEvent : UIEvent { + readonly attribute EventTarget? relatedTarget; +}; + +dictionary FocusEventInit : UIEventInit { + EventTarget? relatedTarget = null; +}; diff --git a/tests/wpt/metadata/DOMEvents/constructors.html.ini b/tests/wpt/metadata/DOMEvents/constructors.html.ini index b0a20934357..f4e1d843e45 100644 --- a/tests/wpt/metadata/DOMEvents/constructors.html.ini +++ b/tests/wpt/metadata/DOMEvents/constructors.html.ini @@ -1,23 +1,5 @@ [constructors.html] type: testharness - [FocusEvent constructor (no argument)] - expected: FAIL - - [FocusEvent constructor (undefined argument)] - expected: FAIL - - [FocusEvent constructor (null argument)] - expected: FAIL - - [FocusEvent constructor (empty argument)] - expected: FAIL - - [FocusEvent constructor (argument with default values)] - expected: FAIL - - [FocusEvent constructor (argument with non-default values)] - expected: FAIL - [MouseEvent constructor (no argument)] expected: FAIL diff --git a/tests/wpt/mozilla/meta/MANIFEST.json b/tests/wpt/mozilla/meta/MANIFEST.json index 3926a713fc3..7d85f3d6091 100644 --- a/tests/wpt/mozilla/meta/MANIFEST.json +++ b/tests/wpt/mozilla/meta/MANIFEST.json @@ -5478,6 +5478,12 @@ "url": "/_mozilla/mozilla/Event.html" } ], + "mozilla/FocusEvent.html": [ + { + "path": "mozilla/FocusEvent.html", + "url": "/_mozilla/mozilla/FocusEvent.html" + } + ], "mozilla/MouseEvent.html": [ { "path": "mozilla/MouseEvent.html", diff --git a/tests/wpt/mozilla/tests/mozilla/FocusEvent.html b/tests/wpt/mozilla/tests/mozilla/FocusEvent.html new file mode 100644 index 00000000000..9e002c1088d --- /dev/null +++ b/tests/wpt/mozilla/tests/mozilla/FocusEvent.html @@ -0,0 +1,94 @@ + + + +
+ +
+ + + + diff --git a/tests/wpt/mozilla/tests/mozilla/interfaces.html b/tests/wpt/mozilla/tests/mozilla/interfaces.html index b8a664c166f..48896836f0b 100644 --- a/tests/wpt/mozilla/tests/mozilla/interfaces.html +++ b/tests/wpt/mozilla/tests/mozilla/interfaces.html @@ -22,6 +22,7 @@ var ecmaGlobals = [ "EvalError", "Float32Array", "Float64Array", + "FocusEvent", "Function", "Infinity", "Int16Array",