Skip to content

Commit

Permalink
🐛 Run clipboard matchers against plain text pastes
Browse files Browse the repository at this point in the history
This is a reimplementation of slab#3530

At the moment, if the clipboard pastes `text/plain` content and no
`text/html` content, the `Clipboard.convert()` function will completely
skip the matching logic.

This is surprising when registering text node clipboard matchers.

This change updates the `convert()` function to match the plain text
against the plain text matchers, just like we do with HTML.

Note that these matchers will run _before_ applying the formats for
["paste and match style"][1], so they won't match an element matchers
for the target formatting (which I think should be expected anyway).

[1]: slab#3927
  • Loading branch information
alecgibson committed Feb 9, 2024
1 parent 15f2e8f commit a6b3e1e
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 1 deletion.
14 changes: 13 additions & 1 deletion packages/quill/src/modules/clipboard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,8 @@ class Clipboard extends Module<ClipboardOptions> {
});
}
if (!html) {
return new Delta().insert(text || '', formats);
const converted = this.convertText(text || '');
return converted.compose(new Delta().retain(converted.length(), formats));
}
const delta = this.convertHTML(html);
// Remove trailing newline
Expand All @@ -123,8 +124,19 @@ class Clipboard extends Module<ClipboardOptions> {
normalizeExternalHTML(doc);
}

protected convertText(text: string) {
const doc = new DOMParser().parseFromString('', 'text/html');
const node = doc.createTextNode(text);
doc.body.appendChild(node);
return this.convertDoc(doc);
}

protected convertHTML(html: string) {
const doc = new DOMParser().parseFromString(html, 'text/html');
return this.convertDoc(doc);
}

private convertDoc(doc: Document) {
this.normalizeHTML(doc);
const container = doc.body;
const nodeMatches = new WeakMap();
Expand Down
25 changes: 25 additions & 0 deletions packages/quill/test/unit/modules/clipboard.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -522,6 +522,31 @@ describe('Clipboard', () => {
expect(delta).toEqual(expected);
});

test('runs a text node matcher on a plain text paste', function () {
const clipboard = createClipboard();
clipboard.addMatcher(Node.TEXT_NODE, (node, delta) => {
let index = 0;
const regex = /https?:\/\/[^\s]+/g;
let match = null;
const composer = new Delta();
// eslint-disable-next-line no-cond-assign
while ((match = regex.exec((node as Text).data))) {
composer.retain(match.index - index);
index = regex.lastIndex;
composer.retain(match[0].length, { link: match[0] });
}
return delta.compose(composer);
});
const delta = clipboard.convert({
text: 'http://github.com https://quilljs.com',
});
const expected = new Delta()
.insert('http://github.com', { link: 'http://github.com' })
.insert(' ')
.insert('https://quilljs.com', { link: 'https://quilljs.com' });
expect(delta).toEqual(expected);
});

test('does not execute javascript', () => {
// @ts-expect-error
window.unsafeFunction = vitest.fn();
Expand Down

0 comments on commit a6b3e1e

Please sign in to comment.