summaryrefslogtreecommitdiff
path: root/ext
diff options
context:
space:
mode:
Diffstat (limited to 'ext')
-rw-r--r--ext/background.html3
-rw-r--r--ext/css/background.css29
-rw-r--r--ext/js/background/backend.js6
-rw-r--r--ext/js/comm/clipboard-monitor.js2
-rw-r--r--ext/js/comm/clipboard-reader.js83
5 files changed, 84 insertions, 39 deletions
diff --git a/ext/background.html b/ext/background.html
index b3f85240..71990295 100644
--- a/ext/background.html
+++ b/ext/background.html
@@ -11,6 +11,7 @@
<link rel="icon" type="image/png" href="/images/icon48.png" sizes="48x48">
<link rel="icon" type="image/png" href="/images/icon64.png" sizes="64x64">
<link rel="icon" type="image/png" href="/images/icon128.png" sizes="128x128">
+ <link rel="stylesheet" type="text/css" href="/css/background.css">
</head>
<body>
@@ -61,7 +62,7 @@
https://bugzilla.mozilla.org/show_bug.cgi?id=1603985
-->
<!-- [html-validate-disable close-order] -->
-<div id="clipboard-image-paste-target" contenteditable="true">
+<div id="clipboard-rich-content-paste-target" contenteditable="true">
</body>
</html>
diff --git a/ext/css/background.css b/ext/css/background.css
new file mode 100644
index 00000000..b2aca1d6
--- /dev/null
+++ b/ext/css/background.css
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2022 Yomichan Authors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/* stylelint-disable declaration-no-important */
+#clipboard-rich-content-paste-target * {
+ background-image: none !important;
+ list-style-image: none !important;
+ content: none !important;
+ cursor: auto !important;
+ border-image-source: none !important;
+ offset-path: none !important;
+ -webkit-mask-image: none !important;
+ mask-image: none !important;
+}
+/* stylelint-enable declaration-no-important */
diff --git a/ext/js/background/backend.js b/ext/js/background/backend.js
index 197734b1..2154b32a 100644
--- a/ext/js/background/backend.js
+++ b/ext/js/background/backend.js
@@ -53,7 +53,7 @@ class Backend {
// eslint-disable-next-line no-undef
document: (typeof document === 'object' && document !== null ? document : null),
pasteTargetSelector: '#clipboard-paste-target',
- imagePasteTargetSelector: '#clipboard-image-paste-target'
+ richContentPasteTargetSelector: '#clipboard-rich-content-paste-target'
});
this._clipboardMonitor = new ClipboardMonitor({
japaneseUtil: this._japaneseUtil,
@@ -596,7 +596,7 @@ class Backend {
}
async _onApiClipboardGet() {
- return this._clipboardReader.getText();
+ return this._clipboardReader.getText(false);
}
async _onApiGetDisplayTemplatesHtml() {
@@ -1773,7 +1773,7 @@ class Backend {
try {
if (clipboardDetails !== null && clipboardDetails.text) {
- clipboardText = await this._clipboardReader.getText();
+ clipboardText = await this._clipboardReader.getText(false);
}
} catch (e) {
errors.push(serializeError(e));
diff --git a/ext/js/comm/clipboard-monitor.js b/ext/js/comm/clipboard-monitor.js
index 7a678461..f5be04e7 100644
--- a/ext/js/comm/clipboard-monitor.js
+++ b/ext/js/comm/clipboard-monitor.js
@@ -39,7 +39,7 @@ class ClipboardMonitor extends EventDispatcher {
let text = null;
try {
- text = await this._clipboardReader.getText();
+ text = await this._clipboardReader.getText(false);
} catch (e) {
// NOP
}
diff --git a/ext/js/comm/clipboard-reader.js b/ext/js/comm/clipboard-reader.js
index aaf4dcd8..9e9a6116 100644
--- a/ext/js/comm/clipboard-reader.js
+++ b/ext/js/comm/clipboard-reader.js
@@ -28,15 +28,15 @@ class ClipboardReader {
* @param {object} details Details about how to set up the instance.
* @param {?Document} details.document The Document object to be used, or null for no support.
* @param {?string} details.pasteTargetSelector The selector for the paste target element.
- * @param {?string} details.imagePasteTargetSelector The selector for the image paste target element.
+ * @param {?string} details.richContentPasteTargetSelector The selector for the rich content paste target element.
*/
- constructor({document=null, pasteTargetSelector=null, imagePasteTargetSelector=null}) {
+ constructor({document=null, pasteTargetSelector=null, richContentPasteTargetSelector=null}) {
this._document = document;
this._browser = null;
this._pasteTarget = null;
this._pasteTargetSelector = pasteTargetSelector;
- this._imagePasteTarget = null;
- this._imagePasteTargetSelector = imagePasteTargetSelector;
+ this._richContentPasteTarget = null;
+ this._richContentPasteTargetSelector = richContentPasteTargetSelector;
}
/**
@@ -56,13 +56,14 @@ class ClipboardReader {
/**
* Gets the text in the clipboard.
+ * @param {boolean} useRichText Whether or not to use rich text for pasting, when possible.
* @returns {string} A string containing the clipboard text.
* @throws {Error} Error if not supported.
*/
- async getText() {
+ async getText(useRichText) {
/*
Notes:
- document.execCommand('paste') doesn't work on Firefox.
+ document.execCommand('paste') sometimes doesn't work on Firefox.
See: https://bugzilla.mozilla.org/show_bug.cgi?id=1603985
Therefore, navigator.clipboard.readText() is used on Firefox.
@@ -72,7 +73,7 @@ class ClipboardReader {
being an extension with clipboard permissions. It effectively asks for the
non-extension permission for clipboard access.
*/
- if (this._isFirefox()) {
+ if (this._isFirefox() && !useRichText) {
try {
return await navigator.clipboard.readText();
} catch (e) {
@@ -86,21 +87,22 @@ class ClipboardReader {
throw new Error('Clipboard reading not supported in this context');
}
- let target = this._pasteTarget;
- if (target === null) {
- target = document.querySelector(this._pasteTargetSelector);
- if (target === null) {
- throw new Error('Clipboard paste target does not exist');
- }
- this._pasteTarget = target;
+ if (useRichText) {
+ const target = this._getRichContentPasteTarget();
+ target.focus();
+ document.execCommand('paste');
+ const result = target.textContent;
+ this._clearRichContent(target);
+ return result;
+ } else {
+ const target = this._getPasteTarget();
+ target.value = '';
+ target.focus();
+ document.execCommand('paste');
+ const result = target.value;
+ target.value = '';
+ return (typeof result === 'string' ? result : '');
}
-
- target.value = '';
- target.focus();
- document.execCommand('paste');
- const result = target.value;
- target.value = '';
- return (typeof result === 'string' ? result : '');
}
/**
@@ -143,23 +145,12 @@ class ClipboardReader {
throw new Error('Clipboard reading not supported in this context');
}
- let target = this._imagePasteTarget;
- if (target === null) {
- target = document.querySelector(this._imagePasteTargetSelector);
- if (target === null) {
- throw new Error('Clipboard paste target does not exist');
- }
- this._imagePasteTarget = target;
- }
-
+ const target = this._getRichContentPasteTarget();
target.focus();
document.execCommand('paste');
const image = target.querySelector('img[src^="data:"]');
const result = (image !== null ? image.getAttribute('src') : null);
- for (const image2 of target.querySelectorAll('img')) {
- image2.removeAttribute('src');
- }
- target.textContent = '';
+ this._clearRichContent(target);
return result;
}
@@ -177,4 +168,28 @@ class ClipboardReader {
reader.readAsDataURL(file);
});
}
+
+ _getPasteTarget() {
+ if (this._pasteTarget === null) { this._pasteTarget = this._findPasteTarget(this._pasteTargetSelector); }
+ return this._pasteTarget;
+ }
+
+ _getRichContentPasteTarget() {
+ if (this._richContentPasteTarget === null) { this._richContentPasteTarget = this._findPasteTarget(this._richContentPasteTargetSelector); }
+ return this._richContentPasteTarget;
+ }
+
+ _findPasteTarget(selector) {
+ const target = this._document.querySelector(selector);
+ if (target === null) { throw new Error('Clipboard paste target does not exist'); }
+ return target;
+ }
+
+ _clearRichContent(element) {
+ for (const image of element.querySelectorAll('img')) {
+ image.removeAttribute('src');
+ image.removeAttribute('srcset');
+ }
+ element.textContent = '';
+ }
}