From 8bc1955258e303fc2cce822a4396b76ab19e7ab8 Mon Sep 17 00:00:00 2001 From: Evan You Date: Fri, 11 Jan 2019 11:07:40 -0500 Subject: [PATCH] fix(ssr): properly handle invalid and numeric style properties Ignores values that are not string or numbers, and append px as default unit to appropriate properties. There will still be certain cases where the user simply provides an invalid string value to a property which will be too costly to detect (it's possible by using the `cssstyle` package, but very heavy). But in such cases the user would already notice the style is not working on the client, so it's not really worth it for the perf implications. fix #9231 --- src/platforms/web/server/modules/style.js | 25 +++++++++++-- src/platforms/web/server/util.js | 45 +++++++++++++++++++++++ src/platforms/web/util/style.js | 1 - test/ssr/ssr-string.spec.js | 41 +++++++++++++++++++++ 4 files changed, 108 insertions(+), 4 deletions(-) diff --git a/src/platforms/web/server/modules/style.js b/src/platforms/web/server/modules/style.js index fc043ef3409..9153b07770d 100644 --- a/src/platforms/web/server/modules/style.js +++ b/src/platforms/web/server/modules/style.js @@ -1,6 +1,6 @@ /* @flow */ -import { escape } from '../util' +import { escape, noUnitNumericStyleProps } from '../util' import { hyphenate } from 'shared/util' import { getStyle } from 'web/util/style' @@ -11,15 +11,34 @@ export function genStyle (style: Object): string { const hyphenatedKey = hyphenate(key) if (Array.isArray(value)) { for (let i = 0, len = value.length; i < len; i++) { - styleText += `${hyphenatedKey}:${value[i]};` + styleText += normalizeValue(hyphenatedKey, value[i]) } } else { - styleText += `${hyphenatedKey}:${value};` + styleText += normalizeValue(hyphenatedKey, value) } } return styleText } +function normalizeValue(key: string, value: any): string { + if (typeof value === 'string') { + return `${key}:${value};` + } else if (typeof value === 'number') { + // Handle numeric values. + // Turns out all evergreen browsers plus IE11 already support setting plain + // numbers on the style object and will automatically convert it to px if + // applicable, so we should support that on the server too. + if (noUnitNumericStyleProps[key]) { + return `${key}:${value};` + } else { + return `${key}:${value}px;` + } + } else { + // invalid values + return `` + } +} + export default function renderStyle (vnode: VNodeWithData): ?string { const styleText = genStyle(getStyle(vnode, false)) if (styleText !== '') { diff --git a/src/platforms/web/server/util.js b/src/platforms/web/server/util.js index 1481da5bc4d..1981d00ae51 100644 --- a/src/platforms/web/server/util.js +++ b/src/platforms/web/server/util.js @@ -54,3 +54,48 @@ export function escape (s: string) { function escapeChar (a) { return ESC[a] || a } + +export const noUnitNumericStyleProps = { + "animation-iteration-count": true, + "border-image-outset": true, + "border-image-slice": true, + "border-image-width": true, + "box-flex": true, + "box-flex-group": true, + "box-ordinal-group": true, + "column-count": true, + "columns": true, + "flex": true, + "flex-grow": true, + "flex-positive": true, + "flex-shrink": true, + "flex-negative": true, + "flex-order": true, + "grid-row": true, + "grid-row-end": true, + "grid-row-span": true, + "grid-row-start": true, + "grid-column": true, + "grid-column-end": true, + "grid-column-span": true, + "grid-column-start": true, + "font-weight": true, + "line-clamp": true, + "line-height": true, + "opacity": true, + "order": true, + "orphans": true, + "tab-size": true, + "widows": true, + "z-index": true, + "zoom": true, + // SVG + "fill-opacity": true, + "flood-opacity": true, + "stop-opacity": true, + "stroke-dasharray": true, + "stroke-dashoffset": true, + "stroke-miterlimit": true, + "stroke-opacity": true, + "stroke-width": true +} diff --git a/src/platforms/web/util/style.js b/src/platforms/web/util/style.js index faa3db382e9..46e69e4e28f 100644 --- a/src/platforms/web/util/style.js +++ b/src/platforms/web/util/style.js @@ -69,4 +69,3 @@ export function getStyle (vnode: VNodeWithData, checkChild: boolean): Object { } return res } - diff --git a/test/ssr/ssr-string.spec.js b/test/ssr/ssr-string.spec.js index 112b50e1b18..35191dcdf5a 100644 --- a/test/ssr/ssr-string.spec.js +++ b/test/ssr/ssr-string.spec.js @@ -1499,6 +1499,47 @@ describe('SSR: renderToString', () => { done() }) }) + + it('invalid style value', done => { + renderVmWithOptions({ + template: '

', + data: { + // all invalid, should not even have "style" attribute + style: { + opacity: {}, + color: null + }, + // mix of valid and invalid + style2: { + opacity: 0, + color: null + } + } + }, result => { + expect(result).toContain( + '

' + ) + done() + }) + }) + + it('numeric style value', done => { + renderVmWithOptions({ + template: '
', + data: { + style: { + opacity: 0, // opacity should display as-is + top: 0, // top and margin should be converted to '0px' + marginTop: 10 + } + } + }, result => { + expect(result).toContain( + '
' + ) + done() + }) + }) }) function renderVmWithOptions (options, cb) {