Просмотр исходного кода

fix attribute decode reggression (fix #3327)

Evan You 9 лет назад
Родитель
Сommit
e84a1a43be

+ 2 - 1
flow/compiler.js

@@ -11,7 +11,8 @@ declare type CompilerOptions = {
   getTagNamespace?: (tag: string) => ?string; // check the namespace for a tag
   transforms?: Array<Function>; // a list of transforms on parsed AST before codegen
   preserveWhitespace?: boolean;
-  shouldDecodeAttr?: boolean;
+  isFromDOM?: boolean;
+  shouldDecodeTags?: boolean;
 
   // runtime user-configurable
   delimiters?: [string, string]; // template delimiters

+ 1 - 6
src/compiler/parser/entity-decoder.js

@@ -2,12 +2,7 @@
 
 const decoder = document.createElement('div')
 
-export function decodeHTML (html: string, asAttribute?: boolean): string {
-  if (asAttribute) {
-    html = html
-      .replace(/</g, '&lt;')
-      .replace(/>/g, '&gt;')
-  }
+export function decodeHTML (html: string): string {
   decoder.innerHTML = html
   return decoder.textContent
 }

+ 14 - 3
src/compiler/parser/html-parser.js

@@ -9,7 +9,6 @@
  * http://erik.eae.net/simplehtmlparser/simplehtmlparser.js
  */
 
-import { decodeHTML } from 'entities'
 import { makeMap, no } from 'shared/util'
 import { isNonPhrasingTag, canBeLeftOpenTag } from 'web/util/index'
 
@@ -49,11 +48,23 @@ const isSpecialTag = makeMap('script,style', true)
 
 const reCache = {}
 
+const ampRE = /&amp;/g
+const ltRE = /&lt;/g
+const gtRE = /&gt;/g
+
+function decodeAttr (value, shouldDecodeTags) {
+  if (shouldDecodeTags) {
+    value = value.replace(ltRE, '<').replace(gtRE, '>')
+  }
+  return value.replace(ampRE, '&')
+}
+
 export function parseHTML (html, options) {
   const stack = []
   const expectHTML = options.expectHTML
   const isUnaryTag = options.isUnaryTag || no
-  const shouldDecodeAttr = options.shouldDecodeAttr
+  const isFromDOM = options.isFromDOM
+  const shouldDecodeTags = options.shouldDecodeTags
   let index = 0
   let last, lastTag
   while (html) {
@@ -203,7 +214,7 @@ export function parseHTML (html, options) {
       const value = args[3] || args[4] || args[5] || ''
       attrs[i] = {
         name: args[1],
-        value: shouldDecodeAttr ? decodeHTML(value, true) : value
+        value: isFromDOM ? decodeAttr(value, shouldDecodeTags) : value
       }
     }
 

+ 2 - 1
src/compiler/parser/index.js

@@ -54,7 +54,8 @@ export function parse (
   parseHTML(template, {
     expectHTML: options.expectHTML,
     isUnaryTag: options.isUnaryTag,
-    shouldDecodeAttr: options.shouldDecodeAttr,
+    isFromDOM: options.isFromDOM,
+    shouldDecodeTags: options.shouldDecodeTags,
     start (tag, attrs, unary) {
       // check namespace.
       // inherit parent ns if there is one

+ 5 - 4
src/entries/web-runtime-with-compiler.js

@@ -2,7 +2,7 @@
 
 import Vue from './web-runtime'
 import { warn, cached } from 'core/util/index'
-import { query, shouldDecodeAttr } from 'web/util/index'
+import { query, shouldDecodeTags } from 'web/util/index'
 import { compileToFunctions } from 'web/compiler/index'
 
 const idToTemplate = cached(id => {
@@ -42,9 +42,10 @@ Vue.prototype.$mount = function (
     }
     if (template) {
       const { render, staticRenderFns } = compileToFunctions(template, {
-        shouldDecodeAttr: isFromDOM && shouldDecodeAttr,
-        delimiters: options.delimiters,
-        warn
+        warn,
+        isFromDOM,
+        shouldDecodeTags,
+        delimiters: options.delimiters
       }, this)
       options.render = render
       options.staticRenderFns = staticRenderFns

+ 3 - 2
src/platforms/web/util/index.js

@@ -11,9 +11,10 @@ export const isIE = UA && /msie|trident/.test(UA)
 export const isIE9 = UA && UA.indexOf('msie 9.0') > 0
 export const isAndroid = UA && UA.indexOf('android') > 0
 
-// some browsers, e.g. PhantomJS, encodes attribute values for innerHTML
+// some browsers, e.g. PhantomJS, encodes angular brackets
+// inside attribute values when retrieving innerHTML.
 // this causes problems with the in-browser parser.
-export const shouldDecodeAttr = inBrowser ? (function () {
+export const shouldDecodeTags = inBrowser ? (function () {
   const div = document.createElement('div')
   div.innerHTML = '<div a=">">'
   return div.innerHTML.indexOf('&gt;') > 0

+ 8 - 0
test/unit/features/options/el.spec.js

@@ -66,6 +66,14 @@ describe('Options el', () => {
     expect(vm.$el.childNodes[1].childNodes[0].childNodes[0].namespaceURI).toContain('svg')
   })
 
+  it('properly encode attribute values', function () {
+    const el = document.createElement('div')
+    el.innerHTML = '<a href="/a?foo=bar&baz=qux" name="<abc>"></a>'
+    const vm = new Vue({ el })
+    expect(vm.$el.children[0].getAttribute('href')).toBe('/a?foo=bar&baz=qux')
+    expect(vm.$el.children[0].getAttribute('name')).toBe('<abc>')
+  })
+
   it('warn cannot find element', () => {
     new Vue({ el: '#non-existent' })
     expect('Cannot find element: #non-existent').toHaveBeenWarned()