@@ -698,5 +669,31 @@ describe('richtext component', () => {
done()
}, 0)
})
+
+ it('v-for', () => {
+ expect(compileSnippet(runtime, `
+
+
+ {{k}}
+
+
+ `, `
+ data: {
+ labels: ['A', 'B', 'C']
+ }
+ `)).toEqual({
+ type: 'div',
+ children: [{
+ type: 'richtext',
+ attr: { value: [{ type: 'span', attr: { value: 'A' }}] }
+ }, {
+ type: 'richtext',
+ attr: { value: [{ type: 'span', attr: { value: 'B' }}] }
+ }, {
+ type: 'richtext',
+ attr: { value: [{ type: 'span', attr: { value: 'C' }}] }
+ }]
+ })
+ })
})
})
From 9bded22a83b6fb9a89a32009e7f47f6201e167a3 Mon Sep 17 00:00:00 2001
From: Hanks
Date: Wed, 26 Jul 2017 17:07:51 +0800
Subject: [PATCH 010/911] test(weex richtext): rename the file path of richtext
test
---
test/weex/runtime/{component => components}/richtext.spec.js | 0
1 file changed, 0 insertions(+), 0 deletions(-)
rename test/weex/runtime/{component => components}/richtext.spec.js (100%)
diff --git a/test/weex/runtime/component/richtext.spec.js b/test/weex/runtime/components/richtext.spec.js
similarity index 100%
rename from test/weex/runtime/component/richtext.spec.js
rename to test/weex/runtime/components/richtext.spec.js
From a8146c0c1074cfd8214a62309c372b25035fd838 Mon Sep 17 00:00:00 2001
From: Hanks
Date: Fri, 28 Jul 2017 12:46:40 +0800
Subject: [PATCH 011/911] feat(weex): remove __weex_require_module__ api
---
src/platforms/weex/entry-framework.js | 4 +---
test/weex/runtime/framework.spec.js | 12 ++++++------
2 files changed, 7 insertions(+), 9 deletions(-)
diff --git a/src/platforms/weex/entry-framework.js b/src/platforms/weex/entry-framework.js
index 6758c0e7c90..375d39bbc2a 100644
--- a/src/platforms/weex/entry-framework.js
+++ b/src/platforms/weex/entry-framework.js
@@ -91,9 +91,7 @@ export function createInstance (
// It will declare some instance variables like `Vue`, HTML5 Timer APIs etc.
const instanceVars = Object.assign({
Vue,
- weex: weexInstanceVar,
- // deprecated
- __weex_require_module__: weexInstanceVar.requireModule // eslint-disable-line
+ weex: weexInstanceVar
}, timerAPIs, env.services)
if (!callFunctionNative(instanceVars, appCode)) {
diff --git a/test/weex/runtime/framework.spec.js b/test/weex/runtime/framework.spec.js
index f280c345f19..e11edaa3e24 100644
--- a/test/weex/runtime/framework.spec.js
+++ b/test/weex/runtime/framework.spec.js
@@ -261,7 +261,7 @@ describe('framework APIs', () => {
const instance = new Instance(runtime)
framework.createInstance(instance.id, `
- const moduleFoo = __weex_require_module__('foo')
+ const moduleFoo = weex.requireModule('foo')
new Vue({
data: {
x: 'Hello'
@@ -361,9 +361,9 @@ describe('framework APIs', () => {
const instance = new Instance(runtime)
framework.createInstance(instance.id, `
- const moduleFoo = __weex_require_module__('foo')
- const moduleBar = __weex_require_module__('bar')
- const moduleBaz = __weex_require_module__('baz')
+ const moduleFoo = weex.requireModule('foo')
+ const moduleBar = weex.requireModule('bar')
+ const moduleBaz = weex.requireModule('baz')
new Vue({
render: function (createElement) {
const value = []
@@ -592,7 +592,7 @@ describe('framework APIs', () => {
const instance = new Instance(runtime)
framework.createInstance(instance.id, `
- const moduleFoo = __weex_require_module__('foo')
+ const moduleFoo = weex.requireModule('foo')
new Vue({
mounted: function () {
moduleFoo.a(a => a + 1)
@@ -624,7 +624,7 @@ describe('framework APIs', () => {
const instance = new Instance(runtime)
framework.createInstance(instance.id, `
- const moduleFoo = __weex_require_module__('foo')
+ const moduleFoo = weex.requireModule('foo')
new Vue({
mounted: function () {
moduleFoo.a(this.$refs.x)
From f975fac2a8657590dcc23ea8ccae791d125bc935 Mon Sep 17 00:00:00 2001
From: Hanks
Date: Fri, 28 Jul 2017 12:53:08 +0800
Subject: [PATCH 012/911] feat(weex): wrap IFFE for appCode
---
src/platforms/weex/entry-framework.js | 2 ++
1 file changed, 2 insertions(+)
diff --git a/src/platforms/weex/entry-framework.js b/src/platforms/weex/entry-framework.js
index 375d39bbc2a..14e1b189cbc 100644
--- a/src/platforms/weex/entry-framework.js
+++ b/src/platforms/weex/entry-framework.js
@@ -94,6 +94,8 @@ export function createInstance (
weex: weexInstanceVar
}, timerAPIs, env.services)
+ appCode = `(function(global){ \n${appCode}\n })(Object.create(this))`
+
if (!callFunctionNative(instanceVars, appCode)) {
// If failed to compile functionBody on native side,
// fallback to 'callFunction()'.
From 0dc27dcdec72c1c2e12fb49fb95dceca45e84115 Mon Sep 17 00:00:00 2001
From: Hanks
Date: Tue, 15 Aug 2017 16:42:28 +0800
Subject: [PATCH 013/911] feat(weex): return instance in createInstance
---
src/platforms/weex/entry-framework.js | 2 ++
1 file changed, 2 insertions(+)
diff --git a/src/platforms/weex/entry-framework.js b/src/platforms/weex/entry-framework.js
index 14e1b189cbc..303ad36b5f0 100644
--- a/src/platforms/weex/entry-framework.js
+++ b/src/platforms/weex/entry-framework.js
@@ -104,6 +104,8 @@ export function createInstance (
// Send `createFinish` signal to native.
instance.document.taskCenter.send('dom', { action: 'createFinish' }, [])
+
+ return instance
}
/**
From 5091e2c9847601e329ac36d17eae90bb5cb77a91 Mon Sep 17 00:00:00 2001
From: Evan You
Date: Tue, 29 Aug 2017 16:42:54 +0200
Subject: [PATCH 014/911] fix(ssr): address possible xss vector
---
src/platforms/web/server/modules/attrs.js | 2 +-
test/ssr/ssr-string.spec.js | 13 +++++++++++++
2 files changed, 14 insertions(+), 1 deletion(-)
diff --git a/src/platforms/web/server/modules/attrs.js b/src/platforms/web/server/modules/attrs.js
index 544350bcde2..d1ebc76b9db 100644
--- a/src/platforms/web/server/modules/attrs.js
+++ b/src/platforms/web/server/modules/attrs.js
@@ -50,7 +50,7 @@ export function renderAttr (key: string, value: string): string {
} else if (isEnumeratedAttr(key)) {
return ` ${key}="${isFalsyAttrValue(value) || value === 'false' ? 'false' : 'true'}"`
} else if (!isFalsyAttrValue(value)) {
- return ` ${key}="${typeof value === 'string' ? cachedEscape(value) : value}"`
+ return ` ${key}="${cachedEscape(String(value))}"`
}
return ''
}
diff --git a/test/ssr/ssr-string.spec.js b/test/ssr/ssr-string.spec.js
index 3dfd3b442ae..24c20f9227f 100644
--- a/test/ssr/ssr-string.spec.js
+++ b/test/ssr/ssr-string.spec.js
@@ -821,6 +821,19 @@ describe('SSR: renderToString', () => {
})
})
+ it('should prevent script xss with v-bind object syntax + array value', done => {
+ renderVmWithOptions({
+ data: {
+ test: ['">'
+ )
+ }).thenWaitFor(_next => { next = _next }).then(() => {
+ // foo afterLeave get called
+ // and bar has already been resolved before afterLeave get called
+ expect(barResolve.calls.count()).toBe(1)
+ expect(vm.$el.innerHTML).toBe('')
+ }).thenWaitFor(nextFrame).then(() => {
+ expect(vm.$el.innerHTML).toBe(
+ 'two
'
+ )
+ assertHookCalls(one, [1, 1, 1, 1, 0])
+ assertHookCalls(two, [1, 1, 1, 0, 0])
+ }).thenWaitFor(nextFrame).then(() => {
+ expect(vm.$el.innerHTML).toBe(
+ 'two
'
+ )
+ }).thenWaitFor(_next => { next = _next }).then(() => {
+ // bar afterEnter get called
+ expect(vm.$el.innerHTML).toBe('two
')
+ }).then(done)
+ }
+ })
}
})
From 59dbd4a414394a3ce581f9fbd9554da9af9e4b1d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E8=B5=B5=E9=91=AB=E6=99=96?= <120797887@qq.com>
Date: Wed, 30 Aug 2017 04:59:39 +0800
Subject: [PATCH 019/911] fix: ensure $attrs and $listeners are always objects
(#6441)
fix #6263
---
flow/component.js | 4 ++--
src/core/instance/lifecycle.js | 4 ++--
src/core/instance/render.js | 9 +++++----
test/unit/features/instance/properties.spec.js | 14 ++++++++++++++
types/vue.d.ts | 4 ++--
5 files changed, 25 insertions(+), 10 deletions(-)
diff --git a/flow/component.js b/flow/component.js
index 08fcfd4a6ba..381943edaf7 100644
--- a/flow/component.js
+++ b/flow/component.js
@@ -29,8 +29,8 @@ declare interface Component {
$slots: { [key: string]: Array };
$scopedSlots: { [key: string]: () => VNodeChildren };
$vnode: VNode; // the placeholder node for the component in parent's render tree
- $attrs: ?{ [key: string] : string };
- $listeners: ?{ [key: string]: Function | Array };
+ $attrs: { [key: string] : string };
+ $listeners: { [key: string]: Function | Array };
$isServer: boolean;
// public methods
diff --git a/src/core/instance/lifecycle.js b/src/core/instance/lifecycle.js
index fd2c9e5b7d4..2f923c0792d 100644
--- a/src/core/instance/lifecycle.js
+++ b/src/core/instance/lifecycle.js
@@ -232,8 +232,8 @@ export function updateChildComponent (
// update $attrs and $listensers hash
// these are also reactive so they may trigger child update if the child
// used them during render
- vm.$attrs = parentVnode.data && parentVnode.data.attrs
- vm.$listeners = listeners
+ vm.$attrs = (parentVnode.data && parentVnode.data.attrs) || emptyObject
+ vm.$listeners = listeners || emptyObject
// update props
if (propsData && vm.$options.props) {
diff --git a/src/core/instance/render.js b/src/core/instance/render.js
index cf2e8fe89a4..62b4878195b 100644
--- a/src/core/instance/render.js
+++ b/src/core/instance/render.js
@@ -49,17 +49,18 @@ export function initRender (vm: Component) {
// $attrs & $listeners are exposed for easier HOC creation.
// they need to be reactive so that HOCs using them are always updated
const parentData = parentVnode && parentVnode.data
+
/* istanbul ignore else */
if (process.env.NODE_ENV !== 'production') {
- defineReactive(vm, '$attrs', parentData && parentData.attrs, () => {
+ defineReactive(vm, '$attrs', parentData && parentData.attrs || emptyObject, () => {
!isUpdatingChildComponent && warn(`$attrs is readonly.`, vm)
}, true)
- defineReactive(vm, '$listeners', vm.$options._parentListeners, () => {
+ defineReactive(vm, '$listeners', vm.$options._parentListeners || emptyObject, () => {
!isUpdatingChildComponent && warn(`$listeners is readonly.`, vm)
}, true)
} else {
- defineReactive(vm, '$attrs', parentData && parentData.attrs, null, true)
- defineReactive(vm, '$listeners', vm.$options._parentListeners, null, true)
+ defineReactive(vm, '$attrs', parentData && parentData.attrs || emptyObject, null, true)
+ defineReactive(vm, '$listeners', vm.$options._parentListeners || emptyObject, null, true)
}
}
diff --git a/test/unit/features/instance/properties.spec.js b/test/unit/features/instance/properties.spec.js
index f14aa52a863..02aca12fcfa 100644
--- a/test/unit/features/instance/properties.spec.js
+++ b/test/unit/features/instance/properties.spec.js
@@ -146,6 +146,20 @@ describe('Instance properties', () => {
}).then(done)
})
+ // #6263
+ it('$attrs should not be undefined when no props passed in', () => {
+ const vm = new Vue({
+ template: ``,
+ data: { foo: 'foo' },
+ components: {
+ foo: {
+ template: `{{ this.foo }}
`
+ }
+ }
+ }).$mount()
+ expect(vm.$attrs).toBeDefined()
+ })
+
it('warn mutating $attrs', () => {
const vm = new Vue()
vm.$attrs = {}
diff --git a/types/vue.d.ts b/types/vue.d.ts
index 0bfce6ae45d..111f5fe99f4 100644
--- a/types/vue.d.ts
+++ b/types/vue.d.ts
@@ -45,8 +45,8 @@ export declare class Vue {
readonly $ssrContext: any;
readonly $props: any;
readonly $vnode: VNode;
- readonly $attrs: { [key: string] : string } | void;
- readonly $listeners: { [key: string]: Function | Array } | void;
+ readonly $attrs: { [key: string] : string };
+ readonly $listeners: { [key: string]: Function | Array };
$mount(elementOrSelector?: Element | String, hydrating?: boolean): this;
$forceUpdate(): void;
From d4120a61680fa0bcbfb5689064dca68ff0cef105 Mon Sep 17 00:00:00 2001
From: Evan You
Date: Tue, 29 Aug 2017 23:01:11 +0200
Subject: [PATCH 020/911] ignore .vscode
---
.gitignore | 1 +
1 file changed, 1 insertion(+)
diff --git a/.gitignore b/.gitignore
index 1ee765a0db6..370a6eddbc0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -16,3 +16,4 @@ packages/vue-server-renderer/build.js
packages/vue-server-renderer/server-plugin.js
packages/vue-server-renderer/client-plugin.js
packages/vue-template-compiler/build.js
+.vscode
From f4ee63ca1059efcbdcacea9f6c1ce4e9433e2baf Mon Sep 17 00:00:00 2001
From: Evan You
Date: Tue, 29 Aug 2017 23:09:20 +0200
Subject: [PATCH 021/911] refactor: also use emptyObject for functional
listeners
---
src/core/vdom/create-functional-component.js | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/src/core/vdom/create-functional-component.js b/src/core/vdom/create-functional-component.js
index 66161759331..f13589370b3 100644
--- a/src/core/vdom/create-functional-component.js
+++ b/src/core/vdom/create-functional-component.js
@@ -8,6 +8,7 @@ import { resolveSlots } from '../instance/render-helpers/resolve-slots'
import {
isDef,
camelize,
+ emptyObject,
validateProp
} from '../util/index'
@@ -22,7 +23,7 @@ export function createFunctionalComponent (
const propOptions = Ctor.options.props
if (isDef(propOptions)) {
for (const key in propOptions) {
- props[key] = validateProp(key, propOptions, propsData || {})
+ props[key] = validateProp(key, propOptions, propsData || emptyObject)
}
} else {
if (isDef(data.attrs)) mergeProps(props, data.attrs)
@@ -37,7 +38,7 @@ export function createFunctionalComponent (
props,
children,
parent: context,
- listeners: data.on || {},
+ listeners: data.on || emptyObject,
injections: resolveInject(Ctor.options.inject, context),
slots: () => resolveSlots(children, context)
})
From 172dbf9faf4cb71dff72c77fdfe80fa1932d1ba3 Mon Sep 17 00:00:00 2001
From: Evan You
Date: Tue, 29 Aug 2017 23:32:28 +0200
Subject: [PATCH 022/911] fix(ssr): should also escape static text content fix
#6345
---
src/server/optimizing-compiler/codegen.js | 4 ++--
test/ssr/ssr-string.spec.js | 9 +++++++++
2 files changed, 11 insertions(+), 2 deletions(-)
diff --git a/src/server/optimizing-compiler/codegen.js b/src/server/optimizing-compiler/codegen.js
index 4e0c6511b1e..f494f8fad0a 100644
--- a/src/server/optimizing-compiler/codegen.js
+++ b/src/server/optimizing-compiler/codegen.js
@@ -22,8 +22,8 @@ import {
applyModelTransform
} from './modules'
+import { escape } from 'web/server/util'
import { optimizability } from './optimizer'
-
import type { CodegenResult } from 'compiler/codegen/index'
export type StringSegment = {
@@ -222,7 +222,7 @@ function nodesToSegments (
} else if (c.type === 2) {
segments.push({ type: INTERPOLATION, value: c.expression })
} else if (c.type === 3) {
- segments.push({ type: RAW, value: c.text })
+ segments.push({ type: RAW, value: escape(c.text) })
}
}
return segments
diff --git a/test/ssr/ssr-string.spec.js b/test/ssr/ssr-string.spec.js
index 24c20f9227f..683b905fda1 100644
--- a/test/ssr/ssr-string.spec.js
+++ b/test/ssr/ssr-string.spec.js
@@ -908,6 +908,15 @@ describe('SSR: renderToString', () => {
done()
})
})
+
+ it('should escape static strings', done => {
+ renderVmWithOptions(({
+ template: `<foo>
`
+ }), res => {
+ expect(res).toBe(`<foo>
`)
+ done()
+ })
+ })
})
function renderVmWithOptions (options, cb) {
From 0529040c17b8632032a43d142aac88386f6b4a1f Mon Sep 17 00:00:00 2001
From: Evan You
Date: Wed, 30 Aug 2017 00:47:10 +0200
Subject: [PATCH 023/911] fix: deep clone slot vnodes on re-render
fix #6372
---
src/core/instance/render.js | 8 +++-
src/core/vdom/vnode.js | 9 ++--
.../features/component/component-slot.spec.js | 43 +++++++++++++++++++
3 files changed, 55 insertions(+), 5 deletions(-)
diff --git a/src/core/instance/render.js b/src/core/instance/render.js
index 62b4878195b..15bc9a30192 100644
--- a/src/core/instance/render.js
+++ b/src/core/instance/render.js
@@ -78,9 +78,13 @@ export function renderMixin (Vue: Class) {
} = vm.$options
if (vm._isMounted) {
- // clone slot nodes on re-renders
+ // if the parent didn't update, the slot nodes will be the ones from
+ // last render. They need to be cloned to ensure "freshness" for this render.
for (const key in vm.$slots) {
- vm.$slots[key] = cloneVNodes(vm.$slots[key])
+ const slot = vm.$slots[key]
+ if (slot._rendered) {
+ vm.$slots[key] = cloneVNodes(slot, true /* deep */)
+ }
}
}
diff --git a/src/core/vdom/vnode.js b/src/core/vdom/vnode.js
index 7e82ec27624..751e1414bf5 100644
--- a/src/core/vdom/vnode.js
+++ b/src/core/vdom/vnode.js
@@ -79,7 +79,7 @@ export function createTextVNode (val: string | number) {
// used for static nodes and slot nodes because they may be reused across
// multiple renders, cloning them avoids errors when DOM manipulations rely
// on their elm reference.
-export function cloneVNode (vnode: VNode): VNode {
+export function cloneVNode (vnode: VNode, deep?: boolean): VNode {
const cloned = new VNode(
vnode.tag,
vnode.data,
@@ -95,14 +95,17 @@ export function cloneVNode (vnode: VNode): VNode {
cloned.key = vnode.key
cloned.isComment = vnode.isComment
cloned.isCloned = true
+ if (deep) {
+ cloned.children = vnode.children && cloneVNodes(vnode.children)
+ }
return cloned
}
-export function cloneVNodes (vnodes: Array): Array {
+export function cloneVNodes (vnodes: Array, deep?: boolean): Array {
const len = vnodes.length
const res = new Array(len)
for (let i = 0; i < len; i++) {
- res[i] = cloneVNode(vnodes[i])
+ res[i] = cloneVNode(vnodes[i], deep)
}
return res
}
diff --git a/test/unit/features/component/component-slot.spec.js b/test/unit/features/component/component-slot.spec.js
index 6eb3927ef04..894dc9f7ed0 100644
--- a/test/unit/features/component/component-slot.spec.js
+++ b/test/unit/features/component/component-slot.spec.js
@@ -685,4 +685,47 @@ describe('Component slot', () => {
}).$mount()
expect(vm.$el.innerHTML).toBe('defaultfoo
')
})
+
+ it('should handle nested components in slots properly', done => {
+ const TestComponent = {
+ template: `
+
+
+
+ `,
+ data () {
+ return {
+ toggleEl: true
+ }
+ }
+ }
+
+ const vm = new Vue({
+ template: `
+
+ `,
+ components: {
+ TestComponent,
+ foo: {
+ template: `foo
`
+ },
+ bar: {
+ template: `bar
`
+ }
+ }
+ }).$mount()
+
+ expect(vm.$el.innerHTML).toBe(`bar
`)
+
+ vm.$refs.test.toggleEl = false
+ waitForUpdate(() => {
+ expect(vm.$el.innerHTML).toBe(`bar
`)
+ }).then(done)
+ })
})
From 986a669e8f0b0b2ad0206d1066cc429cad965226 Mon Sep 17 00:00:00 2001
From: Evan You
Date: Wed, 30 Aug 2017 16:42:30 +0200
Subject: [PATCH 024/911] chore: trim trailing whitespace
---
test/unit/features/component/component-slot.spec.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/test/unit/features/component/component-slot.spec.js b/test/unit/features/component/component-slot.spec.js
index 894dc9f7ed0..95a771e286c 100644
--- a/test/unit/features/component/component-slot.spec.js
+++ b/test/unit/features/component/component-slot.spec.js
@@ -709,7 +709,7 @@ describe('Component slot', () => {