-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbackground.ts
143 lines (133 loc) · 4.97 KB
/
background.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
import type { Message, MessageFromContent, MessageFromPopup } from './message';
import { loadConfig } from './config';
function command(name: string, arg: string | undefined = undefined): void {
if (!document.execCommand(name, false, arg)) {
throw Error(`Command '${name}' failed with argument ${arg}`);
}
}
// Workaround since navigator.clipboard.readText() in content script still requires user permission
// with a permission dialog even if 'clipboardRead' permission is set. This may be a bug of Chrome.
async function readClipboardText(): Promise<string> {
return new Promise<string>(resolve => {
const textarea = document.createElement('textarea');
textarea.addEventListener('input', () => {
resolve(textarea.value);
document.body.removeChild(textarea);
});
document.body.appendChild(textarea);
textarea.focus();
command('paste');
});
}
function writeClipboardText(text: string): void {
const textarea = document.createElement('textarea');
textarea.textContent = text;
document.body.appendChild(textarea);
textarea.select();
command('copy');
textarea.blur();
document.body.removeChild(textarea);
}
async function executeContentScript(): Promise<void> {
// Note: Check `window.unlinkTweetWasLoaded` to load content script only once.
// Content script is not loaded until 'Unlink Tweet' feature is triggered to
// reduce overhead.
return new Promise<void>(resolve => {
chrome.tabs.executeScript(
{
code: 'window.unlinkTweetWasLoaded',
},
([ret]) => {
if (ret) {
resolve();
return;
}
chrome.tabs.executeScript({ file: 'content_script.js' }, () => {
resolve();
});
},
);
});
}
chrome.runtime.onInstalled.addListener(() => {
chrome.contextMenus.create({
id: 'doTweetUnlink',
title: 'Unlink Tweet Text',
contexts: ['selection'],
documentUrlPatterns: ['https://mobile.twitter.com/*', 'https://twitter.com/*'],
});
chrome.declarativeContent.onPageChanged.removeRules(undefined, () => {
chrome.declarativeContent.onPageChanged.addRules([
{
conditions: [
new chrome.declarativeContent.PageStateMatcher({
pageUrl: { hostEquals: 'mobile.twitter.com' },
}),
new chrome.declarativeContent.PageStateMatcher({
pageUrl: { hostEquals: 'twitter.com' },
}),
],
actions: [new chrome.declarativeContent.ShowPageAction()],
},
]);
});
});
chrome.contextMenus.onClicked.addListener((info, tab) => {
if (info.menuItemId !== 'doTweetUnlink') {
return;
}
if (!tab || tab.id === undefined) {
console.error('Tab ID is not allocated!', tab);
return;
}
const tabId = tab.id;
Promise.all([executeContentScript(), readClipboardText(), loadConfig()])
.then(([, text, config]) => {
const msg: Message = {
type: 'contextMenu',
selected: info.selectionText ?? '',
clipboard: text,
config,
};
chrome.tabs.sendMessage(tabId, msg);
})
.catch(err => console.error('Could not handle context menu action', err));
});
chrome.runtime.onMessage.addListener((msg: MessageFromContent | MessageFromPopup, _, sendResponse) => {
switch (msg.type) {
case 'requestCopy': {
writeClipboardText(msg.text);
const res: Message = { type: 'responseCopy' };
sendResponse(res);
break;
}
case 'unlinkTweet': {
executeContentScript()
.then(() => {
chrome.tabs.query({ active: true, currentWindow: true }, tabs => {
if (tabs.length === 0) {
console.error('No active tab found');
return;
}
const tab = tabs[0];
if (tab.id === undefined) {
console.error('Tab ID is not set to active tab:', tab);
return;
}
const tabId = tab.id;
const req: Message = {
type: 'pageAction',
config: msg.config,
};
sendResponse();
chrome.tabs.sendMessage(tabId, req);
});
})
.catch(err => console.error('Failed to execute content script: content_script.js:', err));
break;
}
default:
console.error('FATAL: Unexpected msg:', msg);
break;
}
});