DOMException Crash in Caret Split via Premature deleteContents()
まだ誰も着手していません。
評価
- 難易度
- 3/5
- 見積もり時間
- 1〜2日
- 初心者へのやさしさ
- 72/100
- issue の種類
- バグ
- 明瞭さ
- 明確に書かれている
- 活発さ
- 活発
- 技術スタック
- typescript
- 領域
- frontend
調査の方向性
Start in src/components/modules/caret.ts, especially Caret.extractFragmentFromCaretPosition() around lines 197–233, and reproduce the formatted paragraph split described in the issue. Trace the selection and range boundaries through the native-input and contenteditable paths. Done means pressing Enter across the formatting boundary no longer throws a DOMException and the block splits correctly.
索引モデルが issue の本文から書いたものです。
説明
3. DOMException Crash in Caret Split via Premature deleteContents()
📍 Affected Locations
src/components/modules/caret.ts(Lines 197–233)
🔍 Deep Technical Diagnosis
When pressing Enter to split a block, Caret.extractFragmentFromCaretPosition() is called to split content from the caret to the end of the block.
// src/components/modules/caret.ts: Lines 200-231
if (selection.rangeCount) {
const selectRange = selection.getRangeAt(0);
const currentBlockInput = this.Editor.BlockManager.currentBlock.currentInput;
selectRange.deleteContents(); // <-- BUG: Mutates DOM before boundary inspection!
if (currentBlockInput) {
if ($.isNativeInput(currentBlockInput)) {
// ...
} else {
const range = selectRange.cloneRange();
range.selectNodeContents(currentBlockInput);
range.setStart(selectRange.endContainer, selectRange.endOffset); // <-- CRASH!
return range.extractContents();
}
}
}
Why It Crashes:
selectRange.deleteContents()deletes the selected nodes from the document tree.- In browsers (especially Chrome/WebKit), deleting nodes collapses text nodes and can completely remove the
selectRange.endContainernode from the DOM, or reduce its character length belowselectRange.endOffset. - When
range.setStart(selectRange.endContainer, selectRange.endOffset)runs immediately after:- If
endContaineris no longer insidecurrentBlockInput, the browser throws:
DOMException: Failed to execute 'setStart' on 'Range': The node provided is not a child of this node. - If
endOffsetexceeds the remaining text length, the browser throws:
DOMException: Failed to execute 'setStart' on 'Range': The offset is larger than the node's length.
- If
- This completely halts the block-splitting operation and leaves the editor in a corrupt, uneditable state.
🧪 Reproduction Steps
- Insert a paragraph with formatting:
Hello <b>world and extra</b> text. - Select text spanning across the tag boundary: e.g., select from
worldtoextra. - Press
Enter. - Observed: Console throws uncaught
DOMException: Failed to execute 'setStart' on 'Range'. Block does not split.
💡 Proposed Solution & Patch
Clone the boundary points or collapse the range cleanly before extracting contents:
src/components/modules/caret.ts
@@ -201,8 +201,6 @@ export default class Caret extends Module {
const selectRange = selection.getRangeAt(0);
const currentBlockInput = this.Editor.BlockManager.currentBlock.currentInput;
- selectRange.deleteContents();
-
if (currentBlockInput) {
if ($.isNativeInput(currentBlockInput)) {
/**
@@ -213,6 +211,7 @@ export default class Caret extends Module {
const input = currentBlockInput as HTMLInputElement | HTMLTextAreaElement;
const newFragment = document.createDocumentFragment();
+ selectRange.deleteContents();
const inputRemainingText = input.value.substring(0, input.selectionStart);
const fragmentText = input.value.substring(input.selectionStart);
@@ -222,8 +221,11 @@ export default class Caret extends Module {
return newFragment;
} else {
- const range = selectRange.cloneRange();
+ selectRange.deleteContents();
+ const range = document.createRange();
range.selectNodeContents(currentBlockInput);
- range.setStart(selectRange.endContainer, selectRange.endOffset);
+ const safeSelection = Selection.get();
+ const anchorRange = safeSelection.rangeCount ? safeSelection.getRangeAt(0) : selectRange;
+ range.setStart(anchorRange.startContainer, anchorRange.startOffset);
return range.extractContents();
}
- 主要言語
- TypeScript
- スター
- 32k
- フォーク
- 2.2k
- 平均マージ
- 8時間 45分
- マージ済み PR(30日)
- 1
環境構築
このプロジェクトには開発コンテナ、Dockerfile、コントリビューションガイドがありません。まず README を読み、一般的な手順ははじめてのコントリビューションガイドを参照してください。
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
codex-team/editor.js のほかの issue
-
bug
難易度 2/5 1〜3時間 初心者へのやさしさ 70/100
codex-team/editor.js#3037 · コメント 1 件 ·
-
Floating Promise Anti-Pattern & Premature Caret in Multi-Block Paste対応中かも @anshurajbisoyi98-ctrl が 26 日前に担当しました。 オープンbug good first issue
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
codex-team/editor.js#3032 · コメント 1 件 ·
-
Silent Data Loss & Broken Error Contract in Saver.save()対応中かも @Sion612 が 36 日前に担当しました。 オープンbug
難易度 2/5 1〜3時間 初心者へのやさしさ 82/100
codex-team/editor.js#3030 ·
-
Complete Omission of block.destroy() in Blocks Collection対応中かも @Minhal128 が 34 日前に担当しました。 オープンbug
難易度 2/5 1〜3時間 初心者へのやさしさ 84/100
codex-team/editor.js#3029 ·
-
Bug: Link cannot be removed対応中かも @prathmeshyelne が 141 日前に担当しました。 オープンbug
難易度 2/5 1〜3時間 初心者へのやさしさ 65/100
codex-team/editor.js#3005 · コメント 1 件 ·
codex-team/editor.js の issue をすべて見る
似ている issue
-
難易度 1/5 1〜3時間 初心者へのやさしさ 66/100
メンテナーはふだん 1 日以内に返信
-
area:plugins area:security area:skills bug has repro
難易度 2/5 1〜3時間 初心者へのやさしさ 76/100
anthropics/claude-code#105911 ·
メンテナーはふだん 1 日以内に返信
-
documentation good first issue help wanted
難易度 2/5 1〜3時間 初心者へのやさしさ 74/100
dev-five-git/devup-ui#879 ·
メンテナーはふだん 11 日以内に返信
-
openspec-apply-change skill: when blocked, may select a ready artifact unrelated to missingArtifactsオープン
難易度 2/5 1〜3時間 初心者へのやさしさ 72/100
Fission-AI/OpenSpec#2165 ·
メンテナーはふだん 1 日以内に返信
-
🐞 bug 🧐 unconfirmed
難易度 2/5 1〜3時間 初心者へのやさしさ 66/100
Tencent/tdesign-vue-next#7032 · コメント 1 件 ·
メンテナーはふだん 1 日以内に返信