diff --git a/shared/editor/extensions/PasteHandler.ts b/shared/editor/extensions/PasteHandler.ts index fe133da05..bc4ce2481 100644 --- a/shared/editor/extensions/PasteHandler.ts +++ b/shared/editor/extensions/PasteHandler.ts @@ -9,9 +9,13 @@ import isInCode from "../queries/isInCode"; import isInList from "../queries/isInList"; import { LANGUAGES } from "./Prism"; +/** + * Checks if the HTML string is likely coming from Dropbox Paper. + * + * @param html The HTML string to check. + * @returns True if the HTML string is likely coming from Dropbox Paper. + */ function isDropboxPaper(html: string): boolean { - // The best we have to detect if a paste is likely coming from Paper - // In this case it's actually better to use the text version return html?.includes("usually-unique-id"); } @@ -23,6 +27,34 @@ function sliceSingleNode(slice: Slice) { : null; } +/** + * Parses the text contents of an HTML string and returns the src of the first + * iframe if it exists. + * + * @param text The HTML string to parse. + * @returns The src of the first iframe if it exists, or undefined. + */ +function parseSingleIframeSrc(html: string) { + try { + const parser = new DOMParser(); + const doc = parser.parseFromString(html, "text/html"); + + if ( + doc.body.children.length === 1 && + doc.body.firstElementChild?.tagName === "IFRAME" + ) { + const iframe = doc.body.firstElementChild; + const src = iframe.getAttribute("src"); + if (src) { + return src; + } + } + } catch (e) { + // Ignore the million ways parsing could fail. + } + return undefined; +} + export default class PasteHandler extends Extension { get name() { return "paste-handler"; @@ -65,10 +97,16 @@ export default class PasteHandler extends Extension { return false; } - const text = event.clipboardData.getData("text/plain"); + const { state, dispatch } = view; + const iframeSrc = parseSingleIframeSrc( + event.clipboardData.getData("text/plain") + ); + const text = + iframeSrc && !isInCode(state) + ? iframeSrc + : event.clipboardData.getData("text/plain"); const html = event.clipboardData.getData("text/html"); const vscode = event.clipboardData.getData("vscode-editor-data"); - const { state, dispatch } = view; // first check if the clipboard contents can be parsed as a single // url, this is mainly for allowing pasted urls to become embeds diff --git a/shared/editor/nodes/Embed.tsx b/shared/editor/nodes/Embed.tsx index f7c4d2775..0876d2f37 100644 --- a/shared/editor/nodes/Embed.tsx +++ b/shared/editor/nodes/Embed.tsx @@ -27,7 +27,7 @@ export default class Embed extends Node { }, parseDOM: [ { - tag: "iframe.embed", + tag: "iframe", getAttrs: (dom: HTMLIFrameElement) => { const { embeds } = this.editor.props; const href = dom.getAttribute("src") || ""; @@ -43,7 +43,7 @@ export default class Embed extends Node { } } - return {}; + return false; }, }, ],