This commit is contained in:
45Tatami 2021-12-31 15:49:02 +01:00
parent 924a8f1350
commit 61c0a11524
4 changed files with 117 additions and 76 deletions

View File

@ -1,12 +1,85 @@
#Clipboard Inserter # Native Inserter
A simple addon whose purpose is to automatically insert contents of clipboard into the page. Fork of the Clipboard Inserter whose purpose it is to automatically insert the
data received by an in this repository included native application into a
broswer page.
If you're looking to install this addon, it's available here: It is useful for use with text extraction tools like Textractor for looking up
words in the browser.
- [Firefox](https://addons.mozilla.org/firefox/addon/clipboard-inserter/) This repository contains the browser plugin, native application and native
- [Chrome web store](https://chrome.google.com/webstore/detail/clipboard-inserter/deahejllghicakhplliloeheabddjajm) manifest. Textractor plugin is not included.
Or, if the links don't work, just search for "clipboard inserter" in the appropriate addon market. Currently this addon is not available on any of the browsers Addon Store.
On Chrome, if you want to use this extension with local files (the `file://` protocol), you have to enable the "Allow access to file URLs" setting in the extension details. Only tested on GNU/Linux with Firefox.
## Install
You will need:
- This code
- A go compiler
- A browser supporting native extensions (eg modern Firefox, Chrome)
1) Compile and install the native application
2) Put the native application manifest into your browsers expected directory
3) Install the addon in your browser
For this to be useful you will also need a sender, for example a Textractor
plugin (not included) and a browser page that the can be inserted to.
## How does it work
The browser plugin starts a native application which creates a raw TCP listen
socket for incoming connections.
One or more applications (eg Textractor plugin) will connect to this socket and
send messages. Messages consist of a 4 Byte little-endian length header and
UTF-8 payload.
The native application will forward the UTF-8 data to the browser plugin which
in turn will insert the text into a webpage similar to the original clipboard
version.
## Troubleshooting
The native application is writing to stderr. Browsers usually forward this to
their default log.
The browser addon itself
## (Not so) FAQ
#### Why not the clipboard
Abusing the clipboard functionality to transfer data to the browser is ugly. It
does also not work under Wayland which does not allow unfocused applications to
access the clipboard.
(No opinion here whether the native application/messaging setup is more or less
ugly than the clipboard approach)
It also has the bonus of working with VMs without setting up clipboard
forwarding between host and guest.
#### Why a native application
The APIs including TCP listen sockets are no longer supported by modern
Browsers. The only alternative would be connecting to a WebSocket server, but
this has two drawbacks:
1) The API only supports the client side protocol and does not implement a
server. This would mean the sending side would need to be the server and the
native application the client, which would not work with multiple senders.
2) The sending side (eg C++ Textractor plugin) will need to implement a
Websocket server which is a lot more work than raw tcp.
## Bugs
- Native messaging requires native host order prefixed messages. Golang does
not have an easy way to retrieve this information. The native application
will use little-endian which is the default on most common architectures
- Messages over the native-messaging 1MB limit will be discarded instead of
split up

View File

@ -1,5 +1,6 @@
<html> <html>
<head> <head>
<meta charset="utf-8"/>
<script src="/default-options.js"></script> <script src="/default-options.js"></script>
<script src="monitor.js"></script> <script src="monitor.js"></script>
</head> </head>

View File

@ -1,13 +1,12 @@
console.log("I'm alive") console.log("I'm alive")
let previousContent = ""
let listeningTabs = [] let listeningTabs = []
let timer = null
let options = defaultOptions let options = defaultOptions
chrome.storage.local.get(defaultOptions, chrome.storage.local.get(defaultOptions,
o => options = o) o => options = o)
chrome.storage.onChanged.addListener((changes, area) => { chrome.storage.onChanged.addListener((changes, area) => {
if(area === "local") { if(area === "local") {
const optionKeys = Object.keys(options) const optionKeys = Object.keys(options)
@ -16,78 +15,45 @@ chrome.storage.onChanged.addListener((changes, area) => {
options[key] = changes[key].newValue options[key] = changes[key].newValue
} }
} }
updateTimer()
} }
}) })
chrome.browserAction.onClicked.addListener(() => { chrome.browserAction.onClicked.addListener((tab) => {
chrome.tabs.query({ active: true, currentWindow: true }, toggleTab(tab.id)
([t]) => toggleTab(t.id))
}) })
window.onload = () => { napp = browser.runtime.connectNative("native_inserter")
document.querySelector("#paste-target").addEventListener("paste", e => { napp.onDisconnect.addListener((p) => {
if(e.clipboardData.getData("text/plain") === "") { if (p.error) {
e.preventDefault() // prevent anything that is not representable as plain text from being pasted console.error(`Disconnected native app due to an error: ${p.error.message}`)
} }
}) })
} napp.onMessage.addListener((msg) => {
console.log("Received: " + msg.body)
const pasteTarget = document.querySelector("#paste-target")
pasteTarget.innerText = msg.body
const content = pasteTarget.innerText
listeningTabs.forEach(id => notifyForeground(id, content))
})
function toggleTab(id) { function toggleTab(id) {
const index = listeningTabs.indexOf(id) const index = listeningTabs.indexOf(id)
if(index >= 0) { if(index >= 0) {
uninject(id) uninject(id)
listeningTabs.splice(index, 1) listeningTabs.splice(index, 1)
updateTimer()
chrome.browserAction.setBadgeText({ text: "", tabId: id }) chrome.browserAction.setBadgeText({ text: "", tabId: id })
} else { } else {
chrome.tabs.executeScript({file: "/fg/insert.js"}) chrome.tabs.executeScript({file: "/fg/insert.js"})
listeningTabs.push(id) listeningTabs.push(id)
updateTimer()
chrome.browserAction.setBadgeBackgroundColor({ color: "green", tabId: id }) chrome.browserAction.setBadgeBackgroundColor({ color: "green", tabId: id })
chrome.browserAction.setBadgeText({ text: "ON", tabId: id }) chrome.browserAction.setBadgeText({ text: "ON", tabId: id })
} }
} }
function notifyForeground(id, text) { function notifyForeground(id, text) {
chrome.tabs.sendMessage(id, { chrome.tabs.sendMessage(id, { action: "insert", text, options })
action: "insert", text, options
})
} }
function uninject(id) { function uninject(id) {
chrome.tabs.sendMessage(id, { action: "uninject" }) chrome.tabs.sendMessage(id, { action: "uninject" })
} }
function checkClipboard() {
const pasteTarget = document.querySelector("#paste-target")
pasteTarget.innerText = ""
pasteTarget.focus()
document.execCommand("paste")
const content = pasteTarget.innerText
if(content.trim() !== previousContent.trim() && content != "") {
listeningTabs.forEach(id => notifyForeground(id, content))
previousContent = content
}
}
function updateTimer() {
function stop() {
clearInterval(timer.id)
timer = null
}
function start() {
const id = setInterval(checkClipboard, options.monitorInterval)
timer = { id, interval: options.monitorInterval }
}
if(listeningTabs.length > 0) {
if(timer === null) {
start()
} else if(timer.interval !== options.monitorInterval) {
stop()
start()
}
} else {
stop()
}
}

View File

@ -1,22 +1,23 @@
{ {
"manifest_version": 2, "manifest_version": 2,
"name": "Clipboard Inserter", "name": "Native Inserter",
"version": "0.3.2", "version": "1.0.0",
"description": "A simple addon that inserts clipboard contents into the page. Uses icon made by Google from www.flaticon.com licensed by CC 3.0 BY", "description": "An addon that inserts contents received from a native application into a page. Forked from clipboard-inserter. Uses icon made by Google from www.flaticon.com licensed by CC 3.0 BY",
"icons": {}, "icons": {},
"applications": { "browser_specific_settings": {
"gecko": { "gecko": {
"id": "@clipboard-inserter" "id": "@native-inserter",
"strict_min_version": "50.0"
} }
}, },
"permissions": [ "permissions": [
"activeTab", "activeTab",
"clipboardRead",
"storage", "storage",
"nativeMessaging",
"file://*/*" "file://*/*"
], ],
@ -27,7 +28,7 @@
"32": "icon/icon32.png", "32": "icon/icon32.png",
"64": "icon/icon64.png" "64": "icon/icon64.png"
}, },
"default_title": "Toggle clipboard inserter" "default_title": "Toggle Native Inserter"
}, },
"background": { "background": {