diff options
| author | toasted-nutbread <toasted-nutbread@users.noreply.github.com> | 2021-03-14 18:41:15 -0400 | 
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-03-14 18:41:15 -0400 | 
| commit | 07df1e011794f5a77f7fb7da5cd9ea353a8747e2 (patch) | |
| tree | 98a679d4ef07629b8f0121e244038c557c972bd8 | |
| parent | 52a4d874eada5be121e15d73d1d10e9a8d84bdb8 (diff) | |
Fix dictionary image support (#1526)
* Fix content security policy for images
* Add createBlobFromBase64Content to MediaUtil
* Update MediaLoader to use MediaUtil
* Use blob URLs when importing dictionaries
* Update VM's URL to support createObjectURL and revokeObjectURL
* Fix test
| -rw-r--r-- | dev/data/manifest-variants.json | 4 | ||||
| -rw-r--r-- | dev/database-vm.js | 15 | ||||
| -rw-r--r-- | dev/vm.js | 22 | ||||
| -rw-r--r-- | ext/js/language/dictionary-importer.js | 4 | ||||
| -rw-r--r-- | ext/js/media/media-loader.js | 17 | ||||
| -rw-r--r-- | ext/js/media/media-util.js | 16 | ||||
| -rw-r--r-- | ext/manifest.json | 2 | ||||
| -rw-r--r-- | ext/popup.html | 1 | ||||
| -rw-r--r-- | ext/search.html | 1 | 
9 files changed, 62 insertions, 20 deletions
| diff --git a/dev/data/manifest-variants.json b/dev/data/manifest-variants.json index 14050241..14dd02e2 100644 --- a/dev/data/manifest-variants.json +++ b/dev/data/manifest-variants.json @@ -114,7 +114,7 @@              "popup.html",              "template-renderer.html"          ], -        "content_security_policy": "default-src 'self'; style-src 'self' 'unsafe-inline'; media-src *; connect-src *" +        "content_security_policy": "default-src 'self'; img-src blob: 'self'; style-src 'self' 'unsafe-inline'; media-src *; connect-src *"      },      "variants": [          { @@ -194,7 +194,7 @@                  {                      "action": "set",                      "path": ["content_security_policy"], -                    "value": "default-src 'self'; script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; media-src *; connect-src *" +                    "value": "default-src 'self'; script-src 'self' 'unsafe-eval'; img-src blob: 'self'; style-src 'self' 'unsafe-inline'; media-src *; connect-src *"                  },                  {                      "action": "set", diff --git a/dev/database-vm.js b/dev/database-vm.js index e43daf2f..b682bca3 100644 --- a/dev/database-vm.js +++ b/dev/database-vm.js @@ -76,6 +76,13 @@ class Image {      }  } +class Blob { +    constructor(array, options) { +        this._array = array; +        this._options = options; +    } +} +  async function fetch(url2) {      const filePath = url.fileURLToPath(url2);      await Promise.resolve(); @@ -89,15 +96,21 @@ async function fetch(url2) {      };  } +function atob(data) { +    return Buffer.from(data, 'base64').toString('ascii'); +} +  class DatabaseVM extends VM {      constructor() {          super({              chrome,              Image, +            Blob,              fetch,              indexedDB: global.indexedDB,              IDBKeyRange: global.IDBKeyRange, -            JSZip +            JSZip, +            atob          });          this.context.window = this.context;          this.indexedDB = global.indexedDB; @@ -19,6 +19,7 @@ const fs = require('fs');  const vm = require('vm');  const path = require('path');  const assert = require('assert'); +const crypto = require('crypto');  function getContextEnvironmentRecords(context, names) { @@ -115,9 +116,9 @@ function deepStrictEqual(actual, expected) {  } -function createURLClass() { +function createURLClass(urlMap) {      const BaseURL = URL; -    return function URL(url) { +    const result = function URL(url) {          const u = new BaseURL(url);          this.hash = u.hash;          this.host = u.host; @@ -132,12 +133,23 @@ function createURLClass() {          this.searchParams = u.searchParams;          this.username = u.username;      }; +    result.createObjectURL = (object) => { +        const id = crypto.randomBytes(16).toString('hex'); +        const url = `blob:${id}`; +        urlMap.set(url, object); +        return url; +    }; +    result.revokeObjectURL = (url) => { +        urlMap.delete(url); +    }; +    return result;  }  class VM {      constructor(context={}) { -        context.URL = createURLClass(); +        this._urlMap = new Map(); +        context.URL = createURLClass(this._urlMap);          this._context = vm.createContext(context);          this._assert = {              deepStrictEqual @@ -186,6 +198,10 @@ class VM {          return single ? results[0] : results;      } + +    getUrlObject(url) { +        return this._urlMap.get(url); +    }  } diff --git a/ext/js/language/dictionary-importer.js b/ext/js/language/dictionary-importer.js index b4429315..888d19b0 100644 --- a/ext/js/language/dictionary-importer.js +++ b/ext/js/language/dictionary-importer.js @@ -400,7 +400,9 @@ class DictionaryImporter {                  eventListeners.removeAllEventListeners();                  reject(new Error('Image failed to load'));              }, false); -            image.src = `data:${mediaType};base64,${content}`; +            const blob = MediaUtil.createBlobFromBase64Content(content, mediaType); +            const url = URL.createObjectURL(blob); +            image.src = url;          });      }  } diff --git a/ext/js/media/media-loader.js b/ext/js/media/media-loader.js index d9d40a36..4ac733c5 100644 --- a/ext/js/media/media-loader.js +++ b/ext/js/media/media-loader.js @@ -15,6 +15,10 @@   * along with this program.  If not, see <https://www.gnu.org/licenses/>.   */ +/* global + * MediaUtil + */ +  class MediaLoader {      constructor() {          this._token = {}; @@ -82,22 +86,11 @@ class MediaLoader {          const token = this._token;          const data = (await yomichan.api.getMedia([{path, dictionaryName}]))[0];          if (token === this._token && data !== null) { -            const contentArrayBuffer = this._base64ToArrayBuffer(data.content); -            const blob = new Blob([contentArrayBuffer], {type: data.mediaType}); +            const blob = MediaUtil.createBlobFromBase64Content(data.content, data.mediaType);              const url = URL.createObjectURL(blob);              cachedData.data = data;              cachedData.url = url;          }          return cachedData;      } - -    _base64ToArrayBuffer(content) { -        const binaryContent = window.atob(content); -        const length = binaryContent.length; -        const array = new Uint8Array(length); -        for (let i = 0; i < length; ++i) { -            array[i] = binaryContent.charCodeAt(i); -        } -        return array.buffer; -    }  } diff --git a/ext/js/media/media-util.js b/ext/js/media/media-util.js index 11172c5c..f783038a 100644 --- a/ext/js/media/media-util.js +++ b/ext/js/media/media-util.js @@ -129,4 +129,20 @@ class MediaUtil {                  return null;          }      } + +    /** +     * Creates a new `Blob` object from a base64 string of content. +     * @param content The binary content string encoded in base64. +     * @param mediaType The type of the media. +     * @returns A new `Blob` object corresponding to the specified content. +     */ +    static createBlobFromBase64Content(content, mediaType) { +        const binaryContent = atob(content); +        const length = binaryContent.length; +        const array = new Uint8Array(length); +        for (let i = 0; i < length; ++i) { +            array[i] = binaryContent.charCodeAt(i); +        } +        return new Blob([array.buffer], {type: mediaType}); +    }  } diff --git a/ext/manifest.json b/ext/manifest.json index 7868924f..ee383e84 100644 --- a/ext/manifest.json +++ b/ext/manifest.json @@ -113,5 +113,5 @@          "popup.html",          "template-renderer.html"      ], -    "content_security_policy": "default-src 'self'; style-src 'self' 'unsafe-inline'; media-src *; connect-src *" +    "content_security_policy": "default-src 'self'; img-src blob: 'self'; style-src 'self' 'unsafe-inline'; media-src *; connect-src *"  } diff --git a/ext/popup.html b/ext/popup.html index dfe547b2..78e89997 100644 --- a/ext/popup.html +++ b/ext/popup.html @@ -123,6 +123,7 @@  <script src="/js/language/text-scanner.js"></script>  <script src="/js/media/audio-system.js"></script>  <script src="/js/media/media-loader.js"></script> +<script src="/js/media/media-util.js"></script>  <script src="/js/media/text-to-speech-audio.js"></script>  <script src="/js/script/dynamic-loader.js"></script>  <script src="/js/templates/template-renderer-proxy.js"></script> diff --git a/ext/search.html b/ext/search.html index 9e4c8f2e..48abb8b7 100644 --- a/ext/search.html +++ b/ext/search.html @@ -107,6 +107,7 @@  <script src="/js/language/text-scanner.js"></script>  <script src="/js/media/audio-system.js"></script>  <script src="/js/media/media-loader.js"></script> +<script src="/js/media/media-util.js"></script>  <script src="/js/media/text-to-speech-audio.js"></script>  <script src="/js/script/dynamic-loader.js"></script>  <script src="/js/templates/template-renderer-proxy.js"></script> |