[FIX] web: prevent crash when mixing hotkeys with and without area

Previously, when registering a hotkey without an area then a hotkey with
area, there would be a crash, as that latest hotkey is the candidate
winner, and we then check that there is no other matching hotkey with an
area that's more specific. In doing this, we assume all other candidates
have an area and call it as a function, but since some hotkey
registrations don't have one, we get a crash.

This commit fixes that by filtering the candidates to only those which
have an area before trying to narrow down to the most specific one.

Part-of: odoo/odoo#98535
This commit is contained in:
Samuel Degueldre
2022-08-22 10:27:22 +02:00
parent c574a7f4e9
commit 38e7589042
2 changed files with 19 additions and 2 deletions
@@ -233,7 +233,7 @@ export const hotkeyService = {
let winner = candidates.shift();
if (winner && winner.area) {
// If there is an area, find the closest one
for (const candidate of candidates) {
for (const candidate of candidates.filter((c) => Boolean(c.area))) {
if (candidate.area() && winner.area().contains(candidate.area())) {
winner = candidate;
}
@@ -40,7 +40,7 @@ QUnit.test("register / unregister", async (assert) => {
triggerHotkey(key);
await nextTick();
let removeHotkey = hotkey.add(key, () => assert.step(key));
const removeHotkey = hotkey.add(key, () => assert.step(key));
await nextTick();
triggerHotkey(key);
@@ -1095,3 +1095,20 @@ QUnit.test("operation area with validating option", async (assert) => {
await nextTick();
assert.verifySteps(["RGNTDJÛ!"]);
});
QUnit.test("mixing hotkeys with and without operation area", async (assert) => {
class A extends Component {
setup() {
const areaRef = useRef("area");
useHotkey("space", () => assert.step("withoutArea"));
useHotkey("space", () => assert.step("withArea"), { area: () => areaRef.el });
}
}
A.template = xml`<div class="root" tabindex="0" t-ref="area"/>`;
await mount(A, target, { env });
target.querySelector(".root").focus();
triggerHotkey("Space");
await nextTick();
assert.verifySteps(["withArea"]);
});