diff options
author | toasted-nutbread <toasted-nutbread@users.noreply.github.com> | 2021-04-10 23:55:11 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-04-10 23:55:11 -0400 |
commit | 20d60a2ba79c065586805806ea703a8057839f75 (patch) | |
tree | 3043090e118e03a8e276d2a7f1557e525ac23239 /ext/js | |
parent | b23c4bff4bf319ea79eea0d025e21eb19e6dcd68 (diff) |
Initial safari compatibility (#1609)
* Update environment info to return the 'safari' browser
* Fix popup display on Safari
* Update environment assignment
* Add data-loading-stalled property when loading takes longer than expected
* Add notification when loading has stalled
* Allow getDictionaryInfo invocation on non-privileged contexts
* Update _validatePrivilegedMessageSender
* Don't listen to 'voiceschanged' event unless addEventListener is present
Also expose an event
Diffstat (limited to 'ext/js')
-rw-r--r-- | ext/js/background/backend.js | 14 | ||||
-rw-r--r-- | ext/js/background/environment.js | 14 | ||||
-rw-r--r-- | ext/js/media/audio-system.js | 18 | ||||
-rw-r--r-- | ext/js/pages/action-popup-main.js | 9 | ||||
-rw-r--r-- | ext/js/pages/settings/audio-controller.js | 4 | ||||
-rw-r--r-- | ext/js/pages/settings/settings-main.js | 27 |
6 files changed, 67 insertions, 19 deletions
diff --git a/ext/js/background/backend.js b/ext/js/background/backend.js index a6b9c0dc..21b18e99 100644 --- a/ext/js/background/backend.js +++ b/ext/js/background/backend.js @@ -112,7 +112,7 @@ class Backend { ['getDisplayTemplatesHtml', {async: true, contentScript: true, handler: this._onApiGetDisplayTemplatesHtml.bind(this)}], ['getZoom', {async: true, contentScript: true, handler: this._onApiGetZoom.bind(this)}], ['getDefaultAnkiFieldTemplates', {async: false, contentScript: true, handler: this._onApiGetDefaultAnkiFieldTemplates.bind(this)}], - ['getDictionaryInfo', {async: true, contentScript: false, handler: this._onApiGetDictionaryInfo.bind(this)}], + ['getDictionaryInfo', {async: true, contentScript: true, handler: this._onApiGetDictionaryInfo.bind(this)}], ['getDictionaryCounts', {async: true, contentScript: false, handler: this._onApiGetDictionaryCounts.bind(this)}], ['purgeDatabase', {async: true, contentScript: false, handler: this._onApiPurgeDatabase.bind(this)}], ['getMedia', {async: true, contentScript: true, handler: this._onApiGetMedia.bind(this)}], @@ -1288,10 +1288,14 @@ class Backend { } _validatePrivilegedMessageSender(sender) { - const url = sender.url; - if (!(typeof url === 'string' && yomichan.isExtensionUrl(url))) { - throw new Error('Invalid message sender'); - } + let {url} = sender; + if (typeof url === 'string' && yomichan.isExtensionUrl(url)) { return; } + const {tab} = url; + if (typeof tab === 'object' && tab !== null) { + ({url} = tab); + if (typeof url === 'string' && yomichan.isExtensionUrl(url)) { return; } + } + throw new Error('Invalid message sender'); } _getBrowserIconTitle() { diff --git a/ext/js/background/environment.js b/ext/js/background/environment.js index 04099ca1..8111741f 100644 --- a/ext/js/background/environment.js +++ b/ext/js/background/environment.js @@ -83,9 +83,23 @@ class Environment { } catch (e) { // NOP } + if (this._isSafari()) { + return 'safari'; + } return 'firefox'; } else { return 'chrome'; } } + + _isSafari() { + const {vendor, userAgent} = navigator; + return ( + typeof vendor === 'string' && + typeof userAgent === 'string' && + vendor.includes('Apple') && + !userAgent.includes('CriOS') && + !userAgent.includes('FxiOS') + ); + } } diff --git a/ext/js/media/audio-system.js b/ext/js/media/audio-system.js index cf63511f..cc2bcfc0 100644 --- a/ext/js/media/audio-system.js +++ b/ext/js/media/audio-system.js @@ -19,18 +19,20 @@ * TextToSpeechAudio */ -class AudioSystem { +class AudioSystem extends EventDispatcher { constructor() { + super(); this._fallbackAudio = null; } prepare() { // speechSynthesis.getVoices() will not be populated unless some API call is made. - if (typeof speechSynthesis === 'undefined') { return; } - - const eventListeners = new EventListenerCollection(); - const onVoicesChanged = () => { eventListeners.removeAllEventListeners(); }; - eventListeners.addEventListener(speechSynthesis, 'voiceschanged', onVoicesChanged, false); + if ( + typeof speechSynthesis !== 'undefined' && + typeof speechSynthesis.addEventListener === 'function' + ) { + speechSynthesis.addEventListener('voiceschanged', this._onVoicesChanged.bind(this), false); + } } getFallbackAudio() { @@ -64,6 +66,10 @@ class AudioSystem { // Private + _onVoicesChanged(e) { + this.trigger('voiceschanged', e); + } + _isAudioValid(audio, source) { switch (source) { case 'jpod101': diff --git a/ext/js/pages/action-popup-main.js b/ext/js/pages/action-popup-main.js index 2de986da..4934802b 100644 --- a/ext/js/pages/action-popup-main.js +++ b/ext/js/pages/action-popup-main.js @@ -103,6 +103,10 @@ class DisplayController { let tab; try { tab = await this._getCurrentTab(); + // Safari assigns a tab object to the popup, other browsers do not + if (tab && await this._isSafari()) { + tab = void 0; + } } catch (e) { // NOP } @@ -220,6 +224,11 @@ class DisplayController { node.hidden = false; } } + + async _isSafari() { + const {browser} = await yomichan.api.getEnvironmentInfo(); + return browser === 'safari'; + } } (async () => { diff --git a/ext/js/pages/settings/audio-controller.js b/ext/js/pages/settings/audio-controller.js index e62383a8..6b1ce0b5 100644 --- a/ext/js/pages/settings/audio-controller.js +++ b/ext/js/pages/settings/audio-controller.js @@ -39,9 +39,7 @@ class AudioController { this._audioSourceAddButton.addEventListener('click', this._onAddAudioSource.bind(this), false); - if (typeof speechSynthesis !== 'undefined') { - speechSynthesis.addEventListener('voiceschanged', this._updateTextToSpeechVoices.bind(this), false); - } + this._audioSystem.on('voiceschanged', this._updateTextToSpeechVoices.bind(this), false); this._updateTextToSpeechVoices(); document.querySelector('#text-to-speech-voice-test').addEventListener('click', this._onTestTextToSpeech.bind(this), false); diff --git a/ext/js/pages/settings/settings-main.js b/ext/js/pages/settings/settings-main.js index 2560685c..6f3e2a58 100644 --- a/ext/js/pages/settings/settings-main.js +++ b/ext/js/pages/settings/settings-main.js @@ -24,6 +24,7 @@ * DictionaryController * DictionaryImportController * DocumentFocusController + * Environment * ExtensionKeyboardShortcutController * GenericSettingController * KeyboardShortcutController @@ -47,11 +48,16 @@ */ async function setupEnvironmentInfo() { + const {dataset} = document.documentElement; const {manifest_version: manifestVersion} = chrome.runtime.getManifest(); - const {browser, platform} = await yomichan.api.getEnvironmentInfo(); - document.documentElement.dataset.browser = browser; - document.documentElement.dataset.os = platform.os; - document.documentElement.dataset.manifestVersion = `${manifestVersion}`; + dataset.manifestVersion = `${manifestVersion}`; + + const environment = new Environment(); + await environment.prepare(); + const {browser, platform} = environment.getInfo(); + + dataset.browser = browser; + dataset.os = platform.os; } async function setupGenericSettingsController(genericSettingController) { @@ -67,9 +73,20 @@ async function setupGenericSettingsController(genericSettingController) { const statusFooter = new StatusFooter(document.querySelector('.status-footer-container')); statusFooter.prepare(); + setupEnvironmentInfo(); + + let prepareTimer = setTimeout(() => { + prepareTimer = null; + document.documentElement.dataset.loadingStalled = 'true'; + }, 1000); + await yomichan.prepare(); - setupEnvironmentInfo(); + if (prepareTimer !== null) { + clearTimeout(prepareTimer); + prepareTimer = null; + } + delete document.documentElement.dataset.loadingStalled; const optionsFull = await yomichan.api.optionsGetFull(); |