[FIX] web: concurrency issue when opening form view
From a list or kanban view, quickly click multiple times on a record or on the Create button: a lot of bad stuff could happen from there (e.g. crashes, form view that could not be open anymore afterward). The reason was that we destroyed the legacy form view whereas we shouldn't, as it was still used. In more details, when we clicked first on a record to open it, we created an owl Component to instantiate and wrap the form view (legacy). When we clicked a second time, we another owl Component was instantiated and the first one was destroyed. As an unwanted side-effect, the legacy form view was destroyed as well, but the new controller wanted to reuse it. From that point, anything bad could happen as we were trying to use a destroyed widget. closes odoo-dev/odoo#941 Signed-off-by: Géry Debongnie (ged) <ged@openerp.com>
This commit is contained in:
@@ -69,6 +69,10 @@ class ActionAdapter extends ComponentAdapter {
|
||||
);
|
||||
}
|
||||
|
||||
get actionId() {
|
||||
throw new Error("Should be implement by specific adapters");
|
||||
}
|
||||
|
||||
_trigger_up(ev) {
|
||||
const payload = ev.data;
|
||||
if (ev.name === "do_action") {
|
||||
@@ -147,6 +151,12 @@ class ActionAdapter extends ComponentAdapter {
|
||||
}
|
||||
super.willUnmount();
|
||||
}
|
||||
__destroy() {
|
||||
if (this.actionService.__legacy__isActionInStack(this.actionId)) {
|
||||
this.widget = null;
|
||||
}
|
||||
super.__destroy(...arguments);
|
||||
}
|
||||
}
|
||||
|
||||
export class ClientActionAdapter extends ActionAdapter {
|
||||
@@ -156,6 +166,10 @@ export class ClientActionAdapter extends ActionAdapter {
|
||||
this.env = Component.env;
|
||||
}
|
||||
|
||||
get actionId() {
|
||||
return this.props.widgetArgs[0].jsId;
|
||||
}
|
||||
|
||||
async willStart() {
|
||||
if (this.props.widget) {
|
||||
this.widget = this.props.widget;
|
||||
@@ -240,6 +254,10 @@ export class ViewAdapter extends ActionAdapter {
|
||||
this.env = Component.env;
|
||||
}
|
||||
|
||||
get actionId() {
|
||||
return this.props.viewParams.action.jsId;
|
||||
}
|
||||
|
||||
async willStart() {
|
||||
if (this.props.widget) {
|
||||
this.widget = this.props.widget;
|
||||
@@ -254,7 +272,9 @@ export class ViewAdapter extends ActionAdapter {
|
||||
if (this.__owl__.status === 5 /* DESTROYED */) {
|
||||
// the component might have been destroyed meanwhile, but if so, `this.widget` wasn't
|
||||
// destroyed by OwlCompatibility layer as it wasn't set yet, so destroy it now
|
||||
this.widget.destroy();
|
||||
if (!this.actionService.__legacy__isActionInStack(this.actionId)) {
|
||||
this.widget.destroy();
|
||||
}
|
||||
return Promise.resolve();
|
||||
}
|
||||
return this.widget._widgetRenderAndInsert(() => {});
|
||||
|
||||
@@ -1215,6 +1215,9 @@ function makeActionManager(env) {
|
||||
get currentController() {
|
||||
return _getCurrentController();
|
||||
},
|
||||
__legacy__isActionInStack(actionId) {
|
||||
return controllerStack.find((c) => c.action.jsId === actionId);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -582,4 +582,39 @@ QUnit.module("ActionManager", (hooks) => {
|
||||
"/web/dataset/search_read",
|
||||
]);
|
||||
});
|
||||
|
||||
QUnit.test("click multiple times to open a record", async function (assert) {
|
||||
assert.expect(5);
|
||||
|
||||
const def = testUtils.makeTestPromise();
|
||||
const defs = [null, def];
|
||||
const mockRPC = async (route, args) => {
|
||||
if (args.method === "read") {
|
||||
await Promise.resolve(defs.shift());
|
||||
}
|
||||
};
|
||||
|
||||
const webClient = await createWebClient({ serverData, mockRPC });
|
||||
await doAction(webClient, 3);
|
||||
assert.containsOnce(webClient, ".o_list_view");
|
||||
|
||||
await testUtils.dom.click(webClient.el.querySelector(".o_list_view .o_data_row"));
|
||||
await legacyExtraNextTick();
|
||||
assert.containsOnce(webClient, ".o_form_view");
|
||||
|
||||
await testUtils.dom.click(webClient.el.querySelector(".o_back_button"));
|
||||
await legacyExtraNextTick();
|
||||
|
||||
assert.containsOnce(webClient, ".o_list_view");
|
||||
|
||||
await testUtils.dom.click(webClient.el.querySelector(".o_list_view .o_data_row"));
|
||||
await testUtils.dom.click(webClient.el.querySelector(".o_list_view .o_data_row"));
|
||||
await legacyExtraNextTick();
|
||||
assert.containsOnce(webClient, ".o_list_view");
|
||||
|
||||
def.resolve();
|
||||
await nextTick();
|
||||
await legacyExtraNextTick();
|
||||
assert.containsOnce(webClient, ".o_form_view");
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user