From 59ef804bba9d827d6e986ee168e2a85645275c97 Mon Sep 17 00:00:00 2001 From: Nicolas Bayet Date: Thu, 17 Mar 2022 11:07:15 +0000 Subject: [PATCH] [FIX] web,mass_mailing: bind the wysiwyg iframe document to hotkey service Before this commit When the user hit ctrl+k on a mass mailing, no command palette nor link dialog was opening. This was due to the fact that the hotkey service would not react to hotkeys being pressed from inside of an iframe element. After this commit One can now register an iframe to the hotkey service to let it react to hotkeys being pressed from within the registered iframes. task-2738457 closes odoo/odoo#91102 Forward-port-of: #89940 Forward-port-of: #82994 Signed-off-by: David Monjoie (dmo) --- .../static/src/js/mass_mailing_widget.js | 3 ++ .../static/src/core/hotkeys/hotkey_service.js | 18 ++++++++--- .../core/hotkeys/hotkey_service_tests.js | 32 +++++++++++++++++++ 3 files changed, 49 insertions(+), 4 deletions(-) diff --git a/addons/mass_mailing/static/src/js/mass_mailing_widget.js b/addons/mass_mailing/static/src/js/mass_mailing_widget.js index 053d1611619..5a03e1719f7 100644 --- a/addons/mass_mailing/static/src/js/mass_mailing_widget.js +++ b/addons/mass_mailing/static/src/js/mass_mailing_widget.js @@ -331,6 +331,9 @@ var MassMailingFieldHtml = FieldHtml.extend({ * @override */ _onLoadWysiwyg: function () { + // Let the global hotkey manager know about our iframe. + this.call('hotkey', 'registerIframe', this.wysiwyg.$iframe[0]); + if (this.snippetsLoaded) { this._onSnippetsLoaded(this.snippetsLoaded); } diff --git a/addons/web/static/src/core/hotkeys/hotkey_service.js b/addons/web/static/src/core/hotkeys/hotkey_service.js index 77b13e41f63..3ecf33357f1 100644 --- a/addons/web/static/src/core/hotkeys/hotkey_service.js +++ b/addons/web/static/src/core/hotkeys/hotkey_service.js @@ -33,10 +33,14 @@ export const hotkeyService = { let nextToken = 0; let overlaysVisible = false; - browser.addEventListener("keydown", onKeydown); - browser.addEventListener("keyup", removeHotkeyOverlays); - browser.addEventListener("blur", removeHotkeyOverlays); - browser.addEventListener("click", removeHotkeyOverlays); + addListeners(browser); + + function addListeners(target) { + target.addEventListener("keydown", onKeydown); + target.addEventListener("keyup", removeHotkeyOverlays); + target.addEventListener("blur", removeHotkeyOverlays); + target.addEventListener("click", removeHotkeyOverlays); + } /** * Handler for keydown events. @@ -376,6 +380,12 @@ export const hotkeyService = { unregisterHotkey(token); }; }, + /** + * @param {HTMLIFrameElement} iframe + */ + registerIframe(iframe) { + addListeners(iframe.contentWindow); + }, }; }, }; diff --git a/addons/web/static/tests/core/hotkeys/hotkey_service_tests.js b/addons/web/static/tests/core/hotkeys/hotkey_service_tests.js index 20a73185089..78c425e9d31 100644 --- a/addons/web/static/tests/core/hotkeys/hotkey_service_tests.js +++ b/addons/web/static/tests/core/hotkeys/hotkey_service_tests.js @@ -9,6 +9,7 @@ import { makeTestEnv } from "../../helpers/mock_env"; import { destroy, getFixture, + makeDeferred, mount, nextTick, patchWithCleanup, @@ -842,3 +843,34 @@ QUnit.test("ignore numpad keys", async (assert) => { await nextTick(); assert.verifySteps(["1"]); }); + +QUnit.test("within iframes", async (assert) => { + assert.expect(5); + env.services.hotkey.add("enter", () => assert.step("called")); + await nextTick(); + + // Dispatch directly to target to show that the hotkey service works as expected + target.dispatchEvent(new KeyboardEvent("keydown", { key: "Enter", bubbles: true })); + await nextTick(); + assert.verifySteps(["called"]); + + // Append an iframe to target and wait until it is fully loaded. + const iframe = document.createElement("iframe"); + iframe.srcdoc = "

Hello world!

"; + const def = makeDeferred(); + iframe.onload = def.resolve; + target.appendChild(iframe); + await def; + + // Dispatch an hotkey from within the iframe + const h1 = iframe.contentDocument.querySelector("h1"); + h1.dispatchEvent(new KeyboardEvent("keydown", { key: "Enter", bubbles: true })); + await nextTick(); + assert.verifySteps([]); + + // Register the iframe to the hotkey service + env.services.hotkey.registerIframe(iframe); + h1.dispatchEvent(new KeyboardEvent("keydown", { key: "Enter", bubbles: true })); + await nextTick(); + assert.verifySteps(["called"]); +});