diff options
Diffstat (limited to 'ext')
| -rw-r--r-- | ext/bg/js/backend.js | 50 | ||||
| -rw-r--r-- | ext/bg/js/mecab.js | 33 | ||||
| -rw-r--r-- | ext/bg/js/settings2/mecab-controller.js | 67 | ||||
| -rw-r--r-- | ext/bg/js/settings2/settings-main.js | 4 | ||||
| -rw-r--r-- | ext/bg/settings2.html | 7 | ||||
| -rw-r--r-- | ext/mixed/js/api.js | 4 | 
6 files changed, 163 insertions, 2 deletions
| diff --git a/ext/bg/js/backend.js b/ext/bg/js/backend.js index 67b17cc9..f1983cb3 100644 --- a/ext/bg/js/backend.js +++ b/ext/bg/js/backend.js @@ -122,7 +122,8 @@ class Backend {              ['setAllSettings',               {async: true,  contentScript: false, handler: this._onApiSetAllSettings.bind(this)}],              ['getOrCreateSearchPopup',       {async: true,  contentScript: true,  handler: this._onApiGetOrCreateSearchPopup.bind(this)}],              ['isTabSearchPopup',             {async: true,  contentScript: true,  handler: this._onApiIsTabSearchPopup.bind(this)}], -            ['triggerDatabaseUpdated',       {async: false, contentScript: true,  handler: this._onApiTriggerDatabaseUpdated.bind(this)}] +            ['triggerDatabaseUpdated',       {async: false, contentScript: true,  handler: this._onApiTriggerDatabaseUpdated.bind(this)}], +            ['testMecab',                    {async: true,  contentScript: true,  handler: this._onApiTestMecab.bind(this)}]          ]);          this._messageHandlersWithProgress = new Map([          ]); @@ -676,6 +677,42 @@ class Backend {          this._triggerDatabaseUpdated(type, cause);      } +    async _onApiTestMecab() { +        if (!this._mecab.isEnabled()) { +            throw new Error('MeCab not enabled'); +        } + +        let permissionsOkay = false; +        try { +            permissionsOkay = await this._hasPermissions({permissions: ['nativeMessaging']}); +        } catch (e) { +            // NOP +        } +        if (!permissionsOkay) { +            throw new Error('Insufficient permissions'); +        } + +        const disconnect = !this._mecab.isConnected(); +        try { +            const version = await this._mecab.getVersion(); +            if (version === null) { +                throw new Error('Could not connect to native MeCab component'); +            } + +            const localVersion = this._mecab.getLocalVersion(); +            if (version !== localVersion) { +                throw new Error(`MeCab component version not supported: ${version}`); +            } +        } finally { +            // Disconnect if the connection was previously disconnected +            if (disconnect && this._mecab.isEnabled() && this._mecab.isActive()) { +                this._mecab.disconnect(); +            } +        } + +        return true; +    } +      // Command handlers      async _onCommandOpenSearchPage(params) { @@ -1904,4 +1941,15 @@ class Backend {              });          });      } + +    _hasPermissions(permissions) { +        return new Promise((resolve, reject) => chrome.permissions.contains(permissions, (result) => { +            const e = chrome.runtime.lastError; +            if (e) { +                reject(new Error(e.message)); +            } else { +                resolve(result); +            } +        })); +    }  } diff --git a/ext/bg/js/mecab.js b/ext/bg/js/mecab.js index ef5d6821..4eff2927 100644 --- a/ext/bg/js/mecab.js +++ b/ext/bg/js/mecab.js @@ -54,6 +54,39 @@ class Mecab {      }      /** +     * Disconnects the current port, but does not disable future connections. +     */ +    disconnect() { +        if (this._port !== null) { +            this._clearPort(); +        } +    } + +    /** +     * Returns whether or not the connection to the native application is active. +     * @returns `true` if the connection is active, `false` otherwise. +     */ +    isConnected() { +        return (this._port !== null); +    } + +    /** +     * Returns whether or not any invocation is currently active. +     * @returns `true` if an invocation is active, `false` otherwise. +     */ +    isActive() { +        return (this._invocations.size > 0); +    } + +    /** +     * Gets the local API version being used. +     * @returns An integer representing the API version that Yomichan uses. +     */ +    getLocalVersion() { +        return this._version; +    } + +    /**       * Gets the version of the MeCab component.       * @returns The version of the MeCab component, or `null` if the component was not found.       */ diff --git a/ext/bg/js/settings2/mecab-controller.js b/ext/bg/js/settings2/mecab-controller.js new file mode 100644 index 00000000..ff2a4a66 --- /dev/null +++ b/ext/bg/js/settings2/mecab-controller.js @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2021  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/>. + */ + +/* global + * api + */ + +class MecabController { +    constructor(settingsController) { +        this._settingsController = settingsController; +        this._testButton = null; +        this._resultsContainer = null; +        this._testActive = false; +    } + +    prepare() { +        this._testButton = document.querySelector('#test-mecab-button'); +        this._resultsContainer = document.querySelector('#test-mecab-results'); + +        this._testButton.addEventListener('click', this._onTestButtonClick.bind(this), false); +    } + +    // Private + +    _onTestButtonClick(e) { +        e.preventDefault(); +        this._testMecab(); +    } + +    async _testMecab() { +        if (this._testActive) { return; } + +        try { +            this._testActive = true; +            this._testButton.disabled = true; +            this._resultsContainer.textContent = ''; +            this._resultsContainer.hidden = true; +            await api.testMecab(); +            this._setStatus('Connection was successful', false); +        } catch (e) { +            this._setStatus(e.message, true); +        } finally { +            this._testActive = false; +            this._testButton.disabled = false; +        } +    } + +    _setStatus(message, isError) { +        this._resultsContainer.textContent = message; +        this._resultsContainer.hidden = false; +        this._resultsContainer.classList.toggle('danger-text', isError); +    } +} diff --git a/ext/bg/js/settings2/settings-main.js b/ext/bg/js/settings2/settings-main.js index f4a38d85..24248110 100644 --- a/ext/bg/js/settings2/settings-main.js +++ b/ext/bg/js/settings2/settings-main.js @@ -26,6 +26,7 @@   * ExtensionKeyboardShortcutController   * GenericSettingController   * KeyboardShortcutController + * MecabController   * ModalController   * NestedPopupsController   * PermissionsToggleController @@ -140,6 +141,9 @@ async function setupGenericSettingsController(genericSettingController) {          const popupWindowController = new PopupWindowController();          popupWindowController.prepare(); +        const mecabController = new MecabController(); +        mecabController.prepare(); +          await Promise.all(preparePromises);          document.documentElement.dataset.loaded = 'true'; diff --git a/ext/bg/settings2.html b/ext/bg/settings2.html index 41293a6e..b772ece1 100644 --- a/ext/bg/settings2.html +++ b/ext/bg/settings2.html @@ -1166,7 +1166,11 @@                      In order for Yomichan to use it, both MeCab and a native messaging component must be installed.                      A setup guide can be found <a href="https://github.com/siikamiika/yomichan-mecab-installer/blob/master/README.md" target="_blank" rel="noopener noreferrer">here</a>.                  </p> -                <p> +                <div class="margin-above flex-row-nowrap"> +                    <button id="test-mecab-button">Test</button> +                    <div id="test-mecab-results" class="flex-margin-left" hidden></div> +                </div> +                <p class="margin-above">                      <a class="more-toggle" data-parent-distance="3">Less…</a>                  </p>              </div> @@ -3241,6 +3245,7 @@  <script src="/bg/js/settings2/extension-keyboard-shortcuts-controller.js"></script>  <script src="/bg/js/settings2/keyboard-shortcuts-controller.js"></script> +<script src="/bg/js/settings2/mecab-controller.js"></script>  <script src="/bg/js/settings2/nested-popups-controller.js"></script>  <script src="/bg/js/settings2/popup-window-controller.js"></script>  <script src="/bg/js/settings2/secondary-search-dictionary-controller.js"></script> diff --git a/ext/mixed/js/api.js b/ext/mixed/js/api.js index fc765063..d37b091a 100644 --- a/ext/mixed/js/api.js +++ b/ext/mixed/js/api.js @@ -193,6 +193,10 @@ const api = (() => {              return this._invoke('triggerDatabaseUpdated', {type, cause});          } +        testMecab() { +            return this._invoke('testMecab', {}); +        } +          // Utilities          _createActionPort(timeout=5000) { |