Skip to content
This repository was archived by the owner on Jun 26, 2020. It is now read-only.

Commit 24b97f5

Browse files
authored
Merge pull request #1339 from ckeditor/t/1337
Fix: It should not be possible to move a `model.Node` from a `model.Document` to a `model.DocumentFragment`. Closes #1337.
2 parents a0ad8fe + 2811575 commit 24b97f5

File tree

2 files changed

+39
-52
lines changed

2 files changed

+39
-52
lines changed

src/model/writer.js

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,9 @@ export default class Writer {
144144
*
145145
* Note that if the item already has parent it will be removed from the previous parent.
146146
*
147+
* Note that you cannot re-insert a node from a document to a different document or document fragment. In this case,
148+
* `model-writer-insert-forbidden-move` is thrown.
149+
*
147150
* If you want to move {@link module:engine/model/range~Range range} instead of an
148151
* {@link module:engine/model/item~Item item} use {@link module:engine/model/writer~Writer#move move}.
149152
*
@@ -172,8 +175,14 @@ export default class Writer {
172175
}
173176
// If it isn't the same root.
174177
else {
175-
// We need to remove this item from old position first.
176-
this.remove( item );
178+
if ( item.root.document ) {
179+
// It is forbidden to move a node that was already in a document outside of it.
180+
throw new Error( 'model-writer-insert-forbidden-move: Cannot move a node from a document to a different tree.' );
181+
} else {
182+
// Move between two different document fragments or from document fragment to a document is possible.
183+
// In that case, remove the item from it's original parent.
184+
this.remove( item );
185+
}
177186
}
178187
}
179188

tests/model/writer.js

Lines changed: 28 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -258,29 +258,6 @@ describe( 'Writer', () => {
258258
expect( spy.firstCall.args[ 0 ].delta.batch ).to.equal( batch );
259259
} );
260260

261-
it( 'should move element from one parent to the other within different document (document -> docFrag)', () => {
262-
const root = doc.createRoot();
263-
const docFrag = createDocumentFragment();
264-
const node = createText( 'foo' );
265-
266-
insert( node, root );
267-
268-
const spy = sinon.spy( model, 'applyOperation' );
269-
270-
insert( node, docFrag );
271-
272-
// Verify result.
273-
expect( Array.from( root.getChildren() ) ).to.deep.equal( [] );
274-
expect( Array.from( docFrag.getChildren() ) ).to.deep.equal( [ node ] );
275-
276-
// Verify deltas and operations.
277-
sinon.assert.calledTwice( spy );
278-
expect( spy.firstCall.args[ 0 ].type ).to.equal( 'remove' );
279-
expect( spy.firstCall.args[ 0 ].delta.batch ).to.equal( batch );
280-
expect( spy.secondCall.args[ 0 ].type ).to.equal( 'insert' );
281-
expect( spy.secondCall.args[ 0 ].delta.batch ).to.equal( batch );
282-
} );
283-
284261
it( 'should move element from one parent to the other within different document (docFragA -> docFragB)', () => {
285262
const docFragA = createDocumentFragment();
286263
const docFragB = createDocumentFragment();
@@ -304,6 +281,18 @@ describe( 'Writer', () => {
304281
expect( spy.secondCall.args[ 0 ].delta.batch ).to.equal( batch );
305282
} );
306283

284+
it( 'should throw when moving element from document to document fragment', () => {
285+
const root = doc.createRoot();
286+
const docFrag = createDocumentFragment();
287+
const node = createText( 'foo' );
288+
289+
insert( node, root );
290+
291+
expect( () => {
292+
insert( node, docFrag );
293+
} ).to.throw();
294+
} );
295+
307296
it( 'should transfer markers from given DocumentFragment', () => {
308297
const root = doc.createRoot();
309298

@@ -608,7 +597,7 @@ describe( 'Writer', () => {
608597
expect( spy.lastCall.args[ 0 ].delta.batch ).to.equal( batch );
609598
} );
610599

611-
it( 'should move element from one parent to the other within the same document (documentA -> documentA)', () => {
600+
it( 'should move element from one parent to the other within the same root (rootA -> rootA)', () => {
612601
const rootA = doc.createRoot();
613602

614603
const parent1 = createElement( 'parent' );
@@ -633,7 +622,7 @@ describe( 'Writer', () => {
633622
expect( spy.firstCall.args[ 0 ].delta.batch ).to.equal( batch );
634623
} );
635624

636-
it( 'should move element from one parent to the other within the same document (documentA -> documentB)', () => {
625+
it( 'should move element from one parent to the other within the same document (rootA -> rootB)', () => {
637626
const rootA = doc.createRoot( '$root', 'A' );
638627
const rootB = doc.createRoot( '$root', 'B' );
639628
const node = createText( 'foo' );
@@ -654,7 +643,7 @@ describe( 'Writer', () => {
654643
expect( spy.firstCall.args[ 0 ].delta.batch ).to.equal( batch );
655644
} );
656645

657-
it( 'should move element from one parent to the other within the same document (docFragA -> docFragA)', () => {
646+
it( 'should move element from one parent to the other within the same document fragment (docFragA -> docFragA)', () => {
658647
const docFragA = createDocumentFragment();
659648
const parent1 = createElement( 'parent' );
660649
const parent2 = createElement( 'parent' );
@@ -678,30 +667,7 @@ describe( 'Writer', () => {
678667
expect( spy.firstCall.args[ 0 ].delta.batch ).to.equal( batch );
679668
} );
680669

681-
it( 'should move element from one parent to the other within different document (document -> docFrag)', () => {
682-
const root = doc.createRoot();
683-
const docFrag = createDocumentFragment();
684-
const node = createText( 'foo' );
685-
686-
insert( node, root );
687-
688-
const spy = sinon.spy( model, 'applyOperation' );
689-
690-
append( node, docFrag );
691-
692-
// Verify result.
693-
expect( Array.from( root.getChildren() ) ).to.deep.equal( [] );
694-
expect( Array.from( docFrag.getChildren() ) ).to.deep.equal( [ node ] );
695-
696-
// Verify deltas and operations.
697-
sinon.assert.calledTwice( spy );
698-
expect( spy.firstCall.args[ 0 ].type ).to.equal( 'remove' );
699-
expect( spy.firstCall.args[ 0 ].delta.batch ).to.equal( batch );
700-
expect( spy.secondCall.args[ 0 ].type ).to.equal( 'insert' );
701-
expect( spy.secondCall.args[ 0 ].delta.batch ).to.equal( batch );
702-
} );
703-
704-
it( 'should move element from one parent to the other within different document (docFragA -> docFragB)', () => {
670+
it( 'should move element from one parent to the other within different document fragments (docFragA -> docFragB)', () => {
705671
const docFragA = createDocumentFragment();
706672
const docFragB = createDocumentFragment();
707673
const node = createText( 'foo' );
@@ -723,6 +689,18 @@ describe( 'Writer', () => {
723689
expect( spy.secondCall.args[ 0 ].type ).to.equal( 'insert' );
724690
expect( spy.secondCall.args[ 0 ].delta.batch ).to.equal( batch );
725691
} );
692+
693+
it( 'should throw when moving element from document to document fragment', () => {
694+
const root = doc.createRoot();
695+
const docFrag = createDocumentFragment();
696+
const node = createText( 'foo' );
697+
698+
insert( node, root );
699+
700+
expect( () => {
701+
append( node, docFrag );
702+
} ).to.throw();
703+
} );
726704
} );
727705

728706
describe( 'appendText()', () => {

0 commit comments

Comments
 (0)