nodeOps.ts 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. import { warn } from '@vue/runtime-core'
  2. import type { RendererOptions } from '@vue/runtime-core'
  3. import type {
  4. TrustedHTML,
  5. TrustedTypePolicy,
  6. TrustedTypesWindow,
  7. } from 'trusted-types/lib'
  8. let policy: Pick<TrustedTypePolicy, 'name' | 'createHTML'> | undefined =
  9. undefined
  10. const tt =
  11. typeof window !== 'undefined' &&
  12. (window as unknown as TrustedTypesWindow).trustedTypes
  13. if (tt) {
  14. try {
  15. policy = /*@__PURE__*/ tt.createPolicy('vue', {
  16. createHTML: val => val,
  17. })
  18. } catch (e: unknown) {
  19. // `createPolicy` throws a TypeError if the name is a duplicate
  20. // and the CSP trusted-types directive is not using `allow-duplicates`.
  21. // So we have to catch that error.
  22. __DEV__ && warn(`Error creating trusted types policy: ${e}`)
  23. }
  24. }
  25. // __UNSAFE__
  26. // Reason: potentially setting innerHTML.
  27. // This function merely perform a type-level trusted type conversion
  28. // for use in `innerHTML` assignment, etc.
  29. // Be careful of whatever value passed to this function.
  30. export const unsafeToTrustedHTML: (value: string) => TrustedHTML | string =
  31. policy ? val => policy.createHTML(val) : val => val
  32. export const svgNS = 'http://www.w3.org/2000/svg'
  33. export const mathmlNS = 'http://www.w3.org/1998/Math/MathML'
  34. const doc = (typeof document !== 'undefined' ? document : null) as Document
  35. const templateContainer = doc && /*@__PURE__*/ doc.createElement('template')
  36. export const nodeOps: Omit<RendererOptions<Node, Element>, 'patchProp'> = {
  37. insert: (child, parent, anchor) => {
  38. parent.insertBefore(child, anchor || null)
  39. },
  40. remove: child => {
  41. const parent = child.parentNode
  42. if (parent) {
  43. parent.removeChild(child)
  44. }
  45. },
  46. createElement: (tag, namespace, is, props): Element => {
  47. const el =
  48. namespace === 'svg'
  49. ? doc.createElementNS(svgNS, tag)
  50. : namespace === 'mathml'
  51. ? doc.createElementNS(mathmlNS, tag)
  52. : is
  53. ? doc.createElement(tag, { is })
  54. : doc.createElement(tag)
  55. if (tag === 'select' && props && props.multiple != null) {
  56. ;(el as HTMLSelectElement).setAttribute('multiple', props.multiple)
  57. }
  58. return el
  59. },
  60. createText: text => doc.createTextNode(text),
  61. createComment: text => doc.createComment(text),
  62. setText: (node, text) => {
  63. node.nodeValue = text
  64. },
  65. setElementText: (el, text) => {
  66. el.textContent = text
  67. },
  68. parentNode: node => node.parentNode as Element | null,
  69. nextSibling: node => node.nextSibling,
  70. querySelector: selector => doc.querySelector(selector),
  71. setScopeId(el, id) {
  72. el.setAttribute(id, '')
  73. },
  74. // __UNSAFE__
  75. // Reason: innerHTML.
  76. // Static content here can only come from compiled templates.
  77. // As long as the user only uses trusted templates, this is safe.
  78. insertStaticContent(content, parent, anchor, namespace, start, end) {
  79. // <parent> before | first ... last | anchor </parent>
  80. const before = anchor ? anchor.previousSibling : parent.lastChild
  81. // #5308 can only take cached path if:
  82. // - has a single root node
  83. // - nextSibling info is still available
  84. if (start && (start === end || start.nextSibling)) {
  85. // cached
  86. while (true) {
  87. parent.insertBefore(start!.cloneNode(true), anchor)
  88. if (start === end || !(start = start!.nextSibling)) break
  89. }
  90. } else {
  91. // fresh insert
  92. templateContainer.innerHTML = unsafeToTrustedHTML(
  93. namespace === 'svg'
  94. ? `<svg>${content}</svg>`
  95. : namespace === 'mathml'
  96. ? `<math>${content}</math>`
  97. : content,
  98. ) as string
  99. const template = templateContainer.content
  100. if (namespace === 'svg' || namespace === 'mathml') {
  101. // remove outer svg/math wrapper
  102. const wrapper = template.firstChild!
  103. while (wrapper.firstChild) {
  104. template.appendChild(wrapper.firstChild)
  105. }
  106. template.removeChild(wrapper)
  107. }
  108. parent.insertBefore(template, anchor)
  109. }
  110. return [
  111. // first
  112. before ? before.nextSibling! : parent.firstChild!,
  113. // last
  114. anchor ? anchor.previousSibling! : parent.lastChild!,
  115. ]
  116. },
  117. }