diff options
Diffstat (limited to 'ext')
| -rw-r--r-- | ext/bg/background.html | 1 | ||||
| -rw-r--r-- | ext/bg/context.html | 30 | ||||
| -rw-r--r-- | ext/bg/css/context.css | 44 | ||||
| -rw-r--r-- | ext/bg/info.html | 1 | ||||
| -rw-r--r-- | ext/bg/js/backend.js | 38 | ||||
| -rw-r--r-- | ext/bg/js/context-main.js | 37 | ||||
| -rw-r--r-- | ext/bg/js/permissions-util.js | 126 | ||||
| -rw-r--r-- | ext/bg/js/settings/anki-controller.js | 28 | ||||
| -rw-r--r-- | ext/bg/js/settings/backup-controller.js | 2 | ||||
| -rw-r--r-- | ext/bg/js/settings/permissions-toggle-controller.js | 8 | ||||
| -rw-r--r-- | ext/bg/js/settings/settings-controller.js | 52 | ||||
| -rw-r--r-- | ext/bg/permissions.html | 2 | ||||
| -rw-r--r-- | ext/bg/settings.html | 1 | ||||
| -rw-r--r-- | ext/bg/settings2.html | 1 | ||||
| -rw-r--r-- | ext/bg/welcome.html | 1 | ||||
| -rw-r--r-- | ext/mixed/css/material.css | 1 | ||||
| -rw-r--r-- | ext/mixed/img/key.svg | 1 | ||||
| -rw-r--r-- | ext/sw.js | 1 | 
18 files changed, 241 insertions, 134 deletions
| diff --git a/ext/bg/background.html b/ext/bg/background.html index 29cd1d85..5e4e2703 100644 --- a/ext/bg/background.html +++ b/ext/bg/background.html @@ -37,6 +37,7 @@          <script src="/bg/js/mecab.js"></script>          <script src="/bg/js/media-utility.js"></script>          <script src="/bg/js/options.js"></script> +        <script src="/bg/js/permissions-util.js"></script>          <script src="/bg/js/profile-conditions.js"></script>          <script src="/bg/js/request-builder.js"></script>          <script src="/bg/js/native-simple-dom-parser.js"></script> diff --git a/ext/bg/context.html b/ext/bg/context.html index 76e4db2a..785570d8 100644 --- a/ext/bg/context.html +++ b/ext/bg/context.html @@ -25,19 +25,32 @@          </div>      </label>      <div class="nav-button-container"> -        <button class="nav-button action-select-profile" data-icon="profile" title="Change primary profile" hidden> +        <button class="nav-button action-select-profile" title="Change primary profile" hidden> +            <span class="icon" data-icon="profile"></span>              <span class="profile-select-container"><select class="profile-select" id="profile-select">                  <optgroup label="Primary Profile" id="profile-select-option-group"></optgroup>              </select></span>          </button> -        <a class="nav-button action-open-settings" data-icon="cog" title="Settings" data-hotkey='["global:openSettingsPage","title","Settings ({0})"]'> +        <a class="nav-button action-open-settings" title="Settings" data-hotkey='["global:openSettingsPage","title","Settings ({0})"]'> +            <span class="icon" data-icon="cog"></span>              <div class="nav-button-warning-badge no-dictionaries-enabled-warning" hidden>                  <div class="nav-button-warning-badge-outer"></div>                  <div class="nav-button-warning-badge-inner"></div>              </div>          </a> -        <a class="nav-button action-open-search" data-icon="magnifying-glass" title="Search" data-hotkey='["global:openSearchPage","title","Search ({0})"]'></a> -        <a class="nav-button action-open-info" data-icon="question-mark" title="Information" data-hotkey='["global:openInfoPage","title","Information ({0})"]'></a> +        <a class="nav-button action-open-permissions" title="Permissions" hidden> +            <span class="icon" data-icon="key"></span> +            <div class="nav-button-warning-badge permissions-required-warning" hidden> +                <div class="nav-button-warning-badge-outer"></div> +                <div class="nav-button-warning-badge-inner"></div> +            </div> +        </a> +        <a class="nav-button action-open-search" title="Search" data-hotkey='["global:openSearchPage","title","Search ({0})"]'> +            <span class="icon" data-icon="magnifying-glass"></span> +        </a> +        <a class="nav-button action-open-info" title="Information" data-hotkey='["global:openInfoPage","title","Information ({0})"]'> +            <span class="icon" data-icon="question-mark-circle"></span> +        </a>      </div>  </div> @@ -53,6 +66,13 @@              <div class="flex-margin-left warning-badge no-dictionaries-enabled-warning" hidden><span class="icon" data-icon="exclamation-point-short"></span></div>          </span>      </a> +    <a class="link-group action-open-permissions" hidden> +        <span class="link-group-icon" data-icon="chevron"></span> +        <span class="link-group-label">Permissions</span> +        <span class="link-group-badge"> +            <div class="flex-margin-left warning-badge permissions-required-warning" hidden><span class="icon" data-icon="exclamation-point-short"></span></div> +        </span> +    </a>      <a class="link-group action-open-search">          <span class="link-group-icon" data-icon="chevron"></span><span class="link-group-label">Search</span>      </a> @@ -69,6 +89,8 @@  <script src="/mixed/js/hotkey-help-controller.js"></script>  <script src="/mixed/js/hotkey-util.js"></script> +<script src="/bg/js/permissions-util.js"></script> +  <script src="/bg/js/context-main.js"></script>  </body> diff --git a/ext/bg/css/context.css b/ext/bg/css/context.css index 447c5066..27cbdb92 100644 --- a/ext/bg/css/context.css +++ b/ext/bg/css/context.css @@ -75,6 +75,7 @@ label {  }  .icon[data-icon=profile]                 { --icon-image: url(/mixed/img/profile.svg); }  .icon[data-icon=cog]                     { --icon-image: url(/mixed/img/cog.svg); } +.icon[data-icon=key]                     { --icon-image: url(/mixed/img/key.svg); }  .icon[data-icon=magnifying-glass]        { --icon-image: url(/mixed/img/magnifying-glass.svg); }  .icon[data-icon=exclamation-point-short] { --icon-image: url(/mixed/img/exclamation-point-short.svg); }  .icon[data-icon=question-mark-circle]    { --icon-image: url(/mixed/img/question-mark-circle.svg); } @@ -82,7 +83,6 @@ label {  /* Page-specific styles */  .link-group { -    display: flex;      flex-flow: row nowrap;      align-items: center;      line-height: 1.5em; @@ -95,6 +95,9 @@ label {      transition: background-color 0.125s linear 0s;      max-width: none;  } +.link-group:not([hidden]) { +    display: flex; +}  .link-group:hover,  .link-group:active {      color: #333; @@ -276,23 +279,6 @@ body[data-loaded=true] .toggle-group {  .nav-button+.nav-button {      margin-left: -1px;  } -.nav-button::after { -    content: ''; -    display: block; -    width: 16px; -    height: 16px; -    box-sizing: content-box; -    background-color: #333333; -    mask-repeat: no-repeat; -    mask-position: center center; -    mask-mode: alpha; -    mask-size: 16px 16px; -    -webkit-mask-repeat: no-repeat; -    -webkit-mask-position: center center; -    -webkit-mask-mode: alpha; -    -webkit-mask-size: 16px 16px; -    pointer-events: none; -}  .nav-button:hover {      z-index: 1;      border-color: #aaaaaa; @@ -308,21 +294,13 @@ body[data-loaded=true] .toggle-group {  .nav-button:focus {      outline: none;  } -.nav-button[data-icon=magnifying-glass]::after { -    mask-image: url(/mixed/img/magnifying-glass.svg); -    -webkit-mask-image: url(/mixed/img/magnifying-glass.svg); -} -.nav-button[data-icon=cog]::after { -    mask-image: url(/mixed/img/cog.svg); -    -webkit-mask-image: url(/mixed/img/cog.svg); -} -.nav-button[data-icon=question-mark]::after { -    mask-image: url(/mixed/img/question-mark-circle.svg); -    -webkit-mask-image: url(/mixed/img/question-mark-circle.svg); -} -.nav-button[data-icon=profile]::after { -    mask-image: url(/mixed/img/profile.svg); -    -webkit-mask-image: url(/mixed/img/profile.svg); +.nav-button>.icon { +    --icon-size: 16px 16px; +    display: block; +    width: 16px; +    height: 16px; +    box-sizing: content-box; +    background-color: #333333;  }  .nav-button:first-child,  .nav-button:first-child[hidden]+.nav-button { diff --git a/ext/bg/info.html b/ext/bg/info.html index 07edcbcf..fc5db6e5 100644 --- a/ext/bg/info.html +++ b/ext/bg/info.html @@ -65,6 +65,7 @@  <script src="/mixed/js/document-focus-controller.js"></script>  <script src="/mixed/js/html-template-collection.js"></script> +<script src="/bg/js/permissions-util.js"></script>  <script src="/bg/js/settings/settings-controller.js"></script>  <script src="/bg/js/settings/backup-controller.js"></script> diff --git a/ext/bg/js/backend.js b/ext/bg/js/backend.js index e5f8466e..3dd1955f 100644 --- a/ext/bg/js/backend.js +++ b/ext/bg/js/backend.js @@ -28,6 +28,7 @@   * MediaUtility   * ObjectPropertyAccessor   * OptionsUtil + * PermissionsUtil   * ProfileConditions   * RequestBuilder   * Translator @@ -83,6 +84,8 @@ class Backend {          this._defaultBrowserActionTitle = null;          this._badgePrepareDelayTimer = null;          this._logErrorLevel = null; +        this._permissions = null; +        this._permissionsUtil = new PermissionsUtil();          this._messageHandlers = new Map([              ['requestBackendReadySignal',    {async: false, contentScript: true,  handler: this._onApiRequestBackendReadySignal.bind(this)}], @@ -174,12 +177,17 @@ class Backend {          const onMessage = this._onMessageWrapper.bind(this);          chrome.runtime.onMessage.addListener(onMessage); + +        const onPermissionsChanged = this._onWebExtensionEventWrapper(this._onPermissionsChanged.bind(this)); +        chrome.permissions.onAdded.addListener(onPermissionsChanged); +        chrome.permissions.onRemoved.addListener(onPermissionsChanged);      }      async _prepareInternal() {          try {              this._prepareInternalSync(); +            this._permissions = await this._permissionsUtil.getAllPermissions();              this._defaultBrowserActionTitle = await this._getBrowserIconTitle();              this._badgePrepareDelayTimer = setTimeout(() => {                  this._badgePrepareDelayTimer = null; @@ -357,6 +365,10 @@ class Backend {          this._sendMessageTabIgnoreResponse(tabId, {action: 'zoomChanged', params: {oldZoomFactor, newZoomFactor}});      } +    _onPermissionsChanged() { +        this._checkPermissions(); +    } +      // Message handlers      _onApiRequestBackendReadySignal(_params, sender) { @@ -682,7 +694,7 @@ class Backend {          let permissionsOkay = false;          try { -            permissionsOkay = await this._hasPermissions({permissions: ['nativeMessaging']}); +            permissionsOkay = await this._permissionsUtil.hasPermissions({permissions: ['nativeMessaging']});          } catch (e) {              // NOP          } @@ -1263,6 +1275,10 @@ class Backend {                  text = 'off';                  color = '#555555';                  status = 'Disabled'; +            } else if (!this._hasRequiredPermissionsForSettings(options)) { +                text = '!'; +                color = '#f0ad4e'; +                status = 'Some settings require additional permissions';              } else if (!this._isAnyDictionaryEnabled(options)) {                  text = '!';                  color = '#f0ad4e'; @@ -1941,17 +1957,6 @@ 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); -            } -        })); -    } -      _getTabById(tabId) {          return new Promise((resolve, reject) => {              chrome.tabs.get( @@ -1967,4 +1972,13 @@ class Backend {              );          });      } + +    async _checkPermissions() { +        this._permissions = await this._permissionsUtil.getAllPermissions(); +        this._updateBadge(); +    } + +    _hasRequiredPermissionsForSettings(options) { +        return this._permissions === null || this._permissionsUtil.hasRequiredPermissionsForOptions(this._permissions, options); +    }  } diff --git a/ext/bg/js/context-main.js b/ext/bg/js/context-main.js index 3d9c90ab..a7ea1471 100644 --- a/ext/bg/js/context-main.js +++ b/ext/bg/js/context-main.js @@ -17,12 +17,14 @@  /* global   * HotkeyHelpController + * PermissionsUtil   * api   */  class DisplayController {      constructor() {          this._optionsFull = null; +        this._permissionsUtil = new PermissionsUtil();      }      async prepare() { @@ -40,6 +42,7 @@ class DisplayController {          const optionsPageUrl = optionsFull.global.useSettingsV2 ? '/bg/settings2.html' : manifest.options_ui.page;          this._setupButtonEvents('.action-open-settings', 'openSettingsPage', chrome.runtime.getURL(optionsPageUrl)); +        this._setupButtonEvents('.action-open-permissions', null, chrome.runtime.getURL('/bg/permissions.html'));          const {profiles, profileCurrent} = optionsFull;          const primaryProfile = (profileCurrent >= 0 && profileCurrent < profiles.length) ? profiles[profileCurrent] : null; @@ -68,16 +71,18 @@ class DisplayController {      _setupButtonEvents(selector, command, url) {          const nodes = document.querySelectorAll(selector);          for (const node of nodes) { -            node.addEventListener('click', (e) => { -                if (e.button !== 0) { return; } -                api.commandExec(command, {mode: e.ctrlKey ? 'newTab' : 'existingOrNewTab'}); -                e.preventDefault(); -            }, false); -            node.addEventListener('auxclick', (e) => { -                if (e.button !== 1) { return; } -                api.commandExec(command, {mode: 'newTab'}); -                e.preventDefault(); -            }, false); +            if (typeof command === 'string') { +                node.addEventListener('click', (e) => { +                    if (e.button !== 0) { return; } +                    api.commandExec(command, {mode: e.ctrlKey ? 'newTab' : 'existingOrNewTab'}); +                    e.preventDefault(); +                }, false); +                node.addEventListener('auxclick', (e) => { +                    if (e.button !== 1) { return; } +                    api.commandExec(command, {mode: 'newTab'}); +                    e.preventDefault(); +                }, false); +            }              if (typeof url === 'string') {                  node.href = url; @@ -131,6 +136,7 @@ class DisplayController {              toggle.addEventListener('change', onToggleChanged, false);          }          this._updateDictionariesEnabledWarnings(options); +        this._updatePermissionsWarnings(options);      }      async _setupHotkeys() { @@ -201,6 +207,17 @@ class DisplayController {              node.hidden = hasEnabledDictionary;          }      } + +    async _updatePermissionsWarnings(options) { +        const permissions = await this._permissionsUtil.getAllPermissions(); +        if (this._permissionsUtil.hasRequiredPermissionsForOptions(permissions, options)) { return; } + +        const warnings = document.querySelectorAll('.action-open-permissions,.permissions-required-warning'); +        for (const node of warnings) { +            console.log(node); +            node.hidden = false; +        } +    }  }  (async () => { diff --git a/ext/bg/js/permissions-util.js b/ext/bg/js/permissions-util.js new file mode 100644 index 00000000..bd3a18ce --- /dev/null +++ b/ext/bg/js/permissions-util.js @@ -0,0 +1,126 @@ +/* + * 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/>. + */ + +class PermissionsUtil { +    constructor() { +        this._ankiFieldMarkersRequiringClipboardPermission = new Set([ +            'clipboard-image', +            'clipboard-text' +        ]); +        this._ankiMarkerPattern = /\{([\w-]+)\}/g; +    } + +    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); +            } +        })); +    } + +    setPermissionsGranted(permissions, shouldHave) { +        return ( +            shouldHave ? +            new Promise((resolve, reject) => chrome.permissions.request(permissions, (result) => { +                const e = chrome.runtime.lastError; +                if (e) { +                    reject(new Error(e.message)); +                } else { +                    resolve(result); +                } +            })) : +            new Promise((resolve, reject) => chrome.permissions.remove(permissions, (result) => { +                const e = chrome.runtime.lastError; +                if (e) { +                    reject(new Error(e.message)); +                } else { +                    resolve(!result); +                } +            })) +        ); +    } + +    getAllPermissions() { +        return new Promise((resolve, reject) => chrome.permissions.getAll((result) => { +            const e = chrome.runtime.lastError; +            if (e) { +                reject(new Error(e.message)); +            } else { +                resolve(result); +            } +        })); +    } + +    getRequiredPermissionsForAnkiFieldValue(fieldValue) { +        const markers = this._getAnkiFieldMarkers(fieldValue); +        const markerPermissions = this._ankiFieldMarkersRequiringClipboardPermission; +        for (const marker of markers) { +            if (markerPermissions.has(marker)) { +                return ['clipboardRead']; +            } +        } +        return []; +    } + +    hasRequiredPermissionsForOptions(permissions, options) { +        const permissionsSet = new Set(permissions.permissions); + +        if (!permissionsSet.has('nativeMessaging')) { +            if (options.parsing.enableMecabParser) { +                return false; +            } +        } + +        if (!permissionsSet.has('clipboardRead')) { +            if (options.clipboard.enableBackgroundMonitor || options.clipboard.enableSearchPageMonitor) { +                return false; +            } +            const fieldMarkersRequiringClipboardPermission = this._ankiFieldMarkersRequiringClipboardPermission; +            const fieldsList = [ +                options.anki.terms.fields, +                options.anki.kanji.fields +            ]; +            for (const fields of fieldsList) { +                for (const fieldValue of Object.values(fields)) { +                    const markers = this._getAnkiFieldMarkers(fieldValue); +                    for (const marker of markers) { +                        if (fieldMarkersRequiringClipboardPermission.has(marker)) { +                            return false; +                        } +                    } +                } +            } +        } + +        return true; +    } + +    // Private + +    _getAnkiFieldMarkers(fieldValue) { +        const pattern = this._ankiMarkerPattern; +        const markers = []; +        let match; +        while ((match = pattern.exec(fieldValue)) !== null) { +            markers.push(match[1]); +        } +        return markers; +    } +} diff --git a/ext/bg/js/settings/anki-controller.js b/ext/bg/js/settings/anki-controller.js index cb6922b8..db3e3c14 100644 --- a/ext/bg/js/settings/anki-controller.js +++ b/ext/bg/js/settings/anki-controller.js @@ -34,10 +34,6 @@ class AnkiController {              onRemoved: this._removeCardController.bind(this),              isStale: this._isCardControllerStale.bind(this)          }); -        this._fieldMarkersRequiringClipboardPermission = new Set([ -            'clipboard-image', -            'clipboard-text' -        ]);          this._stringComparer = new Intl.Collator(); // Locale does not matter          this._getAnkiDataPromise = null;          this._ankiErrorContainer = null; @@ -157,13 +153,7 @@ class AnkiController {      }      getRequiredPermissions(fieldValue) { -        const markers = this._getFieldMarkers(fieldValue); -        for (const marker of markers) { -            if (this._fieldMarkersRequiringClipboardPermission.has(marker)) { -                return ['clipboardRead']; -            } -        } -        return []; +        return this._settingsController.permissionsUtil.getRequiredPermissionsForAnkiFieldValue(fieldValue);      }      containsAnyMarker(field) { @@ -338,16 +328,6 @@ class AnkiController {          this._ankiErrorMessageDetailsToggle.hidden = false;      } -    _getFieldMarkers(fieldValue) { -        const pattern = /\{([\w-]+)\}/g; -        const markers = []; -        let match; -        while ((match = pattern.exec(fieldValue)) !== null) { -            markers.push(match[1]); -        } -        return markers; -    } -      _sortStringArray(array) {          const stringComparer = this._stringComparer;          array.sort((a, b) => stringComparer.compare(a, b)); @@ -656,7 +636,7 @@ class AnkiCardController {      async _requestPermissions(permissions) {          try { -            await this._settingsController.setPermissionsGranted(permissions, true); +            await this._settingsController.permissionsUtil.setPermissionsGranted({permissions}, true);          } catch (e) {              yomichan.logError(e);          } @@ -669,8 +649,8 @@ class AnkiCardController {              node.dataset.requiredPermission = permissions.join(' ');              const hasPermissions = await (                  request ? -                this._settingsController.setPermissionsGranted(permissions, true) : -                this._settingsController.hasPermissions(permissions) +                this._settingsController.permissionsUtil.setPermissionsGranted({permissions}, true) : +                this._settingsController.permissionsUtil.hasPermissions({permissions})              );              node.dataset.hasPermissions = `${hasPermissions}`;          } else { diff --git a/ext/bg/js/settings/backup-controller.js b/ext/bg/js/settings/backup-controller.js index 34817ee9..8837b927 100644 --- a/ext/bg/js/settings/backup-controller.js +++ b/ext/bg/js/settings/backup-controller.js @@ -87,7 +87,7 @@ class BackupController {          const optionsFull = await this._settingsController.getOptionsFull();          const environment = await api.getEnvironmentInfo();          const fieldTemplatesDefault = await api.getDefaultAnkiFieldTemplates(); -        const permissions = await this._settingsController.getAllPermissions(); +        const permissions = await this._settingsController.permissionsUtil.getAllPermissions();          // Format options          for (const {options} of optionsFull.profiles) { diff --git a/ext/bg/js/settings/permissions-toggle-controller.js b/ext/bg/js/settings/permissions-toggle-controller.js index 04c8f3f2..f80e7585 100644 --- a/ext/bg/js/settings/permissions-toggle-controller.js +++ b/ext/bg/js/settings/permissions-toggle-controller.js @@ -71,13 +71,13 @@ class PermissionsToggleController {          if (value || !hasPermissionsSetting) {              toggle.checked = valuePre; -            const requiredPermissions = this._getRequiredPermissions(toggle); +            const permissions = this._getRequiredPermissions(toggle);              try { -                value = await this._settingsController.setPermissionsGranted(requiredPermissions, value); +                value = await this._settingsController.permissionsUtil.setPermissionsGranted({permissions}, value);              } catch (error) {                  value = valuePre;                  try { -                    value = await this._settingsController.hasPermissions(requiredPermissions); +                    value = await this._settingsController.permissionsUtil.hasPermissions({permissions});                  } catch (error2) {                      // NOP                  } @@ -113,7 +113,7 @@ class PermissionsToggleController {      }      async _updateValidity() { -        const permissions = await this._settingsController.getAllPermissions(); +        const permissions = await this._settingsController.permissionsUtil.getAllPermissions();          this._onPermissionsChanged({permissions});      } diff --git a/ext/bg/js/settings/settings-controller.js b/ext/bg/js/settings/settings-controller.js index a3885ef6..11a9435c 100644 --- a/ext/bg/js/settings/settings-controller.js +++ b/ext/bg/js/settings/settings-controller.js @@ -18,6 +18,7 @@  /* global   * HtmlTemplateCollection   * OptionsUtil + * PermissionsUtil   * api   */ @@ -29,6 +30,7 @@ class SettingsController extends EventDispatcher {          this._pageExitPreventions = new Set();          this._pageExitPreventionEventListeners = new EventListenerCollection();          this._templates = new HtmlTemplateCollection(document); +        this._permissionsUtil = new PermissionsUtil();      }      get source() { @@ -44,6 +46,10 @@ class SettingsController extends EventDispatcher {          this._setProfileIndex(value);      } +    get permissionsUtil() { +        return this._permissionsUtil; +    } +      prepare() {          yomichan.on('optionsUpdated', this._onOptionsUpdated.bind(this));          chrome.permissions.onAdded.addListener(this._onPermissionsChanged.bind(this)); @@ -134,50 +140,6 @@ class SettingsController extends EventDispatcher {          return optionsFull;      } -    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); -            } -        })); -    } - -    setPermissionsGranted(permissions, shouldHave) { -        return ( -            shouldHave ? -            new Promise((resolve, reject) => chrome.permissions.request({permissions}, (result) => { -                const e = chrome.runtime.lastError; -                if (e) { -                    reject(new Error(e.message)); -                } else { -                    resolve(result); -                } -            })) : -            new Promise((resolve, reject) => chrome.permissions.remove({permissions}, (result) => { -                const e = chrome.runtime.lastError; -                if (e) { -                    reject(new Error(e.message)); -                } else { -                    resolve(!result); -                } -            })) -        ); -    } - -    getAllPermissions() { -        return new Promise((resolve, reject) => chrome.permissions.getAll((result) => { -            const e = chrome.runtime.lastError; -            if (e) { -                reject(new Error(e.message)); -            } else { -                resolve(result); -            } -        })); -    } -      // Private      _setProfileIndex(value) { @@ -242,7 +204,7 @@ class SettingsController extends EventDispatcher {          const event = 'permissionsChanged';          if (!this.hasListeners(event)) { return; } -        const permissions = await this.getAllPermissions(); +        const permissions = await this._permissionsUtil.getAllPermissions();          this.trigger(event, {permissions});      }  } diff --git a/ext/bg/permissions.html b/ext/bg/permissions.html index 45c65a65..de235eeb 100644 --- a/ext/bg/permissions.html +++ b/ext/bg/permissions.html @@ -168,7 +168,7 @@  <script src="/mixed/js/document-focus-controller.js"></script>  <script src="/mixed/js/html-template-collection.js"></script> - +<script src="/bg/js/permissions-util.js"></script>  <script src="/bg/js/settings/permissions-toggle-controller.js"></script>  <script src="/bg/js/settings/settings-controller.js"></script> diff --git a/ext/bg/settings.html b/ext/bg/settings.html index a087ba95..8bbcdb5a 100644 --- a/ext/bg/settings.html +++ b/ext/bg/settings.html @@ -1305,6 +1305,7 @@          <script src="/bg/js/dictionary-importer.js"></script>          <script src="/bg/js/json-schema.js"></script>          <script src="/bg/js/media-utility.js"></script> +        <script src="/bg/js/permissions-util.js"></script>          <script src="/bg/js/template-patcher.js"></script>          <script src="/bg/js/template-renderer-proxy.js"></script> diff --git a/ext/bg/settings2.html b/ext/bg/settings2.html index b772ece1..88a55206 100644 --- a/ext/bg/settings2.html +++ b/ext/bg/settings2.html @@ -3219,6 +3219,7 @@  <script src="/bg/js/dictionary-importer.js"></script>  <script src="/bg/js/json-schema.js"></script>  <script src="/bg/js/media-utility.js"></script> +<script src="/bg/js/permissions-util.js"></script>  <script src="/bg/js/template-patcher.js"></script>  <script src="/bg/js/template-renderer-proxy.js"></script> diff --git a/ext/bg/welcome.html b/ext/bg/welcome.html index 09544627..aeebd9dd 100644 --- a/ext/bg/welcome.html +++ b/ext/bg/welcome.html @@ -336,6 +336,7 @@  <script src="/bg/js/dictionary-importer.js"></script>  <script src="/bg/js/json-schema.js"></script>  <script src="/bg/js/media-utility.js"></script> +<script src="/bg/js/permissions-util.js"></script>  <script src="/bg/js/settings/dictionary-controller.js"></script>  <script src="/bg/js/settings/dictionary-import-controller.js"></script> diff --git a/ext/mixed/css/material.css b/ext/mixed/css/material.css index 6dba7206..365b15ad 100644 --- a/ext/mixed/css/material.css +++ b/ext/mixed/css/material.css @@ -216,6 +216,7 @@  .icon[data-icon=right-chevron]           { --icon-image: url(/mixed/img/right-chevron.svg); }  .icon[data-icon=plus-thick]              { --icon-image: url(/mixed/img/plus-thick.svg); }  .icon[data-icon=clipboard]               { --icon-image: url(/mixed/img/clipboard.svg); } +.icon[data-icon=key]                     { --icon-image: url(/mixed/img/key.svg); }  .icon[data-icon=material-down-arrow] {      --icon-image: url(/mixed/img/material-down-arrow.svg);      --icon-size: var(--material-arrow-dimension2) var(--material-arrow-dimension1); diff --git a/ext/mixed/img/key.svg b/ext/mixed/img/key.svg new file mode 100644 index 00000000..738e07a3 --- /dev/null +++ b/ext/mixed/img/key.svg @@ -0,0 +1 @@ +<?xml version="1.0" encoding="UTF-8"?><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path d="M10.662 1c-2.3128.04784-4.1626 1.9367-4.1621 4.25a4.24996 4.24996 0 00.14648 1.1035L.74988 12.25v1.25l1.25 1.5h2l.5-.5V13h1.5v-1.5h1.5V10h1.5l.64648-.64648a4.2498 4.2498 0 001.1035.14648c2.3472 0 4.25-1.9028 4.25-4.25S13.09706 1 10.74986 1a4.24994 4.24994 0 00-.08789 0zm.83789 2c.82843 0 1.5.67157 1.5 1.5s-.67157 1.5-1.5 1.5-1.5-.67157-1.5-1.5.67157-1.5 1.5-1.5zm-4.75 4.75l.75.75-4.75 4.75-.75-.75z"/></svg>
\ No newline at end of file @@ -36,6 +36,7 @@ self.importScripts(      '/bg/js/mecab.js',      '/bg/js/media-utility.js',      '/bg/js/options.js', +    '/bg/js/permissions-util.js',      '/bg/js/profile-conditions.js',      '/bg/js/request-builder.js',      '/bg/js/simple-dom-parser.js', |