diff options
-rw-r--r-- | core/input/keyboard.js | 10 | ||||
-rw-r--r-- | tests/test.keyboard.js | 61 |
2 files changed, 71 insertions, 0 deletions
diff --git a/core/input/keyboard.js b/core/input/keyboard.js index 48f65cf..ddb5ce0 100644 --- a/core/input/keyboard.js +++ b/core/input/keyboard.js @@ -153,6 +153,16 @@ export default class Keyboard { keysym = this._keyDownList[code]; } + // macOS doesn't send proper key releases if a key is pressed + // while meta is held down + if ((browser.isMac() || browser.isIOS()) && + (e.metaKey && code !== 'MetaLeft' && code !== 'MetaRight')) { + this._sendKeyEvent(keysym, code, true); + this._sendKeyEvent(keysym, code, false); + stopEvent(e); + return; + } + // macOS doesn't send proper key events for modifiers, only // state change events. That gets extra confusing for CapsLock // which toggles on each press, but not on release. So pretend diff --git a/tests/test.keyboard.js b/tests/test.keyboard.js index 381cd30..6b59cde 100644 --- a/tests/test.keyboard.js +++ b/tests/test.keyboard.js @@ -197,6 +197,67 @@ describe('Key Event Handling', function () { }); }); + describe('Meta key combination on iOS and macOS', function () { + let origNavigator; + beforeEach(function () { + // window.navigator is a protected read-only property in many + // environments, so we need to redefine it whilst running these + // tests. + origNavigator = Object.getOwnPropertyDescriptor(window, "navigator"); + + Object.defineProperty(window, "navigator", {value: {}}); + if (window.navigator.platform !== undefined) { + // Object.defineProperty() doesn't work properly in old + // versions of Chrome + this.skip(); + } + }); + + afterEach(function () { + if (origNavigator !== undefined) { + Object.defineProperty(window, "navigator", origNavigator); + } + }); + + it('should send keyup when meta key is pressed on iOS', function () { + window.navigator.platform = "iPad"; + const kbd = new Keyboard(document); + kbd.onkeyevent = sinon.spy(); + + kbd._handleKeyDown(keyevent('keydown', {code: 'MetaRight', key: 'Meta', location: 2, metaKey: true})); + expect(kbd.onkeyevent).to.have.been.calledOnce; + kbd.onkeyevent.resetHistory(); + + kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'a', metaKey: true})); + expect(kbd.onkeyevent).to.have.been.calledTwice; + expect(kbd.onkeyevent).to.have.been.calledWith(0x61, "KeyA", true); + expect(kbd.onkeyevent).to.have.been.calledWith(0x61, "KeyA", false); + kbd.onkeyevent.resetHistory(); + + kbd._handleKeyUp(keyevent('keyup', {code: 'MetaRight', key: 'Meta', location: 2, metaKey: true})); + expect(kbd.onkeyevent).to.have.been.calledOnce; + }); + + it('should send keyup when meta key is pressed on macOS', function () { + window.navigator.platform = "Mac"; + const kbd = new Keyboard(document); + kbd.onkeyevent = sinon.spy(); + + kbd._handleKeyDown(keyevent('keydown', {code: 'MetaRight', key: 'Meta', location: 2, metaKey: true})); + expect(kbd.onkeyevent).to.have.been.calledOnce; + kbd.onkeyevent.resetHistory(); + + kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'a', metaKey: true})); + expect(kbd.onkeyevent).to.have.been.calledTwice; + expect(kbd.onkeyevent).to.have.been.calledWith(0x61, "KeyA", true); + expect(kbd.onkeyevent).to.have.been.calledWith(0x61, "KeyA", false); + kbd.onkeyevent.resetHistory(); + + kbd._handleKeyUp(keyevent('keyup', {code: 'MetaRight', key: 'Meta', location: 2, metaKey: true})); + expect(kbd.onkeyevent).to.have.been.calledOnce; + }); + }); + describe('Caps Lock on iOS and macOS', function () { let origNavigator; beforeEach(function () { |