From 7c578f75827ae0a72b13f04b8342fd8129f702d4 Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Sun, 19 Apr 2020 14:28:29 -0400 Subject: Create popup-preview-frame-main.js --- ext/bg/settings-popup-preview.html | 2 ++ 1 file changed, 2 insertions(+) (limited to 'ext/bg/settings-popup-preview.html') diff --git a/ext/bg/settings-popup-preview.html b/ext/bg/settings-popup-preview.html index f33ecedf..66475b7c 100644 --- a/ext/bg/settings-popup-preview.html +++ b/ext/bg/settings-popup-preview.html @@ -129,5 +129,7 @@ + + -- cgit v1.2.3 From 4e0fc76fe1d4bf38cfbef370fcc55331108e0e0a Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Sun, 26 Apr 2020 16:56:14 -0400 Subject: Add Yomichan icon SVG (#478) * Create Yomichan icon SVG and 32x32 PNG * Update icons * Add 32x32 icon definition to HTML pages --- ext/bg/background.html | 1 + ext/bg/context.html | 1 + ext/bg/guide.html | 3 ++- ext/bg/legal.html | 1 + ext/bg/search.html | 1 + ext/bg/settings-popup-preview.html | 1 + ext/bg/settings.html | 1 + ext/fg/float.html | 1 + ext/manifest.json | 21 ++++++++++++++-- ext/mixed/img/icon32.png | Bin 0 -> 288 bytes ext/mixed/img/yomichan-icon.svg | 5 ++++ resources/icons.svg | 49 +++++++++++++++++++++++++++++-------- 12 files changed, 72 insertions(+), 13 deletions(-) create mode 100644 ext/mixed/img/icon32.png create mode 100644 ext/mixed/img/yomichan-icon.svg (limited to 'ext/bg/settings-popup-preview.html') diff --git a/ext/bg/background.html b/ext/bg/background.html index 3446d9ce..ee5a1f32 100644 --- a/ext/bg/background.html +++ b/ext/bg/background.html @@ -6,6 +6,7 @@ Background + diff --git a/ext/bg/context.html b/ext/bg/context.html index 394869b1..93012d70 100644 --- a/ext/bg/context.html +++ b/ext/bg/context.html @@ -5,6 +5,7 @@ + diff --git a/ext/bg/guide.html b/ext/bg/guide.html index ff9c71ee..cde520d1 100644 --- a/ext/bg/guide.html +++ b/ext/bg/guide.html @@ -6,6 +6,7 @@ Welcome to Yomichan! + @@ -25,7 +26,7 @@

    -
  1. Click on the icon in the browser toolbar to open the Yomichan actions dialog.
  2. +
  3. Click on the icon in the browser toolbar to open the Yomichan actions dialog.
  4. Click on the monkey wrench icon in the middle to open the options page.
  5. Import the dictionaries you wish to use for term and Kanji searches.
  6. Hold down Shift key or the middle mouse button as you move your mouse over text to display definitions.
  7. diff --git a/ext/bg/legal.html b/ext/bg/legal.html index 78acf79a..1ee9a28c 100644 --- a/ext/bg/legal.html +++ b/ext/bg/legal.html @@ -6,6 +6,7 @@ Yomichan Legal + diff --git a/ext/bg/search.html b/ext/bg/search.html index 9a824776..8ed6c838 100644 --- a/ext/bg/search.html +++ b/ext/bg/search.html @@ -6,6 +6,7 @@ Yomichan Search + diff --git a/ext/bg/settings-popup-preview.html b/ext/bg/settings-popup-preview.html index 66475b7c..a332fb22 100644 --- a/ext/bg/settings-popup-preview.html +++ b/ext/bg/settings-popup-preview.html @@ -6,6 +6,7 @@ Yomichan Popup Preview + diff --git a/ext/bg/settings.html b/ext/bg/settings.html index 96c1db82..f0236193 100644 --- a/ext/bg/settings.html +++ b/ext/bg/settings.html @@ -6,6 +6,7 @@ Yomichan Options + diff --git a/ext/fg/float.html b/ext/fg/float.html index 07c3c9e6..deb9e9d2 100644 --- a/ext/fg/float.html +++ b/ext/fg/float.html @@ -6,6 +6,7 @@ + diff --git a/ext/manifest.json b/ext/manifest.json index 4f35b03c..3cb634f0 100644 --- a/ext/manifest.json +++ b/ext/manifest.json @@ -4,9 +4,26 @@ "version": "20.4.18.0", "description": "Japanese dictionary with Anki integration", - "icons": {"16": "mixed/img/icon16.png", "48": "mixed/img/icon48.png", "128": "mixed/img/icon128.png"}, + "icons": { + "16": "mixed/img/icon16.png", + "19": "mixed/img/icon19.png", + "32": "mixed/img/icon32.png", + "38": "mixed/img/icon38.png", + "48": "mixed/img/icon48.png", + "64": "mixed/img/icon48.png", + "128": "mixed/img/icon128.png" + }, "browser_action": { - "default_icon": {"19": "mixed/img/icon19.png", "38": "mixed/img/icon38.png"}, + "default_icon": { + "16": "mixed/img/icon16.png", + "19": "mixed/img/icon19.png", + "32": "mixed/img/icon32.png", + "38": "mixed/img/icon38.png", + "48": "mixed/img/icon48.png", + "64": "mixed/img/icon48.png", + "128": "mixed/img/icon128.png" + }, + "default_title": "Yomichan", "default_popup": "bg/context.html" }, diff --git a/ext/mixed/img/icon32.png b/ext/mixed/img/icon32.png new file mode 100644 index 00000000..05f2f064 Binary files /dev/null and b/ext/mixed/img/icon32.png differ diff --git a/ext/mixed/img/yomichan-icon.svg b/ext/mixed/img/yomichan-icon.svg new file mode 100644 index 00000000..f15ab0aa --- /dev/null +++ b/ext/mixed/img/yomichan-icon.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/resources/icons.svg b/resources/icons.svg index 4bc46c02..f096947b 100644 --- a/resources/icons.svg +++ b/resources/icons.svg @@ -15,10 +15,11 @@ viewBox="0 0 16 16" version="1.1" id="svg8" - inkscape:version="0.92.3 (2405546, 2018-03-11)" + inkscape:version="0.92.4 (5da689c313, 2019-01-14)" sodipodi:docname="icons.svg" - inkscape:export-xdpi="96" - inkscape:export-ydpi="96"> + inkscape:export-xdpi="192" + inkscape:export-ydpi="192" + inkscape:export-filename="../ext/mixed/img/icon32.png"> image/svg+xml - + @@ -520,7 +521,7 @@ style="image-rendering:optimizeSpeed" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0 U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAJvSURBVDjLpZPrS5NhGIf9W7YvBYOkhlko qCklWChv2WyKik7blnNris72bi6dus0DLZ0TDxW1odtopDs4D8MDZuLU0kXq61CijSIIasOvv94V TUfLiB74fXngup7nvrnvJABJ/5PfLnTTdcwOj4RsdYmo5glBWP6iOtzwvIKSWstI0Wgx80SBblpK tE9KQs/We7EaWoT/8wbWP61gMmCH0lMDvokT4j25TiQU/ITFkek9Ow6+7WH2gwsmahCPdwyw75uw 9HEO2gUZSkfyI9zBPCJOoJ2SMmg46N61YO/rNoa39Xi41oFuXysMfh36/Fp0b7bAfWAH6RGi0Hgl WNCbzYgJaFjRv6zGuy+b9It96N3SQvNKiV9HvSaDfFEIxXItnPs23BzJQd6DDEVM0OKsoVwBG/1V MzpXVWhbkUM2K4oJBDYuGmbKIJ0qxsAbHfRLzbjcnUbFBIpx/qH3vQv9b3U03IQ/HfFkERTzfFj8 w8jSpR7GBE123uFEYAzaDRIqX/2JAtJbDat/COkd7CNBva2cMvq0MGxp0PRSCPF8BXjWG3FgNHc9 XPT71Ojy3sMFdfJRCeKxEsVtKwFHwALZfCUk3tIfNR8XiJwc1LmL4dg141JPKtj3WUdNFJqLGFVP C4OkR4BxajTWsChY64wmCnMxsWPCHcutKBxMVp5mxA1S+aMComToaqTRUQknLTH62kHOVEE+VQnj ahscNCy0cMBWsSI0TCQcZc5ALkEYckL5A5noWSBhfm2AecMAjbcRWV0pUTh0HE64TNf0mczcnnQy u/MilaFJCae1nw2fbz1DnVOxyGTlKeZft/Ff8x1BRssfACjTwQAAAABJRU5ErkJggg== " id="image4539" - x="4.7683716e-007" + x="4.7683716e-07" y="292.76669" /> + + + + + + + + -- cgit v1.2.3 From b936c3e4b1bc993e535b02dee91bf6afc15a3564 Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Fri, 8 May 2020 19:04:53 -0400 Subject: Popup proxy host refactor (#516) * Rename PopupProxyHost to PopupFactory * Update FrontendApiReceiver to support non-async handlers * Make some functions non-async * Make setCustomCss non-async * Make setContentScale non-async * Remove static * Rename variables * Pass frameId into PopupFactory's constructor * Change FrontendApiReceiver source from popup-proxy-host to popup-factor * Rename _invokeHostApi to _invoke * Rename PopupProxy.getHostUrl to getUrl --- ext/bg/js/search-main.js | 2 +- ext/bg/js/settings/popup-preview-frame.js | 11 +- ext/bg/settings-popup-preview.html | 2 +- ext/fg/js/content-script-main.js | 28 +++-- ext/fg/js/frontend-api-receiver.js | 27 ++++- ext/fg/js/popup-factory.js | 181 +++++++++++++++++++++++++++++ ext/fg/js/popup-proxy-host.js | 186 ------------------------------ ext/fg/js/popup-proxy.js | 32 ++--- ext/fg/js/popup.js | 2 +- ext/manifest.json | 2 +- 10 files changed, 247 insertions(+), 226 deletions(-) create mode 100644 ext/fg/js/popup-factory.js delete mode 100644 ext/fg/js/popup-proxy-host.js (limited to 'ext/bg/settings-popup-preview.html') diff --git a/ext/bg/js/search-main.js b/ext/bg/js/search-main.js index 1075d46e..6e092fbc 100644 --- a/ext/bg/js/search-main.js +++ b/ext/bg/js/search-main.js @@ -31,7 +31,7 @@ async function injectSearchFrontend() { '/fg/js/frontend-api-receiver.js', '/fg/js/frame-offset-forwarder.js', '/fg/js/popup.js', - '/fg/js/popup-proxy-host.js', + '/fg/js/popup-factory.js', '/fg/js/frontend.js', '/fg/js/content-script-main.js' ]); diff --git a/ext/bg/js/settings/popup-preview-frame.js b/ext/bg/js/settings/popup-preview-frame.js index c7b0c218..8901a0c4 100644 --- a/ext/bg/js/settings/popup-preview-frame.js +++ b/ext/bg/js/settings/popup-preview-frame.js @@ -18,8 +18,9 @@ /* global * Frontend * Popup - * PopupProxyHost + * PopupFactory * TextSourceRange + * apiFrameInformationGet * apiOptionsGet */ @@ -56,10 +57,12 @@ class SettingsPopupPreview { window.apiOptionsGet = this.apiOptionsGet.bind(this); // Overwrite frontend - const popupHost = new PopupProxyHost(); - await popupHost.prepare(); + const {frameId} = await apiFrameInformationGet(); - this.popup = popupHost.getOrCreatePopup(); + const popupFactory = new PopupFactory(frameId); + await popupFactory.prepare(); + + this.popup = popupFactory.getOrCreatePopup(); this.popup.setChildrenSupported(false); this.popupSetCustomOuterCssOld = this.popup.setCustomOuterCss; diff --git a/ext/bg/settings-popup-preview.html b/ext/bg/settings-popup-preview.html index a332fb22..3d7f6455 100644 --- a/ext/bg/settings-popup-preview.html +++ b/ext/bg/settings-popup-preview.html @@ -127,7 +127,7 @@ - + diff --git a/ext/fg/js/content-script-main.js b/ext/fg/js/content-script-main.js index 277e6567..3afc9648 100644 --- a/ext/fg/js/content-script-main.js +++ b/ext/fg/js/content-script-main.js @@ -19,10 +19,11 @@ * DOM * FrameOffsetForwarder * Frontend + * PopupFactory * PopupProxy - * PopupProxyHost * apiBroadcastTab * apiForwardLogsToBackend + * apiFrameInformationGet * apiOptionsGet */ @@ -47,10 +48,17 @@ async function createIframePopupProxy(frameOffsetForwarder, setDisabled) { } async function getOrCreatePopup(depth) { - const popupHost = new PopupProxyHost(); - await popupHost.prepare(); + const {frameId} = await apiFrameInformationGet(); + if (typeof frameId !== 'number') { + const error = new Error('Failed to get frameId'); + yomichan.logError(error); + throw error; + } - const popup = popupHost.getOrCreatePopup(null, null, depth); + const popupFactory = new PopupFactory(frameId); + await popupFactory.prepare(); + + const popup = popupFactory.getOrCreatePopup(null, null, depth); return popup; } @@ -89,20 +97,20 @@ async function createPopupProxy(depth, id, parentFrameId) { }; let urlUpdatedAt = 0; - let proxyHostUrlCached = url; - const getProxyHostUrl = async () => { + let popupProxyUrlCached = url; + const getPopupProxyUrl = async () => { const now = Date.now(); if (popups.proxy !== null && now - urlUpdatedAt > 500) { - proxyHostUrlCached = await popups.proxy.getHostUrl(); + popupProxyUrlCached = await popups.proxy.getUrl(); urlUpdatedAt = now; } - return proxyHostUrlCached; + return popupProxyUrlCached; }; const applyOptions = async () => { const optionsContext = { depth: isSearchPage ? 0 : depth, - url: proxy ? await getProxyHostUrl() : window.location.href + url: proxy ? await getPopupProxyUrl() : window.location.href }; const options = await apiOptionsGet(optionsContext); @@ -124,7 +132,7 @@ async function createPopupProxy(depth, id, parentFrameId) { } if (frontend === null) { - const getUrl = proxy ? getProxyHostUrl : null; + const getUrl = proxy ? getPopupProxyUrl : null; frontend = new Frontend(popup, getUrl); frontendPreparePromise = frontend.prepare(); await frontendPreparePromise; diff --git a/ext/fg/js/frontend-api-receiver.js b/ext/fg/js/frontend-api-receiver.js index c5bb58af..3fa9e8b6 100644 --- a/ext/fg/js/frontend-api-receiver.js +++ b/ext/fg/js/frontend-api-receiver.js @@ -17,9 +17,9 @@ class FrontendApiReceiver { - constructor(source='', handlers=new Map()) { + constructor(source, messageHandlers) { this._source = source; - this._handlers = handlers; + this._messageHandlers = messageHandlers; } prepare() { @@ -35,14 +35,29 @@ class FrontendApiReceiver { _onMessage(port, {id, action, params, target, senderId}) { if (target !== this._source) { return; } - const handler = this._handlers.get(action); - if (typeof handler !== 'function') { return; } + const messageHandler = this._messageHandlers.get(action); + if (typeof messageHandler === 'undefined') { return; } + + const {handler, async} = messageHandler; this._sendAck(port, id, senderId); - this._invokeHandler(handler, params, port, id, senderId); + if (async) { + this._invokeHandlerAsync(handler, params, port, id, senderId); + } else { + this._invokeHandler(handler, params, port, id, senderId); + } + } + + _invokeHandler(handler, params, port, id, senderId) { + try { + const result = handler(params); + this._sendResult(port, id, senderId, {result}); + } catch (error) { + this._sendResult(port, id, senderId, {error: errorToJson(error)}); + } } - async _invokeHandler(handler, params, port, id, senderId) { + async _invokeHandlerAsync(handler, params, port, id, senderId) { try { const result = await handler(params); this._sendResult(port, id, senderId, {result}); diff --git a/ext/fg/js/popup-factory.js b/ext/fg/js/popup-factory.js new file mode 100644 index 00000000..21e64dd0 --- /dev/null +++ b/ext/fg/js/popup-factory.js @@ -0,0 +1,181 @@ +/* + * Copyright (C) 2019-2020 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 . + */ + +/* global + * FrontendApiReceiver + * Popup + */ + +class PopupFactory { + constructor(frameId) { + this._popups = new Map(); + this._frameId = frameId; + } + + // Public functions + + async prepare() { + const apiReceiver = new FrontendApiReceiver(`popup-factory#${this._frameId}`, new Map([ + ['getOrCreatePopup', {async: false, handler: this._onApiGetOrCreatePopup.bind(this)}], + ['setOptionsContext', {async: true, handler: this._onApiSetOptionsContext.bind(this)}], + ['hide', {async: false, handler: this._onApiHide.bind(this)}], + ['isVisible', {async: true, handler: this._onApiIsVisibleAsync.bind(this)}], + ['setVisibleOverride', {async: true, handler: this._onApiSetVisibleOverride.bind(this)}], + ['containsPoint', {async: true, handler: this._onApiContainsPoint.bind(this)}], + ['showContent', {async: true, handler: this._onApiShowContent.bind(this)}], + ['setCustomCss', {async: false, handler: this._onApiSetCustomCss.bind(this)}], + ['clearAutoPlayTimer', {async: false, handler: this._onApiClearAutoPlayTimer.bind(this)}], + ['setContentScale', {async: false, handler: this._onApiSetContentScale.bind(this)}], + ['getUrl', {async: false, handler: this._onApiGetUrl.bind(this)}] + ])); + apiReceiver.prepare(); + } + + getOrCreatePopup(id=null, parentId=null, depth=null) { + // Find by existing id + if (id !== null) { + const popup = this._popups.get(id); + if (typeof popup !== 'undefined') { + return popup; + } + } + + // Find by existing parent id + let parent = null; + if (parentId !== null) { + parent = this._popups.get(parentId); + if (typeof parent !== 'undefined') { + const popup = parent.child; + if (popup !== null) { + return popup; + } + } else { + parent = null; + } + } + + // New unique id + if (id === null) { + id = yomichan.generateId(16); + } + + // Create new popup + if (parent !== null) { + if (depth !== null) { + throw new Error('Depth cannot be set when parent exists'); + } + depth = parent.depth + 1; + } else if (depth === null) { + depth = 0; + } + const popup = new Popup(id, depth, this._frameId); + if (parent !== null) { + popup.setParent(parent); + } + this._popups.set(id, popup); + return popup; + } + + // API message handlers + + _onApiGetOrCreatePopup({id, parentId}) { + const popup = this.getOrCreatePopup(id, parentId); + return { + id: popup.id + }; + } + + async _onApiSetOptionsContext({id, optionsContext, source}) { + const popup = this._getPopup(id); + return await popup.setOptionsContext(optionsContext, source); + } + + _onApiHide({id, changeFocus}) { + const popup = this._getPopup(id); + return popup.hide(changeFocus); + } + + async _onApiIsVisibleAsync({id}) { + const popup = this._getPopup(id); + return await popup.isVisible(); + } + + async _onApiSetVisibleOverride({id, visible}) { + const popup = this._getPopup(id); + return await popup.setVisibleOverride(visible); + } + + async _onApiContainsPoint({id, x, y}) { + const popup = this._getPopup(id); + [x, y] = this._convertPopupPointToRootPagePoint(popup, x, y); + return await popup.containsPoint(x, y); + } + + async _onApiShowContent({id, elementRect, writingMode, type, details, context}) { + const popup = this._getPopup(id); + elementRect = this._convertJsonRectToDOMRect(popup, elementRect); + if (!this._popupCanShow(popup)) { return; } + return await popup.showContent(elementRect, writingMode, type, details, context); + } + + _onApiSetCustomCss({id, css}) { + const popup = this._getPopup(id); + return popup.setCustomCss(css); + } + + _onApiClearAutoPlayTimer({id}) { + const popup = this._getPopup(id); + return popup.clearAutoPlayTimer(); + } + + _onApiSetContentScale({id, scale}) { + const popup = this._getPopup(id); + return popup.setContentScale(scale); + } + + _onApiGetUrl() { + return window.location.href; + } + + // Private functions + + _getPopup(id) { + const popup = this._popups.get(id); + if (typeof popup === 'undefined') { + throw new Error(`Invalid popup ID ${id}`); + } + return popup; + } + + _convertJsonRectToDOMRect(popup, jsonRect) { + const [x, y] = this._convertPopupPointToRootPagePoint(popup, jsonRect.x, jsonRect.y); + return new DOMRect(x, y, jsonRect.width, jsonRect.height); + } + + _convertPopupPointToRootPagePoint(popup, x, y) { + if (popup.parent !== null) { + const popupRect = popup.parent.getContainerRect(); + x += popupRect.x; + y += popupRect.y; + } + return [x, y]; + } + + _popupCanShow(popup) { + return popup.parent === null || popup.parent.isVisibleSync(); + } +} diff --git a/ext/fg/js/popup-proxy-host.js b/ext/fg/js/popup-proxy-host.js deleted file mode 100644 index 39150bc7..00000000 --- a/ext/fg/js/popup-proxy-host.js +++ /dev/null @@ -1,186 +0,0 @@ -/* - * Copyright (C) 2019-2020 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 . - */ - -/* global - * FrontendApiReceiver - * Popup - * apiFrameInformationGet - */ - -class PopupProxyHost { - constructor() { - this._popups = new Map(); - this._frameId = null; - } - - // Public functions - - async prepare() { - const {frameId} = await apiFrameInformationGet(); - if (typeof frameId !== 'number') { return; } - this._frameId = frameId; - - const apiReceiver = new FrontendApiReceiver(`popup-proxy-host#${this._frameId}`, new Map([ - ['getOrCreatePopup', this._onApiGetOrCreatePopup.bind(this)], - ['setOptionsContext', this._onApiSetOptionsContext.bind(this)], - ['hide', this._onApiHide.bind(this)], - ['isVisible', this._onApiIsVisibleAsync.bind(this)], - ['setVisibleOverride', this._onApiSetVisibleOverride.bind(this)], - ['containsPoint', this._onApiContainsPoint.bind(this)], - ['showContent', this._onApiShowContent.bind(this)], - ['setCustomCss', this._onApiSetCustomCss.bind(this)], - ['clearAutoPlayTimer', this._onApiClearAutoPlayTimer.bind(this)], - ['setContentScale', this._onApiSetContentScale.bind(this)], - ['getHostUrl', this._onApiGetHostUrl.bind(this)] - ])); - apiReceiver.prepare(); - } - - getOrCreatePopup(id=null, parentId=null, depth=null) { - // Find by existing id - if (id !== null) { - const popup = this._popups.get(id); - if (typeof popup !== 'undefined') { - return popup; - } - } - - // Find by existing parent id - let parent = null; - if (parentId !== null) { - parent = this._popups.get(parentId); - if (typeof parent !== 'undefined') { - const popup = parent.child; - if (popup !== null) { - return popup; - } - } else { - parent = null; - } - } - - // New unique id - if (id === null) { - id = yomichan.generateId(16); - } - - // Create new popup - if (parent !== null) { - if (depth !== null) { - throw new Error('Depth cannot be set when parent exists'); - } - depth = parent.depth + 1; - } else if (depth === null) { - depth = 0; - } - const popup = new Popup(id, depth, this._frameId); - if (parent !== null) { - popup.setParent(parent); - } - this._popups.set(id, popup); - return popup; - } - - // API message handlers - - async _onApiGetOrCreatePopup({id, parentId}) { - const popup = this.getOrCreatePopup(id, parentId); - return { - id: popup.id - }; - } - - async _onApiSetOptionsContext({id, optionsContext, source}) { - const popup = this._getPopup(id); - return await popup.setOptionsContext(optionsContext, source); - } - - async _onApiHide({id, changeFocus}) { - const popup = this._getPopup(id); - return popup.hide(changeFocus); - } - - async _onApiIsVisibleAsync({id}) { - const popup = this._getPopup(id); - return await popup.isVisible(); - } - - async _onApiSetVisibleOverride({id, visible}) { - const popup = this._getPopup(id); - return await popup.setVisibleOverride(visible); - } - - async _onApiContainsPoint({id, x, y}) { - const popup = this._getPopup(id); - [x, y] = PopupProxyHost._convertPopupPointToRootPagePoint(popup, x, y); - return await popup.containsPoint(x, y); - } - - async _onApiShowContent({id, elementRect, writingMode, type, details, context}) { - const popup = this._getPopup(id); - elementRect = PopupProxyHost._convertJsonRectToDOMRect(popup, elementRect); - if (!PopupProxyHost._popupCanShow(popup)) { return; } - return await popup.showContent(elementRect, writingMode, type, details, context); - } - - async _onApiSetCustomCss({id, css}) { - const popup = this._getPopup(id); - return popup.setCustomCss(css); - } - - async _onApiClearAutoPlayTimer({id}) { - const popup = this._getPopup(id); - return popup.clearAutoPlayTimer(); - } - - async _onApiSetContentScale({id, scale}) { - const popup = this._getPopup(id); - return popup.setContentScale(scale); - } - - async _onApiGetHostUrl() { - return window.location.href; - } - - // Private functions - - _getPopup(id) { - const popup = this._popups.get(id); - if (typeof popup === 'undefined') { - throw new Error(`Invalid popup ID ${id}`); - } - return popup; - } - - static _convertJsonRectToDOMRect(popup, jsonRect) { - const [x, y] = PopupProxyHost._convertPopupPointToRootPagePoint(popup, jsonRect.x, jsonRect.y); - return new DOMRect(x, y, jsonRect.width, jsonRect.height); - } - - static _convertPopupPointToRootPagePoint(popup, x, y) { - if (popup.parent !== null) { - const popupRect = popup.parent.getContainerRect(); - x += popupRect.x; - y += popupRect.y; - } - return [x, y]; - } - - static _popupCanShow(popup) { - return popup.parent === null || popup.parent.isVisibleSync(); - } -} diff --git a/ext/fg/js/popup-proxy.js b/ext/fg/js/popup-proxy.js index 93418202..6a84e000 100644 --- a/ext/fg/js/popup-proxy.js +++ b/ext/fg/js/popup-proxy.js @@ -51,7 +51,7 @@ class PopupProxy { // Public functions async prepare() { - const {id} = await this._invokeHostApi('getOrCreatePopup', {id: this._id, parentId: this._parentId}); + const {id} = await this._invoke('getOrCreatePopup', {id: this._id, parentId: this._parentId}); this._id = id; } @@ -60,19 +60,19 @@ class PopupProxy { } async setOptionsContext(optionsContext, source) { - return await this._invokeHostApi('setOptionsContext', {id: this._id, optionsContext, source}); + return await this._invoke('setOptionsContext', {id: this._id, optionsContext, source}); } hide(changeFocus) { - this._invokeHostApi('hide', {id: this._id, changeFocus}); + this._invoke('hide', {id: this._id, changeFocus}); } async isVisible() { - return await this._invokeHostApi('isVisible', {id: this._id}); + return await this._invoke('isVisible', {id: this._id}); } setVisibleOverride(visible) { - this._invokeHostApi('setVisibleOverride', {id: this._id, visible}); + this._invoke('setVisibleOverride', {id: this._id, visible}); } async containsPoint(x, y) { @@ -80,7 +80,7 @@ class PopupProxy { await this._updateFrameOffset(); [x, y] = this._applyFrameOffset(x, y); } - return await this._invokeHostApi('containsPoint', {id: this._id, x, y}); + return await this._invoke('containsPoint', {id: this._id, x, y}); } async showContent(elementRect, writingMode, type, details, context) { @@ -90,32 +90,32 @@ class PopupProxy { [x, y] = this._applyFrameOffset(x, y); } elementRect = {x, y, width, height}; - return await this._invokeHostApi('showContent', {id: this._id, elementRect, writingMode, type, details, context}); + return await this._invoke('showContent', {id: this._id, elementRect, writingMode, type, details, context}); } - async setCustomCss(css) { - return await this._invokeHostApi('setCustomCss', {id: this._id, css}); + setCustomCss(css) { + this._invoke('setCustomCss', {id: this._id, css}); } clearAutoPlayTimer() { - this._invokeHostApi('clearAutoPlayTimer', {id: this._id}); + this._invoke('clearAutoPlayTimer', {id: this._id}); } - async setContentScale(scale) { - this._invokeHostApi('setContentScale', {id: this._id, scale}); + setContentScale(scale) { + this._invoke('setContentScale', {id: this._id, scale}); } - async getHostUrl() { - return await this._invokeHostApi('getHostUrl', {}); + async getUrl() { + return await this._invoke('getUrl', {}); } // Private - _invokeHostApi(action, params={}) { + _invoke(action, params={}) { if (typeof this._parentFrameId !== 'number') { return Promise.reject(new Error('Invalid frame')); } - return this._apiSender.invoke(action, params, `popup-proxy-host#${this._parentFrameId}`); + return this._apiSender.invoke(action, params, `popup-factory#${this._parentFrameId}`); } async _updateFrameOffset() { diff --git a/ext/fg/js/popup.js b/ext/fg/js/popup.js index 7db53f0d..79b37251 100644 --- a/ext/fg/js/popup.js +++ b/ext/fg/js/popup.js @@ -139,7 +139,7 @@ class Popup { this._invokeApi('setContent', {type, details}); } - async setCustomCss(css) { + setCustomCss(css) { this._invokeApi('setCustomCss', {css}); } diff --git a/ext/manifest.json b/ext/manifest.json index 3cb634f0..80823fc4 100644 --- a/ext/manifest.json +++ b/ext/manifest.json @@ -44,9 +44,9 @@ "fg/js/frontend-api-receiver.js", "fg/js/popup.js", "fg/js/source.js", + "fg/js/popup-factory.js", "fg/js/frame-offset-forwarder.js", "fg/js/popup-proxy.js", - "fg/js/popup-proxy-host.js", "fg/js/frontend.js", "fg/js/content-script-main.js" ], -- cgit v1.2.3 From dd673f0b2626c2bffbcb301dc364009f077c3d08 Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Tue, 19 May 2020 20:33:06 -0400 Subject: Dynamic loader load style (#521) * Remove unnecessary load of /fg/css/client.css * Replace dynamicLoader.loadStyles with dynamicLoader.loadStyle * Replace Popup._injectStylesheet with dynamicLoader.loadStyle * Remove unused global --- ext/bg/js/search-main.js | 3 -- ext/bg/settings-popup-preview.html | 1 + ext/fg/js/popup.js | 78 ++------------------------------------ ext/manifest.json | 1 + ext/mixed/js/dynamic-loader.js | 77 +++++++++++++++++++++++++++++++------ 5 files changed, 70 insertions(+), 90 deletions(-) (limited to 'ext/bg/settings-popup-preview.html') diff --git a/ext/bg/js/search-main.js b/ext/bg/js/search-main.js index 6e092fbc..54fa549d 100644 --- a/ext/bg/js/search-main.js +++ b/ext/bg/js/search-main.js @@ -23,9 +23,6 @@ */ async function injectSearchFrontend() { - dynamicLoader.loadStyles([ - '/fg/css/client.css' - ]); await dynamicLoader.loadScripts([ '/mixed/js/text-scanner.js', '/fg/js/frontend-api-receiver.js', diff --git a/ext/bg/settings-popup-preview.html b/ext/bg/settings-popup-preview.html index 3d7f6455..2f0b841b 100644 --- a/ext/bg/settings-popup-preview.html +++ b/ext/bg/settings-popup-preview.html @@ -121,6 +121,7 @@ + diff --git a/ext/fg/js/popup.js b/ext/fg/js/popup.js index 9e9debd8..b7d4b57e 100644 --- a/ext/fg/js/popup.js +++ b/ext/fg/js/popup.js @@ -17,8 +17,8 @@ /* global * DOM - * apiInjectStylesheet * apiOptionsGet + * dynamicLoader */ class Popup { @@ -180,12 +180,7 @@ class Popup { } async setCustomOuterCss(css, useWebExtensionApi) { - return await this._injectStylesheet( - 'yomichan-popup-outer-user-stylesheet', - 'code', - css, - useWebExtensionApi - ); + return await dynamicLoader.loadStyle('yomichan-popup-outer-user-stylesheet', 'code', css, useWebExtensionApi); } setChildrenSupported(value) { @@ -391,7 +386,7 @@ class Popup { async _injectStyles() { try { - await this._injectStylesheet('yomichan-popup-outer-stylesheet', 'file', '/fg/css/client.css', true); + await dynamicLoader.loadStyle('yomichan-popup-outer-stylesheet', 'file', '/fg/css/client.css', true); } catch (e) { // NOP } @@ -717,71 +712,6 @@ class Popup { }; } - async _injectStylesheet(id, type, value, useWebExtensionApi) { - const injectedStylesheets = Popup._injectedStylesheets; - - if (yomichan.isExtensionUrl(window.location.href)) { - // Permissions error will occur if trying to use the WebExtension API to inject - // into an extension page. - useWebExtensionApi = false; - } - - let styleNode = injectedStylesheets.get(id); - if (typeof styleNode !== 'undefined') { - if (styleNode === null) { - // Previously injected via WebExtension API - throw new Error(`Stylesheet with id ${id} has already been injected using the WebExtension API`); - } - } else { - styleNode = null; - } - - if (useWebExtensionApi) { - // Inject via WebExtension API - if (styleNode !== null && styleNode.parentNode !== null) { - styleNode.parentNode.removeChild(styleNode); - } - - await apiInjectStylesheet(type, value); - - injectedStylesheets.set(id, null); - return null; - } - - // Create node in document - const parentNode = document.head; - if (parentNode === null) { - throw new Error('No parent node'); - } - - // Create or reuse node - const isFile = (type === 'file'); - const tagName = isFile ? 'link' : 'style'; - if (styleNode === null || styleNode.nodeName.toLowerCase() !== tagName) { - if (styleNode !== null && styleNode.parentNode !== null) { - styleNode.parentNode.removeChild(styleNode); - } - styleNode = document.createElement(tagName); - styleNode.id = id; - } - - // Update node style - if (isFile) { - styleNode.rel = value; - } else { - styleNode.textContent = value; - } - - // Update parent - if (styleNode.parentNode !== parentNode) { - parentNode.appendChild(styleNode); - } - - // Add to map - injectedStylesheets.set(id, styleNode); - return styleNode; - } - static isFrameAboutBlank(frame) { try { const contentDocument = frame.contentDocument; @@ -793,5 +723,3 @@ class Popup { } } } - -Popup._injectedStylesheets = new Map(); diff --git a/ext/manifest.json b/ext/manifest.json index 865fe3f3..c87b9296 100644 --- a/ext/manifest.json +++ b/ext/manifest.json @@ -38,6 +38,7 @@ "mixed/js/core.js", "mixed/js/dom.js", "mixed/js/api.js", + "mixed/js/dynamic-loader.js", "mixed/js/text-scanner.js", "fg/js/document.js", "fg/js/frontend-api-sender.js", diff --git a/ext/mixed/js/dynamic-loader.js b/ext/mixed/js/dynamic-loader.js index 51b6821b..ce946109 100644 --- a/ext/mixed/js/dynamic-loader.js +++ b/ext/mixed/js/dynamic-loader.js @@ -15,19 +15,72 @@ * along with this program. If not, see . */ +/* global + * apiInjectStylesheet + */ + const dynamicLoader = (() => { - function loadStyles(urls) { - const parent = document.head; - for (const url of urls) { - const node = parent.querySelector(`link[href='${escapeCSSAttribute(url)}']`); - if (node !== null) { continue; } - - const style = document.createElement('link'); - style.rel = 'stylesheet'; - style.type = 'text/css'; - style.href = url; - parent.appendChild(style); + const injectedStylesheets = new Map(); + + async function loadStyle(id, type, value, useWebExtensionApi=false) { + if (useWebExtensionApi && yomichan.isExtensionUrl(window.location.href)) { + // Permissions error will occur if trying to use the WebExtension API to inject into an extension page + useWebExtensionApi = false; + } + + let styleNode = injectedStylesheets.get(id); + if (typeof styleNode !== 'undefined') { + if (styleNode === null) { + // Previously injected via WebExtension API + throw new Error(`Stylesheet with id ${id} has already been injected using the WebExtension API`); + } + } else { + styleNode = null; + } + + if (useWebExtensionApi) { + // Inject via WebExtension API + if (styleNode !== null && styleNode.parentNode !== null) { + styleNode.parentNode.removeChild(styleNode); + } + + injectedStylesheets.set(id, null); + await apiInjectStylesheet(type, value); + return null; } + + // Create node in document + const parentNode = document.head; + if (parentNode === null) { + throw new Error('No parent node'); + } + + // Create or reuse node + const isFile = (type === 'file'); + const tagName = isFile ? 'link' : 'style'; + if (styleNode === null || styleNode.nodeName.toLowerCase() !== tagName) { + if (styleNode !== null && styleNode.parentNode !== null) { + styleNode.parentNode.removeChild(styleNode); + } + styleNode = document.createElement(tagName); + } + + // Update node style + if (isFile) { + styleNode.rel = 'stylesheet'; + styleNode.href = value; + } else { + styleNode.textContent = value; + } + + // Update parent + if (styleNode.parentNode !== parentNode) { + parentNode.appendChild(styleNode); + } + + // Add to map + injectedStylesheets.set(id, styleNode); + return styleNode; } function loadScripts(urls) { @@ -80,7 +133,7 @@ const dynamicLoader = (() => { return { - loadStyles, + loadStyle, loadScripts }; })(); -- cgit v1.2.3