mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
protect against widget destruction before being started
This commit is contained in:
@@ -1,3 +1,4 @@
|
|||||||
|
import h from "../../../libs/snabbdom/src/h";
|
||||||
import sdAttrs from "../../../libs/snabbdom/src/modules/attributes";
|
import sdAttrs from "../../../libs/snabbdom/src/modules/attributes";
|
||||||
import sdListeners from "../../../libs/snabbdom/src/modules/eventlisteners";
|
import sdListeners from "../../../libs/snabbdom/src/modules/eventlisteners";
|
||||||
import { init } from "../../../libs/snabbdom/src/snabbdom";
|
import { init } from "../../../libs/snabbdom/src/snabbdom";
|
||||||
@@ -231,7 +232,7 @@ export class Component<
|
|||||||
this.__widget__.renderProps = this.props;
|
this.__widget__.renderProps = this.props;
|
||||||
this.__widget__.renderPromise = this.willStart().then(() => {
|
this.__widget__.renderPromise = this.willStart().then(() => {
|
||||||
if (this.__widget__.isDestroyed) {
|
if (this.__widget__.isDestroyed) {
|
||||||
return Promise.resolve(this.env.qweb.render("default"));
|
return Promise.resolve(h("div"));
|
||||||
}
|
}
|
||||||
this.__widget__.isStarted = true;
|
this.__widget__.isStarted = true;
|
||||||
if (this.inlineTemplate) {
|
if (this.inlineTemplate) {
|
||||||
|
|||||||
@@ -821,12 +821,12 @@ const widgetDirective: Directive = {
|
|||||||
ctx.dedent();
|
ctx.dedent();
|
||||||
ctx.addLine(`} else {`); // not started
|
ctx.addLine(`} else {`); // not started
|
||||||
ctx.indent();
|
ctx.indent();
|
||||||
|
ctx.addLine(`isNew${widgetID} = true`);
|
||||||
ctx.addLine(
|
ctx.addLine(
|
||||||
`if (props${widgetID} === w${widgetID}.__widget__.renderProps) {`
|
`if (props${widgetID} === w${widgetID}.__widget__.renderProps) {`
|
||||||
);
|
);
|
||||||
ctx.indent();
|
ctx.indent();
|
||||||
ctx.addLine(`def${defID} = w${widgetID}.__widget__.renderPromise;`);
|
ctx.addLine(`def${defID} = w${widgetID}.__widget__.renderPromise;`);
|
||||||
ctx.addLine(`isNew${widgetID} = true`);
|
|
||||||
ctx.dedent();
|
ctx.dedent();
|
||||||
ctx.addLine(`} else {`);
|
ctx.addLine(`} else {`);
|
||||||
ctx.indent();
|
ctx.indent();
|
||||||
@@ -880,7 +880,7 @@ const widgetDirective: Directive = {
|
|||||||
ctx.addLine(`} else {`);
|
ctx.addLine(`} else {`);
|
||||||
ctx.indent();
|
ctx.indent();
|
||||||
ctx.addLine(
|
ctx.addLine(
|
||||||
`def${defID} = def${defID}.then(()=>{let vnode=h(w${widgetID}.__widget__.vnode.sel, {key: ${templateID}});vnode.elm=w${widgetID}.el;c${
|
`def${defID} = def${defID}.then(()=>{if (!w${widgetID}.__widget__.vnode) {return};let vnode=h(w${widgetID}.__widget__.vnode.sel, {key: ${templateID}});vnode.elm=w${widgetID}.el;c${
|
||||||
ctx.parentNode
|
ctx.parentNode
|
||||||
}[_${dummyID}_index]=vnode;vnode.data.hook = {insert(a){a.elm.parentNode.replaceChild(w${widgetID}.el,a.elm);a.elm=w${widgetID}.el;w${widgetID}.__mount();},remove(){w${widgetID}.${
|
}[_${dummyID}_index]=vnode;vnode.data.hook = {insert(a){a.elm.parentNode.replaceChild(w${widgetID}.el,a.elm);a.elm=w${widgetID}.el;w${widgetID}.__mount();},remove(){w${widgetID}.${
|
||||||
keepAlive ? "detach" : "destroy"
|
keepAlive ? "detach" : "destroy"
|
||||||
|
|||||||
@@ -796,6 +796,60 @@ describe("random stuff/miscellaneous", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("async rendering", () => {
|
describe("async rendering", () => {
|
||||||
|
test("destroying a widget before start is over", async () => {
|
||||||
|
let def = makeDeferred();
|
||||||
|
class W extends Widget {
|
||||||
|
inlineTemplate = "invalid><";
|
||||||
|
willStart(): Promise<void> {
|
||||||
|
return def;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const w = new W(env);
|
||||||
|
w.mount(fixture);
|
||||||
|
expect(w.__widget__.isDestroyed).toBe(false);
|
||||||
|
expect(w.__widget__.isMounted).toBe(false);
|
||||||
|
expect(w.__widget__.isStarted).toBe(false);
|
||||||
|
w.destroy();
|
||||||
|
def.resolve();
|
||||||
|
await nextTick();
|
||||||
|
expect(w.__widget__.isDestroyed).toBe(true);
|
||||||
|
expect(w.__widget__.isMounted).toBe(false);
|
||||||
|
expect(w.__widget__.isStarted).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("destroying/recreating a subwidget with different props (if start is not over)", async () => {
|
||||||
|
let def = makeDeferred();
|
||||||
|
let n = 0;
|
||||||
|
class W extends Widget {
|
||||||
|
inlineTemplate = `<div><t t-if="state.val > 1"><t t-widget="Child" t-props="{val: state.val}"/></t></div>`;
|
||||||
|
widgets = { Child };
|
||||||
|
state = { val: 1 };
|
||||||
|
}
|
||||||
|
|
||||||
|
class Child extends Widget {
|
||||||
|
inlineTemplate = `<span>child:<t t-esc="props.val"/></span>`;
|
||||||
|
constructor(parent, props) {
|
||||||
|
super(parent, props);
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
willStart(): Promise<void> {
|
||||||
|
return def;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const w = new W(env);
|
||||||
|
await w.mount(fixture);
|
||||||
|
expect(n).toBe(0);
|
||||||
|
w.updateState({ val: 2 });
|
||||||
|
expect(n).toBe(1);
|
||||||
|
await nextTick();
|
||||||
|
w.updateState({ val: 3 });
|
||||||
|
expect(n).toBe(2);
|
||||||
|
def.resolve();
|
||||||
|
await nextTick();
|
||||||
|
expect(children(w).length).toBe(1);
|
||||||
|
expect(fixture.innerHTML).toBe("<div><span>child:3</span></div>");
|
||||||
|
});
|
||||||
|
|
||||||
test("creating two async widgets, scenario 1", async () => {
|
test("creating two async widgets, scenario 1", async () => {
|
||||||
let defA = makeDeferred();
|
let defA = makeDeferred();
|
||||||
let defB = makeDeferred();
|
let defB = makeDeferred();
|
||||||
|
|||||||
Reference in New Issue
Block a user