Add example with advanced clipboard manipulation (#207)

(tested with Firefox 52 and 54)
This commit is contained in:
Rob Wu
2017-04-24 23:09:19 +02:00
committed by wbamberg
parent 5dd59a4028
commit 277ac935fa
5 changed files with 148 additions and 0 deletions

View 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.

View 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, "&amp;")
.replace(/"/g, "&quot;").replace(/'/g, "&#39;")
.replace(/</g, "&lt;").replace(/>/g, "&gt;");
}

View 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");
}

View 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"
]
}

View 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>