mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
[FIX] qweb: do not crash in some rare t-call situation
Before this commit, QWeb crashed when it had to deal with a t-call with multiple sub nodes on the same line, and one text node: <t t-call="SomeTemplate"> <span>hey</span> <span>hey</span> </t> This was because we need to compile the content to be able to extract all possible t-set t-value statements. But doing so means that we had a multiple root templates, which caused a crash in this case. Solving this issue is simple: the sub template is compiled with the flag allowMultipleRoots set to true.
This commit is contained in:
@@ -200,6 +200,7 @@ QWeb.addDirective({
|
|||||||
// extract variables from nodecopy
|
// extract variables from nodecopy
|
||||||
const tempCtx = new Context();
|
const tempCtx = new Context();
|
||||||
tempCtx.nextID = ctx.rootContext.nextID;
|
tempCtx.nextID = ctx.rootContext.nextID;
|
||||||
|
tempCtx.allowMultipleRoots = true;
|
||||||
qweb._compileNode(nodeCopy, tempCtx);
|
qweb._compileNode(nodeCopy, tempCtx);
|
||||||
const vars = Object.assign({}, ctx.variables, tempCtx.variables);
|
const vars = Object.assign({}, ctx.variables, tempCtx.variables);
|
||||||
ctx.rootContext.nextID = tempCtx.nextID;
|
ctx.rootContext.nextID = tempCtx.nextID;
|
||||||
|
|||||||
@@ -3051,8 +3051,8 @@ describe("t-slot directive", () => {
|
|||||||
);
|
);
|
||||||
expect(env.qweb.templates.Parent.fn.toString()).toMatchSnapshot();
|
expect(env.qweb.templates.Parent.fn.toString()).toMatchSnapshot();
|
||||||
expect(env.qweb.templates.Dialog.fn.toString()).toMatchSnapshot();
|
expect(env.qweb.templates.Dialog.fn.toString()).toMatchSnapshot();
|
||||||
expect(env.qweb.slots['1_header'].toString()).toMatchSnapshot();
|
expect(env.qweb.slots["1_header"].toString()).toMatchSnapshot();
|
||||||
expect(env.qweb.slots['1_footer'].toString()).toMatchSnapshot();
|
expect(env.qweb.slots["1_footer"].toString()).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
test("slots are rendered with proper context", async () => {
|
test("slots are rendered with proper context", async () => {
|
||||||
@@ -3088,7 +3088,7 @@ describe("t-slot directive", () => {
|
|||||||
expect(fixture.innerHTML).toBe(
|
expect(fixture.innerHTML).toBe(
|
||||||
'<div><span class="counter">1</span><span><button>do something</button></span></div>'
|
'<div><span class="counter">1</span><span><button>do something</button></span></div>'
|
||||||
);
|
);
|
||||||
expect(env.qweb.slots['1_footer'].toString()).toMatchSnapshot();
|
expect(env.qweb.slots["1_footer"].toString()).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
test("slots are rendered with proper context, part 2", async () => {
|
test("slots are rendered with proper context, part 2", async () => {
|
||||||
@@ -3126,7 +3126,7 @@ describe("t-slot directive", () => {
|
|||||||
expect(fixture.innerHTML).toBe(
|
expect(fixture.innerHTML).toBe(
|
||||||
'<div><u><li><a href="/user/1">User Aaron</a></li><li><a href="/user/2">User Mathieu</a></li></u></div>'
|
'<div><u><li><a href="/user/1">User Aaron</a></li><li><a href="/user/2">User Mathieu</a></li></u></div>'
|
||||||
);
|
);
|
||||||
expect(env.qweb.slots['1_default'].toString()).toMatchSnapshot();
|
expect(env.qweb.slots["1_default"].toString()).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
test("slots are rendered with proper context, part 3", async () => {
|
test("slots are rendered with proper context, part 3", async () => {
|
||||||
@@ -3165,7 +3165,7 @@ describe("t-slot directive", () => {
|
|||||||
expect(fixture.innerHTML).toBe(
|
expect(fixture.innerHTML).toBe(
|
||||||
'<div><u><li><a href="/user/1">User Aaron</a></li><li><a href="/user/2">User Mathieu</a></li></u></div>'
|
'<div><u><li><a href="/user/1">User Aaron</a></li><li><a href="/user/2">User Mathieu</a></li></u></div>'
|
||||||
);
|
);
|
||||||
expect(env.qweb.slots['1_default'].toString()).toMatchSnapshot();
|
expect(env.qweb.slots["1_default"].toString()).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
test("slots are rendered with proper context, part 4", async () => {
|
test("slots are rendered with proper context, part 4", async () => {
|
||||||
@@ -3198,7 +3198,7 @@ describe("t-slot directive", () => {
|
|||||||
app.state.user.name = "David";
|
app.state.user.name = "David";
|
||||||
await nextTick();
|
await nextTick();
|
||||||
expect(fixture.innerHTML).toBe('<div><a href="/user/1">User David</a></div>');
|
expect(fixture.innerHTML).toBe('<div><a href="/user/1">User David</a></div>');
|
||||||
expect(env.qweb.slots['1_default'].toString()).toMatchSnapshot();
|
expect(env.qweb.slots["1_default"].toString()).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
test("refs are properly bound in slots", async () => {
|
test("refs are properly bound in slots", async () => {
|
||||||
@@ -3234,7 +3234,7 @@ describe("t-slot directive", () => {
|
|||||||
expect(fixture.innerHTML).toBe(
|
expect(fixture.innerHTML).toBe(
|
||||||
'<div><span class="counter">1</span><span><button>do something</button></span></div>'
|
'<div><span class="counter">1</span><span><button>do something</button></span></div>'
|
||||||
);
|
);
|
||||||
expect(env.qweb.slots['1_footer'].toString()).toMatchSnapshot();
|
expect(env.qweb.slots["1_footer"].toString()).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
test("content is the default slot", async () => {
|
test("content is the default slot", async () => {
|
||||||
@@ -3256,7 +3256,7 @@ describe("t-slot directive", () => {
|
|||||||
await parent.mount(fixture);
|
await parent.mount(fixture);
|
||||||
|
|
||||||
expect(fixture.innerHTML).toBe("<div><div><span>sts rocks</span></div></div>");
|
expect(fixture.innerHTML).toBe("<div><div><span>sts rocks</span></div></div>");
|
||||||
expect(env.qweb.slots['1_default'].toString()).toMatchSnapshot();
|
expect(env.qweb.slots["1_default"].toString()).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
test("default slot work with text nodes", async () => {
|
test("default slot work with text nodes", async () => {
|
||||||
@@ -3276,7 +3276,7 @@ describe("t-slot directive", () => {
|
|||||||
await parent.mount(fixture);
|
await parent.mount(fixture);
|
||||||
|
|
||||||
expect(fixture.innerHTML).toBe("<div><div>sts rocks</div></div>");
|
expect(fixture.innerHTML).toBe("<div><div>sts rocks</div></div>");
|
||||||
expect(env.qweb.slots['1_default'].toString()).toMatchSnapshot();
|
expect(env.qweb.slots["1_default"].toString()).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
test("multiple roots are allowed in a named slot", async () => {
|
test("multiple roots are allowed in a named slot", async () => {
|
||||||
@@ -3301,7 +3301,7 @@ describe("t-slot directive", () => {
|
|||||||
await parent.mount(fixture);
|
await parent.mount(fixture);
|
||||||
|
|
||||||
expect(fixture.innerHTML).toBe("<div><div><span>sts</span><span>rocks</span></div></div>");
|
expect(fixture.innerHTML).toBe("<div><div><span>sts</span><span>rocks</span></div></div>");
|
||||||
expect(env.qweb.slots['1_content'].toString()).toMatchSnapshot();
|
expect(env.qweb.slots["1_content"].toString()).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
test("multiple roots are allowed in a default slot", async () => {
|
test("multiple roots are allowed in a default slot", async () => {
|
||||||
@@ -3324,7 +3324,7 @@ describe("t-slot directive", () => {
|
|||||||
await parent.mount(fixture);
|
await parent.mount(fixture);
|
||||||
|
|
||||||
expect(fixture.innerHTML).toBe("<div><div><span>sts</span><span>rocks</span></div></div>");
|
expect(fixture.innerHTML).toBe("<div><div><span>sts</span><span>rocks</span></div></div>");
|
||||||
expect(env.qweb.slots['1_default'].toString()).toMatchSnapshot();
|
expect(env.qweb.slots["1_default"].toString()).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
test("missing slots are ignored", async () => {
|
test("missing slots are ignored", async () => {
|
||||||
@@ -3624,7 +3624,7 @@ describe("t-model directive", () => {
|
|||||||
</templates>`);
|
</templates>`);
|
||||||
|
|
||||||
class SomeComponent extends Widget {
|
class SomeComponent extends Widget {
|
||||||
state = { something: {text: "" }};
|
state = { something: { text: "" } };
|
||||||
}
|
}
|
||||||
const comp = new SomeComponent(env);
|
const comp = new SomeComponent(env);
|
||||||
await comp.mount(fixture);
|
await comp.mount(fixture);
|
||||||
|
|||||||
@@ -775,6 +775,28 @@ exports[`t-call (template calling basic caller 1`] = `
|
|||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`t-call (template calling call with several sub nodes on same line 1`] = `
|
||||||
|
"function anonymous(context,extra
|
||||||
|
) {
|
||||||
|
var h = this.h;
|
||||||
|
let c1 = [], p1 = {key:1};
|
||||||
|
var vn1 = h('div', p1, c1);
|
||||||
|
let c5 = [], p5 = {key:5};
|
||||||
|
var vn5 = h('div', p5, c5);
|
||||||
|
c1.push(vn5);
|
||||||
|
let c6 = [], p6 = {key:6};
|
||||||
|
var vn6 = h('span', p6, c6);
|
||||||
|
c5.push(vn6);
|
||||||
|
c6.push({text: \`hey\`});
|
||||||
|
c5.push({text: \` \`});
|
||||||
|
let c7 = [], p7 = {key:7};
|
||||||
|
var vn7 = h('span', p7, c7);
|
||||||
|
c5.push(vn7);
|
||||||
|
c7.push({text: \`yay\`});
|
||||||
|
return vn1;
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`t-call (template calling inherit context 1`] = `
|
exports[`t-call (template calling inherit context 1`] = `
|
||||||
"function anonymous(context,extra
|
"function anonymous(context,extra
|
||||||
) {
|
) {
|
||||||
|
|||||||
+24
-6
@@ -574,6 +574,24 @@ describe("t-call (template calling", () => {
|
|||||||
expect(trim(renderToString(qweb, "caller"))).toBe(expected);
|
expect(trim(renderToString(qweb, "caller"))).toBe(expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("call with several sub nodes on same line", () => {
|
||||||
|
qweb.addTemplates(`
|
||||||
|
<templates>
|
||||||
|
<div t-name="SubTemplate">
|
||||||
|
<t t-raw="0"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div t-name="main">
|
||||||
|
<t t-call="SubTemplate">
|
||||||
|
<span>hey</span> <span>yay</span>
|
||||||
|
</t>
|
||||||
|
</div>
|
||||||
|
</templates>
|
||||||
|
`);
|
||||||
|
const expected = "<div><div><span>hey</span> <span>yay</span></div></div>";
|
||||||
|
expect(renderToString(qweb, "main")).toBe(expected);
|
||||||
|
});
|
||||||
|
|
||||||
test("recursive template, part 1", () => {
|
test("recursive template, part 1", () => {
|
||||||
qweb.addTemplates(`
|
qweb.addTemplates(`
|
||||||
<templates>
|
<templates>
|
||||||
@@ -589,7 +607,6 @@ describe("t-call (template calling", () => {
|
|||||||
expect(renderToString(qweb, "recursive")).toBe(expected);
|
expect(renderToString(qweb, "recursive")).toBe(expected);
|
||||||
const recursiveFn = Object.values(qweb.recursiveFns)[0];
|
const recursiveFn = Object.values(qweb.recursiveFns)[0];
|
||||||
expect(recursiveFn.toString()).toMatchSnapshot();
|
expect(recursiveFn.toString()).toMatchSnapshot();
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test("recursive template, part 2", () => {
|
test("recursive template, part 2", () => {
|
||||||
@@ -611,9 +628,9 @@ describe("t-call (template calling", () => {
|
|||||||
|
|
||||||
</templates>
|
</templates>
|
||||||
`);
|
`);
|
||||||
const root = { val: "a", children: [{val: "b"}, {val: "c"}]};
|
const root = { val: "a", children: [{ val: "b" }, { val: "c" }] };
|
||||||
const expected = "<div><div><p>a</p><div><p>b</p></div><div><p>c</p></div></div></div>";
|
const expected = "<div><div><p>a</p><div><p>b</p></div><div><p>c</p></div></div></div>";
|
||||||
expect(renderToString(qweb, "Parent", {root })).toBe(expected);
|
expect(renderToString(qweb, "Parent", { root })).toBe(expected);
|
||||||
const recursiveFn = Object.values(qweb.recursiveFns)[0];
|
const recursiveFn = Object.values(qweb.recursiveFns)[0];
|
||||||
expect(recursiveFn.toString()).toMatchSnapshot();
|
expect(recursiveFn.toString()).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
@@ -637,9 +654,10 @@ describe("t-call (template calling", () => {
|
|||||||
|
|
||||||
</templates>
|
</templates>
|
||||||
`);
|
`);
|
||||||
const root = { val: "a", children: [{val: "b", children: [{val: "d"}]}, {val: "c"}]};
|
const root = { val: "a", children: [{ val: "b", children: [{ val: "d" }] }, { val: "c" }] };
|
||||||
const expected = "<div><div><p>a</p><div><p>b</p><div><p>d</p></div></div><div><p>c</p></div></div></div>";
|
const expected =
|
||||||
expect(renderToString(qweb, "Parent", {root })).toBe(expected);
|
"<div><div><p>a</p><div><p>b</p><div><p>d</p></div></div><div><p>c</p></div></div></div>";
|
||||||
|
expect(renderToString(qweb, "Parent", { root })).toBe(expected);
|
||||||
const recursiveFn = Object.values(qweb.recursiveFns)[0];
|
const recursiveFn = Object.values(qweb.recursiveFns)[0];
|
||||||
expect(recursiveFn.toString()).toMatchSnapshot();
|
expect(recursiveFn.toString()).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user