[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:

<form>
   <field name="o2m" onchange="1">
       <tree>
          <field name="description"/>
          <field name="date" readonly="1"/>
          <field name="type"/>
       </tree>
   </field>
</form>

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) <aab@odoo.com>
This commit is contained in:
fw-bot
2019-11-07 15:42:02 +00:00
parent aca5feb6e0
commit 6fae598d2c
@@ -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: '<form>' +
'<group>' +
'<field name="display_name"/>' +
'<field name="o2m">' +
'<tree editable="bottom">' +
'<field name="foo"/>' +
'<field name="date" readonly="1"/>' +
'<field name="int_field"/>' +
'</tree>' +
'</field>' +
'</group>' +
'</form>',
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);