From ec8f039ffdd0b08551989f79556c0bd6fc1e2a6f Mon Sep 17 00:00:00 2001 From: "SOUTHAMERICA\\bvalverde" Date: Tue, 26 Nov 2024 17:09:46 -0600 Subject: [PATCH] Add pasteWhiteSpaceFormatParser and integrate it into sanitizing context --- .../createDomToModelContextForSanitizing.1.ts | 65 +++++++++++++++++++ .../createDomToModelContextForSanitizing.ts | 2 + .../createModelFromHtml.ts | 2 +- .../lib/command/paste/mergePasteContent.ts | 2 +- .../override/pasteWhiteSpaceFormatParser.ts | 14 ++++ .../pasteWhiteSpaceFormatParserTest.ts | 39 +++++++++++ 6 files changed, 122 insertions(+), 2 deletions(-) create mode 100644 packages/roosterjs-content-model-core/lib/command/createModelFromHtml/createDomToModelContextForSanitizing.1.ts create mode 100644 packages/roosterjs-content-model-core/lib/override/pasteWhiteSpaceFormatParser.ts create mode 100644 packages/roosterjs-content-model-core/test/overrides/pasteWhiteSpaceFormatParserTest.ts diff --git a/packages/roosterjs-content-model-core/lib/command/createModelFromHtml/createDomToModelContextForSanitizing.1.ts b/packages/roosterjs-content-model-core/lib/command/createModelFromHtml/createDomToModelContextForSanitizing.1.ts new file mode 100644 index 00000000000..3ec4f7a1acd --- /dev/null +++ b/packages/roosterjs-content-model-core/lib/command/createModelFromHtml/createDomToModelContextForSanitizing.1.ts @@ -0,0 +1,65 @@ +import { containerSizeFormatParser } from 'roosterjs-content-model-core/lib/override/containerSizeFormatParser'; +import { createDomToModelContext } from 'roosterjs-content-model-dom/lib'; +import { createPasteEntityProcessor } from 'roosterjs-content-model-core/lib/override/pasteEntityProcessor'; +import { createPasteGeneralProcessor } from 'roosterjs-content-model-core/lib/override/pasteGeneralProcessor'; +import { DefaultSanitizingOption } from './createDomToModelContextForSanitizing'; +import { getRootComputedStyleForContext } from 'roosterjs-content-model-core/lib/coreApi/createEditorContext/getRootComputedStyleForContext'; +import { pasteBlockEntityParser } from 'roosterjs-content-model-core/lib/override/pasteCopyBlockEntityParser'; +import { pasteDisplayFormatParser } from 'roosterjs-content-model-core/lib/override/pasteDisplayFormatParser'; +import { pasteTextProcessor } from 'roosterjs-content-model-core/lib/override/pasteTextProcessor'; +import type { + ContentModelSegmentFormat, + DomToModelOption, + DomToModelOptionForSanitizing, + DomToModelContext, +} from 'roosterjs-content-model-types/lib'; + +/** + * @internal + */ + +export function createDomToModelContextForSanitizing( + document: Document, + defaultFormat?: ContentModelSegmentFormat, + defaultOption?: DomToModelOption, + additionalSanitizingOption?: Partial +): DomToModelContext { + const sanitizingOption: DomToModelOptionForSanitizing = { + ...DefaultSanitizingOption, + ...additionalSanitizingOption, + }; + + return createDomToModelContext( + { + defaultFormat, + ...getRootComputedStyleForContext(document), + experimentalFeatures: [], + }, + defaultOption, + { + processorOverride: { + '#text': pasteTextProcessor, + entity: createPasteEntityProcessor(sanitizingOption), + '*': createPasteGeneralProcessor(sanitizingOption), + }, + formatParserOverride: { + display: pasteDisplayFormatParser, + whiteSpace: (format, element, context, defaultStyle) => { + if (element.style.whiteSpace != 'pre') { + context.defaultFormatParsers.whiteSpace?.( + format, + element, + context, + defaultStyle + ); + } + }, + }, + additionalFormatParsers: { + container: [containerSizeFormatParser], + entity: [pasteBlockEntityParser], + }, + }, + sanitizingOption + ); +} diff --git a/packages/roosterjs-content-model-core/lib/command/createModelFromHtml/createDomToModelContextForSanitizing.ts b/packages/roosterjs-content-model-core/lib/command/createModelFromHtml/createDomToModelContextForSanitizing.ts index b88c24ae8af..d3d62bc0d5d 100644 --- a/packages/roosterjs-content-model-core/lib/command/createModelFromHtml/createDomToModelContextForSanitizing.ts +++ b/packages/roosterjs-content-model-core/lib/command/createModelFromHtml/createDomToModelContextForSanitizing.ts @@ -6,6 +6,7 @@ import { getRootComputedStyleForContext } from '../../coreApi/createEditorContex import { pasteBlockEntityParser } from '../../override/pasteCopyBlockEntityParser'; import { pasteDisplayFormatParser } from '../../override/pasteDisplayFormatParser'; import { pasteTextProcessor } from '../../override/pasteTextProcessor'; +import { pasteWhiteSpaceFormatParser } from '../../override/pasteWhiteSpaceFormatParser'; import type { ContentModelSegmentFormat, DomToModelContext, @@ -52,6 +53,7 @@ export function createDomToModelContextForSanitizing( }, formatParserOverride: { display: pasteDisplayFormatParser, + whiteSpace: pasteWhiteSpaceFormatParser, }, additionalFormatParsers: { container: [containerSizeFormatParser], diff --git a/packages/roosterjs-content-model-core/lib/command/createModelFromHtml/createModelFromHtml.ts b/packages/roosterjs-content-model-core/lib/command/createModelFromHtml/createModelFromHtml.ts index 68b34cbc3a9..ca88f9209ca 100644 --- a/packages/roosterjs-content-model-core/lib/command/createModelFromHtml/createModelFromHtml.ts +++ b/packages/roosterjs-content-model-core/lib/command/createModelFromHtml/createModelFromHtml.ts @@ -1,5 +1,5 @@ import { convertInlineCss, retrieveCssRules } from './convertInlineCss'; -import { createDomToModelContextForSanitizing } from './createDomToModelContextForSanitizing'; +import { createDomToModelContextForSanitizing } from './createDomToModelContextForSanitizing.1'; import { createEmptyModel, domToContentModel, parseFormat } from 'roosterjs-content-model-dom'; import type { ContentModelDocument, diff --git a/packages/roosterjs-content-model-core/lib/command/paste/mergePasteContent.ts b/packages/roosterjs-content-model-core/lib/command/paste/mergePasteContent.ts index fb0e61e966a..5ffc278d8f7 100644 --- a/packages/roosterjs-content-model-core/lib/command/paste/mergePasteContent.ts +++ b/packages/roosterjs-content-model-core/lib/command/paste/mergePasteContent.ts @@ -1,4 +1,4 @@ -import { createDomToModelContextForSanitizing } from '../createModelFromHtml/createDomToModelContextForSanitizing'; +import { createDomToModelContextForSanitizing } from '../createModelFromHtml/createDomToModelContextForSanitizing.1'; import { ChangeSource, EmptySegmentFormat, diff --git a/packages/roosterjs-content-model-core/lib/override/pasteWhiteSpaceFormatParser.ts b/packages/roosterjs-content-model-core/lib/override/pasteWhiteSpaceFormatParser.ts new file mode 100644 index 00000000000..35b4a2271d0 --- /dev/null +++ b/packages/roosterjs-content-model-core/lib/override/pasteWhiteSpaceFormatParser.ts @@ -0,0 +1,14 @@ +import type { FormatParser, WhiteSpaceFormat } from 'roosterjs-content-model-types/lib'; + +const WhiteSpacePre = 'pre'; + +export const pasteWhiteSpaceFormatParser: FormatParser = ( + format, + element, + context, + defaultStyle +) => { + if (element.style.whiteSpace != WhiteSpacePre) { + context.defaultFormatParsers.whiteSpace?.(format, element, context, defaultStyle); + } +}; diff --git a/packages/roosterjs-content-model-core/test/overrides/pasteWhiteSpaceFormatParserTest.ts b/packages/roosterjs-content-model-core/test/overrides/pasteWhiteSpaceFormatParserTest.ts new file mode 100644 index 00000000000..d153176d9ab --- /dev/null +++ b/packages/roosterjs-content-model-core/test/overrides/pasteWhiteSpaceFormatParserTest.ts @@ -0,0 +1,39 @@ +import { pasteWhiteSpaceFormatParser } from '../../lib/override/pasteWhiteSpaceFormatParser'; +import { WhiteSpaceFormat } from 'roosterjs-content-model-types/lib'; + +describe('pasteWhiteSpaceFormatParser', () => { + let format: WhiteSpaceFormat; + let element: HTMLElement; + let context: any; + let defaultStyle: any; + let defaultParserSpy: jasmine.Spy; + + beforeEach(() => { + format = {}; + element = document.createElement('div'); + defaultParserSpy = jasmine.createSpy(); + context = { + defaultFormatParsers: { + whiteSpace: defaultParserSpy, + }, + }; + defaultStyle = {}; + }); + + it('should call default whiteSpace parser when element.style.whiteSpace is not "pre"', () => { + element.style.whiteSpace = 'normal'; + pasteWhiteSpaceFormatParser(format, element, context, defaultStyle); + expect(context.defaultFormatParsers.whiteSpace).toHaveBeenCalledWith( + format, + element, + context, + defaultStyle + ); + }); + + it('should not call default whiteSpace parser when element.style.whiteSpace is "pre"', () => { + element.style.whiteSpace = 'pre'; + pasteWhiteSpaceFormatParser(format, element, context, defaultStyle); + expect(context.defaultFormatParsers.whiteSpace).not.toHaveBeenCalled(); + }); +});