diff --git a/ext/fg/js/float.js b/ext/fg/js/float.js index 3b752df5..9f58439f 100644 --- a/ext/fg/js/float.js +++ b/ext/fg/js/float.js @@ -29,6 +29,8 @@ class DisplayFloat extends Display { this._windowMessageHandlers = new Map([ ['extensionUnloaded', {async: false, handler: this._onMessageExtensionUnloaded.bind(this)}] ]); + this._browser = null; + this._copyTextarea = null; this.registerActions([ ['copyHostSelection', () => this._copySelection()] @@ -43,6 +45,9 @@ class DisplayFloat extends Display { async prepare() { await super.prepare(); + const {browser} = await api.getEnvironmentInfo(); + this._browser = browser; + this.registerDirectMessageHandlers([ ['configure', {async: true, handler: this._onMessageConfigure.bind(this)}], ['setContentScale', {async: false, handler: this._onMessageSetContentScale.bind(this)}] @@ -150,10 +155,47 @@ class DisplayFloat extends Display { _copySelection() { if (window.getSelection().toString()) { return false; } - this._invokeOwner('copySelection'); + this._copyHostSelection(); return true; } + async _copyHostSelection() { + switch (this._browser) { + case 'firefox': + case 'firefox-mobile': + { + let text; + try { + text = await this._invokeOwner('getSelectionText'); + } catch (e) { + break; + } + this._copyText(text); + } + break; + default: + this._invokeOwner('copySelection'); + break; + } + } + + _copyText(text) { + const parent = document.body; + if (parent === null) { return; } + + let textarea = this._copyTextarea; + if (textarea === null) { + textarea = document.createElement('textarea'); + this._copyTextarea = textarea; + } + + textarea.value = text; + parent.appendChild(textarea); + textarea.select(); + document.execCommand('copy'); + parent.removeChild(textarea); + } + _setContentScale(scale) { const body = document.body; if (body === null) { return; } diff --git a/ext/fg/js/frontend.js b/ext/fg/js/frontend.js index 82a56f1e..2ee9b4a1 100644 --- a/ext/fg/js/frontend.js +++ b/ext/fg/js/frontend.js @@ -115,6 +115,7 @@ class Frontend { ['getUrl', {async: false, handler: this._onApiGetUrl.bind(this)}], ['closePopup', {async: false, handler: this._onApiClosePopup.bind(this)}], ['copySelection', {async: false, handler: this._onApiCopySelection.bind(this)}], + ['getSelectionText', {async: false, handler: this._onApiGetSelectionText.bind(this)}], ['getPopupInfo', {async: false, handler: this._onApiGetPopupInfo.bind(this)}], ['getDocumentInformation', {async: false, handler: this._onApiGetDocumentInformation.bind(this)}] ]); @@ -168,9 +169,14 @@ class Frontend { } _onApiCopySelection() { + // This will not work on Firefox if a popup has focus, which is usually the case when this function is called. document.execCommand('copy'); } + _onApiGetSelectionText() { + return document.getSelection().toString(); + } + _onApiGetPopupInfo() { return { popupId: (this._popup !== null ? this._popup.id : null) diff --git a/ext/mixed/js/display.js b/ext/mixed/js/display.js index c0102379..f79ba303 100644 --- a/ext/mixed/js/display.js +++ b/ext/mixed/js/display.js @@ -466,7 +466,7 @@ class Display extends EventDispatcher { this._closePopups(); this._setEventListenersActive(false); - let asigned = false; + let assigned = false; const eventArgs = {type, urlSearchParams, token}; this._historyHasChanged = true; this._contentType = type; @@ -476,7 +476,7 @@ class Display extends EventDispatcher { case 'kanji': { const isTerms = (type === 'terms'); - asigned = await this._setContentTermsOrKanji(token, isTerms, urlSearchParams, eventArgs); + assigned = await this._setContentTermsOrKanji(token, isTerms, urlSearchParams, eventArgs); } break; case 'unloaded': @@ -485,14 +485,14 @@ class Display extends EventDispatcher { eventArgs.content = content; this.trigger('contentUpdating', eventArgs); this._setContentExtensionUnloaded(); - asigned = true; + assigned = true; } break; } const stale = (this._setContentToken !== token); if (!stale) { - if (!asigned) { + if (!assigned) { type = 'clear'; this._contentType = type; const {content} = this._history; @@ -910,9 +910,7 @@ class Display extends EventDispatcher { container.appendChild(entry); } - if (typeof focusEntry === 'number') { - this._focusEntry(focusEntry, false); - } + this._focusEntry(typeof focusEntry === 'number' ? focusEntry : 0, false); if (typeof scrollX === 'number' || typeof scrollY === 'number') { let {x, y} = this._windowScroll; if (typeof scrollX === 'number') { x = scrollX; }