mirror of
https://github.com/mdn/webextensions-examples.git
synced 2026-04-16 06:18:35 +02:00
Add example with advanced clipboard manipulation (#207)
(tested with Firefox 52 and 54)
This commit is contained in:
35
context-menu-copy-link-with-types/README.md
Normal file
35
context-menu-copy-link-with-types/README.md
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
# Context menu: Copy link with types
|
||||||
|
|
||||||
|
This example adds a context menu item to every link that copies the URL to the
|
||||||
|
clipboard, as plain text and as rich HTML.
|
||||||
|
|
||||||
|
## What it does
|
||||||
|
|
||||||
|
This extension includes:
|
||||||
|
|
||||||
|
* a background script that:
|
||||||
|
- Registers a context menu item for every link.
|
||||||
|
- Upon click, it invokes the function to copy text and HTML to the clipboard.
|
||||||
|
* a helper script, "clipboard-helper.js" that provides the copy-to-clipboard functionality.
|
||||||
|
In the example, this script is run as a content script, but the actual functionality can also
|
||||||
|
be used in visible extension pages such as extension button popups or extension tabs.
|
||||||
|
* a page, "preview.html" for testing the effect of copying to the clipboard.
|
||||||
|
This page does not need to be part of the extension, and can directly be opened in the browser.
|
||||||
|
|
||||||
|
To test the extension, right-click on any link to open a context menu, and choose the
|
||||||
|
"Copy link to clipboard" option. Then open preview.html and paste the clipboard content
|
||||||
|
in the two displayed boxes. The first box will display "This is text: ..." and the second
|
||||||
|
box will display "This is HTML: ...".
|
||||||
|
|
||||||
|
Note: since the add-on relies on a content script for copying the text, the copy operation
|
||||||
|
will only succeed if the add-on is allowed to run scripts in the current page.
|
||||||
|
If you wish to successfully copy the text even if the current page cannot be scripted, then
|
||||||
|
you can open an (extension) page in a new tab as a fallback.
|
||||||
|
|
||||||
|
## What it shows
|
||||||
|
|
||||||
|
* how to put data on the [clipboard](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Interact_with_the_clipboard)
|
||||||
|
with custom types ("text/plain" and "text/html" in the example).
|
||||||
|
* how to safely construct HTML from given text.
|
||||||
|
* how to safely create JavaScript code to run as a dynamic content script.
|
||||||
|
* how to dynamically run a static content script only once.
|
||||||
54
context-menu-copy-link-with-types/background.js
Normal file
54
context-menu-copy-link-with-types/background.js
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
browser.contextMenus.create({
|
||||||
|
id: "copy-link-to-clipboard",
|
||||||
|
title: "Copy link to clipboard",
|
||||||
|
contexts: ["link"],
|
||||||
|
});
|
||||||
|
browser.contextMenus.onClicked.addListener(function(info, tab) {
|
||||||
|
if (info.menuItemId === "copy-link-to-clipboard") {
|
||||||
|
// Examples: text and HTML to be copied.
|
||||||
|
const text = "This is text: " + info.linkUrl;
|
||||||
|
// Always HTML-escape external input to avoid XSS.
|
||||||
|
const safeUrl = escapeHTML(info.linkUrl);
|
||||||
|
const html = `This is HTML: <a href="${safeUrl}">${safeUrl}</a>`;
|
||||||
|
|
||||||
|
// The example will show how data can be copied, but since background
|
||||||
|
// pages cannot directly write to the clipboard, we will run a content
|
||||||
|
// script that copies the actual content.
|
||||||
|
|
||||||
|
// clipboard-helper.js defines function copyToClipboard.
|
||||||
|
const code = "copyToClipboard(" +
|
||||||
|
JSON.stringify(text) + "," +
|
||||||
|
JSON.stringify(html) + ");";
|
||||||
|
|
||||||
|
browser.tabs.executeScript({
|
||||||
|
code: "typeof copyToClipboard === 'function';",
|
||||||
|
}).then(function(results) {
|
||||||
|
// The content script's last expression will be true if the function
|
||||||
|
// has been defined. If this is not the case, then we need to run
|
||||||
|
// clipboard-helper.js to define function copyToClipboard.
|
||||||
|
if (!results || results[0] !== true) {
|
||||||
|
return browser.tabs.executeScript(tab.id, {
|
||||||
|
file: "clipboard-helper.js",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}).then(function() {
|
||||||
|
return browser.tabs.executeScript(tab.id, {
|
||||||
|
code,
|
||||||
|
});
|
||||||
|
}).catch(function(error) {
|
||||||
|
// This could happen if the extension is not allowed to run code in
|
||||||
|
// the page, for example if the tab is a privileged page.
|
||||||
|
console.error("Failed to copy text: " + error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// https://gist.github.com/Rob--W/ec23b9d6db9e56b7e4563f1544e0d546
|
||||||
|
function escapeHTML(str) {
|
||||||
|
// Note: string cast using String; may throw if `str` is non-serializable, e.g. a Symbol.
|
||||||
|
// Most often this is not the case though.
|
||||||
|
return String(str)
|
||||||
|
.replace(/&/g, "&")
|
||||||
|
.replace(/"/g, """).replace(/'/g, "'")
|
||||||
|
.replace(/</g, "<").replace(/>/g, ">");
|
||||||
|
}
|
||||||
18
context-menu-copy-link-with-types/clipboard-helper.js
Normal file
18
context-menu-copy-link-with-types/clipboard-helper.js
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
// This function must be called in a visible page, such as a browserAction popup
|
||||||
|
// or a content script. Calling it in a background page has no effect!
|
||||||
|
function copyToClipboard(text, html) {
|
||||||
|
function oncopy(event) {
|
||||||
|
document.removeEventListener("copy", oncopy, true);
|
||||||
|
// Hide the event from the page to prevent tampering.
|
||||||
|
event.stopImmediatePropagation();
|
||||||
|
|
||||||
|
// Overwrite the clipboard content.
|
||||||
|
event.preventDefault();
|
||||||
|
event.clipboardData.setData("text/plain", text);
|
||||||
|
event.clipboardData.setData("text/html", html);
|
||||||
|
}
|
||||||
|
document.addEventListener("copy", oncopy, true);
|
||||||
|
|
||||||
|
// Requires the clipboardWrite permission, or a user gesture:
|
||||||
|
document.execCommand("copy");
|
||||||
|
}
|
||||||
19
context-menu-copy-link-with-types/manifest.json
Normal file
19
context-menu-copy-link-with-types/manifest.json
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"manifest_version": 2,
|
||||||
|
"name": "Context menu: Copy link with types",
|
||||||
|
"description": "Add a context menu option to links to copy the link to the clipboard, as plain text and as a link in rich HTML.",
|
||||||
|
"version": "1.0",
|
||||||
|
"homepage_url": "https://github.com/mdn/webextensions-examples/tree/master/context-menu-copy-link-with-types",
|
||||||
|
|
||||||
|
"background": {
|
||||||
|
"scripts": [
|
||||||
|
"background.js"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
"permissions": [
|
||||||
|
"activeTab",
|
||||||
|
"contextMenus",
|
||||||
|
"clipboardWrite"
|
||||||
|
]
|
||||||
|
}
|
||||||
22
context-menu-copy-link-with-types/preview.html
Normal file
22
context-menu-copy-link-with-types/preview.html
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<style>
|
||||||
|
textarea, div[contentEditable] {
|
||||||
|
display: block;
|
||||||
|
border: 1px solid;
|
||||||
|
height: 5em;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
Use Ctrl+V (or Cmd+V) to paste plain text here:
|
||||||
|
<textarea></textarea>
|
||||||
|
|
||||||
|
Use Ctrl+V (or Cmd+V) to paste rich text (HTML) here:
|
||||||
|
<div contentEditable></div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Reference in New Issue
Block a user