From 6fae598d2cf32d3ca5119d86f72a96a618e71de5 Mon Sep 17 00:00:00 2001 From: fw-bot Date: Thu, 7 Nov 2019 11:20:59 +0000 Subject: [PATCH] [FIX] web: fix crash on editable list view when moving over a RO field In editable list view, moving to next cell using TAB key crash when the following field is read-only (i.e non-focusable) and an onchange() event is triggered. Consider an editable tree view like this:
1. Adding a new line will give focus to `description` field widget (currentFieldIndex is 0) 2. issuing a TAB keypress, will call _onNavigationMove which calls _selectCell() with fieldIndex of 1 3. The _selectCell() method set widget currentFieldIndex to the new value (currentFieldIndex is 1) add call _activateFieldWidget() to activate on the corresponding widget. 4. _activateFieldWidget() will fail to activate the `date` field as it's readonly, then try for next ones and succeed to activate the `type` field cell 5. When focus is given a `type`, the `description` field is blurred which trigger an onchange() and the controller apply those changes to the editable list rendererd using the confirmUpdate() method. The confirmUpdate() will try to get the current selection, but that field is actually set to the `date` field (currentFieldIndex = 1), which is readonly and so has no focusedElement - triggering the crash. This commit ensure we don't try to call getSelectionRange() it current widget has no focusable element. OPW-2075229 closes odoo/odoo#39976 X-original-commit: 93c7ae9ba5309810c41c58fc8f2fafb3c3dac76e Signed-off-by: Aaron Bohy (aab) --- addons/web/static/tests/views/list_tests.js | 72 +++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/addons/web/static/tests/views/list_tests.js b/addons/web/static/tests/views/list_tests.js index ee6edf8941c..8e4e43f6749 100644 --- a/addons/web/static/tests/views/list_tests.js +++ b/addons/web/static/tests/views/list_tests.js @@ -4448,6 +4448,78 @@ QUnit.module('Views', { list.destroy(); }); + QUnit.test('edition, then navigation with tab (with a readonly field and onchange)', async function (assert) { + // This test makes sure that if we have a read-only cell in a row, in + // case the keyboard navigation move over it and there a unsaved changes + // (which will trigger an onchange), the focus of the next activable + // field will not crash + assert.expect(4); + + this.data.bar.onchanges = { + o2m: function () {}, + }; + this.data.bar.fields.o2m = {string: "O2M field", type: "one2many", relation: "foo"}; + this.data.bar.records[0].o2m = [1, 4]; + + var form = await createView({ + View: FormView, + model: 'bar', + res_id: 1, + data: this.data, + arch: '
' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '
', + mockRPC: function (route, args) { + if (args.method === 'onchange') { + assert.step(args.method + ':' + args.model); + } + return this._super.apply(this, arguments); + }, + fieldDebounce: 1, + viewOptions: { + mode: 'edit', + }, + }); + + var jq_evspecial_focus_trigger = $.event.special.focus.trigger; + // As KeyboardEvent will be triggered by JS and not from the + // User-Agent itself, the focus event will not trigger default + // action (event not being trusted), we need to manually trigger + // 'change' event on the currently focused element + $.event.special.focus.trigger = function () { + if (this !== document.activeElement && this.focus) { + var activeElement = document.activeElement; + this.focus(); + $(activeElement).trigger('change'); + } + }; + + // editable list, click on first td and press TAB + await testUtils.dom.click(form.$('.o_data_cell:contains(yop)')); + assert.strictEqual(document.activeElement, form.$('tr.o_selected_row input[name="foo"]')[0], + "focus should be on an input with name = foo"); + await testUtils.fields.editInput(form.$('tr.o_selected_row input[name="foo"]'), 'new value'); + var tabEvent = $.Event("keydown", { which: $.ui.keyCode.TAB }); + await testUtils.dom.triggerEvents(form.$('tr.o_selected_row input[name="foo"]'), [tabEvent]); + assert.strictEqual(document.activeElement, form.$('tr.o_selected_row input[name="int_field"]')[0], + "focus should be on an input with name = int_field"); + + // Restore origin jQuery special trigger for 'focus' + $.event.special.focus.trigger = jq_evspecial_focus_trigger; + + assert.verifySteps(["onchange:bar"], "onchange method should have been called"); + form.destroy(); + }); + QUnit.test('pressing SHIFT-TAB in editable list with a readonly field [REQUIRE FOCUS]', async function (assert) { assert.expect(4);