Ver código fonte

Do not alter passed arguments when fixing selection passed to model.deleteContent().

Maciej Gołaszewski 6 anos atrás
pai
commit
bb04274796

+ 31 - 17
packages/ckeditor5-restricted-editing/src/restrictededitingmodeediting.js

@@ -162,23 +162,7 @@ export default class RestrictedEditingModeEditing extends Plugin {
 		doc.registerPostFixer( extendMarkerOnTypingPostFixer( editor ) );
 		doc.registerPostFixer( resurrectCollapsedMarkerPostFixer( editor ) );
 
-		editor.model.on( 'deleteContent', ( evt, args ) => {
-			// console.log( 'bofore:deleteContent', evt.stop() );
-			const [ selection, ] = args;
-
-			const marker = getMarkerAtPosition( editor, selection.focus ) || getMarkerAtPosition( editor, selection.anchor );
-
-			if ( !marker ) {
-				evt.stop();
-				return;
-			}
-
-			const intersection = marker.getRange().getIntersection( selection.getFirstRange() );
-
-			const allowedToDelete = model.createSelection( intersection );
-
-			args.splice( 0, 1, allowedToDelete );
-		}, { priority: 'high' } );
+		this.listenTo( model, 'deleteContent', restrictDeleteContent( editor ), { priority: 'high' } );
 
 		setupExceptionHighlighting( editor );
 	}
@@ -302,3 +286,33 @@ function filterDeleteCommandsOnMarkerBoundaries( selection, markerRange ) {
 		return true;
 	};
 }
+
+// Ensures that model.deleteContent() does not delete outside exception markers ranges.
+//
+// The enforced restrictions are:
+// - only execute deleteContent() inside exception markers
+// - restrict passed selection to exception marker
+function restrictDeleteContent( editor ) {
+	return ( evt, args ) => {
+		const [ selection ] = args;
+
+		const marker = getMarkerAtPosition( editor, selection.focus ) || getMarkerAtPosition( editor, selection.anchor );
+
+		// Stop method execution if marker was not found at selection focus.
+		if ( !marker ) {
+			evt.stop();
+
+			return;
+		}
+
+		// Collapsed selection inside exception marker does not require fixing.
+		if ( selection.isCollapsed ) {
+			return;
+		}
+
+		const allowedToDelete = marker.getRange().getIntersection( selection.getFirstRange() );
+
+		// Shrink the selection to the range inside exception marker.
+		selection.setTo( allowedToDelete );
+	};
+}

+ 60 - 11
packages/ckeditor5-restricted-editing/tests/restrictededitingmodeediting.js

@@ -53,8 +53,6 @@ describe( 'RestrictedEditingModeEditing', () => {
 	} );
 
 	describe( 'conversion', () => {
-		let model;
-
 		beforeEach( async () => {
 			editor = await VirtualTestEditor.create( { plugins: [ Paragraph, RestrictedEditingModeEditing ] } );
 			model = editor.model;
@@ -164,8 +162,6 @@ describe( 'RestrictedEditingModeEditing', () => {
 	} );
 
 	describe( 'editing behavior', () => {
-		let model;
-
 		beforeEach( async () => {
 			editor = await VirtualTestEditor.create( { plugins: [ Paragraph, Typing, RestrictedEditingModeEditing ] } );
 			model = editor.model;
@@ -348,26 +344,74 @@ describe( 'RestrictedEditingModeEditing', () => {
 		} );
 	} );
 
-	describe.only( 'post-fixer', () => {
+	describe( 'post-fixer', () => {
 		beforeEach( async () => {
 			editor = await VirtualTestEditor.create( { plugins: [ Paragraph, Typing, RestrictedEditingModeEditing ] } );
 			model = editor.model;
 		} );
 
-		afterEach( () => {
-			return editor.destroy();
+		afterEach( async () => {
+			await editor.destroy();
 		} );
 
-		it( 'should not allow to change text outside restricted area', () => {
+		it( 'should not allow to delete content outside restricted area', () => {
+			setModelData( model, '<paragraph>[]foo bar baz</paragraph>' );
+			const firstParagraph = model.document.getRoot().getChild( 0 );
+
+			addExceptionMarker( 3, 9, firstParagraph );
+
+			model.change( writer => {
+				writer.setSelection( firstParagraph, 2 );
+			} );
+
+			model.deleteContent( model.document.selection );
+
+			assertEqualMarkup( getModelData( model ), '<paragraph>fo[]o bar baz</paragraph>' );
+		} );
+
+		it( 'should trim deleted content to a exception marker (focus in marker)', () => {
 			setModelData( model, '<paragraph>[]foofoo bar baz</paragraph>' );
 			const firstParagraph = model.document.getRoot().getChild( 0 );
 
 			addExceptionMarker( 3, 9, firstParagraph );
 
 			model.change( writer => {
-				writer.setSelection( firstParagraph, 6 );
+				const selection = writer.createSelection( writer.createRange(
+					writer.createPositionAt( firstParagraph, 0 ),
+					writer.createPositionAt( firstParagraph, 6 )
+				) );
+				model.deleteContent( selection );
 			} );
 
+			assertEqualMarkup( getModelData( model ), '<paragraph>[]foo bar baz</paragraph>' );
+		} );
+
+		it( 'should trim deleted content to a exception marker (anchor in marker)', () => {
+			setModelData( model, '<paragraph>[]foo bar baz</paragraph>' );
+			const firstParagraph = model.document.getRoot().getChild( 0 );
+
+			addExceptionMarker( 4, 7, firstParagraph );
+
+			model.change( writer => {
+				const selection = writer.createSelection( writer.createRange(
+					writer.createPositionAt( firstParagraph, 5 ),
+					writer.createPositionAt( firstParagraph, 8 )
+				) );
+				model.deleteContent( selection );
+			} );
+
+			assertEqualMarkup( getModelData( model ), '<paragraph>[]foo b baz</paragraph>' );
+		} );
+
+		it( 'should trim deleted content to a exception marker and alter the selection argument (delete command integration)', () => {
+			setModelData( model, '<paragraph>[]foofoo bar baz</paragraph>' );
+			const firstParagraph = model.document.getRoot().getChild( 0 );
+
+			addExceptionMarker( 3, 9, firstParagraph );
+
+			model.change( writer => {
+				writer.setSelection( firstParagraph, 6 );
+			} );
 			editor.execute( 'delete', { unit: 'word' } );
 
 			assertEqualMarkup( getModelData( model ), '<paragraph>foo[] bar baz</paragraph>' );
@@ -900,10 +944,15 @@ describe( 'RestrictedEditingModeEditing', () => {
 		} );
 	} );
 
-	function addExceptionMarker( start, end = start, parent, id = 1 ) {
+	// Helper method that creates an exception marker inside given parent.
+	// Marker range is set to given position offsets (start, end).
+	function addExceptionMarker( startOffset, endOffset = startOffset, parent, id = 1 ) {
 		model.change( writer => {
 			writer.addMarker( `restrictedEditingException:${ id }`, {
-				range: writer.createRange( writer.createPositionAt( parent, start ), writer.createPositionAt( parent, end ) ),
+				range: writer.createRange(
+					writer.createPositionAt( parent, startOffset ),
+					writer.createPositionAt( parent, endOffset )
+				),
 				usingOperation: true,
 				affectsData: true
 			} );