1
0
mirror of https://github.com/ianstormtaylor/slate.git synced 2025-04-21 22:02:05 +02:00

Fix bug: setting selection from contentEditable:false element causes crash (#4584)

* Fix bug: setting selection from contentEditable:false element causes crash

Fixes https://github.com/ianstormtaylor/slate/issues/4583

When clicking some text in a `contentEditable:false` element, if the
handler for this sets the selection, Slate crashes. Slate tries to find
a Slate range for the text that was clicked on, but there is no such
range, because the text is inside a `contentEditable:false` element.
Slate seems to be making a bad assumption that the current DOM selection
necessarily corresponds to a Slate range, but this is not the case if
the user just clicked into an element with `contentEditable: false`.
To fix this, I changed `exactMatch: false` to `exactMatch: true`,
which seems to mean "fail gracefully if there is no exact match".

* changeset

* Revert "Fix bug: setting selection from contentEditable:false element causes crash"

This reverts commit 71234284cd454993b139ce065a9ab2db431abce8.

* Unconflate exactMatch flag: add suppressThrow flag for separate behavior

* Fix bug: setting selection from contentEditable:false element causes crash

Fixes #4583

When clicking some text in a `contentEditable:false` element, if the
handler for this sets the selection, Slate crashes. Slate tries to find
a Slate range for the text that was clicked on, but there is no such
range, because the text is inside a `contentEditable:false` element.
Slate seems to be making a bad assumption that the current DOM selection
necessarily corresponds to a Slate range, but this is not the case if
the user just clicked into an element with `contentEditable: false`.
This commit is contained in:
Jim Fisher 2021-10-13 19:45:00 +01:00 committed by GitHub
parent 43e740c88d
commit f40e515dc7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 29 additions and 5 deletions

View File

@ -0,0 +1,5 @@
---
'slate-react': patch
---
Fixed bug: setting selection from `contentEditable:false` element causes crash

View File

@ -124,6 +124,7 @@ export const AndroidEditable = (props: EditableProps): JSX.Element => {
if (hasDomSelection && hasDomSelectionInEditor && selection) {
const slateRange = ReactEditor.toSlateRange(editor, domSelection, {
exactMatch: true,
suppressThrow: true,
})
if (slateRange && Range.equals(slateRange, selection)) {
return
@ -137,6 +138,7 @@ export const AndroidEditable = (props: EditableProps): JSX.Element => {
if (selection && !ReactEditor.hasRange(editor, selection)) {
editor.selection = ReactEditor.toSlateRange(editor, domSelection, {
exactMatch: false,
suppressThrow: false,
})
return
}
@ -266,6 +268,7 @@ export const AndroidEditable = (props: EditableProps): JSX.Element => {
if (anchorNodeSelectable && focusNodeSelectable) {
const range = ReactEditor.toSlateRange(editor, domSelection, {
exactMatch: false,
suppressThrow: false,
})
Transforms.select(editor, range)
} else {

View File

@ -183,6 +183,10 @@ export const Editable = (props: EditableProps) => {
if (hasDomSelection && hasDomSelectionInEditor && selection) {
const slateRange = ReactEditor.toSlateRange(editor, domSelection, {
exactMatch: false,
// domSelection is not necessarily a valid Slate range
// (e.g. when clicking on contentEditable:false element)
suppressThrow: true,
})
if (slateRange && Range.equals(slateRange, selection)) {
return
@ -196,6 +200,7 @@ export const Editable = (props: EditableProps) => {
if (selection && !ReactEditor.hasRange(editor, selection)) {
editor.selection = ReactEditor.toSlateRange(editor, domSelection, {
exactMatch: false,
suppressThrow: false,
})
return
}
@ -323,6 +328,7 @@ export const Editable = (props: EditableProps) => {
if (targetRange) {
const range = ReactEditor.toSlateRange(editor, targetRange, {
exactMatch: false,
suppressThrow: false,
})
if (!selection || !Range.equals(selection, range)) {
@ -503,6 +509,7 @@ export const Editable = (props: EditableProps) => {
if (anchorNodeSelectable && focusNodeSelectable) {
const range = ReactEditor.toSlateRange(editor, domSelection, {
exactMatch: false,
suppressThrow: false,
})
Transforms.select(editor, range)
}

View File

@ -430,6 +430,7 @@ export const ReactEditor = {
// Resolve a Slate range from the DOM range.
const range = ReactEditor.toSlateRange(editor, domRange, {
exactMatch: false,
suppressThrow: false,
})
return range
},
@ -441,8 +442,12 @@ export const ReactEditor = {
toSlatePoint<T extends boolean>(
editor: ReactEditor,
domPoint: DOMPoint,
exactMatch: T
options: {
exactMatch: T
suppressThrow: T
}
): T extends true ? Point | null : Point {
const { exactMatch, suppressThrow } = options
const [nearestNode, nearestOffset] = exactMatch
? domPoint
: normalizeDOMPoint(domPoint)
@ -522,7 +527,7 @@ export const ReactEditor = {
}
if (!textNode) {
if (exactMatch) {
if (suppressThrow) {
return null as T extends true ? Point | null : Point
}
throw new Error(
@ -547,9 +552,10 @@ export const ReactEditor = {
domRange: DOMRange | DOMStaticRange | DOMSelection,
options: {
exactMatch: T
suppressThrow: T
}
): T extends true ? Range | null : Range {
const { exactMatch } = options
const { exactMatch, suppressThrow } = options
const el = isDOMSelection(domRange)
? domRange.anchorNode
: domRange.startContainer
@ -599,7 +605,7 @@ export const ReactEditor = {
const anchor = ReactEditor.toSlatePoint(
editor,
[anchorNode, anchorOffset],
exactMatch
{ exactMatch, suppressThrow }
)
if (!anchor) {
return null as T extends true ? Range | null : Range
@ -607,7 +613,10 @@ export const ReactEditor = {
const focus = isCollapsed
? anchor
: ReactEditor.toSlatePoint(editor, [focusNode, focusOffset], exactMatch)
: ReactEditor.toSlatePoint(editor, [focusNode, focusOffset], {
exactMatch,
suppressThrow,
})
if (!focus) {
return null as T extends true ? Range | null : Range
}