diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/content/sanitizePaste.js | 71 |
1 files changed, 38 insertions, 33 deletions
diff --git a/src/content/sanitizePaste.js b/src/content/sanitizePaste.js index 703f5b3..a1bcf53 100644 --- a/src/content/sanitizePaste.js +++ b/src/content/sanitizePaste.js @@ -1,58 +1,63 @@ 'use strict'; - -window.addEventListener("paste", e => { - let data = e.clipboardData; - let html = data.getData("text/html"); - let t = e.target; - if (t.nodeType !== 1) t = t.parentElement; - - try { - let node = t.cloneNode(); - - node.innerHTML = html; - - if (sanitizeExtras(node)) { - let sanitized = node.innerHTML; - setTimeout(function() { try { - if (sanitizeExtras(t)) { - console.log(`[NoScript] Sanitized\n<PASTE>\n${html}\n</PASTE>to\n<PASTE>\n${t.innerHTML}\n</PASTE>`, t); +{ + let urlAttributes = ['href', 'to', 'from', 'by', 'values']; + let selector = urlAttributes.map(a => `[${a}]`).join(','); + + for (let evType of ["drop", "paste"]) window.addEventListener(evType, e => { + let container = e.target; + let editing = false; + for (let el = container; el; el = el.parentElement) { + if (el.setRangeText || el.contentEditable) { + editing = true; + break; + } + } + if (!editing) return; + + let html = container.innerHTML; + // we won't touch DOM elements which are already there + let oldNodes = new Set(container.querySelectorAll(selector + ",form")); + window.setTimeout(() => { + // we delay our custom sanitization after the browser performed the paste + // or drop job, rather than replacing it, in order to avoid interferences + // with built-in sanitization + try { + if (sanitizeExtras(container, oldNodes)) { + let t = e.type; + console.log(`[NoScript] Sanitized\n<${t}>\n${html}\n</${t}>to\n<${t}>\n${container.innerHTML}\n</${t}>`, container); } } catch(ex) { console.log(ex); - }}, 0); - } - } catch(ex) { - console.log(ex); - } + } + }, 0); + }, true); function removeAttribute(node, name, value = node.getAttribute(name)) { node.setAttribute(`data-noscript-removed-${name}`, value); node.removeAttribute(name); } - function sanitizeExtras(el) { + function sanitizeExtras(container, oldNodes = []) { let ret = false; // remove attributes from forms - for (let f of el.getElementsByTagName("form")) { - for (let a of f.attributes) { - f.removeAttribute(a.name); - ret = true; + for (let f of container.getElementsByTagName("form")) { + if (oldNodes.has(f)) continue; + for (let a of [...f.attributes]) { + removeAttribute(f, a.name); } } - let urlAttributes = ['href', 'to', 'from', 'by', 'values']; - let selector = urlAttributes.map(a => `[${a}]`).join(','); - for (let node of el.querySelectorAll(selector)) { + for (let node of container.querySelectorAll(selector)) { + if (oldNodes.has(node)) continue; for (let name of urlAttributes) { let value = node.getAttribute(name); if (/^\W*(?:(?:javascript|data):|https?:[\s\S]+[[(<])/i.test(unescape(value))) { - node.setAttribute(`data-noscript-removed-${name}`, value); - node.removeAttribute(name); + removeAttribute(node, name, value); ret = true; } } } return ret; } -}, true); +} |