Skip to content

Commit fa4dec5

Browse files
committed
Added Ofscreen for Clipboard
1 parent 3176238 commit fa4dec5

File tree

4 files changed

+103
-12
lines changed

4 files changed

+103
-12
lines changed

html/offscreen.html

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<!DOCTYPE html>
2+
<textarea id="text"></textarea>
3+
<script src="../js/offscreen.js"></script>

js/offscreen.js

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
// Copyright 2023 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
// Once the message has been posted from the service worker, checks are made to
16+
// confirm the message type and target before proceeding. This is so that the
17+
// module can easily be adapted into existing workflows where secondary uses for
18+
// the document (or alternate offscreen documents) might be implemented.
19+
20+
// Registering this listener when the script is first executed ensures that the
21+
// offscreen document will be able to receive messages when the promise returned
22+
// by `offscreen.createDocument()` resolves.
23+
chrome.runtime.onMessage.addListener(handleMessages);
24+
25+
// This function performs basic filtering and error checking on messages before
26+
// dispatching the
27+
// message to a more specific message handler.
28+
async function handleMessages(message) {
29+
// Return early if this message isn't meant for the offscreen document.
30+
if (message.target !== 'offscreen-doc') {
31+
return;
32+
}
33+
34+
// Dispatch the message to an appropriate handler.
35+
switch (message.type) {
36+
case 'copy-data-to-clipboard':
37+
handleClipboardWrite(message.data);
38+
break;
39+
default:
40+
console.warn(`Unexpected message type received: '${message.type}'.`);
41+
}
42+
}
43+
44+
// We use a <textarea> element for two main reasons:
45+
// 1. preserve the formatting of multiline text,
46+
// 2. select the node's content using this element's `.select()` method.
47+
const textEl = document.querySelector('#text');
48+
49+
// Use the offscreen document's `document` interface to write a new value to the
50+
// system clipboard.
51+
//
52+
// At the time this demo was created (Jan 2023) the `navigator.clipboard` API
53+
// requires that the window is focused, but offscreen documents cannot be
54+
// focused. As such, we have to fall back to `document.execCommand()`.
55+
async function handleClipboardWrite(data) {
56+
try {
57+
// Error if we received the wrong kind of data.
58+
if (typeof data !== 'string') {
59+
throw new TypeError(
60+
`Value provided must be a 'string', got '${typeof data}'.`
61+
);
62+
}
63+
64+
// `document.execCommand('copy')` works against the user's selection in a web
65+
// page. As such, we must insert the string we want to copy to the web page
66+
// and to select that content in the page before calling `execCommand()`.
67+
textEl.value = data;
68+
textEl.select();
69+
document.execCommand('copy');
70+
} finally {
71+
// Job's done! Close the offscreen document.
72+
window.close();
73+
}
74+
}

js/service-worker.js

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,18 @@
11

2-
async function send_notification(title, text){
2+
async function addToClipboard(value) {
3+
await chrome.offscreen.createDocument({
4+
url: 'html/offscreen.html',
5+
reasons: [chrome.offscreen.Reason.CLIPBOARD],
6+
justification: 'Write text to the clipboard.'
7+
});
8+
chrome.runtime.sendMessage({
9+
type: 'copy-data-to-clipboard',
10+
target: 'offscreen-doc',
11+
data: value
12+
});
13+
}
14+
15+
async function sendNotification(title, text){
316
chrome.notifications.create({
417
type: 'basic',
518
iconUrl: '/images/logo128.png',
@@ -9,7 +22,7 @@ async function send_notification(title, text){
922
});
1023
}
1124

12-
async function post_url(endpoint, url){
25+
async function postURL(endpoint, url){
1326
console.log('Processing URL: ' + url);
1427

1528
let _url = (await chrome.storage.local.get('url'))['url'];
@@ -39,19 +52,20 @@ async function genericOnClick(ctx) {
3952
console.log('Processing URL: ' + ctx.srcUrl);
4053
let response;
4154
try {
42-
response = await post_url('remote', ctx.srcUrl)
55+
response = await postURL('remote', ctx.srcUrl)
4356
} catch (error) {
44-
await send_notification('Fetch Error', 'Error: ' + error.message);
57+
await sendNotification('Fetch Error', 'Error: ' + error.message);
4558
}
4659
const data = await response.json();
4760
console.log(data);
4861
if (response.ok) {
4962
console.log(data['url']);
50-
await send_notification('Image Uploaded', data['url']);
63+
await addToClipboard(data['url']);
64+
await sendNotification('Image Uploaded', data['url']);
5165
// await navigator.clipboard.writeText(data['url']);
5266
} else {
5367
console.log(data['error']);
54-
await send_notification('Processing Error', 'Error: ' + data['error']);
68+
await sendNotification('Processing Error', 'Error: ' + data['error']);
5569
}
5670
}
5771
break;
@@ -61,19 +75,19 @@ async function genericOnClick(ctx) {
6175
console.log('Processing URL: ' + ctx.linkUrl);
6276
let response;
6377
try {
64-
response = await post_url('shorten', ctx.linkUrl)
78+
response = await postURL('shorten', ctx.linkUrl);
6579
} catch (error) {
66-
await send_notification('Fetch Error', 'Error: ' + error.message);
80+
await sendNotification('Fetch Error', 'Error: ' + error.message);
6781
}
6882
const data = await response.json();
6983
console.log(data);
7084
if (response.ok) {
7185
console.log(data['url']);
72-
await send_notification('Short Created', data['url']);
73-
// await navigator.clipboard.writeText(data['url']);
86+
await addToClipboard(data['url']);
87+
await sendNotification('Short Created', data['url']);
7488
} else {
7589
console.log(data['error']);
76-
await send_notification('Processing Error', 'Error: ' + data['error']);
90+
await sendNotification('Processing Error', 'Error: ' + data['error']);
7791
}
7892
}
7993
break;

manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"description": "Django Files Chrome Extension",
44
"version": "0.0.2",
55
"manifest_version": 3,
6-
"permissions": ["storage", "activeTab", "scripting", "contextMenus", "notifications", "clipboardWrite"],
6+
"permissions": ["storage", "activeTab", "scripting", "contextMenus", "notifications", "clipboardWrite", "offscreen"],
77
"options_page": "html/options.html",
88
"background": {
99
"service_worker": "js/service-worker.js"

0 commit comments

Comments
 (0)