From 1c64dabb150da4152957b2c6e16f30d5201328e1 Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Thu, 2 Oct 2014 03:11:50 -0400 Subject: [PATCH 01/10] Pass all key events to the current constellation frame. --- components/compositing/compositor.rs | 13 ++- components/compositing/constellation.rs | 15 ++- components/compositing/windowing.rs | 4 + components/msg/constellation_msg.rs | 132 +++++++++++++++++++++++ components/script/script_task.rs | 8 +- components/script_traits/lib.rs | 5 +- ports/glfw/window.rs | 136 +++++++++++++++++++++++- 7 files changed, 304 insertions(+), 9 deletions(-) diff --git a/components/compositing/compositor.rs b/components/compositing/compositor.rs index 95714929ad2..738d5abab0a 100644 --- a/components/compositing/compositor.rs +++ b/components/compositing/compositor.rs @@ -19,7 +19,7 @@ use windowing::{MouseWindowEvent, MouseWindowEventClass, MouseWindowMouseDownEve use windowing::{MouseWindowMouseUpEvent, MouseWindowMoveEventClass, NavigationWindowEvent}; use windowing::{QuitWindowEvent, RefreshWindowEvent, ResizeWindowEvent, ScrollWindowEvent}; use windowing::{WindowEvent, WindowMethods, WindowNavigateMsg, ZoomWindowEvent}; -use windowing::{PinchZoomWindowEvent}; +use windowing::{PinchZoomWindowEvent, KeyEvent}; use azure::azure_hl; use std::cmp; @@ -43,7 +43,7 @@ use servo_msg::compositor_msg::{Blank, Epoch, FinishedLoading, IdleRenderState, use servo_msg::compositor_msg::{ReadyState, RenderingRenderState, RenderState, Scrollable}; use servo_msg::constellation_msg::{ConstellationChan, ExitMsg, LoadUrlMsg}; use servo_msg::constellation_msg::{NavigateMsg, LoadData, PipelineId, ResizedWindowMsg}; -use servo_msg::constellation_msg::{WindowSizeData}; +use servo_msg::constellation_msg::{WindowSizeData, KeyState, Key}; use servo_msg::constellation_msg; use servo_util::geometry::{PagePx, ScreenPx, ViewportPx}; use servo_util::memory::MemoryProfilerChan; @@ -707,6 +707,10 @@ impl IOCompositor { self.on_navigation_window_event(direction); } + KeyEvent(key, state) => { + self.on_key_event(key, state); + } + FinishedWindowEvent => { let exit = opts::get().exit_after_load; if exit { @@ -878,6 +882,11 @@ impl IOCompositor { chan.send(NavigateMsg(direction)) } + fn on_key_event(&self, key: Key, state: KeyState) { + let ConstellationChan(ref chan) = self.constellation_chan; + chan.send(constellation_msg::KeyEvent(key, state)) + } + fn convert_buffer_requests_to_pipeline_requests_map(&self, requests: Vec<(Rc>, Vec)>) -> diff --git a/components/compositing/constellation.rs b/components/compositing/constellation.rs index 7506d64b870..096bc76870e 100644 --- a/components/compositing/constellation.rs +++ b/components/compositing/constellation.rs @@ -13,7 +13,8 @@ use gfx::render_task; use layers::geometry::DevicePixel; use layout_traits::{LayoutControlChan, LayoutTaskFactory, ExitNowMsg}; use libc; -use script_traits::{ResizeMsg, ResizeInactiveMsg, ExitPipelineMsg}; +use script_traits; +use script_traits::{ResizeMsg, ResizeInactiveMsg, ExitPipelineMsg, SendEventMsg}; use script_traits::{ScriptControlChan, ScriptTaskFactory}; use servo_msg::compositor_msg::LayerId; use servo_msg::constellation_msg::{ConstellationChan, ExitMsg, FailureMsg, Failure, FrameRectMsg}; @@ -21,6 +22,7 @@ use servo_msg::constellation_msg::{IFrameSandboxState, IFrameUnsandboxed, InitLo use servo_msg::constellation_msg::{LoadCompleteMsg, LoadUrlMsg, LoadData, Msg, NavigateMsg}; use servo_msg::constellation_msg::{NavigationType, PipelineId, RendererReadyMsg, ResizedWindowMsg}; use servo_msg::constellation_msg::{ScriptLoadedURLInIFrameMsg, SubpageId, WindowSizeData}; +use servo_msg::constellation_msg::{KeyEvent, Key, KeyState}; use servo_msg::constellation_msg; use servo_net::image_cache_task::{ImageCacheTask, ImageCacheTaskClient}; use servo_net::resource_task::ResourceTask; @@ -450,6 +452,10 @@ impl Constellation { debug!("constellation got window resize message"); self.handle_resized_window_msg(new_size); } + KeyEvent(key, state) => { + debug!("constellation got key event message"); + self.handle_key_msg(key, state); + } } true } @@ -761,6 +767,13 @@ impl Constellation { .any(|current_frame| current_frame.contains(pipeline_id)) } + fn handle_key_msg(&self, key: Key, state: KeyState) { + self.current_frame().as_ref().map(|frame| { + let ScriptControlChan(ref chan) = frame.pipeline.script_chan; + chan.send(SendEventMsg(frame.pipeline.id, script_traits::KeyEvent(key, state))); + }); + } + fn handle_renderer_ready_msg(&mut self, pipeline_id: PipelineId) { debug!("Renderer {} ready to send paint msg", pipeline_id); // This message could originate from a pipeline in the navigation context or diff --git a/components/compositing/windowing.rs b/components/compositing/windowing.rs index 5815de6e2b5..c564c34b29a 100644 --- a/components/compositing/windowing.rs +++ b/components/compositing/windowing.rs @@ -11,6 +11,7 @@ use geom::scale_factor::ScaleFactor; use geom::size::TypedSize2D; use layers::geometry::DevicePixel; use layers::platform::surface::NativeGraphicsMetadata; +use servo_msg::constellation_msg::{Key, KeyState}; use servo_msg::compositor_msg::{ReadyState, RenderState}; use servo_util::geometry::ScreenPx; use std::fmt::{FormatError, Formatter, Show}; @@ -58,6 +59,8 @@ pub enum WindowEvent { FinishedWindowEvent, /// Sent when the user quits the application QuitWindowEvent, + /// Sent when a key input state changes + KeyEvent(Key, KeyState), } impl Show for WindowEvent { @@ -66,6 +69,7 @@ impl Show for WindowEvent { IdleWindowEvent => write!(f, "Idle"), RefreshWindowEvent => write!(f, "Refresh"), ResizeWindowEvent(..) => write!(f, "Resize"), + KeyEvent(..) => write!(f, "Key"), LoadUrlWindowEvent(..) => write!(f, "LoadUrl"), MouseWindowEventClass(..) => write!(f, "Mouse"), MouseWindowMoveEventClass(..) => write!(f, "MouseMove"), diff --git a/components/msg/constellation_msg.rs b/components/msg/constellation_msg.rs index e043a04d3fd..40280a89afc 100644 --- a/components/msg/constellation_msg.rs +++ b/components/msg/constellation_msg.rs @@ -50,6 +50,137 @@ pub struct WindowSizeData { pub device_pixel_ratio: ScaleFactor, } +pub enum KeyState { + Pressed, + Released, + Repeated, +} + +//N.B. Straight up copied from glfw-rs +pub enum Key { + KeySpace, + KeyApostrophe, + KeyComma, + KeyMinus, + KeyPeriod, + KeySlash, + Key0, + Key1, + Key2, + Key3, + Key4, + Key5, + Key6, + Key7, + Key8, + Key9, + KeySemicolon, + KeyEqual, + KeyA, + KeyB, + KeyC, + KeyD, + KeyE, + KeyF, + KeyG, + KeyH, + KeyI, + KeyJ, + KeyK, + KeyL, + KeyM, + KeyN, + KeyO, + KeyP, + KeyQ, + KeyR, + KeyS, + KeyT, + KeyU, + KeyV, + KeyW, + KeyX, + KeyY, + KeyZ, + KeyLeftBracket, + KeyBackslash, + KeyRightBracket, + KeyGraveAccent, + KeyWorld1, + KeyWorld2, + + KeyEscape, + KeyEnter, + KeyTab, + KeyBackspace, + KeyInsert, + KeyDelete, + KeyRight, + KeyLeft, + KeyDown, + KeyUp, + KeyPageUp, + KeyPageDown, + KeyHome, + KeyEnd, + KeyCapsLock, + KeyScrollLock, + KeyNumLock, + KeyPrintScreen, + KeyPause, + KeyF1, + KeyF2, + KeyF3, + KeyF4, + KeyF5, + KeyF6, + KeyF7, + KeyF8, + KeyF9, + KeyF10, + KeyF11, + KeyF12, + KeyF13, + KeyF14, + KeyF15, + KeyF16, + KeyF17, + KeyF18, + KeyF19, + KeyF20, + KeyF21, + KeyF22, + KeyF23, + KeyF24, + KeyF25, + KeyKp0, + KeyKp1, + KeyKp2, + KeyKp3, + KeyKp4, + KeyKp5, + KeyKp6, + KeyKp7, + KeyKp8, + KeyKp9, + KeyKpDecimal, + KeyKpDivide, + KeyKpMultiply, + KeyKpSubtract, + KeyKpAdd, + KeyKpEnter, + KeyKpEqual, + KeyLeftShift, + KeyLeftControl, + KeyLeftAlt, + KeyLeftSuper, + KeyRightShift, + KeyRightControl, + KeyRightAlt, + KeyRightSuper, + KeyMenu, +} + /// Messages from the compositor and script to the constellation. pub enum Msg { ExitMsg, @@ -62,6 +193,7 @@ pub enum Msg { NavigateMsg(NavigationDirection), RendererReadyMsg(PipelineId), ResizedWindowMsg(WindowSizeData), + KeyEvent(Key, KeyState), } /// Similar to net::resource_task::LoadData diff --git a/components/script/script_task.rs b/components/script/script_task.rs index c6f4b636d50..31f3abb289c 100644 --- a/components/script/script_task.rs +++ b/components/script/script_task.rs @@ -42,7 +42,7 @@ use script_traits::{CompositorEvent, ResizeEvent, ReflowEvent, ClickEvent, Mouse use script_traits::{MouseMoveEvent, MouseUpEvent, ConstellationControlMsg, ScriptTaskFactory}; use script_traits::{ResizeMsg, AttachLayoutMsg, LoadMsg, ViewportMsg, SendEventMsg}; use script_traits::{ResizeInactiveMsg, ExitPipelineMsg, NewLayoutInfo, OpaqueScriptLayoutChannel}; -use script_traits::{ScriptControlChan, ReflowCompleteMsg, UntrustedNodeAddress}; +use script_traits::{ScriptControlChan, ReflowCompleteMsg, UntrustedNodeAddress, KeyEvent}; use servo_msg::compositor_msg::{FinishedLoading, LayerId, Loading}; use servo_msg::compositor_msg::{ScriptListener}; use servo_msg::constellation_msg::{ConstellationChan, LoadCompleteMsg, LoadUrlMsg, NavigationDirection}; @@ -907,6 +907,10 @@ impl ScriptTask { MouseMoveEvent(point) => { self.handle_mouse_move_event(pipeline_id, point); } + + KeyEvent(key, state) => { + println!("key {} is {}", key as int, state as int); + } } } @@ -1093,7 +1097,7 @@ impl ScriptTask { } None => {} - } + } } } diff --git a/components/script_traits/lib.rs b/components/script_traits/lib.rs index beedd0895d3..e0e300307fe 100644 --- a/components/script_traits/lib.rs +++ b/components/script_traits/lib.rs @@ -25,7 +25,7 @@ extern crate serialize; use devtools_traits::DevtoolsControlChan; use libc::c_void; use servo_msg::constellation_msg::{ConstellationChan, PipelineId, Failure, WindowSizeData}; -use servo_msg::constellation_msg::{LoadData, SubpageId}; +use servo_msg::constellation_msg::{LoadData, SubpageId, Key, KeyState}; use servo_msg::compositor_msg::ScriptListener; use servo_net::image_cache_task::ImageCacheTask; use servo_net::resource_task::ResourceTask; @@ -74,7 +74,8 @@ pub enum CompositorEvent { ClickEvent(uint, Point2D), MouseDownEvent(uint, Point2D), MouseUpEvent(uint, Point2D), - MouseMoveEvent(Point2D) + MouseMoveEvent(Point2D), + KeyEvent(Key, KeyState), } /// An opaque wrapper around script<->layout channels to avoid leaking message types into diff --git a/ports/glfw/window.rs b/ports/glfw/window.rs index ffc2a08497e..bc75c083853 100644 --- a/ports/glfw/window.rs +++ b/ports/glfw/window.rs @@ -10,7 +10,7 @@ use alert::{Alert, AlertMethods}; use compositing::compositor_task::{mod, CompositorProxy, CompositorReceiver}; use compositing::windowing::{Forward, Back}; use compositing::windowing::{IdleWindowEvent, ResizeWindowEvent, LoadUrlWindowEvent}; -use compositing::windowing::{MouseWindowClickEvent, MouseWindowMouseDownEvent}; +use compositing::windowing::{KeyEvent, MouseWindowClickEvent, MouseWindowMouseDownEvent}; use compositing::windowing::{MouseWindowEventClass, MouseWindowMoveEventClass}; use compositing::windowing::{MouseWindowMouseUpEvent, RefreshWindowEvent}; use compositing::windowing::{NavigationWindowEvent, ScrollWindowEvent, ZoomWindowEvent}; @@ -25,6 +25,7 @@ use layers::platform::surface::NativeGraphicsMetadata; use libc::c_int; use msg::compositor_msg::{FinishedLoading, Blank, Loading, PerformingLayout, ReadyState}; use msg::compositor_msg::{IdleRenderState, RenderState, RenderingRenderState}; +use msg::constellation_msg; use std::cell::{Cell, RefCell}; use std::comm::Receiver; use std::rc::Rc; @@ -207,8 +208,15 @@ impl Window { match event { glfw::KeyEvent(key, _, action, mods) => { if action == glfw::Press { - self.handle_key(key, mods) + self.handle_key(key, mods); } + let key = glfw_key_to_script_key(key); + let state = match action { + glfw::Press => constellation_msg::Pressed, + glfw::Release => constellation_msg::Released, + glfw::Repeat => constellation_msg::Repeated, + }; + self.event_queue.borrow_mut().push(KeyEvent(key, state)); }, glfw::FramebufferSizeEvent(width, height) => { self.event_queue.borrow_mut().push( @@ -428,3 +436,127 @@ extern "C" fn on_framebuffer_size(_glfw_window: *mut glfw::ffi::GLFWwindow, } } +fn glfw_key_to_script_key(key: glfw::Key) -> constellation_msg::Key { + match key { + glfw::KeySpace => constellation_msg::KeySpace, + glfw::KeyApostrophe => constellation_msg::KeyApostrophe, + glfw::KeyComma => constellation_msg::KeyComma, + glfw::KeyMinus => constellation_msg::KeyMinus, + glfw::KeyPeriod => constellation_msg::KeyPeriod, + glfw::KeySlash => constellation_msg::KeySlash, + glfw::Key0 => constellation_msg::Key0, + glfw::Key1 => constellation_msg::Key1, + glfw::Key2 => constellation_msg::Key2, + glfw::Key3 => constellation_msg::Key3, + glfw::Key4 => constellation_msg::Key4, + glfw::Key5 => constellation_msg::Key5, + glfw::Key6 => constellation_msg::Key6, + glfw::Key7 => constellation_msg::Key7, + glfw::Key8 => constellation_msg::Key8, + glfw::Key9 => constellation_msg::Key9, + glfw::KeySemicolon => constellation_msg::KeySemicolon, + glfw::KeyEqual => constellation_msg::KeyEqual, + glfw::KeyA => constellation_msg::KeyA, + glfw::KeyB => constellation_msg::KeyB, + glfw::KeyC => constellation_msg::KeyC, + glfw::KeyD => constellation_msg::KeyD, + glfw::KeyE => constellation_msg::KeyE, + glfw::KeyF => constellation_msg::KeyF, + glfw::KeyG => constellation_msg::KeyG, + glfw::KeyH => constellation_msg::KeyH, + glfw::KeyI => constellation_msg::KeyI, + glfw::KeyJ => constellation_msg::KeyJ, + glfw::KeyK => constellation_msg::KeyK, + glfw::KeyL => constellation_msg::KeyL, + glfw::KeyM => constellation_msg::KeyM, + glfw::KeyN => constellation_msg::KeyN, + glfw::KeyO => constellation_msg::KeyO, + glfw::KeyP => constellation_msg::KeyP, + glfw::KeyQ => constellation_msg::KeyQ, + glfw::KeyR => constellation_msg::KeyR, + glfw::KeyS => constellation_msg::KeyS, + glfw::KeyT => constellation_msg::KeyT, + glfw::KeyU => constellation_msg::KeyU, + glfw::KeyV => constellation_msg::KeyV, + glfw::KeyW => constellation_msg::KeyW, + glfw::KeyX => constellation_msg::KeyX, + glfw::KeyY => constellation_msg::KeyY, + glfw::KeyZ => constellation_msg::KeyZ, + glfw::KeyLeftBracket => constellation_msg::KeyLeftBracket, + glfw::KeyBackslash => constellation_msg::KeyBackslash, + glfw::KeyRightBracket => constellation_msg::KeyRightBracket, + glfw::KeyGraveAccent => constellation_msg::KeyGraveAccent, + glfw::KeyWorld1 => constellation_msg::KeyWorld1, + glfw::KeyWorld2 => constellation_msg::KeyWorld2, + glfw::KeyEscape => constellation_msg::KeyEscape, + glfw::KeyEnter => constellation_msg::KeyEnter, + glfw::KeyTab => constellation_msg::KeyTab, + glfw::KeyBackspace => constellation_msg::KeyBackspace, + glfw::KeyInsert => constellation_msg::KeyInsert, + glfw::KeyDelete => constellation_msg::KeyDelete, + glfw::KeyRight => constellation_msg::KeyRight, + glfw::KeyLeft => constellation_msg::KeyLeft, + glfw::KeyDown => constellation_msg::KeyDown, + glfw::KeyUp => constellation_msg::KeyUp, + glfw::KeyPageUp => constellation_msg::KeyPageUp, + glfw::KeyPageDown => constellation_msg::KeyPageDown, + glfw::KeyHome => constellation_msg::KeyHome, + glfw::KeyEnd => constellation_msg::KeyEnd, + glfw::KeyCapsLock => constellation_msg::KeyCapsLock, + glfw::KeyScrollLock => constellation_msg::KeyScrollLock, + glfw::KeyNumLock => constellation_msg::KeyNumLock, + glfw::KeyPrintScreen => constellation_msg::KeyPrintScreen, + glfw::KeyPause => constellation_msg::KeyPause, + glfw::KeyF1 => constellation_msg::KeyF1, + glfw::KeyF2 => constellation_msg::KeyF2, + glfw::KeyF3 => constellation_msg::KeyF3, + glfw::KeyF4 => constellation_msg::KeyF4, + glfw::KeyF5 => constellation_msg::KeyF5, + glfw::KeyF6 => constellation_msg::KeyF6, + glfw::KeyF7 => constellation_msg::KeyF7, + glfw::KeyF8 => constellation_msg::KeyF8, + glfw::KeyF9 => constellation_msg::KeyF9, + glfw::KeyF10 => constellation_msg::KeyF10, + glfw::KeyF11 => constellation_msg::KeyF11, + glfw::KeyF12 => constellation_msg::KeyF12, + glfw::KeyF13 => constellation_msg::KeyF13, + glfw::KeyF14 => constellation_msg::KeyF14, + glfw::KeyF15 => constellation_msg::KeyF15, + glfw::KeyF16 => constellation_msg::KeyF16, + glfw::KeyF17 => constellation_msg::KeyF17, + glfw::KeyF18 => constellation_msg::KeyF18, + glfw::KeyF19 => constellation_msg::KeyF19, + glfw::KeyF20 => constellation_msg::KeyF20, + glfw::KeyF21 => constellation_msg::KeyF21, + glfw::KeyF22 => constellation_msg::KeyF22, + glfw::KeyF23 => constellation_msg::KeyF23, + glfw::KeyF24 => constellation_msg::KeyF24, + glfw::KeyF25 => constellation_msg::KeyF25, + glfw::KeyKp0 => constellation_msg::KeyKp0, + glfw::KeyKp1 => constellation_msg::KeyKp1, + glfw::KeyKp2 => constellation_msg::KeyKp2, + glfw::KeyKp3 => constellation_msg::KeyKp3, + glfw::KeyKp4 => constellation_msg::KeyKp4, + glfw::KeyKp5 => constellation_msg::KeyKp5, + glfw::KeyKp6 => constellation_msg::KeyKp6, + glfw::KeyKp7 => constellation_msg::KeyKp7, + glfw::KeyKp8 => constellation_msg::KeyKp8, + glfw::KeyKp9 => constellation_msg::KeyKp9, + glfw::KeyKpDecimal => constellation_msg::KeyKpDecimal, + glfw::KeyKpDivide => constellation_msg::KeyKpDivide, + glfw::KeyKpMultiply => constellation_msg::KeyKpMultiply, + glfw::KeyKpSubtract => constellation_msg::KeyKpSubtract, + glfw::KeyKpAdd => constellation_msg::KeyKpAdd, + glfw::KeyKpEnter => constellation_msg::KeyKpEnter, + glfw::KeyKpEqual => constellation_msg::KeyKpEqual, + glfw::KeyLeftShift => constellation_msg::KeyLeftShift, + glfw::KeyLeftControl => constellation_msg::KeyLeftControl, + glfw::KeyLeftAlt => constellation_msg::KeyLeftAlt, + glfw::KeyLeftSuper => constellation_msg::KeyLeftSuper, + glfw::KeyRightShift => constellation_msg::KeyRightShift, + glfw::KeyRightControl => constellation_msg::KeyRightControl, + glfw::KeyRightAlt => constellation_msg::KeyRightAlt, + glfw::KeyRightSuper => constellation_msg::KeyRightSuper, + glfw::KeyMenu => constellation_msg::KeyMenu, + } +} From bb7074698afbe02776ea02861d795d885ace923e Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Fri, 3 Oct 2014 11:12:33 -0400 Subject: [PATCH 02/10] Add KeyboardEvent stub. --- .../dom/bindings/codegen/CodegenRust.py | 14 +++--- .../dom/bindings/codegen/parser/WebIDL.py | 3 ++ components/script/dom/event.rs | 2 +- components/script/dom/keyboardevent.rs | 43 +++++++++++++++++++ components/script/dom/mouseevent.rs | 18 ++++---- .../script/dom/webidls/KeyboardEvent.webidl | 36 ++++++++++++++++ .../script/dom/webidls/MouseEvent.webidl | 6 +-- .../SharedMouseAndKeyboardEventInit.webidl | 23 ++++++++++ components/script/lib.rs | 1 + 9 files changed, 122 insertions(+), 24 deletions(-) create mode 100644 components/script/dom/keyboardevent.rs create mode 100644 components/script/dom/webidls/KeyboardEvent.webidl create mode 100644 components/script/dom/webidls/SharedMouseAndKeyboardEventInit.webidl diff --git a/components/script/dom/bindings/codegen/CodegenRust.py b/components/script/dom/bindings/codegen/CodegenRust.py index 9ca15459ba1..aaac17a9002 100644 --- a/components/script/dom/bindings/codegen/CodegenRust.py +++ b/components/script/dom/bindings/codegen/CodegenRust.py @@ -4280,7 +4280,7 @@ class CGDictionary(CGThing): d = self.dictionary if d.parent: inheritance = " pub parent: %s::%s<'a, 'b>,\n" % (self.makeModuleName(d.parent), - self.makeClassName(d.parent)) + self.makeClassName(d.parent)) else: inheritance = "" memberDecls = [" pub %s: %s," % @@ -4347,12 +4347,7 @@ class CGDictionary(CGThing): @staticmethod def makeModuleName(dictionary): - name = dictionary.identifier.name - if name.endswith('Init'): - return toBindingNamespace(name.replace('Init', '')) - #XXXjdm This breaks on the test webidl files, sigh. - #raise TypeError("No idea how to find this dictionary's definition: " + name) - return "/* uh oh */ %s" % name + return dictionary.module() def getMemberType(self, memberInfo): member, (_, _, declType, _) = memberInfo @@ -4535,7 +4530,7 @@ class CGBindingRoot(CGThing): 'dom::bindings::utils::{DOMJSClass, JSCLASS_DOM_GLOBAL}', 'dom::bindings::utils::{FindEnumStringIndex, GetArrayIndexFromId}', 'dom::bindings::utils::{GetPropertyOnPrototype, GetProtoOrIfaceArray}', - 'dom::bindings::utils::{HasPropertyOnPrototype, IntVal}', + 'dom::bindings::utils::{HasPropertyOnPrototype, IntVal, UintVal}', 'dom::bindings::utils::{Reflectable}', 'dom::bindings::utils::{squirrel_away_unique}', 'dom::bindings::utils::{ThrowingConstructor, unwrap, unwrap_jsmanaged}', @@ -5430,7 +5425,8 @@ class GlobalGenRoots(): def Bindings(config): descriptors = (set(d.name + "Binding" for d in config.getDescriptors(register=True)) | - set(d.unroll().module() for d in config.callbacks)) + set(d.unroll().module() for d in config.callbacks) | + set(d.module() for d in config.getDictionaries())) curr = CGList([CGGeneric("pub mod %s;\n" % name) for name in sorted(descriptors)]) curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT) return curr diff --git a/components/script/dom/bindings/codegen/parser/WebIDL.py b/components/script/dom/bindings/codegen/parser/WebIDL.py index 32f80e82c56..370ac7df7c0 100644 --- a/components/script/dom/bindings/codegen/parser/WebIDL.py +++ b/components/script/dom/bindings/codegen/parser/WebIDL.py @@ -1422,6 +1422,9 @@ class IDLDictionary(IDLObjectWithScope): self.identifier.name, [member.location] + locations) + def module(self): + return self.location.filename().split('/')[-1].split('.webidl')[0] + 'Binding' + def addExtendedAttributes(self, attrs): assert len(attrs) == 0 diff --git a/components/script/dom/event.rs b/components/script/dom/event.rs index d6dc2cfe00f..bbeb2cb7df2 100644 --- a/components/script/dom/event.rs +++ b/components/script/dom/event.rs @@ -29,7 +29,7 @@ pub enum EventPhase { pub enum EventTypeId { CustomEventTypeId, HTMLEventTypeId, - KeyEventTypeId, + KeyboardEventTypeId, MessageEventTypeId, MouseEventTypeId, ProgressEventTypeId, diff --git a/components/script/dom/keyboardevent.rs b/components/script/dom/keyboardevent.rs new file mode 100644 index 00000000000..de9c8992d9d --- /dev/null +++ b/components/script/dom/keyboardevent.rs @@ -0,0 +1,43 @@ +/* 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::KeyboardEventBinding; +use dom::bindings::codegen::Bindings::KeyboardEventBinding::KeyboardEventMethods; +use dom::bindings::codegen::InheritTypes::KeyboardEventDerived; +use dom::bindings::error::Fallible; +use dom::bindings::global::GlobalRef; +use dom::bindings::js::{JSRef, Temporary}; +use dom::bindings::utils::{Reflectable, Reflector/*, reflect_dom_object*/}; +use dom::event::{Event, KeyboardEventTypeId}; +use dom::uievent::UIEvent; +use servo_util::str::DOMString; + +#[jstraceable] +#[must_root] +pub struct KeyboardEvent { + uievent: UIEvent, +} + +impl KeyboardEventDerived for Event { + fn is_keyboardevent(&self) -> bool { + *self.type_id() == KeyboardEventTypeId + } +} + +impl KeyboardEvent { + pub fn Constructor(_global: &GlobalRef, + _type_: DOMString, + _init: &KeyboardEventBinding::KeyboardEventInit) -> Fallible> { + fail!() + } +} + +impl<'a> KeyboardEventMethods for JSRef<'a, KeyboardEvent> { +} + +impl Reflectable for KeyboardEvent { + fn reflector<'a>(&'a self) -> &'a Reflector { + self.uievent.reflector() + } +} diff --git a/components/script/dom/mouseevent.rs b/components/script/dom/mouseevent.rs index a4d3a0a9e09..c65a53217b7 100644 --- a/components/script/dom/mouseevent.rs +++ b/components/script/dom/mouseevent.rs @@ -21,7 +21,7 @@ use std::default::Default; #[dom_struct] pub struct MouseEvent { - mouseevent: UIEvent, + uievent: UIEvent, screen_x: Cell, screen_y: Cell, client_x: Cell, @@ -43,7 +43,7 @@ impl MouseEventDerived for Event { impl MouseEvent { fn new_inherited() -> MouseEvent { MouseEvent { - mouseevent: UIEvent::new_inherited(MouseEventTypeId), + uievent: UIEvent::new_inherited(MouseEventTypeId), screen_x: Cell::new(0), screen_y: Cell::new(0), client_x: Cell::new(0), @@ -91,13 +91,13 @@ impl MouseEvent { type_: DOMString, init: &MouseEventBinding::MouseEventInit) -> Fallible> { let event = MouseEvent::new(global.as_window(), type_, - init.parent.parent.bubbles, - init.parent.parent.cancelable, - init.parent.view.root_ref(), - init.parent.detail, + init.parent.parent.parent.bubbles, + init.parent.parent.parent.cancelable, + init.parent.parent.view.root_ref(), + init.parent.parent.detail, init.screenX, init.screenY, - init.clientX, init.clientY, init.ctrlKey, - init.altKey, init.shiftKey, init.metaKey, + init.clientX, init.clientY, init.parent.ctrlKey, + init.parent.altKey, init.parent.shiftKey, init.parent.metaKey, init.button, init.relatedTarget.root_ref()); Ok(event) } @@ -178,6 +178,6 @@ impl<'a> MouseEventMethods for JSRef<'a, MouseEvent> { impl Reflectable for MouseEvent { fn reflector<'a>(&'a self) -> &'a Reflector { - self.mouseevent.reflector() + self.uievent.reflector() } } diff --git a/components/script/dom/webidls/KeyboardEvent.webidl b/components/script/dom/webidls/KeyboardEvent.webidl new file mode 100644 index 00000000000..589e39393b5 --- /dev/null +++ b/components/script/dom/webidls/KeyboardEvent.webidl @@ -0,0 +1,36 @@ +/* -*- 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/. + * + * The origin of this IDL file is + * https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#interface-KeyboardEvent + * + */ + +[Constructor(DOMString typeArg, optional KeyboardEventInit keyboardEventInitDict)] +interface KeyboardEvent : UIEvent { + // KeyLocationCode + const unsigned long DOM_KEY_LOCATION_STANDARD = 0x00; + const unsigned long DOM_KEY_LOCATION_LEFT = 0x01; + const unsigned long DOM_KEY_LOCATION_RIGHT = 0x02; + const unsigned long DOM_KEY_LOCATION_NUMPAD = 0x03; + //readonly attribute DOMString key; + //readonly attribute DOMString code; + //readonly attribute unsigned long location; + //readonly attribute boolean ctrlKey; + //readonly attribute boolean shiftKey; + //readonly attribute boolean altKey; + //readonly attribute boolean metaKey; + //readonly attribute boolean repeat; + //readonly attribute boolean isComposing; + //boolean getModifierState (DOMString keyArg); +}; + +dictionary KeyboardEventInit : SharedKeyboardAndMouseEventInit { + DOMString key = ""; + DOMString code = ""; + unsigned long location = 0; + boolean repeat = false; + boolean isComposing = false; +}; diff --git a/components/script/dom/webidls/MouseEvent.webidl b/components/script/dom/webidls/MouseEvent.webidl index cdef58228c1..a46a44938ca 100644 --- a/components/script/dom/webidls/MouseEvent.webidl +++ b/components/script/dom/webidls/MouseEvent.webidl @@ -22,15 +22,11 @@ interface MouseEvent : UIEvent { }; // https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#idl-def-MouseEventInit -dictionary MouseEventInit : UIEventInit { +dictionary MouseEventInit : SharedKeyboardAndMouseEventInit { long screenX = 0; long screenY = 0; long clientX = 0; long clientY = 0; - boolean ctrlKey = false; - boolean shiftKey = false; - boolean altKey = false; - boolean metaKey = false; short button = 0; //unsigned short buttons = 0; EventTarget? relatedTarget = null; diff --git a/components/script/dom/webidls/SharedMouseAndKeyboardEventInit.webidl b/components/script/dom/webidls/SharedMouseAndKeyboardEventInit.webidl new file mode 100644 index 00000000000..eb852604ed1 --- /dev/null +++ b/components/script/dom/webidls/SharedMouseAndKeyboardEventInit.webidl @@ -0,0 +1,23 @@ +/* -*- 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://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#idl-def-SharedKeyboardAndMouseEventInit +dictionary SharedKeyboardAndMouseEventInit : UIEventInit { + boolean ctrlKey = false; + boolean shiftKey = false; + boolean altKey = false; + boolean metaKey = false; + boolean keyModifierStateAltGraph = false; + boolean keyModifierStateCapsLock = false; + boolean keyModifierStateFn = false; + boolean keyModifierStateFnLock = false; + boolean keyModifierStateHyper = false; + boolean keyModifierStateNumLock = false; + boolean keyModifierStateOS = false; + boolean keyModifierStateScrollLock = false; + boolean keyModifierStateSuper = false; + boolean keyModifierStateSymbol = false; + boolean keyModifierStateSymbolLock = false; +}; diff --git a/components/script/lib.rs b/components/script/lib.rs index 1d64397e20e..959c245b63f 100644 --- a/components/script/lib.rs +++ b/components/script/lib.rs @@ -178,6 +178,7 @@ pub mod dom { pub mod htmlulistelement; pub mod htmlvideoelement; pub mod htmlunknownelement; + pub mod keyboardevent; pub mod location; pub mod messageevent; pub mod mouseevent; From e999843183f27286337705a032ed3d88d361b372 Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Sat, 4 Oct 2014 08:52:15 -0400 Subject: [PATCH 03/10] Fill in KeyboardEvent. --- components/script/dom/keyboardevent.rs | 167 +++++++++++++++++- components/script/dom/mouseevent.rs | 1 - .../script/dom/webidls/KeyboardEvent.webidl | 42 +++-- 3 files changed, 192 insertions(+), 18 deletions(-) diff --git a/components/script/dom/keyboardevent.rs b/components/script/dom/keyboardevent.rs index de9c8992d9d..be96a181b30 100644 --- a/components/script/dom/keyboardevent.rs +++ b/components/script/dom/keyboardevent.rs @@ -4,19 +4,34 @@ use dom::bindings::codegen::Bindings::KeyboardEventBinding; use dom::bindings::codegen::Bindings::KeyboardEventBinding::KeyboardEventMethods; -use dom::bindings::codegen::InheritTypes::KeyboardEventDerived; +use dom::bindings::codegen::Bindings::UIEventBinding::UIEventMethods; +use dom::bindings::codegen::InheritTypes::{UIEventCast, KeyboardEventDerived}; use dom::bindings::error::Fallible; use dom::bindings::global::GlobalRef; -use dom::bindings::js::{JSRef, Temporary}; -use dom::bindings::utils::{Reflectable, Reflector/*, reflect_dom_object*/}; +use dom::bindings::global; +use dom::bindings::js::{JSRef, Temporary, RootedReference}; +use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object}; use dom::event::{Event, KeyboardEventTypeId}; use dom::uievent::UIEvent; +use dom::window::Window; use servo_util::str::DOMString; +use std::cell::{RefCell, Cell}; #[jstraceable] #[must_root] pub struct KeyboardEvent { uievent: UIEvent, + key: RefCell, + code: RefCell, + location: Cell, + ctrl: Cell, + alt: Cell, + shift: Cell, + meta: Cell, + repeat: Cell, + is_composing: Cell, + char_code: Cell>, + key_code: Cell, } impl KeyboardEventDerived for Event { @@ -26,14 +41,152 @@ impl KeyboardEventDerived for Event { } impl KeyboardEvent { - pub fn Constructor(_global: &GlobalRef, - _type_: DOMString, - _init: &KeyboardEventBinding::KeyboardEventInit) -> Fallible> { - fail!() + fn new_inherited() -> KeyboardEvent { + KeyboardEvent { + uievent: UIEvent::new_inherited(KeyboardEventTypeId), + key: RefCell::new("".to_string()), + code: RefCell::new("".to_string()), + location: Cell::new(0), + ctrl: Cell::new(false), + alt: Cell::new(false), + shift: Cell::new(false), + meta: Cell::new(false), + repeat: Cell::new(false), + is_composing: Cell::new(false), + char_code: Cell::new(None), + key_code: Cell::new(0), + } + } + + fn new_uninitialized(window: JSRef) -> Temporary { + reflect_dom_object(box KeyboardEvent::new_inherited(), + &global::Window(window), + KeyboardEventBinding::Wrap) + } + + pub fn new(window: JSRef, + type_: DOMString, + canBubble: bool, + cancelable: bool, + view: Option>, + _detail: i32, + key: DOMString, + code: DOMString, + location: u32, + repeat: bool, + isComposing: bool, + ctrlKey: bool, + altKey: bool, + shiftKey: bool, + metaKey: bool, + char_code: Option, + key_code: u32) -> Temporary { + let ev = KeyboardEvent::new_uninitialized(window).root(); + ev.deref().InitKeyboardEvent(type_, canBubble, cancelable, view, key, location, + "".to_string(), repeat, "".to_string()); + *ev.code.borrow_mut() = code; + ev.ctrl.set(ctrlKey); + ev.alt.set(altKey); + ev.shift.set(shiftKey); + ev.meta.set(metaKey); + ev.char_code.set(char_code); + ev.key_code.set(key_code); + ev.is_composing.set(isComposing); + Temporary::from_rooted(*ev) + } + + pub fn Constructor(global: &GlobalRef, + type_: DOMString, + init: &KeyboardEventBinding::KeyboardEventInit) -> Fallible> { + let event = KeyboardEvent::new(global.as_window(), type_, + init.parent.parent.parent.bubbles, + init.parent.parent.parent.cancelable, + init.parent.parent.view.root_ref(), + init.parent.parent.detail, + init.key.clone(), init.code.clone(), init.location, + init.repeat, init.isComposing, init.parent.ctrlKey, + init.parent.altKey, init.parent.shiftKey, init.parent.metaKey, + None, 0); + Ok(event) } } impl<'a> KeyboardEventMethods for JSRef<'a, KeyboardEvent> { + fn InitKeyboardEvent(self, + typeArg: DOMString, + canBubbleArg: bool, + cancelableArg: bool, + viewArg: Option>, + keyArg: DOMString, + locationArg: u32, + _modifiersListArg: DOMString, + repeat: bool, + _locale: DOMString) { + let uievent: JSRef = UIEventCast::from_ref(self); + uievent.InitUIEvent(typeArg, canBubbleArg, cancelableArg, viewArg, 0); + *self.key.borrow_mut() = keyArg; + self.location.set(locationArg); + self.repeat.set(repeat); + } + + fn Key(self) -> DOMString { + self.key.borrow().clone() + } + + fn Code(self) -> DOMString { + self.code.borrow().clone() + } + + fn Location(self) -> u32 { + self.location.get() + } + + fn CtrlKey(self) -> bool { + self.ctrl.get() + } + + fn ShiftKey(self) -> bool { + self.shift.get() + } + + fn AltKey(self) -> bool { + self.alt.get() + } + + fn MetaKey(self) -> bool { + self.meta.get() + } + + fn Repeat(self) -> bool { + self.repeat.get() + } + + fn IsComposing(self) -> bool { + self.is_composing.get() + } + + fn GetModifierState(self, keyArg: DOMString) -> bool { + match keyArg.as_slice() { + "Ctrl" => self.CtrlKey(), + "Alt" => self.AltKey(), + "Shift" => self.ShiftKey(), + "Meta" => self.MetaKey(), + "AltGraph" | "CapsLock" | "NumLock" | "ScrollLock" => false, //FIXME + _ => false, + } + } + + fn CharCode(self) -> u32 { + self.char_code.get().unwrap_or(0) + } + + fn KeyCode(self) -> u32 { + self.key_code.get() + } + + fn Which(self) -> u32 { + self.char_code.get().unwrap_or(self.KeyCode()) + } } impl Reflectable for KeyboardEvent { diff --git a/components/script/dom/mouseevent.rs b/components/script/dom/mouseevent.rs index c65a53217b7..b617505c41e 100644 --- a/components/script/dom/mouseevent.rs +++ b/components/script/dom/mouseevent.rs @@ -175,7 +175,6 @@ impl<'a> MouseEventMethods for JSRef<'a, MouseEvent> { } } - impl Reflectable for MouseEvent { fn reflector<'a>(&'a self) -> &'a Reflector { self.uievent.reflector() diff --git a/components/script/dom/webidls/KeyboardEvent.webidl b/components/script/dom/webidls/KeyboardEvent.webidl index 589e39393b5..d0a185a14de 100644 --- a/components/script/dom/webidls/KeyboardEvent.webidl +++ b/components/script/dom/webidls/KeyboardEvent.webidl @@ -15,18 +15,33 @@ interface KeyboardEvent : UIEvent { const unsigned long DOM_KEY_LOCATION_LEFT = 0x01; const unsigned long DOM_KEY_LOCATION_RIGHT = 0x02; const unsigned long DOM_KEY_LOCATION_NUMPAD = 0x03; - //readonly attribute DOMString key; - //readonly attribute DOMString code; - //readonly attribute unsigned long location; - //readonly attribute boolean ctrlKey; - //readonly attribute boolean shiftKey; - //readonly attribute boolean altKey; - //readonly attribute boolean metaKey; - //readonly attribute boolean repeat; - //readonly attribute boolean isComposing; - //boolean getModifierState (DOMString keyArg); + readonly attribute DOMString key; + readonly attribute DOMString code; + readonly attribute unsigned long location; + readonly attribute boolean ctrlKey; + readonly attribute boolean shiftKey; + readonly attribute boolean altKey; + readonly attribute boolean metaKey; + readonly attribute boolean repeat; + readonly attribute boolean isComposing; + boolean getModifierState (DOMString keyArg); }; +// https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#idl-interface-KeyboardEvent-initializers +partial interface KeyboardEvent { + // Originally introduced (and deprecated) in DOM Level 3 + void initKeyboardEvent (DOMString typeArg, boolean bubblesArg, boolean cancelableArg, Window? viewArg, DOMString keyArg, unsigned long locationArg, DOMString modifiersListArg, boolean repeat, DOMString locale); +}; + +// https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#KeyboardEvent-supplemental-interface +partial interface KeyboardEvent { + // The following support legacy user agents + readonly attribute unsigned long charCode; + readonly attribute unsigned long keyCode; + readonly attribute unsigned long which; +}; + +// https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#interface-KeyboardEvent dictionary KeyboardEventInit : SharedKeyboardAndMouseEventInit { DOMString key = ""; DOMString code = ""; @@ -34,3 +49,10 @@ dictionary KeyboardEventInit : SharedKeyboardAndMouseEventInit { boolean repeat = false; boolean isComposing = false; }; + +// https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#events-KeyboardEventInit-supplemental +/*partial dictionary KeyboardEventInit { + unsigned long charCode = 0; + unsigned long keyCode = 0; + unsigned long which = 0; +};*/ From 329ba56fca3bd3808a37aae6bc3ed3c5a5d23524 Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Sat, 4 Oct 2014 09:46:50 -0400 Subject: [PATCH 04/10] Dispatch keydown, keyup, and keypress events at appropriate times. --- components/compositing/compositor.rs | 10 ++--- components/compositing/constellation.rs | 10 ++--- components/compositing/windowing.rs | 4 +- components/msg/constellation_msg.rs | 12 +++++- components/script/dom/keyboardevent.rs | 27 ++++++++++++ components/script/script_task.rs | 56 +++++++++++++++++++++++-- components/script_traits/lib.rs | 4 +- ports/glfw/window.rs | 20 ++++++++- 8 files changed, 124 insertions(+), 19 deletions(-) diff --git a/components/compositing/compositor.rs b/components/compositing/compositor.rs index 738d5abab0a..e20ea06f0e8 100644 --- a/components/compositing/compositor.rs +++ b/components/compositing/compositor.rs @@ -43,7 +43,7 @@ use servo_msg::compositor_msg::{Blank, Epoch, FinishedLoading, IdleRenderState, use servo_msg::compositor_msg::{ReadyState, RenderingRenderState, RenderState, Scrollable}; use servo_msg::constellation_msg::{ConstellationChan, ExitMsg, LoadUrlMsg}; use servo_msg::constellation_msg::{NavigateMsg, LoadData, PipelineId, ResizedWindowMsg}; -use servo_msg::constellation_msg::{WindowSizeData, KeyState, Key}; +use servo_msg::constellation_msg::{WindowSizeData, KeyState, Key, KeyModifiers}; use servo_msg::constellation_msg; use servo_util::geometry::{PagePx, ScreenPx, ViewportPx}; use servo_util::memory::MemoryProfilerChan; @@ -707,8 +707,8 @@ impl IOCompositor { self.on_navigation_window_event(direction); } - KeyEvent(key, state) => { - self.on_key_event(key, state); + KeyEvent(key, state, modifiers) => { + self.on_key_event(key, state, modifiers); } FinishedWindowEvent => { @@ -882,9 +882,9 @@ impl IOCompositor { chan.send(NavigateMsg(direction)) } - fn on_key_event(&self, key: Key, state: KeyState) { + fn on_key_event(&self, key: Key, state: KeyState, modifiers: KeyModifiers) { let ConstellationChan(ref chan) = self.constellation_chan; - chan.send(constellation_msg::KeyEvent(key, state)) + chan.send(constellation_msg::KeyEvent(key, state, modifiers)) } fn convert_buffer_requests_to_pipeline_requests_map(&self, diff --git a/components/compositing/constellation.rs b/components/compositing/constellation.rs index 096bc76870e..d8831c02c90 100644 --- a/components/compositing/constellation.rs +++ b/components/compositing/constellation.rs @@ -22,7 +22,7 @@ use servo_msg::constellation_msg::{IFrameSandboxState, IFrameUnsandboxed, InitLo use servo_msg::constellation_msg::{LoadCompleteMsg, LoadUrlMsg, LoadData, Msg, NavigateMsg}; use servo_msg::constellation_msg::{NavigationType, PipelineId, RendererReadyMsg, ResizedWindowMsg}; use servo_msg::constellation_msg::{ScriptLoadedURLInIFrameMsg, SubpageId, WindowSizeData}; -use servo_msg::constellation_msg::{KeyEvent, Key, KeyState}; +use servo_msg::constellation_msg::{KeyEvent, Key, KeyState, KeyModifiers}; use servo_msg::constellation_msg; use servo_net::image_cache_task::{ImageCacheTask, ImageCacheTaskClient}; use servo_net::resource_task::ResourceTask; @@ -452,9 +452,9 @@ impl Constellation { debug!("constellation got window resize message"); self.handle_resized_window_msg(new_size); } - KeyEvent(key, state) => { + KeyEvent(key, state, modifiers) => { debug!("constellation got key event message"); - self.handle_key_msg(key, state); + self.handle_key_msg(key, state, modifiers); } } true @@ -767,10 +767,10 @@ impl Constellation { .any(|current_frame| current_frame.contains(pipeline_id)) } - fn handle_key_msg(&self, key: Key, state: KeyState) { + fn handle_key_msg(&self, key: Key, state: KeyState, mods: KeyModifiers) { self.current_frame().as_ref().map(|frame| { let ScriptControlChan(ref chan) = frame.pipeline.script_chan; - chan.send(SendEventMsg(frame.pipeline.id, script_traits::KeyEvent(key, state))); + chan.send(SendEventMsg(frame.pipeline.id, script_traits::KeyEvent(key, state, mods))); }); } diff --git a/components/compositing/windowing.rs b/components/compositing/windowing.rs index c564c34b29a..e9b95f551b7 100644 --- a/components/compositing/windowing.rs +++ b/components/compositing/windowing.rs @@ -11,7 +11,7 @@ use geom::scale_factor::ScaleFactor; use geom::size::TypedSize2D; use layers::geometry::DevicePixel; use layers::platform::surface::NativeGraphicsMetadata; -use servo_msg::constellation_msg::{Key, KeyState}; +use servo_msg::constellation_msg::{Key, KeyState, KeyModifiers}; use servo_msg::compositor_msg::{ReadyState, RenderState}; use servo_util::geometry::ScreenPx; use std::fmt::{FormatError, Formatter, Show}; @@ -60,7 +60,7 @@ pub enum WindowEvent { /// Sent when the user quits the application QuitWindowEvent, /// Sent when a key input state changes - KeyEvent(Key, KeyState), + KeyEvent(Key, KeyState, KeyModifiers), } impl Show for WindowEvent { diff --git a/components/msg/constellation_msg.rs b/components/msg/constellation_msg.rs index 40280a89afc..550bafd4096 100644 --- a/components/msg/constellation_msg.rs +++ b/components/msg/constellation_msg.rs @@ -50,6 +50,7 @@ pub struct WindowSizeData { pub device_pixel_ratio: ScaleFactor, } +#[deriving(PartialEq)] pub enum KeyState { Pressed, Released, @@ -181,6 +182,15 @@ pub enum Key { KeyMenu, } +bitflags! { + flags KeyModifiers: u8 { + const Shift = 0x01, + const Control = 0x02, + const Alt = 0x04, + const Super = 0x08, + } +} + /// Messages from the compositor and script to the constellation. pub enum Msg { ExitMsg, @@ -193,7 +203,7 @@ pub enum Msg { NavigateMsg(NavigationDirection), RendererReadyMsg(PipelineId), ResizedWindowMsg(WindowSizeData), - KeyEvent(Key, KeyState), + KeyEvent(Key, KeyState, KeyModifiers), } /// Similar to net::resource_task::LoadData diff --git a/components/script/dom/keyboardevent.rs b/components/script/dom/keyboardevent.rs index be96a181b30..2c8f5e49a10 100644 --- a/components/script/dom/keyboardevent.rs +++ b/components/script/dom/keyboardevent.rs @@ -14,6 +14,7 @@ use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object}; use dom::event::{Event, KeyboardEventTypeId}; use dom::uievent::UIEvent; use dom::window::Window; +use servo_msg::constellation_msg; use servo_util::str::DOMString; use std::cell::{RefCell, Cell}; @@ -109,6 +110,32 @@ impl KeyboardEvent { None, 0); Ok(event) } + + pub fn key_properties(key: constellation_msg::Key) -> KeyEventProperties { + match key { + _ => KeyEventProperties { + key: "".to_string(), + code: "".to_string(), + location: 0, + char_code: None, + key_code: 0, + } + } + } +} + +pub struct KeyEventProperties { + pub key: DOMString, + pub code: DOMString, + pub location: u32, + pub char_code: Option, + pub key_code: u32, +} + +impl KeyEventProperties { + pub fn is_printable(&self) -> bool { + self.char_code.is_some() + } } impl<'a> KeyboardEventMethods for JSRef<'a, KeyboardEvent> { diff --git a/components/script/script_task.rs b/components/script/script_task.rs index 31f3abb289c..ff499fb474b 100644 --- a/components/script/script_task.rs +++ b/components/script/script_task.rs @@ -10,6 +10,7 @@ use dom::bindings::codegen::Bindings::DocumentBinding::{DocumentMethods, Documen use dom::bindings::codegen::Bindings::DOMRectBinding::DOMRectMethods; use dom::bindings::codegen::Bindings::ElementBinding::ElementMethods; use dom::bindings::codegen::Bindings::EventTargetBinding::EventTargetMethods; +use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods; use dom::bindings::codegen::InheritTypes::{EventTargetCast, NodeCast, EventCast, ElementCast}; use dom::bindings::conversions; use dom::bindings::conversions::{FromJSValConvertible, Empty}; @@ -23,6 +24,7 @@ use dom::element::{HTMLSelectElementTypeId, HTMLTextAreaElementTypeId, HTMLOptio use dom::event::{Event, Bubbles, DoesNotBubble, Cancelable, NotCancelable}; use dom::uievent::UIEvent; use dom::eventtarget::{EventTarget, EventTargetHelpers}; +use dom::keyboardevent::KeyboardEvent; use dom::node; use dom::node::{ElementNodeTypeId, Node, NodeHelpers}; use dom::window::{Window, WindowHelpers}; @@ -46,7 +48,9 @@ use script_traits::{ScriptControlChan, ReflowCompleteMsg, UntrustedNodeAddress, use servo_msg::compositor_msg::{FinishedLoading, LayerId, Loading}; use servo_msg::compositor_msg::{ScriptListener}; use servo_msg::constellation_msg::{ConstellationChan, LoadCompleteMsg, LoadUrlMsg, NavigationDirection}; -use servo_msg::constellation_msg::{LoadData, PipelineId, Failure, FailureMsg, WindowSizeData}; +use servo_msg::constellation_msg::{LoadData, PipelineId, Failure, FailureMsg, WindowSizeData, Key, KeyState}; +use servo_msg::constellation_msg::{KeyModifiers, Super, Shift, Control, Alt, Repeated, Pressed}; +use servo_msg::constellation_msg::{Released}; use servo_msg::constellation_msg; use servo_net::image_cache_task::ImageCacheTask; use servo_net::resource_task::ResourceTask; @@ -908,12 +912,58 @@ impl ScriptTask { self.handle_mouse_move_event(pipeline_id, point); } - KeyEvent(key, state) => { - println!("key {} is {}", key as int, state as int); + KeyEvent(key, state, modifiers) => { + self.dispatch_key_event(key, state, modifiers, pipeline_id); } } } + /// The entry point for all key processing for web content + fn dispatch_key_event(&self, key: Key, + state: KeyState, + modifiers: KeyModifiers, + pipeline_id: PipelineId) { + println!("key {} is {}", key as int, state as int); + let page = get_page(&*self.page.borrow(), pipeline_id); + let frame = page.frame(); + let window = frame.as_ref().unwrap().window.root(); + let doc = window.Document().root(); + let body = doc.GetBody().root(); + + let target: JSRef = match body { + Some(body) => EventTargetCast::from_ref(*body), + None => EventTargetCast::from_ref(*window), + }; + + let ctrl = modifiers.contains(Control); + let alt = modifiers.contains(Alt); + let shift = modifiers.contains(Shift); + let meta = modifiers.contains(Super); + + let is_composing = false; + let is_repeating = state == Repeated; + let ev_type = match state { + Pressed | Repeated => "keydown", + Released => "keyup", + }.to_string(); + + let props = KeyboardEvent::key_properties(key); + + let event = KeyboardEvent::new(*window, ev_type, 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)); + + 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 _ = target.DispatchEvent(EventCast::from_ref(*event)); + } + } + /// 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, load_data: LoadData) { diff --git a/components/script_traits/lib.rs b/components/script_traits/lib.rs index e0e300307fe..10bc1964e01 100644 --- a/components/script_traits/lib.rs +++ b/components/script_traits/lib.rs @@ -25,7 +25,7 @@ extern crate serialize; use devtools_traits::DevtoolsControlChan; use libc::c_void; use servo_msg::constellation_msg::{ConstellationChan, PipelineId, Failure, WindowSizeData}; -use servo_msg::constellation_msg::{LoadData, SubpageId, Key, KeyState}; +use servo_msg::constellation_msg::{LoadData, SubpageId, Key, KeyState, KeyModifiers}; use servo_msg::compositor_msg::ScriptListener; use servo_net::image_cache_task::ImageCacheTask; use servo_net::resource_task::ResourceTask; @@ -75,7 +75,7 @@ pub enum CompositorEvent { MouseDownEvent(uint, Point2D), MouseUpEvent(uint, Point2D), MouseMoveEvent(Point2D), - KeyEvent(Key, KeyState), + KeyEvent(Key, KeyState, KeyModifiers), } /// An opaque wrapper around script<->layout channels to avoid leaking message types into diff --git a/ports/glfw/window.rs b/ports/glfw/window.rs index bc75c083853..2e943197370 100644 --- a/ports/glfw/window.rs +++ b/ports/glfw/window.rs @@ -216,7 +216,8 @@ impl Window { glfw::Release => constellation_msg::Released, glfw::Repeat => constellation_msg::Repeated, }; - self.event_queue.borrow_mut().push(KeyEvent(key, state)); + let modifiers = glfw_mods_to_script_mods(mods); + self.event_queue.borrow_mut().push(KeyEvent(key, state, modifiers)); }, glfw::FramebufferSizeEvent(width, height) => { self.event_queue.borrow_mut().push( @@ -436,6 +437,23 @@ extern "C" fn on_framebuffer_size(_glfw_window: *mut glfw::ffi::GLFWwindow, } } +fn glfw_mods_to_script_mods(mods: glfw::Modifiers) -> constellation_msg::KeyModifiers { + let mut result = constellation_msg::KeyModifiers::from_bits(0).unwrap(); + if mods.contains(glfw::Shift) { + result.insert(constellation_msg::Shift); + } + if mods.contains(glfw::Alt) { + result.insert(constellation_msg::Alt); + } + if mods.contains(glfw::Control) { + result.insert(constellation_msg::Control); + } + if mods.contains(glfw::Super) { + result.insert(constellation_msg::Super); + } + result +} + fn glfw_key_to_script_key(key: glfw::Key) -> constellation_msg::Key { match key { glfw::KeySpace => constellation_msg::KeySpace, From 84bc17e7ad99d11ff416f2126acb2031b680dc19 Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Sat, 4 Oct 2014 10:31:47 -0400 Subject: [PATCH 05/10] Implement document focus context and hook it up to click events. --- components/script/dom/document.rs | 34 +++++++++++++++++++++++ components/script/dom/htmlinputelement.rs | 3 ++ components/script/script_task.rs | 20 ++++++++----- tests/html/test_focus.html | 7 +++++ 4 files changed, 57 insertions(+), 7 deletions(-) create mode 100644 tests/html/test_focus.html 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 @@ + + + + From 80764f65e3e4895d3a012d6bc1d3bb8183ae15da Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Mon, 6 Oct 2014 11:51:44 -0400 Subject: [PATCH 06/10] Add single-line text input with no visible cursor. --- components/msg/constellation_msg.rs | 9 +- components/script/dom/element.rs | 26 +- components/script/dom/htmlinputelement.rs | 77 ++-- components/script/dom/keyboardevent.rs | 433 +++++++++++++++++++++- components/script/lib.rs | 1 + components/script/script_task.rs | 26 +- components/script/textinput.rs | 259 +++++++++++++ ports/glfw/window.rs | 8 +- 8 files changed, 776 insertions(+), 63 deletions(-) create mode 100644 components/script/textinput.rs diff --git a/components/msg/constellation_msg.rs b/components/msg/constellation_msg.rs index 550bafd4096..f05c16dfb42 100644 --- a/components/msg/constellation_msg.rs +++ b/components/msg/constellation_msg.rs @@ -58,6 +58,7 @@ pub enum KeyState { } //N.B. Straight up copied from glfw-rs +#[deriving(Show)] pub enum Key { KeySpace, KeyApostrophe, @@ -184,10 +185,10 @@ pub enum Key { bitflags! { flags KeyModifiers: u8 { - const Shift = 0x01, - const Control = 0x02, - const Alt = 0x04, - const Super = 0x08, + const SHIFT = 0x01, + const CONTROL = 0x02, + const ALT = 0x04, + const SUPER = 0x08, } } diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs index 9c2031c79cf..87d3f889544 100644 --- a/components/script/dom/element.rs +++ b/components/script/dom/element.rs @@ -26,7 +26,7 @@ use dom::document::{Document, DocumentHelpers, LayoutDocumentHelpers}; use dom::domtokenlist::DOMTokenList; use dom::eventtarget::{EventTarget, NodeTargetTypeId}; use dom::htmlcollection::HTMLCollection; -use dom::htmlinputelement::{HTMLInputElement, LayoutHTMLInputElementHelpers}; +use dom::htmlinputelement::{HTMLInputElement, RawLayoutHTMLInputElementHelpers}; use dom::htmlserializer::serialize; use dom::htmltablecellelement::{HTMLTableCellElement, HTMLTableCellElementHelpers}; use dom::node::{ElementNodeTypeId, Node, NodeHelpers, NodeIterator, document_from_node}; @@ -231,17 +231,24 @@ pub trait RawLayoutElementHelpers { -> Option; } +#[inline] +#[allow(unrooted_must_root)] +unsafe fn get_attr_for_layout<'a>(elem: &'a Element, namespace: &Namespace, name: &Atom) -> Option<&'a JS> { + // cast to point to T in RefCell directly + let attrs: *const Vec> = mem::transmute(&elem.attrs); + (*attrs).iter().find(|attr: & &JS| { + let attr = attr.unsafe_get(); + *name == (*attr).local_name_atom_forever() && + (*attr).namespace() == namespace + }) +} + impl RawLayoutElementHelpers for Element { #[inline] #[allow(unrooted_must_root)] unsafe fn get_attr_val_for_layout<'a>(&'a self, namespace: &Namespace, name: &Atom) -> Option<&'a str> { - let attrs = self.attrs.borrow_for_layout(); - (*attrs).iter().find(|attr: & &JS| { - let attr = attr.unsafe_get(); - *name == (*attr).local_name_atom_forever() && - (*attr).namespace() == namespace - }).map(|attr| { + get_attr_for_layout(self, namespace, name).map(|attr| { let attr = attr.unsafe_get(); (*attr).value_ref_forever() }) @@ -337,6 +344,7 @@ impl RawLayoutElementHelpers for Element { pub trait LayoutElementHelpers { unsafe fn html_element_in_html_document_for_layout(&self) -> bool; + unsafe fn has_attr_for_layout(&self, namespace: &Namespace, name: &Atom) -> bool; } impl LayoutElementHelpers for JS { @@ -349,6 +357,10 @@ impl LayoutElementHelpers for JS { let node: JS = self.transmute_copy(); node.owner_doc_for_layout().is_html_document_for_layout() } + + unsafe fn has_attr_for_layout(&self, namespace: &Namespace, name: &Atom) -> bool { + get_attr_for_layout(&*self.unsafe_get(), namespace, name).is_some() + } } pub trait ElementHelpers<'a> { diff --git a/components/script/dom/htmlinputelement.rs b/components/script/dom/htmlinputelement.rs index 44e8b21ab18..547c0f57aa2 100644 --- a/components/script/dom/htmlinputelement.rs +++ b/components/script/dom/htmlinputelement.rs @@ -13,16 +13,20 @@ use dom::bindings::codegen::Bindings::HTMLInputElementBinding::HTMLInputElementM use dom::bindings::codegen::Bindings::NodeListBinding::NodeListMethods; use dom::bindings::codegen::InheritTypes::{ElementCast, HTMLElementCast, HTMLFormElementCast, HTMLInputElementCast, NodeCast}; use dom::bindings::codegen::InheritTypes::{HTMLInputElementDerived, HTMLFieldSetElementDerived}; +use dom::bindings::codegen::InheritTypes::KeyboardEventCast; use dom::bindings::js::{JS, JSRef, Temporary, OptionalRootable, ResultRootable}; use dom::bindings::utils::{Reflectable, Reflector}; use dom::document::{Document, DocumentHelpers}; -use dom::element::{AttributeHandlers, Element, HTMLInputElementTypeId}; +use dom::element::{AttributeHandlers, Element, HTMLInputElementTypeId, LayoutElementHelpers}; +use dom::element::RawLayoutElementHelpers; use dom::event::Event; use dom::eventtarget::{EventTarget, NodeTargetTypeId}; use dom::htmlelement::HTMLElement; +use dom::keyboardevent::KeyboardEvent; use dom::htmlformelement::{InputElement, FormOwner, HTMLFormElement, HTMLFormElementHelpers, NotFromFormSubmitMethod}; use dom::node::{DisabledStateHelpers, Node, NodeHelpers, ElementNodeTypeId, document_from_node, window_from_node}; use dom::virtualmethods::VirtualMethods; +use textinput::{TextInput, TriggerDefaultAction, DispatchInput, Nothing}; use servo_util::str::DOMString; use string_cache::Atom; @@ -51,9 +55,8 @@ pub struct HTMLInputElement { htmlelement: HTMLElement, input_type: Cell, checked: Cell, - uncommitted_value: DOMRefCell>, - value: DOMRefCell>, size: Cell, + textinput: DOMRefCell, } impl HTMLInputElementDerived for EventTarget { @@ -70,9 +73,8 @@ impl HTMLInputElement { htmlelement: HTMLElement::new_inherited(HTMLInputElementTypeId, localName, prefix, document), input_type: Cell::new(InputText), checked: Cell::new(false), - uncommitted_value: DOMRefCell::new(None), - value: DOMRefCell::new(None), size: Cell::new(DEFAULT_INPUT_SIZE), + textinput: DOMRefCell::new(TextInput::new(false, "".to_string())), } } @@ -84,40 +86,55 @@ impl HTMLInputElement { } pub trait LayoutHTMLInputElementHelpers { - unsafe fn get_value_for_layout(&self) -> String; + unsafe fn get_value_for_layout(self) -> String; + unsafe fn get_size_for_layout(self) -> u32; +} + +pub trait RawLayoutHTMLInputElementHelpers { unsafe fn get_size_for_layout(&self) -> u32; } -impl LayoutHTMLInputElementHelpers for HTMLInputElement { +impl LayoutHTMLInputElementHelpers for JS { #[allow(unrooted_must_root)] - unsafe fn get_value_for_layout(&self) -> String { - match self.input_type.get() { + unsafe fn get_value_for_layout(self) -> String { + unsafe fn get_raw_textinput_value(input: JS) -> Option { + let elem: JS = input.transmute_copy(); + if !elem.has_attr_for_layout(&ns!(""), &atom!("value")) { + return None; + } + Some((*input.unsafe_get()).textinput.borrow_for_layout().get_content()) + } + + unsafe fn get_raw_attr_value(input: JS) -> Option { + let elem: JS = input.transmute_copy(); + (*elem.unsafe_get()).get_attr_val_for_layout(&ns!(""), &atom!("value")) + .map(|s| s.to_string()) + } + + match (*self.unsafe_get()).input_type.get() { InputCheckbox | InputRadio => "".to_string(), InputFile | InputImage => "".to_string(), - InputButton(ref default) => self.value.borrow_for_layout().clone() + InputButton(ref default) => get_raw_attr_value(self) .or_else(|| default.map(|v| v.to_string())) .unwrap_or_else(|| "".to_string()), InputPassword => { - let raw = self.value.borrow_for_layout().clone().unwrap_or_else(|| "".to_string()); - String::from_char(raw.len(), '*') + let raw = get_raw_textinput_value(self).unwrap_or_else(|| "".to_string()); + String::from_char(raw.len(), '●') } - _ => self.value.borrow_for_layout().clone().unwrap_or_else(|| "".to_string()), + _ => get_raw_textinput_value(self).unwrap_or_else(|| "".to_string()), } } #[allow(unrooted_must_root)] - unsafe fn get_size_for_layout(&self) -> u32 { - self.size.get() + unsafe fn get_size_for_layout(self) -> u32 { + (*self.unsafe_get()).get_size_for_layout() } } -impl LayoutHTMLInputElementHelpers for JS { - unsafe fn get_value_for_layout(&self) -> String { - (*self.unsafe_get()).get_value_for_layout() - } - +impl RawLayoutHTMLInputElementHelpers for HTMLInputElement { + #[allow(unrooted_must_root)] unsafe fn get_size_for_layout(&self) -> u32 { - (*self.unsafe_get()).get_size_for_layout() + self.size.get() } } @@ -156,7 +173,7 @@ impl<'a> HTMLInputElementMethods for JSRef<'a, HTMLInputElement> { // https://html.spec.whatwg.org/multipage/forms.html#dom-input-value fn Value(self) -> DOMString { - self.value.borrow().clone().unwrap_or("".to_string()) + self.textinput.borrow().get_content() } // https://html.spec.whatwg.org/multipage/forms.html#dom-input-value @@ -309,7 +326,7 @@ impl<'a> VirtualMethods for JSRef<'a, HTMLInputElement> { self.force_relayout(); } &atom!("value") => { - *self.value.borrow_mut() = Some(attr.value().as_slice().to_string()); + self.textinput.borrow_mut().set_content(attr.value().as_slice().to_string()); self.force_relayout(); } &atom!("name") => { @@ -353,7 +370,7 @@ impl<'a> VirtualMethods for JSRef<'a, HTMLInputElement> { self.force_relayout(); } &atom!("value") => { - *self.value.borrow_mut() = None; + self.textinput.borrow_mut().set_content("".to_string()); self.force_relayout(); } &atom!("name") => { @@ -418,6 +435,18 @@ impl<'a> VirtualMethods for JSRef<'a, HTMLInputElement> { let doc = document_from_node(*self).root(); doc.request_focus(ElementCast::from_ref(*self)); + } else if "keydown" == event.Type().as_slice() && !event.DefaultPrevented() && + (self.input_type.get() == InputText|| self.input_type.get() == InputPassword) { + let keyevent: Option> = KeyboardEventCast::to_ref(event); + keyevent.map(|event| { + match self.textinput.borrow_mut().handle_keydown(event) { + TriggerDefaultAction => (), + DispatchInput => { + self.force_relayout(); + } + Nothing => (), + } + }); } } } diff --git a/components/script/dom/keyboardevent.rs b/components/script/dom/keyboardevent.rs index 2c8f5e49a10..a6b488d947e 100644 --- a/components/script/dom/keyboardevent.rs +++ b/components/script/dom/keyboardevent.rs @@ -3,7 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use dom::bindings::codegen::Bindings::KeyboardEventBinding; -use dom::bindings::codegen::Bindings::KeyboardEventBinding::KeyboardEventMethods; +use dom::bindings::codegen::Bindings::KeyboardEventBinding::{KeyboardEventMethods, KeyboardEventConstants}; use dom::bindings::codegen::Bindings::UIEventBinding::UIEventMethods; use dom::bindings::codegen::InheritTypes::{UIEventCast, KeyboardEventDerived}; use dom::bindings::error::Fallible; @@ -111,22 +111,431 @@ impl KeyboardEvent { Ok(event) } - pub fn key_properties(key: constellation_msg::Key) -> KeyEventProperties { - match key { - _ => KeyEventProperties { - key: "".to_string(), - code: "".to_string(), - location: 0, - char_code: None, - key_code: 0, + pub fn key_properties(key: constellation_msg::Key, mods: constellation_msg::KeyModifiers) + -> KeyEventProperties { + KeyEventProperties { + key: key_value(key, mods), + code: code_value(key), + location: key_location(key), + char_code: key_char_code(key, mods), + key_code: key_key_code(key), } - } + } +} + +// https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3Events-key.html +fn key_value(key: constellation_msg::Key, mods: constellation_msg::KeyModifiers) -> &'static str { + let shift = mods.contains(constellation_msg::SHIFT); + match key { + constellation_msg::KeySpace => " ", + constellation_msg::KeyApostrophe if shift => "\"", + constellation_msg::KeyApostrophe => "'", + constellation_msg::KeyComma if shift => "<", + constellation_msg::KeyComma => ",", + constellation_msg::KeyMinus if shift => "_", + constellation_msg::KeyMinus => "-", + constellation_msg::KeyPeriod if shift => ">", + constellation_msg::KeyPeriod => ".", + constellation_msg::KeySlash if shift => "?", + constellation_msg::KeySlash => "/", + constellation_msg::Key0 if shift => ")", + constellation_msg::Key0 => "0", + constellation_msg::Key1 if shift => "!", + constellation_msg::Key1 => "1", + constellation_msg::Key2 if shift => "@", + constellation_msg::Key2 => "2", + constellation_msg::Key3 if shift => "#", + constellation_msg::Key3 => "3", + constellation_msg::Key4 if shift => "$", + constellation_msg::Key4 => "4", + constellation_msg::Key5 if shift => "%", + constellation_msg::Key5 => "5", + constellation_msg::Key6 if shift => "^", + constellation_msg::Key6 => "6", + constellation_msg::Key7 if shift => "&", + constellation_msg::Key7 => "7", + constellation_msg::Key8 if shift => "*", + constellation_msg::Key8 => "8", + constellation_msg::Key9 if shift => "(", + constellation_msg::Key9 => "9", + constellation_msg::KeySemicolon if shift => ":", + constellation_msg::KeySemicolon => ";", + constellation_msg::KeyEqual if shift => "+", + constellation_msg::KeyEqual => "=", + constellation_msg::KeyA if shift => "A", + constellation_msg::KeyA => "a", + constellation_msg::KeyB if shift => "B", + constellation_msg::KeyB => "b", + constellation_msg::KeyC if shift => "C", + constellation_msg::KeyC => "c", + constellation_msg::KeyD if shift => "D", + constellation_msg::KeyD => "d", + constellation_msg::KeyE if shift => "E", + constellation_msg::KeyE => "e", + constellation_msg::KeyF if shift => "F", + constellation_msg::KeyF => "f", + constellation_msg::KeyG if shift => "G", + constellation_msg::KeyG => "g", + constellation_msg::KeyH if shift => "H", + constellation_msg::KeyH => "h", + constellation_msg::KeyI if shift => "I", + constellation_msg::KeyI => "i", + constellation_msg::KeyJ if shift => "J", + constellation_msg::KeyJ => "j", + constellation_msg::KeyK if shift => "K", + constellation_msg::KeyK => "k", + constellation_msg::KeyL if shift => "L", + constellation_msg::KeyL => "l", + constellation_msg::KeyM if shift => "M", + constellation_msg::KeyM => "m", + constellation_msg::KeyN if shift => "N", + constellation_msg::KeyN => "n", + constellation_msg::KeyO if shift => "O", + constellation_msg::KeyO => "o", + constellation_msg::KeyP if shift => "P", + constellation_msg::KeyP => "p", + constellation_msg::KeyQ if shift => "Q", + constellation_msg::KeyQ => "q", + constellation_msg::KeyR if shift => "R", + constellation_msg::KeyR => "r", + constellation_msg::KeyS if shift => "S", + constellation_msg::KeyS => "s", + constellation_msg::KeyT if shift => "T", + constellation_msg::KeyT => "t", + constellation_msg::KeyU if shift => "U", + constellation_msg::KeyU => "u", + constellation_msg::KeyV if shift => "V", + constellation_msg::KeyV => "v", + constellation_msg::KeyW if shift => "W", + constellation_msg::KeyW => "w", + constellation_msg::KeyX if shift => "X", + constellation_msg::KeyX => "x", + constellation_msg::KeyY if shift => "Y", + constellation_msg::KeyY => "y", + constellation_msg::KeyZ if shift => "Z", + constellation_msg::KeyZ => "z", + constellation_msg::KeyLeftBracket if shift => "{", + constellation_msg::KeyLeftBracket => "{", + constellation_msg::KeyBackslash if shift => "|", + constellation_msg::KeyBackslash => "\\", + constellation_msg::KeyRightBracket if shift => "}", + constellation_msg::KeyRightBracket => "]", + constellation_msg::KeyGraveAccent => "Dead", + constellation_msg::KeyWorld1 => "Unidentified", + constellation_msg::KeyWorld2 => "Unidentified", + constellation_msg::KeyEscape => "Escape", + constellation_msg::KeyEnter => "Enter", + constellation_msg::KeyTab => "Tab", + constellation_msg::KeyBackspace => "Backspace", + constellation_msg::KeyInsert => "Insert", + constellation_msg::KeyDelete => "Delete", + constellation_msg::KeyRight => "ArrowRight", + constellation_msg::KeyLeft => "ArrowLeft", + constellation_msg::KeyDown => "ArrowDown", + constellation_msg::KeyUp => "ArrowUp", + constellation_msg::KeyPageUp => "PageUp", + constellation_msg::KeyPageDown => "PageDown", + constellation_msg::KeyHome => "Home", + constellation_msg::KeyEnd => "End", + constellation_msg::KeyCapsLock => "CapsLock", + constellation_msg::KeyScrollLock => "ScrollLock", + constellation_msg::KeyNumLock => "NumLock", + constellation_msg::KeyPrintScreen => "PrintScreen", + constellation_msg::KeyPause => "Pause", + constellation_msg::KeyF1 => "F1", + constellation_msg::KeyF2 => "F2", + constellation_msg::KeyF3 => "F3", + constellation_msg::KeyF4 => "F4", + constellation_msg::KeyF5 => "F5", + constellation_msg::KeyF6 => "F6", + constellation_msg::KeyF7 => "F7", + constellation_msg::KeyF8 => "F8", + constellation_msg::KeyF9 => "F9", + constellation_msg::KeyF10 => "F10", + constellation_msg::KeyF11 => "F11", + constellation_msg::KeyF12 => "F12", + constellation_msg::KeyF13 => "F13", + constellation_msg::KeyF14 => "F14", + constellation_msg::KeyF15 => "F15", + constellation_msg::KeyF16 => "F16", + constellation_msg::KeyF17 => "F17", + constellation_msg::KeyF18 => "F18", + constellation_msg::KeyF19 => "F19", + constellation_msg::KeyF20 => "F20", + constellation_msg::KeyF21 => "F21", + constellation_msg::KeyF22 => "F22", + constellation_msg::KeyF23 => "F23", + constellation_msg::KeyF24 => "F24", + constellation_msg::KeyF25 => "F25", + constellation_msg::KeyKp0 => "0", + constellation_msg::KeyKp1 => "1", + constellation_msg::KeyKp2 => "2", + constellation_msg::KeyKp3 => "3", + constellation_msg::KeyKp4 => "4", + constellation_msg::KeyKp5 => "5", + constellation_msg::KeyKp6 => "6", + constellation_msg::KeyKp7 => "7", + constellation_msg::KeyKp8 => "8", + constellation_msg::KeyKp9 => "9", + constellation_msg::KeyKpDecimal => ".", + constellation_msg::KeyKpDivide => "/", + constellation_msg::KeyKpMultiply => "*", + constellation_msg::KeyKpSubtract => "-", + constellation_msg::KeyKpAdd => "+", + constellation_msg::KeyKpEnter => "Enter", + constellation_msg::KeyKpEqual => "=", + constellation_msg::KeyLeftShift => "Shift", + constellation_msg::KeyLeftControl => "Control", + constellation_msg::KeyLeftAlt => "Alt", + constellation_msg::KeyLeftSuper => "Super", + constellation_msg::KeyRightShift => "Shift", + constellation_msg::KeyRightControl => "Control", + constellation_msg::KeyRightAlt => "Alt", + constellation_msg::KeyRightSuper => "Super", + constellation_msg::KeyMenu => "ContextMenu", + } +} + +// https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3Events-code.html +fn code_value(key: constellation_msg::Key) -> &'static str { + match key { + constellation_msg::KeySpace => "Space", + constellation_msg::KeyApostrophe => "Quote", + constellation_msg::KeyComma => "Comma", + constellation_msg::KeyMinus => "Minus", + constellation_msg::KeyPeriod => "Period", + constellation_msg::KeySlash => "Slash", + constellation_msg::Key0 => "Digit0", + constellation_msg::Key1 => "Digit1", + constellation_msg::Key2 => "Digit2", + constellation_msg::Key3 => "Digit3", + constellation_msg::Key4 => "Digit4", + constellation_msg::Key5 => "Digit5", + constellation_msg::Key6 => "Digit6", + constellation_msg::Key7 => "Digit7", + constellation_msg::Key8 => "Digit8", + constellation_msg::Key9 => "Digit9", + constellation_msg::KeySemicolon => "Semicolon", + constellation_msg::KeyEqual => "Equals", + constellation_msg::KeyA => "KeyA", + constellation_msg::KeyB => "KeyB", + constellation_msg::KeyC => "KeyC", + constellation_msg::KeyD => "KeyD", + constellation_msg::KeyE => "KeyE", + constellation_msg::KeyF => "KeyF", + constellation_msg::KeyG => "KeyG", + constellation_msg::KeyH => "KeyH", + constellation_msg::KeyI => "KeyI", + constellation_msg::KeyJ => "KeyJ", + constellation_msg::KeyK => "KeyK", + constellation_msg::KeyL => "KeyL", + constellation_msg::KeyM => "KeyM", + constellation_msg::KeyN => "KeyN", + constellation_msg::KeyO => "KeyO", + constellation_msg::KeyP => "KeyP", + constellation_msg::KeyQ => "KeyQ", + constellation_msg::KeyR => "KeyR", + constellation_msg::KeyS => "KeyS", + constellation_msg::KeyT => "KeyT", + constellation_msg::KeyU => "KeyU", + constellation_msg::KeyV => "KeyV", + constellation_msg::KeyW => "KeyW", + constellation_msg::KeyX => "KeyX", + constellation_msg::KeyY => "KeyY", + constellation_msg::KeyZ => "KeyZ", + constellation_msg::KeyLeftBracket => "BracketLeft", + constellation_msg::KeyBackslash => "Backslash", + constellation_msg::KeyRightBracket => "BracketRight", + + constellation_msg::KeyGraveAccent | + constellation_msg::KeyWorld1 | + constellation_msg::KeyWorld2 => panic!("unknown char code for {}", key), + + constellation_msg::KeyEscape => "Escape", + constellation_msg::KeyEnter => "Enter", + constellation_msg::KeyTab => "Tab", + constellation_msg::KeyBackspace => "Backspace", + constellation_msg::KeyInsert => "Insert", + constellation_msg::KeyDelete => "Delete", + constellation_msg::KeyRight => "ArrowRight", + constellation_msg::KeyLeft => "ArrowLeft", + constellation_msg::KeyDown => "ArrowDown", + constellation_msg::KeyUp => "ArrowUp", + constellation_msg::KeyPageUp => "PageUp", + constellation_msg::KeyPageDown => "PageDown", + constellation_msg::KeyHome => "Home", + constellation_msg::KeyEnd => "End", + constellation_msg::KeyCapsLock => "CapsLock", + constellation_msg::KeyScrollLock => "ScrollLock", + constellation_msg::KeyNumLock => "NumLock", + constellation_msg::KeyPrintScreen => "PrintScreen", + constellation_msg::KeyPause => "Pause", + constellation_msg::KeyF1 => "F1", + constellation_msg::KeyF2 => "F2", + constellation_msg::KeyF3 => "F3", + constellation_msg::KeyF4 => "F4", + constellation_msg::KeyF5 => "F5", + constellation_msg::KeyF6 => "F6", + constellation_msg::KeyF7 => "F7", + constellation_msg::KeyF8 => "F8", + constellation_msg::KeyF9 => "F9", + constellation_msg::KeyF10 => "F10", + constellation_msg::KeyF11 => "F11", + constellation_msg::KeyF12 => "F12", + constellation_msg::KeyF13 => "F13", + constellation_msg::KeyF14 => "F14", + constellation_msg::KeyF15 => "F15", + constellation_msg::KeyF16 => "F16", + constellation_msg::KeyF17 => "F17", + constellation_msg::KeyF18 => "F18", + constellation_msg::KeyF19 => "F19", + constellation_msg::KeyF20 => "F20", + constellation_msg::KeyF21 => "F21", + constellation_msg::KeyF22 => "F22", + constellation_msg::KeyF23 => "F23", + constellation_msg::KeyF24 => "F24", + constellation_msg::KeyF25 => "F25", + constellation_msg::KeyKp0 => "Numpad0", + constellation_msg::KeyKp1 => "Numpad1", + constellation_msg::KeyKp2 => "Numpad2", + constellation_msg::KeyKp3 => "Numpad3", + constellation_msg::KeyKp4 => "Numpad4", + constellation_msg::KeyKp5 => "Numpad5", + constellation_msg::KeyKp6 => "Numpad6", + constellation_msg::KeyKp7 => "Numpad7", + constellation_msg::KeyKp8 => "Numpad8", + constellation_msg::KeyKp9 => "Numpad9", + constellation_msg::KeyKpDecimal => "NumpadDecimal", + constellation_msg::KeyKpDivide => "NumpadDivide", + constellation_msg::KeyKpMultiply => "NumpadMultiply", + constellation_msg::KeyKpSubtract => "NumpadSubtract", + constellation_msg::KeyKpAdd => "NumpadAdd", + constellation_msg::KeyKpEnter => "NumpadEnter", + constellation_msg::KeyKpEqual => "NumpadEquals", + constellation_msg::KeyLeftShift | constellation_msg::KeyRightShift => "Shift", + constellation_msg::KeyLeftControl | constellation_msg::KeyRightControl => "Control", + constellation_msg::KeyLeftAlt | constellation_msg::KeyRightAlt => "Alt", + constellation_msg::KeyLeftSuper | constellation_msg::KeyRightSuper => "Super", + constellation_msg::KeyMenu => "Menu", + } +} + +fn key_location(key: constellation_msg::Key) -> u32 { + match key { + constellation_msg::KeyKp0 | constellation_msg::KeyKp1 | constellation_msg::KeyKp2 | + constellation_msg::KeyKp3 | constellation_msg::KeyKp4 | constellation_msg::KeyKp5 | + constellation_msg::KeyKp6 | constellation_msg::KeyKp7 | constellation_msg::KeyKp8 | + constellation_msg::KeyKp9 | constellation_msg::KeyKpDecimal | + constellation_msg::KeyKpDivide | constellation_msg::KeyKpMultiply | + constellation_msg::KeyKpSubtract | constellation_msg::KeyKpAdd | + constellation_msg::KeyKpEnter | constellation_msg::KeyKpEqual => + KeyboardEventConstants::DOM_KEY_LOCATION_NUMPAD, + + constellation_msg::KeyLeftShift | constellation_msg::KeyLeftAlt | + constellation_msg::KeyLeftControl | constellation_msg::KeyLeftSuper => + KeyboardEventConstants::DOM_KEY_LOCATION_LEFT, + + constellation_msg::KeyRightShift | constellation_msg::KeyRightAlt | + constellation_msg::KeyRightControl | constellation_msg::KeyRightSuper => + KeyboardEventConstants::DOM_KEY_LOCATION_RIGHT, + + _ => KeyboardEventConstants::DOM_KEY_LOCATION_STANDARD, + } +} + +// https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#widl-KeyboardEvent-charCode +fn key_char_code(key: constellation_msg::Key, mods: constellation_msg::KeyModifiers) -> Option { + let key = key_value(key, mods); + if key.len() == 1 { + Some(key.char_at(0) as u32) + } else { + None + } +} + +// https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#legacy-key-models +fn key_key_code(key: constellation_msg::Key) -> u32 { + match key { + // https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#legacy-key-models + constellation_msg::KeyBackspace => 8, + constellation_msg::KeyTab => 9, + constellation_msg::KeyEnter => 13, + constellation_msg::KeyLeftShift | constellation_msg::KeyRightShift => 16, + constellation_msg::KeyLeftControl | constellation_msg::KeyRightControl => 17, + constellation_msg::KeyLeftAlt | constellation_msg::KeyRightAlt => 18, + constellation_msg::KeyCapsLock => 20, + constellation_msg::KeyEscape => 27, + constellation_msg::KeySpace => 32, + constellation_msg::KeyPageUp => 33, + constellation_msg::KeyPageDown => 34, + constellation_msg::KeyEnd => 35, + constellation_msg::KeyHome => 36, + constellation_msg::KeyLeft => 37, + constellation_msg::KeyUp => 38, + constellation_msg::KeyRight => 39, + constellation_msg::KeyDown => 40, + constellation_msg::KeyDelete => 46, + + // https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#optionally-fixed-virtual-key-codes + constellation_msg::KeySemicolon => 186, + constellation_msg::KeyEqual => 187, + constellation_msg::KeyComma => 188, + constellation_msg::KeyMinus => 189, + constellation_msg::KeyPeriod => 190, + constellation_msg::KeySlash => 191, + constellation_msg::KeyLeftBracket => 219, + constellation_msg::KeyBackslash => 220, + constellation_msg::KeyRightBracket => 221, + constellation_msg::KeyApostrophe => 222, + + //§ B.2.1.3 + constellation_msg::Key0 | + constellation_msg::Key1 | + constellation_msg::Key2 | + constellation_msg::Key3 | + constellation_msg::Key4 | + constellation_msg::Key5 | + constellation_msg::Key6 | + constellation_msg::Key7 | + constellation_msg::Key8 | + constellation_msg::Key9 => key as u32 - constellation_msg::Key0 as u32 + '0' as u32, + + //§ B.2.1.4 + constellation_msg::KeyA | + constellation_msg::KeyB | + constellation_msg::KeyC | + constellation_msg::KeyD | + constellation_msg::KeyE | + constellation_msg::KeyF | + constellation_msg::KeyG | + constellation_msg::KeyH | + constellation_msg::KeyI | + constellation_msg::KeyJ | + constellation_msg::KeyK | + constellation_msg::KeyL | + constellation_msg::KeyM | + constellation_msg::KeyN | + constellation_msg::KeyO | + constellation_msg::KeyP | + constellation_msg::KeyQ | + constellation_msg::KeyR | + constellation_msg::KeyS | + constellation_msg::KeyT | + constellation_msg::KeyU | + constellation_msg::KeyV | + constellation_msg::KeyW | + constellation_msg::KeyX | + constellation_msg::KeyY | + constellation_msg::KeyZ => key as u32 - constellation_msg::KeyA as u32 + 'A' as u32, + + //§ B.2.1.8 + _ => 0 } } pub struct KeyEventProperties { - pub key: DOMString, - pub code: DOMString, + pub key: &'static str, + pub code: &'static str, pub location: u32, pub char_code: Option, pub key_code: u32, diff --git a/components/script/lib.rs b/components/script/lib.rs index 959c245b63f..123417e9429 100644 --- a/components/script/lib.rs +++ b/components/script/lib.rs @@ -221,3 +221,4 @@ pub mod layout_interface; pub mod page; pub mod script_task; mod timers; +pub mod textinput; diff --git a/components/script/script_task.rs b/components/script/script_task.rs index 684ae327ad2..5ed9023b5e5 100644 --- a/components/script/script_task.rs +++ b/components/script/script_task.rs @@ -49,7 +49,7 @@ use servo_msg::compositor_msg::{FinishedLoading, LayerId, Loading}; use servo_msg::compositor_msg::{ScriptListener}; use servo_msg::constellation_msg::{ConstellationChan, LoadCompleteMsg, LoadUrlMsg, NavigationDirection}; use servo_msg::constellation_msg::{LoadData, PipelineId, Failure, FailureMsg, WindowSizeData, Key, KeyState}; -use servo_msg::constellation_msg::{KeyModifiers, Super, Shift, Control, Alt, Repeated, Pressed}; +use servo_msg::constellation_msg::{KeyModifiers, SUPER, SHIFT, CONTROL, ALT, Repeated, Pressed}; use servo_msg::constellation_msg::{Released}; use servo_msg::constellation_msg; use servo_net::image_cache_task::ImageCacheTask; @@ -923,7 +923,6 @@ impl ScriptTask { state: KeyState, modifiers: KeyModifiers, pipeline_id: PipelineId) { - println!("key {} is {}", key as int, state as int); let page = get_page(&*self.page.borrow(), pipeline_id); let frame = page.frame(); let window = frame.as_ref().unwrap().window.root(); @@ -937,10 +936,10 @@ impl ScriptTask { (&None, &None) => EventTargetCast::from_ref(*window), }; - let ctrl = modifiers.contains(Control); - let alt = modifiers.contains(Alt); - let shift = modifiers.contains(Shift); - let meta = modifiers.contains(Super); + let ctrl = modifiers.contains(CONTROL); + let alt = modifiers.contains(ALT); + let shift = modifiers.contains(SHIFT); + let meta = modifiers.contains(SUPER); let is_composing = false; let is_repeating = state == Repeated; @@ -949,21 +948,24 @@ impl ScriptTask { Released => "keyup", }.to_string(); - let props = KeyboardEvent::key_properties(key); + let props = KeyboardEvent::key_properties(key, modifiers); let event = KeyboardEvent::new(*window, ev_type, true, true, Some(*window), 0, - props.key.clone(), props.code.clone(), props.location, + props.key.to_string(), props.code.to_string(), props.location, is_repeating, is_composing, ctrl, alt, shift, meta, - props.char_code, props.key_code).root(); + None, props.key_code).root(); 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(); + 0, props.key.to_string(), props.code.to_string(), + props.location, is_repeating, is_composing, + ctrl, alt, shift, meta, + props.char_code, 0).root(); let _ = target.DispatchEvent(EventCast::from_ref(*event)); } + + window.flush_layout(); } /// The entry point for content to notify that a new load has been requested diff --git a/components/script/textinput.rs b/components/script/textinput.rs new file mode 100644 index 00000000000..0a570e15810 --- /dev/null +++ b/components/script/textinput.rs @@ -0,0 +1,259 @@ +/* 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/. */ + +//! Common handling of keyboard input and state management for text input controls + +use dom::bindings::codegen::Bindings::KeyboardEventBinding::KeyboardEventMethods; +use dom::bindings::js::JSRef; +use dom::keyboardevent::KeyboardEvent; +use servo_util::str::DOMString; + +use std::cmp::{min, max}; +use std::default::Default; + +#[jstraceable] +struct TextPoint { + line: uint, + index: uint, +} + +#[jstraceable] +pub struct TextInput { + /// Current text input content, split across lines without trailing '\n' + lines: Vec, + /// Current cursor input point + edit_point: TextPoint, + /// Selection range, beginning and end point that can span multiple lines. + _selection: Option<(TextPoint, TextPoint)>, + /// Is this ia multiline input? + multiline: bool, +} + +pub enum KeyReaction { + TriggerDefaultAction, + DispatchInput, + Nothing, +} + +impl Default for TextPoint { + fn default() -> TextPoint { + TextPoint { + line: 0, + index: 0, + } + } +} + +impl TextInput { + pub fn new(multiline: bool, initial: DOMString) -> TextInput { + let mut i = TextInput { + lines: vec!(), + edit_point: Default::default(), + _selection: None, + multiline: multiline, + }; + i.set_content(initial); + i + } + + fn get_current_line(&self) -> &DOMString { + &self.lines[self.edit_point.line] + } + + fn insert_char(&mut self, ch: char) { + //TODO: handle replacing selection with character + let new_line = { + let prefix = self.get_current_line().as_slice().slice_chars(0, self.edit_point.index); + let suffix = self.get_current_line().as_slice().slice_chars(self.edit_point.index, + self.current_line_length()); + let mut new_line = prefix.to_string(); + new_line.push(ch); + new_line.push_str(suffix.as_slice()); + new_line + }; + + self.lines[self.edit_point.line] = new_line; + self.edit_point.index += 1; + } + + fn delete_char(&mut self, forward: bool) { + //TODO: handle deleting selection + let prefix_end = if forward { + self.edit_point.index + } else { + //TODO: handle backspacing from position 0 of current line + if self.multiline { + assert!(self.edit_point.index > 0); + } else if self.edit_point.index == 0 { + return; + } + self.edit_point.index - 1 + }; + let suffix_start = if forward { + let is_eol = self.edit_point.index == self.current_line_length() - 1; + if self.multiline { + //TODO: handle deleting from end position of current line + assert!(!is_eol); + } else if is_eol { + return; + } + self.edit_point.index + 1 + } else { + self.edit_point.index + }; + + let new_line = { + let prefix = self.get_current_line().as_slice().slice_chars(0, prefix_end); + let suffix = self.get_current_line().as_slice().slice_chars(suffix_start, + self.current_line_length()); + let mut new_line = prefix.to_string(); + new_line.push_str(suffix); + new_line + }; + + self.lines[self.edit_point.line] = new_line; + + if !forward { + self.adjust_horizontal(-1); + } + } + + fn current_line_length(&self) -> uint { + self.lines[self.edit_point.line].len() + } + + fn adjust_vertical(&mut self, adjust: int) { + if !self.multiline { + return; + } + + if adjust < 0 && self.edit_point.line as int + adjust < 0 { + self.edit_point.index = 0; + self.edit_point.line = 0; + return; + } else if adjust > 0 && self.edit_point.line >= min(0, self.lines.len() - adjust as uint) { + self.edit_point.index = self.current_line_length(); + self.edit_point.line = self.lines.len() - 1; + return; + } + + self.edit_point.line = (self.edit_point.line as int + adjust) as uint; + self.edit_point.index = min(self.current_line_length(), self.edit_point.index); + } + + fn adjust_horizontal(&mut self, adjust: int) { + if adjust < 0 { + if self.multiline { + let remaining = self.edit_point.index; + if adjust.abs() as uint > remaining { + self.edit_point.index = 0; + self.adjust_vertical(-1); + self.edit_point.index = self.current_line_length(); + self.adjust_horizontal(adjust + remaining as int); + } else { + self.edit_point.index = (self.edit_point.index as int + adjust) as uint; + } + } else { + self.edit_point.index = max(0, self.edit_point.index as int + adjust) as uint; + } + } else { + if self.multiline { + let remaining = self.current_line_length() - self.edit_point.index; + if adjust as uint > remaining { + self.edit_point.index = 0; + self.adjust_vertical(1); + self.adjust_horizontal(adjust - remaining as int); + } else { + self.edit_point.index += adjust as uint; + } + } else { + self.edit_point.index = min(self.current_line_length(), + self.edit_point.index + adjust as uint); + } + } + } + + fn handle_return(&mut self) -> KeyReaction { + if !self.multiline { + return TriggerDefaultAction; + } + + //TODO: support replacing selection with newline + let prefix = self.get_current_line().as_slice().slice_chars(0, self.edit_point.index).to_string(); + let suffix = self.get_current_line().as_slice().slice_chars(self.edit_point.index, + self.current_line_length()).to_string(); + self.lines[self.edit_point.line] = prefix; + self.lines.insert(self.edit_point.line + 1, suffix); + return DispatchInput; + } + + pub fn handle_keydown(&mut self, event: JSRef) -> KeyReaction { + match event.Key().as_slice() { + c if c.len() == 1 => { + self.insert_char(c.char_at(0)); + return DispatchInput; + } + "Space" => { + self.insert_char(' '); + DispatchInput + } + "Delete" => { + self.delete_char(true); + DispatchInput + } + "Backspace" => { + self.delete_char(false); + DispatchInput + } + "ArrowLeft" => { + self.adjust_horizontal(-1); + Nothing + } + "ArrowRight" => { + self.adjust_horizontal(1); + Nothing + } + "ArrowUp" => { + self.adjust_vertical(-1); + Nothing + } + "ArrowDown" => { + self.adjust_vertical(1); + Nothing + } + "Enter" => self.handle_return(), + "Home" => { + self.edit_point.index = 0; + Nothing + } + "End" => { + self.edit_point.index = self.current_line_length(); + Nothing + } + "Tab" => TriggerDefaultAction, + _ => Nothing, + } + } + + pub fn get_content(&self) -> DOMString { + let mut content = "".to_string(); + for (i, line) in self.lines.iter().enumerate() { + content.push_str(line.as_slice()); + if i < self.lines.len() - 1 { + content.push('\n'); + } + } + content + } + + pub fn set_content(&mut self, content: DOMString) { + self.lines = if self.multiline { + content.as_slice().split('\n').map(|s| s.to_string()).collect() + } else { + vec!(content) + }; + self.edit_point.line = min(self.edit_point.line, self.lines.len() - 1); + self.edit_point.index = min(self.edit_point.index, self.current_line_length() - 1); + } +} diff --git a/ports/glfw/window.rs b/ports/glfw/window.rs index 2e943197370..a13bf571a6d 100644 --- a/ports/glfw/window.rs +++ b/ports/glfw/window.rs @@ -440,16 +440,16 @@ extern "C" fn on_framebuffer_size(_glfw_window: *mut glfw::ffi::GLFWwindow, fn glfw_mods_to_script_mods(mods: glfw::Modifiers) -> constellation_msg::KeyModifiers { let mut result = constellation_msg::KeyModifiers::from_bits(0).unwrap(); if mods.contains(glfw::Shift) { - result.insert(constellation_msg::Shift); + result.insert(constellation_msg::SHIFT); } if mods.contains(glfw::Alt) { - result.insert(constellation_msg::Alt); + result.insert(constellation_msg::ALT); } if mods.contains(glfw::Control) { - result.insert(constellation_msg::Control); + result.insert(constellation_msg::CONTROL); } if mods.contains(glfw::Super) { - result.insert(constellation_msg::Super); + result.insert(constellation_msg::SUPER); } result } From cbed5da9fa963958a42cf088baa8a68fea13a654 Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Mon, 6 Oct 2014 11:52:11 -0400 Subject: [PATCH 07/10] Flush layout after executing timers. --- components/script/dom/window.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index b4318b69fa3..a3664207b85 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -382,6 +382,7 @@ impl<'a> WindowHelpers for JSRef<'a, Window> { fn handle_fire_timer(self, timer_id: TimerId, cx: *mut JSContext) { let this_value = self.reflector().get_jsobject(); self.timers.fire_timer(timer_id, this_value, cx); + self.flush_layout(); } } From 89a27dd11a5767423535ec0610e2cb560c2bb3a3 Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Mon, 6 Oct 2014 12:17:12 -0400 Subject: [PATCH 08/10] More documentation. --- components/script/dom/htmlinputelement.rs | 6 ++- components/script/textinput.rs | 48 ++++++++++++++++++++--- 2 files changed, 46 insertions(+), 8 deletions(-) diff --git a/components/script/dom/htmlinputelement.rs b/components/script/dom/htmlinputelement.rs index 547c0f57aa2..680b77cd423 100644 --- a/components/script/dom/htmlinputelement.rs +++ b/components/script/dom/htmlinputelement.rs @@ -26,7 +26,7 @@ use dom::keyboardevent::KeyboardEvent; use dom::htmlformelement::{InputElement, FormOwner, HTMLFormElement, HTMLFormElementHelpers, NotFromFormSubmitMethod}; use dom::node::{DisabledStateHelpers, Node, NodeHelpers, ElementNodeTypeId, document_from_node, window_from_node}; use dom::virtualmethods::VirtualMethods; -use textinput::{TextInput, TriggerDefaultAction, DispatchInput, Nothing}; +use textinput::{Single, TextInput, TriggerDefaultAction, DispatchInput, Nothing}; use servo_util::str::DOMString; use string_cache::Atom; @@ -74,7 +74,7 @@ impl HTMLInputElement { input_type: Cell::new(InputText), checked: Cell::new(false), size: Cell::new(DEFAULT_INPUT_SIZE), - textinput: DOMRefCell::new(TextInput::new(false, "".to_string())), + textinput: DOMRefCell::new(TextInput::new(Single, "".to_string())), } } @@ -433,6 +433,8 @@ impl<'a> VirtualMethods for JSRef<'a, HTMLInputElement> { _ => {} } + //TODO: set the editing position for text inputs + let doc = document_from_node(*self).root(); doc.request_focus(ElementCast::from_ref(*self)); } else if "keydown" == event.Type().as_slice() && !event.DefaultPrevented() && diff --git a/components/script/textinput.rs b/components/script/textinput.rs index 0a570e15810..6dc24962aea 100644 --- a/components/script/textinput.rs +++ b/components/script/textinput.rs @@ -14,10 +14,13 @@ use std::default::Default; #[jstraceable] struct TextPoint { + /// 0-based line number line: uint, + /// 0-based column number index: uint, } +/// Encapsulated state for handling keyboard input in a single or multiline text input control. #[jstraceable] pub struct TextInput { /// Current text input content, split across lines without trailing '\n' @@ -26,10 +29,11 @@ pub struct TextInput { edit_point: TextPoint, /// Selection range, beginning and end point that can span multiple lines. _selection: Option<(TextPoint, TextPoint)>, - /// Is this ia multiline input? + /// Is this a multiline input? multiline: bool, } +/// Resulting action to be taken by the owner of a text input that is handling an event. pub enum KeyReaction { TriggerDefaultAction, DispatchInput, @@ -45,22 +49,39 @@ impl Default for TextPoint { } } +/// Control whether this control should allow multiple lines. +#[deriving(PartialEq)] +pub enum Lines { + Single, + Multiple, +} + +/// The direction in which to delete a character. +#[deriving(PartialEq)] +enum DeleteDir { + Forward, + Backward +} + impl TextInput { - pub fn new(multiline: bool, initial: DOMString) -> TextInput { + /// Instantiate a new text input control + pub fn new(lines: Lines, initial: DOMString) -> TextInput { let mut i = TextInput { lines: vec!(), edit_point: Default::default(), _selection: None, - multiline: multiline, + multiline: lines == Multiple, }; i.set_content(initial); i } + /// Return the current line under the editing point fn get_current_line(&self) -> &DOMString { &self.lines[self.edit_point.line] } + /// Insert a character at the current editing point fn insert_char(&mut self, ch: char) { //TODO: handle replacing selection with character let new_line = { @@ -77,7 +98,10 @@ impl TextInput { self.edit_point.index += 1; } - fn delete_char(&mut self, forward: bool) { + /// Remove a character at the current editing point + fn delete_char(&mut self, dir: DeleteDir) { + let forward = dir == Forward; + //TODO: handle deleting selection let prefix_end = if forward { self.edit_point.index @@ -119,10 +143,13 @@ impl TextInput { } } + /// Return the length of the current line under the editing point. fn current_line_length(&self) -> uint { self.lines[self.edit_point.line].len() } + /// Adjust the editing point position by a given of lines. The resulting column is + /// as close to the original column position as possible. fn adjust_vertical(&mut self, adjust: int) { if !self.multiline { return; @@ -142,6 +169,9 @@ impl TextInput { self.edit_point.index = min(self.current_line_length(), self.edit_point.index); } + /// Adjust the editing point position by a given number of columns. If the adjustment + /// requested is larger than is available in the current line, the editing point is + /// adjusted vertically and the process repeats with the remaining adjustment requested. fn adjust_horizontal(&mut self, adjust: int) { if adjust < 0 { if self.multiline { @@ -174,6 +204,7 @@ impl TextInput { } } + /// Deal with a newline input. fn handle_return(&mut self) -> KeyReaction { if !self.multiline { return TriggerDefaultAction; @@ -188,8 +219,10 @@ impl TextInput { return DispatchInput; } + /// Process a given `KeyboardEvent` and return an action for the caller to execute. pub fn handle_keydown(&mut self, event: JSRef) -> KeyReaction { match event.Key().as_slice() { + // printable characters have single-character key values c if c.len() == 1 => { self.insert_char(c.char_at(0)); return DispatchInput; @@ -199,11 +232,11 @@ impl TextInput { DispatchInput } "Delete" => { - self.delete_char(true); + self.delete_char(Forward); DispatchInput } "Backspace" => { - self.delete_char(false); + self.delete_char(Backward); DispatchInput } "ArrowLeft" => { @@ -236,6 +269,7 @@ impl TextInput { } } + /// Get the current contents of the text input. Multiple lines are joined by \n. pub fn get_content(&self) -> DOMString { let mut content = "".to_string(); for (i, line) in self.lines.iter().enumerate() { @@ -247,6 +281,8 @@ impl TextInput { content } + /// Set the current contents of the text input. If this is control supports multiple lines, + /// any \n encountered will be stripped and force a new logical line. pub fn set_content(&mut self, content: DOMString) { self.lines = if self.multiline { content.as_slice().split('\n').map(|s| s.to_string()).collect() From 642a3592c7ea1b82e3a3a660370b9871aa5b5e96 Mon Sep 17 00:00:00 2001 From: Keegan McAllister Date: Mon, 3 Nov 2014 17:21:18 -0800 Subject: [PATCH 09/10] Fix interfaces test --- .../dom/bindings/codegen/parser/module.patch | 9 + components/script/dom/htmlinputelement.rs | 2 +- components/script/dom/keyboardevent.rs | 14 +- components/script/script_task.rs | 19 +- components/script/textinput.rs | 10 +- ports/glfw/window.rs | 252 +++++++++--------- tests/content/test_interfaces.html | 1 + 7 files changed, 169 insertions(+), 138 deletions(-) diff --git a/components/script/dom/bindings/codegen/parser/module.patch b/components/script/dom/bindings/codegen/parser/module.patch index 977947b4c63..f2ed1aff944 100644 --- a/components/script/dom/bindings/codegen/parser/module.patch +++ b/components/script/dom/bindings/codegen/parser/module.patch @@ -1,5 +1,14 @@ --- WebIDL.py +++ WebIDL.py +@@ -1422,6 +1422,9 @@ class IDLDictionary(IDLObjectWithScope): + self.identifier.name, + [member.location] + locations) + ++ def module(self): ++ return self.location.filename().split('/')[-1].split('.webidl')[0] + 'Binding' ++ + def addExtendedAttributes(self, attrs): + assert len(attrs) == 0 @@ -3398,6 +3398,9 @@ class IDLCallbackType(IDLType, IDLObjectWithScope): self._treatNonCallableAsNull = False self._treatNonObjectAsNull = False diff --git a/components/script/dom/htmlinputelement.rs b/components/script/dom/htmlinputelement.rs index 680b77cd423..9c7050ec8a1 100644 --- a/components/script/dom/htmlinputelement.rs +++ b/components/script/dom/htmlinputelement.rs @@ -438,7 +438,7 @@ impl<'a> VirtualMethods for JSRef<'a, HTMLInputElement> { let doc = document_from_node(*self).root(); doc.request_focus(ElementCast::from_ref(*self)); } else if "keydown" == event.Type().as_slice() && !event.DefaultPrevented() && - (self.input_type.get() == InputText|| self.input_type.get() == InputPassword) { + (self.input_type.get() == InputText || self.input_type.get() == InputPassword) { let keyevent: Option> = KeyboardEventCast::to_ref(event); keyevent.map(|event| { match self.textinput.borrow_mut().handle_keydown(event) { diff --git a/components/script/dom/keyboardevent.rs b/components/script/dom/keyboardevent.rs index a6b488d947e..3a097fb54e2 100644 --- a/components/script/dom/keyboardevent.rs +++ b/components/script/dom/keyboardevent.rs @@ -117,8 +117,8 @@ impl KeyboardEvent { key: key_value(key, mods), code: code_value(key), location: key_location(key), - char_code: key_char_code(key, mods), - key_code: key_key_code(key), + char_code: key_charcode(key, mods), + key_code: key_keycode(key), } } } @@ -215,7 +215,7 @@ fn key_value(key: constellation_msg::Key, mods: constellation_msg::KeyModifiers) constellation_msg::KeyZ if shift => "Z", constellation_msg::KeyZ => "z", constellation_msg::KeyLeftBracket if shift => "{", - constellation_msg::KeyLeftBracket => "{", + constellation_msg::KeyLeftBracket => "[", constellation_msg::KeyBackslash if shift => "|", constellation_msg::KeyBackslash => "\\", constellation_msg::KeyRightBracket if shift => "}", @@ -444,7 +444,7 @@ fn key_location(key: constellation_msg::Key) -> u32 { } // https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#widl-KeyboardEvent-charCode -fn key_char_code(key: constellation_msg::Key, mods: constellation_msg::KeyModifiers) -> Option { +fn key_charcode(key: constellation_msg::Key, mods: constellation_msg::KeyModifiers) -> Option { let key = key_value(key, mods); if key.len() == 1 { Some(key.char_at(0) as u32) @@ -454,7 +454,7 @@ fn key_char_code(key: constellation_msg::Key, mods: constellation_msg::KeyModifi } // https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#legacy-key-models -fn key_key_code(key: constellation_msg::Key) -> u32 { +fn key_keycode(key: constellation_msg::Key) -> u32 { match key { // https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#legacy-key-models constellation_msg::KeyBackspace => 8, @@ -601,13 +601,15 @@ impl<'a> KeyboardEventMethods for JSRef<'a, KeyboardEvent> { self.is_composing.get() } + // https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#widl-KeyboardEvent-getModifierState fn GetModifierState(self, keyArg: DOMString) -> bool { match keyArg.as_slice() { "Ctrl" => self.CtrlKey(), "Alt" => self.AltKey(), "Shift" => self.ShiftKey(), "Meta" => self.MetaKey(), - "AltGraph" | "CapsLock" | "NumLock" | "ScrollLock" => false, //FIXME + "AltGraph" | "CapsLock" | "NumLock" | "ScrollLock" | "Accel" | + "Fn" | "FnLock" | "Hyper" | "OS" | "Symbol" | "SymbolLock" => false, //FIXME _ => false, } } diff --git a/components/script/script_task.rs b/components/script/script_task.rs index 5ed9023b5e5..087e3fdf043 100644 --- a/components/script/script_task.rs +++ b/components/script/script_task.rs @@ -9,6 +9,7 @@ use dom::bindings::cell::DOMRefCell; use dom::bindings::codegen::Bindings::DocumentBinding::{DocumentMethods, DocumentReadyStateValues}; use dom::bindings::codegen::Bindings::DOMRectBinding::DOMRectMethods; use dom::bindings::codegen::Bindings::ElementBinding::ElementMethods; +use dom::bindings::codegen::Bindings::EventBinding::EventMethods; use dom::bindings::codegen::Bindings::EventTargetBinding::EventTargetMethods; use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods; use dom::bindings::codegen::InheritTypes::{EventTargetCast, NodeCast, EventCast, ElementCast}; @@ -950,19 +951,25 @@ impl ScriptTask { let props = KeyboardEvent::key_properties(key, modifiers); - let event = KeyboardEvent::new(*window, ev_type, true, true, Some(*window), 0, - props.key.to_string(), props.code.to_string(), props.location, - is_repeating, is_composing, ctrl, alt, shift, meta, - None, props.key_code).root(); - let _ = target.DispatchEvent(EventCast::from_ref(*event)); + let keyevent = KeyboardEvent::new(*window, ev_type, true, true, Some(*window), 0, + props.key.to_string(), props.code.to_string(), + props.location, is_repeating, is_composing, + ctrl, alt, shift, meta, + None, props.key_code).root(); + let event = EventCast::from_ref(*keyevent); + let _ = target.DispatchEvent(event); - if state != Released && props.is_printable() { + // https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#keys-cancelable-keys + if state != Released && props.is_printable() && !event.DefaultPrevented() { + // https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#keypress-event-order let event = KeyboardEvent::new(*window, "keypress".to_string(), true, true, Some(*window), 0, props.key.to_string(), props.code.to_string(), props.location, is_repeating, is_composing, ctrl, alt, shift, meta, props.char_code, 0).root(); let _ = target.DispatchEvent(EventCast::from_ref(*event)); + + // TODO: if keypress event is canceled, prevent firing input events } window.flush_layout(); diff --git a/components/script/textinput.rs b/components/script/textinput.rs index 6dc24962aea..940b9919e76 100644 --- a/components/script/textinput.rs +++ b/components/script/textinput.rs @@ -106,9 +106,11 @@ impl TextInput { let prefix_end = if forward { self.edit_point.index } else { - //TODO: handle backspacing from position 0 of current line if self.multiline { - assert!(self.edit_point.index > 0); + //TODO: handle backspacing from position 0 of current line + if self.edit_point.index == 0 { + return; + } } else if self.edit_point.index == 0 { return; } @@ -118,7 +120,9 @@ impl TextInput { let is_eol = self.edit_point.index == self.current_line_length() - 1; if self.multiline { //TODO: handle deleting from end position of current line - assert!(!is_eol); + if is_eol { + return; + } } else if is_eol { return; } diff --git a/ports/glfw/window.rs b/ports/glfw/window.rs index a13bf571a6d..0fe1b30f4b2 100644 --- a/ports/glfw/window.rs +++ b/ports/glfw/window.rs @@ -454,127 +454,135 @@ fn glfw_mods_to_script_mods(mods: glfw::Modifiers) -> constellation_msg::KeyModi result } +macro_rules! glfw_keys_to_script_keys( + ($key:expr, $($name:ident),+) => ( + match $key { + $(glfw::$name => constellation_msg::$name,)+ + } + ); +) + fn glfw_key_to_script_key(key: glfw::Key) -> constellation_msg::Key { - match key { - glfw::KeySpace => constellation_msg::KeySpace, - glfw::KeyApostrophe => constellation_msg::KeyApostrophe, - glfw::KeyComma => constellation_msg::KeyComma, - glfw::KeyMinus => constellation_msg::KeyMinus, - glfw::KeyPeriod => constellation_msg::KeyPeriod, - glfw::KeySlash => constellation_msg::KeySlash, - glfw::Key0 => constellation_msg::Key0, - glfw::Key1 => constellation_msg::Key1, - glfw::Key2 => constellation_msg::Key2, - glfw::Key3 => constellation_msg::Key3, - glfw::Key4 => constellation_msg::Key4, - glfw::Key5 => constellation_msg::Key5, - glfw::Key6 => constellation_msg::Key6, - glfw::Key7 => constellation_msg::Key7, - glfw::Key8 => constellation_msg::Key8, - glfw::Key9 => constellation_msg::Key9, - glfw::KeySemicolon => constellation_msg::KeySemicolon, - glfw::KeyEqual => constellation_msg::KeyEqual, - glfw::KeyA => constellation_msg::KeyA, - glfw::KeyB => constellation_msg::KeyB, - glfw::KeyC => constellation_msg::KeyC, - glfw::KeyD => constellation_msg::KeyD, - glfw::KeyE => constellation_msg::KeyE, - glfw::KeyF => constellation_msg::KeyF, - glfw::KeyG => constellation_msg::KeyG, - glfw::KeyH => constellation_msg::KeyH, - glfw::KeyI => constellation_msg::KeyI, - glfw::KeyJ => constellation_msg::KeyJ, - glfw::KeyK => constellation_msg::KeyK, - glfw::KeyL => constellation_msg::KeyL, - glfw::KeyM => constellation_msg::KeyM, - glfw::KeyN => constellation_msg::KeyN, - glfw::KeyO => constellation_msg::KeyO, - glfw::KeyP => constellation_msg::KeyP, - glfw::KeyQ => constellation_msg::KeyQ, - glfw::KeyR => constellation_msg::KeyR, - glfw::KeyS => constellation_msg::KeyS, - glfw::KeyT => constellation_msg::KeyT, - glfw::KeyU => constellation_msg::KeyU, - glfw::KeyV => constellation_msg::KeyV, - glfw::KeyW => constellation_msg::KeyW, - glfw::KeyX => constellation_msg::KeyX, - glfw::KeyY => constellation_msg::KeyY, - glfw::KeyZ => constellation_msg::KeyZ, - glfw::KeyLeftBracket => constellation_msg::KeyLeftBracket, - glfw::KeyBackslash => constellation_msg::KeyBackslash, - glfw::KeyRightBracket => constellation_msg::KeyRightBracket, - glfw::KeyGraveAccent => constellation_msg::KeyGraveAccent, - glfw::KeyWorld1 => constellation_msg::KeyWorld1, - glfw::KeyWorld2 => constellation_msg::KeyWorld2, - glfw::KeyEscape => constellation_msg::KeyEscape, - glfw::KeyEnter => constellation_msg::KeyEnter, - glfw::KeyTab => constellation_msg::KeyTab, - glfw::KeyBackspace => constellation_msg::KeyBackspace, - glfw::KeyInsert => constellation_msg::KeyInsert, - glfw::KeyDelete => constellation_msg::KeyDelete, - glfw::KeyRight => constellation_msg::KeyRight, - glfw::KeyLeft => constellation_msg::KeyLeft, - glfw::KeyDown => constellation_msg::KeyDown, - glfw::KeyUp => constellation_msg::KeyUp, - glfw::KeyPageUp => constellation_msg::KeyPageUp, - glfw::KeyPageDown => constellation_msg::KeyPageDown, - glfw::KeyHome => constellation_msg::KeyHome, - glfw::KeyEnd => constellation_msg::KeyEnd, - glfw::KeyCapsLock => constellation_msg::KeyCapsLock, - glfw::KeyScrollLock => constellation_msg::KeyScrollLock, - glfw::KeyNumLock => constellation_msg::KeyNumLock, - glfw::KeyPrintScreen => constellation_msg::KeyPrintScreen, - glfw::KeyPause => constellation_msg::KeyPause, - glfw::KeyF1 => constellation_msg::KeyF1, - glfw::KeyF2 => constellation_msg::KeyF2, - glfw::KeyF3 => constellation_msg::KeyF3, - glfw::KeyF4 => constellation_msg::KeyF4, - glfw::KeyF5 => constellation_msg::KeyF5, - glfw::KeyF6 => constellation_msg::KeyF6, - glfw::KeyF7 => constellation_msg::KeyF7, - glfw::KeyF8 => constellation_msg::KeyF8, - glfw::KeyF9 => constellation_msg::KeyF9, - glfw::KeyF10 => constellation_msg::KeyF10, - glfw::KeyF11 => constellation_msg::KeyF11, - glfw::KeyF12 => constellation_msg::KeyF12, - glfw::KeyF13 => constellation_msg::KeyF13, - glfw::KeyF14 => constellation_msg::KeyF14, - glfw::KeyF15 => constellation_msg::KeyF15, - glfw::KeyF16 => constellation_msg::KeyF16, - glfw::KeyF17 => constellation_msg::KeyF17, - glfw::KeyF18 => constellation_msg::KeyF18, - glfw::KeyF19 => constellation_msg::KeyF19, - glfw::KeyF20 => constellation_msg::KeyF20, - glfw::KeyF21 => constellation_msg::KeyF21, - glfw::KeyF22 => constellation_msg::KeyF22, - glfw::KeyF23 => constellation_msg::KeyF23, - glfw::KeyF24 => constellation_msg::KeyF24, - glfw::KeyF25 => constellation_msg::KeyF25, - glfw::KeyKp0 => constellation_msg::KeyKp0, - glfw::KeyKp1 => constellation_msg::KeyKp1, - glfw::KeyKp2 => constellation_msg::KeyKp2, - glfw::KeyKp3 => constellation_msg::KeyKp3, - glfw::KeyKp4 => constellation_msg::KeyKp4, - glfw::KeyKp5 => constellation_msg::KeyKp5, - glfw::KeyKp6 => constellation_msg::KeyKp6, - glfw::KeyKp7 => constellation_msg::KeyKp7, - glfw::KeyKp8 => constellation_msg::KeyKp8, - glfw::KeyKp9 => constellation_msg::KeyKp9, - glfw::KeyKpDecimal => constellation_msg::KeyKpDecimal, - glfw::KeyKpDivide => constellation_msg::KeyKpDivide, - glfw::KeyKpMultiply => constellation_msg::KeyKpMultiply, - glfw::KeyKpSubtract => constellation_msg::KeyKpSubtract, - glfw::KeyKpAdd => constellation_msg::KeyKpAdd, - glfw::KeyKpEnter => constellation_msg::KeyKpEnter, - glfw::KeyKpEqual => constellation_msg::KeyKpEqual, - glfw::KeyLeftShift => constellation_msg::KeyLeftShift, - glfw::KeyLeftControl => constellation_msg::KeyLeftControl, - glfw::KeyLeftAlt => constellation_msg::KeyLeftAlt, - glfw::KeyLeftSuper => constellation_msg::KeyLeftSuper, - glfw::KeyRightShift => constellation_msg::KeyRightShift, - glfw::KeyRightControl => constellation_msg::KeyRightControl, - glfw::KeyRightAlt => constellation_msg::KeyRightAlt, - glfw::KeyRightSuper => constellation_msg::KeyRightSuper, - glfw::KeyMenu => constellation_msg::KeyMenu, - } + glfw_keys_to_script_keys!(key, + KeySpace, + KeyApostrophe, + KeyComma, + KeyMinus, + KeyPeriod, + KeySlash, + Key0, + Key1, + Key2, + Key3, + Key4, + Key5, + Key6, + Key7, + Key8, + Key9, + KeySemicolon, + KeyEqual, + KeyA, + KeyB, + KeyC, + KeyD, + KeyE, + KeyF, + KeyG, + KeyH, + KeyI, + KeyJ, + KeyK, + KeyL, + KeyM, + KeyN, + KeyO, + KeyP, + KeyQ, + KeyR, + KeyS, + KeyT, + KeyU, + KeyV, + KeyW, + KeyX, + KeyY, + KeyZ, + KeyLeftBracket, + KeyBackslash, + KeyRightBracket, + KeyGraveAccent, + KeyWorld1, + KeyWorld2, + + KeyEscape, + KeyEnter, + KeyTab, + KeyBackspace, + KeyInsert, + KeyDelete, + KeyRight, + KeyLeft, + KeyDown, + KeyUp, + KeyPageUp, + KeyPageDown, + KeyHome, + KeyEnd, + KeyCapsLock, + KeyScrollLock, + KeyNumLock, + KeyPrintScreen, + KeyPause, + KeyF1, + KeyF2, + KeyF3, + KeyF4, + KeyF5, + KeyF6, + KeyF7, + KeyF8, + KeyF9, + KeyF10, + KeyF11, + KeyF12, + KeyF13, + KeyF14, + KeyF15, + KeyF16, + KeyF17, + KeyF18, + KeyF19, + KeyF20, + KeyF21, + KeyF22, + KeyF23, + KeyF24, + KeyF25, + KeyKp0, + KeyKp1, + KeyKp2, + KeyKp3, + KeyKp4, + KeyKp5, + KeyKp6, + KeyKp7, + KeyKp8, + KeyKp9, + KeyKpDecimal, + KeyKpDivide, + KeyKpMultiply, + KeyKpSubtract, + KeyKpAdd, + KeyKpEnter, + KeyKpEqual, + KeyLeftShift, + KeyLeftControl, + KeyLeftAlt, + KeyLeftSuper, + KeyRightShift, + KeyRightControl, + KeyRightAlt, + KeyRightSuper, + KeyMenu) } diff --git a/tests/content/test_interfaces.html b/tests/content/test_interfaces.html index 696d6dcc7da..1a1fa9f1cd0 100644 --- a/tests/content/test_interfaces.html +++ b/tests/content/test_interfaces.html @@ -142,6 +142,7 @@ var interfaceNamesInGlobalScope = [ "HTMLUListElement", "HTMLUnknownElement", "HTMLVideoElement", + "KeyboardEvent", "Location", "MessageEvent", "MouseEvent", From c23edf6f5a020f24008c84957c1a290241632c6d Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Wed, 12 Nov 2014 14:23:52 -0500 Subject: [PATCH 10/10] Short-circuit initFooEvent while dispatching events. --- components/script/dom/customevent.rs | 6 +++++- components/script/dom/event.rs | 3 ++- components/script/dom/keyboardevent.rs | 7 ++++++- components/script/dom/mouseevent.rs | 7 ++++++- components/script/dom/uievent.rs | 4 ++++ 5 files changed, 23 insertions(+), 4 deletions(-) diff --git a/components/script/dom/customevent.rs b/components/script/dom/customevent.rs index 3cad0e2ddba..e1ac0125bb8 100644 --- a/components/script/dom/customevent.rs +++ b/components/script/dom/customevent.rs @@ -65,8 +65,12 @@ impl<'a> CustomEventMethods for JSRef<'a, CustomEvent> { can_bubble: bool, cancelable: bool, detail: JSVal) { - self.detail.set(detail); let event: JSRef = EventCast::from_ref(self); + if event.dispatching() { + return; + } + + self.detail.set(detail); event.InitEvent(type_, can_bubble, cancelable); } } diff --git a/components/script/dom/event.rs b/components/script/dom/event.rs index bbeb2cb7df2..f04b6d0a055 100644 --- a/components/script/dom/event.rs +++ b/components/script/dom/event.rs @@ -219,10 +219,11 @@ impl<'a> EventMethods for JSRef<'a, Event> { type_: DOMString, bubbles: bool, cancelable: bool) { - self.initialized.set(true); if self.dispatching.get() { return; } + + self.initialized.set(true); self.stop_propagation.set(false); self.stop_immediate.set(false); self.canceled.set(false); diff --git a/components/script/dom/keyboardevent.rs b/components/script/dom/keyboardevent.rs index 3a097fb54e2..b48a3295299 100644 --- a/components/script/dom/keyboardevent.rs +++ b/components/script/dom/keyboardevent.rs @@ -5,7 +5,7 @@ use dom::bindings::codegen::Bindings::KeyboardEventBinding; use dom::bindings::codegen::Bindings::KeyboardEventBinding::{KeyboardEventMethods, KeyboardEventConstants}; use dom::bindings::codegen::Bindings::UIEventBinding::UIEventMethods; -use dom::bindings::codegen::InheritTypes::{UIEventCast, KeyboardEventDerived}; +use dom::bindings::codegen::InheritTypes::{EventCast, UIEventCast, KeyboardEventDerived}; use dom::bindings::error::Fallible; use dom::bindings::global::GlobalRef; use dom::bindings::global; @@ -558,6 +558,11 @@ impl<'a> KeyboardEventMethods for JSRef<'a, KeyboardEvent> { _modifiersListArg: DOMString, repeat: bool, _locale: DOMString) { + let event: JSRef = EventCast::from_ref(self); + if event.dispatching() { + return; + } + let uievent: JSRef = UIEventCast::from_ref(self); uievent.InitUIEvent(typeArg, canBubbleArg, cancelableArg, viewArg, 0); *self.key.borrow_mut() = keyArg; diff --git a/components/script/dom/mouseevent.rs b/components/script/dom/mouseevent.rs index b617505c41e..81d23bf8afb 100644 --- a/components/script/dom/mouseevent.rs +++ b/components/script/dom/mouseevent.rs @@ -5,7 +5,7 @@ use dom::bindings::codegen::Bindings::MouseEventBinding; use dom::bindings::codegen::Bindings::MouseEventBinding::MouseEventMethods; use dom::bindings::codegen::Bindings::UIEventBinding::UIEventMethods; -use dom::bindings::codegen::InheritTypes::{UIEventCast, MouseEventDerived}; +use dom::bindings::codegen::InheritTypes::{EventCast, UIEventCast, MouseEventDerived}; use dom::bindings::error::Fallible; use dom::bindings::global::GlobalRef; use dom::bindings::global; @@ -160,6 +160,11 @@ impl<'a> MouseEventMethods for JSRef<'a, MouseEvent> { metaKeyArg: bool, buttonArg: i16, relatedTargetArg: Option>) { + let event: JSRef = EventCast::from_ref(self); + if event.dispatching() { + return; + } + let uievent: JSRef = UIEventCast::from_ref(self); uievent.InitUIEvent(typeArg, canBubbleArg, cancelableArg, viewArg, detailArg); self.screen_x.set(screenXArg); diff --git a/components/script/dom/uievent.rs b/components/script/dom/uievent.rs index 6a55f6ccbf8..036de7bfb64 100644 --- a/components/script/dom/uievent.rs +++ b/components/script/dom/uievent.rs @@ -89,6 +89,10 @@ impl<'a> UIEventMethods for JSRef<'a, UIEvent> { view: Option>, detail: i32) { let event: JSRef = EventCast::from_ref(self); + if event.dispatching() { + return; + } + event.InitEvent(type_, can_bubble, cancelable); self.view.assign(view); self.detail.set(detail);