├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── background.js ├── clipboard-active-128.png ├── clipboard-active-32.png ├── clipboard-active-48.png ├── clipboard-active.png ├── clipboard-inactive-128.png ├── clipboard-inactive-32.png ├── clipboard-inactive-48.png ├── clipboard-inactive.png ├── content.js ├── dfwp.js ├── dialog-example.png ├── manifest.json ├── options.html ├── options.js ├── popup.html ├── popup.js ├── styles.css └── test ├── iframe.html ├── index.html └── script.js /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | ## [Unreleased] 6 | No changes 7 | 8 | ## [3.1] - 2024-08-09 9 | ### Changed 10 | - Tweak styles 11 | 12 | ## [3.0] - 2024-08-09 13 | ### Changed 14 | - Upgrade to manifest v3 15 | 16 | ## [2.9] - 2022-05-19 17 | ### Changed 18 | - Extension's URL 19 | 20 | ## [2.8] - 2022-05-13 21 | ### Changed 22 | - Extension's name 23 | 24 | ## [2.7] - 2020-02-01 25 | ### Changed 26 | - Use runtime.openOptionsPage to open options page 27 | - Access extension APIs through browser or chrome 28 | 29 | ## [2.6] - 2019-04-30 30 | ### Changed 31 | - Link to options page from popup 32 | - Reword "Add Rule" button to "Add Pattern" to match other verbiage 33 | 34 | ## [2.5] - 2018-08-02 35 | ### Changed 36 | - Run activeness check on all active tabs on rule change 37 | 38 | ## [2.4] - 2018-05-17 39 | ### Changed 40 | - Prevent saving of empty rules 41 | - Prevent empty rules from matching all sites 42 | - Remove upgrade notification 43 | - Prevent blocking of cut events 44 | - Escape periods in auto-generated patterns 45 | 46 | ## [2.3] - 2018-03-14 47 | ### Changed 48 | - Fix multi-window support 49 | 50 | ## [2.2] - 2018-03-14 51 | ### Changed 52 | - Fix Popup tab URL lookup 53 | 54 | ## [2.1] - 2018-03-14 55 | ### Changed 56 | - Fix Regular link in popup 57 | - Fix Chromium bug around adding event listener to null delete button 58 | 59 | ## [2.0] - 2018-03-13 60 | ### Changed 61 | - Switch to just blacklist of sites to run on 62 | 63 | ### Added 64 | - Browser action popup to add rule 65 | - Background script to determine activeness 66 | 67 | ## [1.1] - 2016-08-29 68 | ### Added 69 | - This CHANGELOG file 70 | - Copy event blocking prevention 71 | 72 | [Unreleased]: https://github.com/jswanner/DontF-WithPaste/compare/v3.1...HEAD 73 | [3.1]: https://github.com/jswanner/DontF-WithPaste/compare/v3.0...v3.1 74 | [3.0]: https://github.com/jswanner/DontF-WithPaste/compare/v2.9...v3.0 75 | [2.9]: https://github.com/jswanner/DontF-WithPaste/compare/v2.8...v2.9 76 | [2.8]: https://github.com/jswanner/DontF-WithPaste/compare/v2.7...v2.8 77 | [2.7]: https://github.com/jswanner/DontF-WithPaste/compare/v2.6...v2.7 78 | [2.6]: https://github.com/jswanner/DontF-WithPaste/compare/v2.5...v2.6 79 | [2.5]: https://github.com/jswanner/DontF-WithPaste/compare/v2.4...v2.5 80 | [2.4]: https://github.com/jswanner/DontF-WithPaste/compare/v2.3...v2.4 81 | [2.3]: https://github.com/jswanner/DontF-WithPaste/compare/v2.2...v2.3 82 | [2.2]: https://github.com/jswanner/DontF-WithPaste/compare/v2.1...v2.2 83 | [2.1]: https://github.com/jswanner/DontF-WithPaste/compare/v2.0...v2.1 84 | [2.0]: https://github.com/jswanner/DontF-WithPaste/compare/v1.1...v2.0 85 | [1.1]: https://github.com/jswanner/DontF-WithPaste/compare/v1.0...v1.1 86 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Jacob Swanner 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Don't f*** with copy and paste 2 | 3 | [Don't F*** With Paste - Chrome Web Store](https://chrome.google.com/webstore/detail/dont-f-with-paste/nkgllhigpcljnhoakjkgaieabnkmgdkb) 4 | 5 | ## Background 6 | 7 | It annoys me to no end when a web application prevents me from being able to 8 | paste content into an input field, or copy it out. If I paste an incorrect 9 | email address, that's my own damn fault. I use tools like 1Password to 10 | remember all kinds of things for me, and it's actually more error prone for me 11 | to type out all the characters than it is for me to copy from 1Password and 12 | paste into a text box. 13 | 14 | ## Solution 15 | 16 | This is a dead simple Google Chrome extension that removes copy, cut and paste 17 | blocking, by preventing sites from interfering with "copy", "cut", and "paste" 18 | browser events. 19 | 20 | This extension does not try to prevent a site from also interfering with 21 | keyboard shortcuts related to those browser actions (control-v, command-v, 22 | etc.), nor does this extension prevent sites from interfering with the 23 | "contextmenu" event (right click menu). For those super annoying sites that 24 | interfere with keyboard shortcuts and context menu, using Edit -> Paste from 25 | the browser's menu might be required. 26 | 27 | ## Usage 28 | 29 | The easiest way to activate the extension for a given site is to click on the 30 | extension icon ![inactive 31 | icon](https://raw.githubusercontent.com/jswanner/DontF-WithPaste/09339b4f43d5bac9ddbdeea75051c6d9c017951f/clipboard-inactive-32.png), 32 | then optionally edit the auto-generated pattern, lastly click "Save": 33 | 34 | ![New pattern dialog 35 | example](https://raw.githubusercontent.com/jswanner/DontF-WithPaste/73e5d11eba02213ae28ac0ced28f54a1d1af6a09/dialog-example.png) 36 | 37 | After that, the extension icon should now be blue, meaning the extension is 38 | active for your current tab: ![active 39 | icon](https://raw.githubusercontent.com/jswanner/DontF-WithPaste/73e5d11eba02213ae28ac0ced28f54a1d1af6a09/clipboard-active-32.png) 40 | 41 | ## Version 2 Upgrade 42 | 43 | Version 2 is a major update to the extension. It makes it much easier to ensure 44 | the extension is only running on sites that are bad actors with copy & paste 45 | events and it also provides visibility into the active/inactive state of the 46 | extension for each tab. 47 | 48 | In order to provide the smoothest experience as possible, the extension needs 49 | to know when you change active tabs. In order for the extension to know about 50 | that event, it needs the `tabs` permission, which Chrome describes as "can read 51 | and change all your data on websites you visit." That description is very 52 | scary, and is certainly not what this extension is doing. Being an open-sourced 53 | project, you can always read all the code to see how this extension works, and 54 | what it's [not] doing with your data. 55 | 56 | To read more about the version 2 upgrade, see: [the wiki 57 | page](https://github.com/jswanner/DontF-WithPaste/wiki/Version-2.0). 58 | 59 | ## Bookmarklet 60 | 61 | If for some reason you prefer to use a [bookmarklet][] to accomplish the same 62 | goal, you can do so by adding a bookmark to the following URI: 63 | 64 | ```js 65 | javascript:forceBrowserDefault=(e=>{e.stopImmediatePropagation();return true;});['copy','cut','paste'].forEach(e=>document.addEventListener(e,forceBrowserDefault,true)); 66 | ``` 67 | 68 | ![bookmarklet](https://user-images.githubusercontent.com/576853/166342567-e7ed37ce-e2be-442b-a6b3-c5705f92ac9f.png) 69 | ![chrome bookmarks](https://user-images.githubusercontent.com/261/167724011-7b9a3fa5-ad1d-44eb-86b9-d396edcb17bf.png) 70 | 71 | Now if you encounter a problematic page, you can click on this bookmark (or 72 | enter a keyword like `dfwp` into the address bar) in order to liberate your 73 | clipboard once more. Using this method can also help to mitigate 74 | [fingerprinting][], even if you are using Chrome. Note: this will not prevent 75 | blocking of clipboard events in iframes. 76 | 77 | [bookmarklet]: https://en.wikipedia.org/wiki/Bookmarklet 78 | [fingerprinting]: https://en.wikipedia.org/wiki/Device_fingerprint#Browser_extensions 79 | -------------------------------------------------------------------------------- /background.js: -------------------------------------------------------------------------------- 1 | import DFWP, { Rules } from "./dfwp.js"; 2 | const { browser, storage } = DFWP; 3 | let rules = new Rules(); 4 | 5 | const checkIfActive = async (tabId) => { 6 | const tab = await browser.tabs.get(tabId); 7 | const match = rules.some((r) => r.test(tab.url)); 8 | 9 | if (match) { 10 | browser.action.setIcon({ path: 'clipboard-active-32.png' }); 11 | browser.action.setTitle({ title: "Don't F*** With Paste (active)" }); 12 | try { 13 | await browser.tabs.sendMessage(tab.id, { active: true }); 14 | } catch { } 15 | } else { 16 | browser.action.setIcon({ path: 'clipboard-inactive-32.png' }); 17 | browser.action.setTitle({ title: "Don't F*** With Paste (inactive)" }); 18 | try { 19 | await browser.tabs.sendMessage(tab.id, { active: false }); 20 | } catch { } 21 | } 22 | }; 23 | 24 | const fetchRules = (cb) => { 25 | storage.get({ rules: [] }, ({ rules: values }) => { 26 | rules = Rules.deserialize(values); 27 | if (cb) { cb(); } 28 | }); 29 | }; 30 | 31 | browser.runtime.onMessage.addListener(async ({ didLoad }) => { 32 | if (didLoad) { 33 | const [tab] = await browser.tabs.query({ active: true, windowId: browser.windows.WINDOW_ID_CURRENT }) 34 | checkIfActive(tab.id); 35 | } 36 | }); 37 | 38 | browser.storage.onChanged.addListener(() => { 39 | fetchRules(async () => { 40 | const tabs = await browser.tabs.query({ active: true }); 41 | tabs.forEach(tab => checkIfActive(tab.id)); 42 | }) 43 | }); 44 | 45 | browser.tabs.onActivated.addListener(({ tabId }) => { 46 | checkIfActive(tabId) 47 | }); 48 | 49 | fetchRules(); 50 | -------------------------------------------------------------------------------- /clipboard-active-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jswanner/DontF-WithPaste/c1d42a04f7f08c65eeb7a78413f5df126f6ecad1/clipboard-active-128.png -------------------------------------------------------------------------------- /clipboard-active-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jswanner/DontF-WithPaste/c1d42a04f7f08c65eeb7a78413f5df126f6ecad1/clipboard-active-32.png -------------------------------------------------------------------------------- /clipboard-active-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jswanner/DontF-WithPaste/c1d42a04f7f08c65eeb7a78413f5df126f6ecad1/clipboard-active-48.png -------------------------------------------------------------------------------- /clipboard-active.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jswanner/DontF-WithPaste/c1d42a04f7f08c65eeb7a78413f5df126f6ecad1/clipboard-active.png -------------------------------------------------------------------------------- /clipboard-inactive-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jswanner/DontF-WithPaste/c1d42a04f7f08c65eeb7a78413f5df126f6ecad1/clipboard-inactive-128.png -------------------------------------------------------------------------------- /clipboard-inactive-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jswanner/DontF-WithPaste/c1d42a04f7f08c65eeb7a78413f5df126f6ecad1/clipboard-inactive-32.png -------------------------------------------------------------------------------- /clipboard-inactive-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jswanner/DontF-WithPaste/c1d42a04f7f08c65eeb7a78413f5df126f6ecad1/clipboard-inactive-48.png -------------------------------------------------------------------------------- /clipboard-inactive.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jswanner/DontF-WithPaste/c1d42a04f7f08c65eeb7a78413f5df126f6ecad1/clipboard-inactive.png -------------------------------------------------------------------------------- /content.js: -------------------------------------------------------------------------------- 1 | let runtime; 2 | 3 | try { 4 | runtime = window.browser.runtime; 5 | } catch { 6 | runtime = window.chrome.runtime; 7 | } 8 | 9 | const forceBrowserDefault = function(e){ 10 | e.stopImmediatePropagation(); 11 | return true; 12 | }; 13 | 14 | runtime.onMessage.addListener(({ active }) => { 15 | if (active) { 16 | document.addEventListener('copy', forceBrowserDefault, true); 17 | document.addEventListener('cut', forceBrowserDefault, true); 18 | document.addEventListener('paste', forceBrowserDefault, true); 19 | } else { 20 | document.removeEventListener('copy', forceBrowserDefault, true); 21 | document.removeEventListener('cut', forceBrowserDefault, true); 22 | document.removeEventListener('paste', forceBrowserDefault, true); 23 | } 24 | }); 25 | 26 | runtime.sendMessage({ didLoad: true }); 27 | -------------------------------------------------------------------------------- /dfwp.js: -------------------------------------------------------------------------------- 1 | const DFWP = {}; 2 | 3 | try { 4 | DFWP.browser = browser; 5 | } catch { 6 | DFWP.browser = chrome; 7 | } 8 | 9 | if (DFWP.browser.storage.sync) { 10 | DFWP.storage = DFWP.browser.storage.sync; 11 | } else { 12 | DFWP.storage = DFWP.browser.storage.local; 13 | } 14 | 15 | export default DFWP; 16 | 17 | export class Rule { 18 | constructor(value) { 19 | this.value = value || ''; 20 | } 21 | 22 | get pattern() { 23 | return new RegExp(this.value || '(?=a)b'); 24 | } 25 | 26 | test(string) { 27 | return this.pattern.test(string); 28 | } 29 | } 30 | 31 | export class Rules extends Set { 32 | static get [Symbol.species]() { return Set; } 33 | 34 | static deserialize(values) { 35 | const rules = values.map((v) => new Rule(v)); 36 | return new Rules(rules); 37 | } 38 | 39 | get array() { 40 | return Array.from(this); 41 | } 42 | 43 | filter(cb) { 44 | return this.array.filter(cb); 45 | } 46 | 47 | find(cb) { 48 | return this.array.find(cb); 49 | } 50 | 51 | serialize() { 52 | return this.array.map(r => r.value).filter(v => v.length); 53 | } 54 | 55 | some(cb) { 56 | return this.array.some(cb); 57 | } 58 | } 59 | 60 | export class RuleView { 61 | constructor(rule, rules) { 62 | this.rule = rule; 63 | this.rules = rules; 64 | } 65 | 66 | onclick(event) { 67 | this.rules.delete(this.rule); 68 | event.target.parentNode.remove(); 69 | } 70 | 71 | oninput(event) { 72 | this.rule.value = event.target.value; 73 | } 74 | 75 | render(container, templateSelector = '#template') { 76 | const clone = document.importNode(this.template(templateSelector).content, true); 77 | const input = clone.querySelector('.input'); 78 | input.value = this.rule.value; 79 | input.addEventListener('input', this.oninput.bind(this)); 80 | 81 | const button = clone.querySelector('.delete'); 82 | if (button) { 83 | button.addEventListener('click', this.onclick.bind(this)); 84 | } 85 | 86 | container.appendChild(clone); 87 | } 88 | 89 | template(templateSelector) { 90 | return document.querySelector(templateSelector); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /dialog-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jswanner/DontF-WithPaste/c1d42a04f7f08c65eeb7a78413f5df126f6ecad1/dialog-example.png -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "action": { 3 | "default_icon": "clipboard-inactive-32.png", 4 | "default_popup": "popup.html" 5 | }, 6 | "background": { 7 | "service_worker": "background.js" , 8 | "type": "module" 9 | }, 10 | "content_scripts": [ 11 | { 12 | "all_frames": true, 13 | "js": [ "content.js" ], 14 | "matches": [""] 15 | } 16 | ], 17 | "description": "Prevents the blocking of copying from & pasting into input fields", 18 | "homepage_url": "https://github.com/jswanner/DontF-WithPaste", 19 | "icons": { 20 | "48": "clipboard-inactive-48.png", 21 | "128": "clipboard-inactive-128.png" 22 | }, 23 | "manifest_version": 3, 24 | "name": "Don't F*** With Paste", 25 | "options_ui": { 26 | "page": "options.html" 27 | }, 28 | "permissions": [ 29 | "storage", 30 | "tabs" 31 | ], 32 | "version": "3.1" 33 | } 34 | -------------------------------------------------------------------------------- /options.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 |
10 |

This extension will be active for sites that match any of the following patterns:

11 |
    12 |
13 | 14 |

Each pattern will be evaluated as a JavaScript Regular Expression.

15 |
16 |
17 | 18 |
19 |
20 | 21 |

active icon - active on current tab

22 |

inactive icon - inactive on current tab

23 |
24 |
25 | 26 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /options.js: -------------------------------------------------------------------------------- 1 | import DFWP, { Rule, Rules, RuleView } from "./dfwp.js"; 2 | const { browser, storage } = DFWP; 3 | 4 | let rules = new Rules(); 5 | 6 | function displayRules(container) { 7 | while (container.firstChild) { 8 | container.removeChild(container.firstChild); 9 | } 10 | 11 | storage.get({ rules: [] }, ({ rules: values }) => { 12 | rules = Rules.deserialize(values); 13 | rules.forEach((rule) => new RuleView(rule, rules).render(container)); 14 | }); 15 | } 16 | 17 | document.addEventListener('DOMContentLoaded', () => { 18 | const container = document.querySelector('.container'); 19 | const form = document.querySelector('.form'); 20 | 21 | form.addEventListener('submit', (event) => { 22 | event.preventDefault(); 23 | storage.set({ rules: rules.serialize() }); 24 | }); 25 | 26 | document.querySelector('.add').addEventListener('click', () => { 27 | const rule = new Rule(); 28 | rules.add(rule); 29 | new RuleView(rule, rules).render(container); 30 | }); 31 | 32 | browser.storage.onChanged.addListener(() => { 33 | displayRules(container); 34 | }); 35 | 36 | displayRules(container); 37 | }); 38 | -------------------------------------------------------------------------------- /popup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 |
10 |

Add/edit patterns used to activate this extension:

11 |
    12 |
13 | 14 |

Each pattern will be evaluated as a JavaScript Regular Expression.

15 |

The above list of patterns are those that match the current page's location, along with those you may have just added, to see all configured patterns view the extension's options page.

16 |
17 |
18 | 19 | 20 |
21 |
22 | 23 | 29 | 30 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /popup.js: -------------------------------------------------------------------------------- 1 | import DFWP, { Rule, Rules, RuleView } from "./dfwp.js"; 2 | const { browser, storage } = DFWP; 3 | 4 | document.addEventListener('DOMContentLoaded', () => { 5 | const add = document.querySelector('.add'); 6 | const cancel = document.querySelector('.cancel'); 7 | const container = document.querySelector('.container'); 8 | const form = document.querySelector('.form'); 9 | const options = document.querySelector('.options'); 10 | let rules = new Rules(); 11 | 12 | form.addEventListener('submit', event => { 13 | event.preventDefault(); 14 | storage.set({ rules: rules.serialize() }); 15 | window.close(); 16 | }); 17 | 18 | cancel.addEventListener('click', () => { 19 | window.close(); 20 | }); 21 | 22 | options.addEventListener('click', () => { 23 | browser.runtime.openOptionsPage(); 24 | }); 25 | 26 | storage.get({ rules: [] }, ({ rules: values }) => { 27 | rules = Rules.deserialize(values); 28 | 29 | browser.tabs.query({ active: true, windowId: browser.windows.WINDOW_ID_CURRENT }, ([tab]) => { 30 | const addHandler = () => { 31 | const rule = new Rule(new URL(tab.url).origin.replace(/\./g, '\\.')); 32 | rules.add(rule); 33 | new RuleView(rule, rules).render(container, '#new'); 34 | }; 35 | 36 | add.addEventListener('click', addHandler); 37 | 38 | const matching = rules.filter(rule => rule.test(tab.url)); 39 | matching.forEach(rule => new RuleView(rule, rules).render(container, '#existing')); 40 | 41 | if (!matching.length) { 42 | addHandler(); 43 | } 44 | 45 | const remove = document.querySelector('.delete'); 46 | if (remove) { 47 | remove.addEventListener('click', () => { 48 | storage.set({ rules: rules.serialize() }); 49 | window.close(); 50 | }); 51 | } 52 | }); 53 | }); 54 | 55 | }); 56 | -------------------------------------------------------------------------------- /styles.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-size: 14px; 3 | margin: 0; 4 | width: 40em; 5 | } 6 | 7 | .delete { 8 | min-height: inherit; 9 | min-width: inherit; 10 | padding: 0; 11 | width: 1.5em; 12 | } 13 | 14 | fieldset { 15 | border: 0 none; 16 | padding: 1em; 17 | } 18 | 19 | fieldset + fieldset { 20 | border-top: 1px solid #D2D2D2; 21 | } 22 | 23 | img { 24 | height: 14px; 25 | vertical-align: middle; 26 | width: 14px; 27 | } 28 | 29 | input { 30 | width: 100%; 31 | } 32 | 33 | label { 34 | display: block; 35 | } 36 | 37 | li { 38 | display: flex; 39 | } 40 | 41 | p { 42 | color: #888; 43 | font-size: smaller; 44 | line-height: 14px; 45 | margin: 0; 46 | } 47 | 48 | .inline { 49 | display: inline-block; 50 | margin-right: 1em; 51 | } 52 | 53 | * + p { 54 | margin-top: 1em; 55 | } 56 | 57 | ul { 58 | list-style-type: none; 59 | padding-left: 0; 60 | } 61 | -------------------------------------------------------------------------------- /test/iframe.html: -------------------------------------------------------------------------------- 1 | 2 |

(This is a form inside of an iframe.)

3 |
4 | 5 | 6 | 7 | 8 |
9 | -------------------------------------------------------------------------------- /test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | I'm fucking with paste 4 | 5 | 6 | 7 |

The input boxes below doesn't let you copy or paste stuff in it; every 8 | time a page like this is created, a kitten is killed.

9 |

(This is a form in the root document.)

10 |
11 |

Go ahead, try to paste:

12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /test/script.js: -------------------------------------------------------------------------------- 1 | window.onload = function(){ 2 | const preventDefault = e => e.preventDefault(); 3 | 4 | const listener = document.getElementById('listener'); 5 | const property = document.getElementById('property'); 6 | 7 | listener.addEventListener('copy', preventDefault, false); 8 | listener.addEventListener('cut', preventDefault, false); 9 | listener.addEventListener('paste', preventDefault, false); 10 | property.oncopy = preventDefault; 11 | property.oncut = preventDefault; 12 | property.onpaste = preventDefault; 13 | }; 14 | --------------------------------------------------------------------------------