mirror of
https://github.com/ueberdosis/tiptap.git
synced 2024-12-15 11:09:01 +08:00
Bug fix for issue #724; XSS issue when importing through getHTML() function; remove usage of innerHTML and pre-parse the string using native JS DOMParser
This commit is contained in:
parent
4954f8297c
commit
5d17f68c0c
@ -4,18 +4,18 @@ import {
|
|||||||
PluginKey,
|
PluginKey,
|
||||||
TextSelection,
|
TextSelection,
|
||||||
} from 'prosemirror-state'
|
} from 'prosemirror-state'
|
||||||
import {EditorView} from 'prosemirror-view'
|
import { EditorView } from 'prosemirror-view'
|
||||||
import {Schema, DOMParser, DOMSerializer} from 'prosemirror-model'
|
import { Schema, DOMParser, DOMSerializer } from 'prosemirror-model'
|
||||||
import {dropCursor} from 'prosemirror-dropcursor'
|
import { dropCursor } from 'prosemirror-dropcursor'
|
||||||
import {gapCursor} from 'prosemirror-gapcursor'
|
import { gapCursor } from 'prosemirror-gapcursor'
|
||||||
import {keymap} from 'prosemirror-keymap'
|
import { keymap } from 'prosemirror-keymap'
|
||||||
import {baseKeymap} from 'prosemirror-commands'
|
import { baseKeymap } from 'prosemirror-commands'
|
||||||
import {inputRules, undoInputRule} from 'prosemirror-inputrules'
|
import { inputRules, undoInputRule } from 'prosemirror-inputrules'
|
||||||
import {
|
import {
|
||||||
markIsActive,
|
markIsActive,
|
||||||
nodeIsActive,
|
nodeIsActive,
|
||||||
getMarkAttrs,
|
getMarkAttrs,
|
||||||
getNodeAttrs,
|
getNodeAttrs,
|
||||||
} from 'tiptap-utils'
|
} from 'tiptap-utils'
|
||||||
import {
|
import {
|
||||||
injectCSS,
|
injectCSS,
|
||||||
@ -25,7 +25,7 @@ import {
|
|||||||
ComponentView,
|
ComponentView,
|
||||||
minMax,
|
minMax,
|
||||||
} from './Utils'
|
} from './Utils'
|
||||||
import {Doc, Paragraph, Text} from './Nodes'
|
import { Doc, Paragraph, Text } from './Nodes'
|
||||||
import css from './style.css'
|
import css from './style.css'
|
||||||
|
|
||||||
export default class Editor extends Emitter {
|
export default class Editor extends Emitter {
|
||||||
@ -52,20 +52,13 @@ export default class Editor extends Emitter {
|
|||||||
dropCursor: {},
|
dropCursor: {},
|
||||||
parseOptions: {},
|
parseOptions: {},
|
||||||
injectCSS: true,
|
injectCSS: true,
|
||||||
onInit: () => {
|
onInit: () => {},
|
||||||
},
|
onTransaction: () => {},
|
||||||
onTransaction: () => {
|
onUpdate: () => {},
|
||||||
},
|
onFocus: () => {},
|
||||||
onUpdate: () => {
|
onBlur: () => {},
|
||||||
},
|
onPaste: () => {},
|
||||||
onFocus: () => {
|
onDrop: () => {},
|
||||||
},
|
|
||||||
onBlur: () => {
|
|
||||||
},
|
|
||||||
onPaste: () => {
|
|
||||||
},
|
|
||||||
onDrop: () => {
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.events = [
|
this.events = [
|
||||||
@ -87,7 +80,7 @@ export default class Editor extends Emitter {
|
|||||||
...options,
|
...options,
|
||||||
})
|
})
|
||||||
this.focused = false
|
this.focused = false
|
||||||
this.selection = {from: 0, to: 0}
|
this.selection = { from: 0, to: 0 }
|
||||||
this.element = document.createElement('div')
|
this.element = document.createElement('div')
|
||||||
this.extensions = this.createExtensions()
|
this.extensions = this.createExtensions()
|
||||||
this.nodes = this.createNodes()
|
this.nodes = this.createNodes()
|
||||||
@ -110,8 +103,7 @@ export default class Editor extends Emitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.events.forEach(name => {
|
this.events.forEach(name => {
|
||||||
this.on(name, this.options[camelCase(`on ${name}`)] || (() => {
|
this.on(name, this.options[camelCase(`on ${name}`)] || (() => {}))
|
||||||
}))
|
|
||||||
})
|
})
|
||||||
|
|
||||||
this.emit('init', {
|
this.emit('init', {
|
||||||
@ -283,7 +275,6 @@ export default class Editor extends Emitter {
|
|||||||
const htmlString = `<div>${content}</div>`;
|
const htmlString = `<div>${content}</div>`;
|
||||||
const parser = new window.DOMParser;
|
const parser = new window.DOMParser;
|
||||||
const element = parser.parseFromString(htmlString, "text/html").body.firstChild;
|
const element = parser.parseFromString(htmlString, "text/html").body.firstChild;
|
||||||
|
|
||||||
return DOMParser.fromSchema(this.schema).parse(element, parseOptions)
|
return DOMParser.fromSchema(this.schema).parse(element, parseOptions)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -293,12 +284,8 @@ export default class Editor extends Emitter {
|
|||||||
createView() {
|
createView() {
|
||||||
return new EditorView(this.element, {
|
return new EditorView(this.element, {
|
||||||
state: this.createState(),
|
state: this.createState(),
|
||||||
handlePaste: (...args) => {
|
handlePaste: (...args) => { this.emit('paste', ...args) },
|
||||||
this.emit('paste', ...args)
|
handleDrop: (...args) => { this.emit('drop', ...args) },
|
||||||
},
|
|
||||||
handleDrop: (...args) => {
|
|
||||||
this.emit('drop', ...args)
|
|
||||||
},
|
|
||||||
dispatchTransaction: this.dispatchTransaction.bind(this),
|
dispatchTransaction: this.dispatchTransaction.bind(this),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -319,7 +306,7 @@ export default class Editor extends Emitter {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
initNodeViews({parent, extensions}) {
|
initNodeViews({ parent, extensions }) {
|
||||||
return extensions
|
return extensions
|
||||||
.filter(extension => ['node', 'mark'].includes(extension.type))
|
.filter(extension => ['node', 'mark'].includes(extension.type))
|
||||||
.filter(extension => extension.view)
|
.filter(extension => extension.view)
|
||||||
@ -390,7 +377,7 @@ export default class Editor extends Emitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (position === 'end') {
|
if (position === 'end') {
|
||||||
const {doc} = this.state
|
const { doc } = this.state
|
||||||
return {
|
return {
|
||||||
from: doc.content.size,
|
from: doc.content.size,
|
||||||
to: doc.content.size,
|
to: doc.content.size,
|
||||||
@ -408,14 +395,14 @@ export default class Editor extends Emitter {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const {from, to} = this.resolveSelection(position)
|
const { from, to } = this.resolveSelection(position)
|
||||||
|
|
||||||
this.setSelection(from, to)
|
this.setSelection(from, to)
|
||||||
setTimeout(() => this.view.focus(), 10)
|
setTimeout(() => this.view.focus(), 10)
|
||||||
}
|
}
|
||||||
|
|
||||||
setSelection(from = 0, to = 0) {
|
setSelection(from = 0, to = 0) {
|
||||||
const {doc, tr} = this.state
|
const { doc, tr } = this.state
|
||||||
const resolvedFrom = minMax(from, 0, doc.content.size)
|
const resolvedFrom = minMax(from, 0, doc.content.size)
|
||||||
const resolvedEnd = minMax(to, 0, doc.content.size)
|
const resolvedEnd = minMax(to, 0, doc.content.size)
|
||||||
const selection = TextSelection.create(doc, resolvedFrom, resolvedEnd)
|
const selection = TextSelection.create(doc, resolvedFrom, resolvedEnd)
|
||||||
@ -451,7 +438,7 @@ export default class Editor extends Emitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setContent(content = {}, emitUpdate = false, parseOptions) {
|
setContent(content = {}, emitUpdate = false, parseOptions) {
|
||||||
const {doc, tr} = this.state
|
const { doc, tr } = this.state
|
||||||
const document = this.createDocument(content, parseOptions)
|
const document = this.createDocument(content, parseOptions)
|
||||||
const selection = TextSelection.create(doc, 0, doc.content.size)
|
const selection = TextSelection.create(doc, 0, doc.content.size)
|
||||||
const transaction = tr
|
const transaction = tr
|
||||||
@ -515,7 +502,7 @@ export default class Editor extends Emitter {
|
|||||||
const plugins = typeof handlePlugins === 'function'
|
const plugins = typeof handlePlugins === 'function'
|
||||||
? handlePlugins(plugin, this.state.plugins)
|
? handlePlugins(plugin, this.state.plugins)
|
||||||
: [plugin, ...this.state.plugins]
|
: [plugin, ...this.state.plugins]
|
||||||
const newState = this.state.reconfigure({plugins})
|
const newState = this.state.reconfigure({ plugins })
|
||||||
this.view.updateState(newState)
|
this.view.updateState(newState)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user