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
This commit is contained in:
parent
b972f8cbf6
commit
b936c3e4b1
@ -31,7 +31,7 @@ async function injectSearchFrontend() {
|
|||||||
'/fg/js/frontend-api-receiver.js',
|
'/fg/js/frontend-api-receiver.js',
|
||||||
'/fg/js/frame-offset-forwarder.js',
|
'/fg/js/frame-offset-forwarder.js',
|
||||||
'/fg/js/popup.js',
|
'/fg/js/popup.js',
|
||||||
'/fg/js/popup-proxy-host.js',
|
'/fg/js/popup-factory.js',
|
||||||
'/fg/js/frontend.js',
|
'/fg/js/frontend.js',
|
||||||
'/fg/js/content-script-main.js'
|
'/fg/js/content-script-main.js'
|
||||||
]);
|
]);
|
||||||
|
@ -18,8 +18,9 @@
|
|||||||
/* global
|
/* global
|
||||||
* Frontend
|
* Frontend
|
||||||
* Popup
|
* Popup
|
||||||
* PopupProxyHost
|
* PopupFactory
|
||||||
* TextSourceRange
|
* TextSourceRange
|
||||||
|
* apiFrameInformationGet
|
||||||
* apiOptionsGet
|
* apiOptionsGet
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -56,10 +57,12 @@ class SettingsPopupPreview {
|
|||||||
window.apiOptionsGet = this.apiOptionsGet.bind(this);
|
window.apiOptionsGet = this.apiOptionsGet.bind(this);
|
||||||
|
|
||||||
// Overwrite frontend
|
// Overwrite frontend
|
||||||
const popupHost = new PopupProxyHost();
|
const {frameId} = await apiFrameInformationGet();
|
||||||
await popupHost.prepare();
|
|
||||||
|
|
||||||
this.popup = popupHost.getOrCreatePopup();
|
const popupFactory = new PopupFactory(frameId);
|
||||||
|
await popupFactory.prepare();
|
||||||
|
|
||||||
|
this.popup = popupFactory.getOrCreatePopup();
|
||||||
this.popup.setChildrenSupported(false);
|
this.popup.setChildrenSupported(false);
|
||||||
|
|
||||||
this.popupSetCustomOuterCssOld = this.popup.setCustomOuterCss;
|
this.popupSetCustomOuterCssOld = this.popup.setCustomOuterCss;
|
||||||
|
@ -127,7 +127,7 @@
|
|||||||
<script src="/fg/js/frontend-api-receiver.js"></script>
|
<script src="/fg/js/frontend-api-receiver.js"></script>
|
||||||
<script src="/fg/js/popup.js"></script>
|
<script src="/fg/js/popup.js"></script>
|
||||||
<script src="/fg/js/source.js"></script>
|
<script src="/fg/js/source.js"></script>
|
||||||
<script src="/fg/js/popup-proxy-host.js"></script>
|
<script src="/fg/js/popup-factory.js"></script>
|
||||||
<script src="/fg/js/frontend.js"></script>
|
<script src="/fg/js/frontend.js"></script>
|
||||||
<script src="/bg/js/settings/popup-preview-frame.js"></script>
|
<script src="/bg/js/settings/popup-preview-frame.js"></script>
|
||||||
|
|
||||||
|
@ -19,10 +19,11 @@
|
|||||||
* DOM
|
* DOM
|
||||||
* FrameOffsetForwarder
|
* FrameOffsetForwarder
|
||||||
* Frontend
|
* Frontend
|
||||||
|
* PopupFactory
|
||||||
* PopupProxy
|
* PopupProxy
|
||||||
* PopupProxyHost
|
|
||||||
* apiBroadcastTab
|
* apiBroadcastTab
|
||||||
* apiForwardLogsToBackend
|
* apiForwardLogsToBackend
|
||||||
|
* apiFrameInformationGet
|
||||||
* apiOptionsGet
|
* apiOptionsGet
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -47,10 +48,17 @@ async function createIframePopupProxy(frameOffsetForwarder, setDisabled) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function getOrCreatePopup(depth) {
|
async function getOrCreatePopup(depth) {
|
||||||
const popupHost = new PopupProxyHost();
|
const {frameId} = await apiFrameInformationGet();
|
||||||
await popupHost.prepare();
|
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;
|
return popup;
|
||||||
}
|
}
|
||||||
@ -89,20 +97,20 @@ async function createPopupProxy(depth, id, parentFrameId) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let urlUpdatedAt = 0;
|
let urlUpdatedAt = 0;
|
||||||
let proxyHostUrlCached = url;
|
let popupProxyUrlCached = url;
|
||||||
const getProxyHostUrl = async () => {
|
const getPopupProxyUrl = async () => {
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
if (popups.proxy !== null && now - urlUpdatedAt > 500) {
|
if (popups.proxy !== null && now - urlUpdatedAt > 500) {
|
||||||
proxyHostUrlCached = await popups.proxy.getHostUrl();
|
popupProxyUrlCached = await popups.proxy.getUrl();
|
||||||
urlUpdatedAt = now;
|
urlUpdatedAt = now;
|
||||||
}
|
}
|
||||||
return proxyHostUrlCached;
|
return popupProxyUrlCached;
|
||||||
};
|
};
|
||||||
|
|
||||||
const applyOptions = async () => {
|
const applyOptions = async () => {
|
||||||
const optionsContext = {
|
const optionsContext = {
|
||||||
depth: isSearchPage ? 0 : depth,
|
depth: isSearchPage ? 0 : depth,
|
||||||
url: proxy ? await getProxyHostUrl() : window.location.href
|
url: proxy ? await getPopupProxyUrl() : window.location.href
|
||||||
};
|
};
|
||||||
const options = await apiOptionsGet(optionsContext);
|
const options = await apiOptionsGet(optionsContext);
|
||||||
|
|
||||||
@ -124,7 +132,7 @@ async function createPopupProxy(depth, id, parentFrameId) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (frontend === null) {
|
if (frontend === null) {
|
||||||
const getUrl = proxy ? getProxyHostUrl : null;
|
const getUrl = proxy ? getPopupProxyUrl : null;
|
||||||
frontend = new Frontend(popup, getUrl);
|
frontend = new Frontend(popup, getUrl);
|
||||||
frontendPreparePromise = frontend.prepare();
|
frontendPreparePromise = frontend.prepare();
|
||||||
await frontendPreparePromise;
|
await frontendPreparePromise;
|
||||||
|
@ -17,9 +17,9 @@
|
|||||||
|
|
||||||
|
|
||||||
class FrontendApiReceiver {
|
class FrontendApiReceiver {
|
||||||
constructor(source='', handlers=new Map()) {
|
constructor(source, messageHandlers) {
|
||||||
this._source = source;
|
this._source = source;
|
||||||
this._handlers = handlers;
|
this._messageHandlers = messageHandlers;
|
||||||
}
|
}
|
||||||
|
|
||||||
prepare() {
|
prepare() {
|
||||||
@ -35,14 +35,29 @@ class FrontendApiReceiver {
|
|||||||
_onMessage(port, {id, action, params, target, senderId}) {
|
_onMessage(port, {id, action, params, target, senderId}) {
|
||||||
if (target !== this._source) { return; }
|
if (target !== this._source) { return; }
|
||||||
|
|
||||||
const handler = this._handlers.get(action);
|
const messageHandler = this._messageHandlers.get(action);
|
||||||
if (typeof handler !== 'function') { return; }
|
if (typeof messageHandler === 'undefined') { return; }
|
||||||
|
|
||||||
|
const {handler, async} = messageHandler;
|
||||||
|
|
||||||
this._sendAck(port, id, senderId);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async _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 _invokeHandlerAsync(handler, params, port, id, senderId) {
|
||||||
try {
|
try {
|
||||||
const result = await handler(params);
|
const result = await handler(params);
|
||||||
this._sendResult(port, id, senderId, {result});
|
this._sendResult(port, id, senderId, {result});
|
||||||
|
@ -18,34 +18,29 @@
|
|||||||
/* global
|
/* global
|
||||||
* FrontendApiReceiver
|
* FrontendApiReceiver
|
||||||
* Popup
|
* Popup
|
||||||
* apiFrameInformationGet
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class PopupProxyHost {
|
class PopupFactory {
|
||||||
constructor() {
|
constructor(frameId) {
|
||||||
this._popups = new Map();
|
this._popups = new Map();
|
||||||
this._frameId = null;
|
this._frameId = frameId;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Public functions
|
// Public functions
|
||||||
|
|
||||||
async prepare() {
|
async prepare() {
|
||||||
const {frameId} = await apiFrameInformationGet();
|
const apiReceiver = new FrontendApiReceiver(`popup-factory#${this._frameId}`, new Map([
|
||||||
if (typeof frameId !== 'number') { return; }
|
['getOrCreatePopup', {async: false, handler: this._onApiGetOrCreatePopup.bind(this)}],
|
||||||
this._frameId = frameId;
|
['setOptionsContext', {async: true, handler: this._onApiSetOptionsContext.bind(this)}],
|
||||||
|
['hide', {async: false, handler: this._onApiHide.bind(this)}],
|
||||||
const apiReceiver = new FrontendApiReceiver(`popup-proxy-host#${this._frameId}`, new Map([
|
['isVisible', {async: true, handler: this._onApiIsVisibleAsync.bind(this)}],
|
||||||
['getOrCreatePopup', this._onApiGetOrCreatePopup.bind(this)],
|
['setVisibleOverride', {async: true, handler: this._onApiSetVisibleOverride.bind(this)}],
|
||||||
['setOptionsContext', this._onApiSetOptionsContext.bind(this)],
|
['containsPoint', {async: true, handler: this._onApiContainsPoint.bind(this)}],
|
||||||
['hide', this._onApiHide.bind(this)],
|
['showContent', {async: true, handler: this._onApiShowContent.bind(this)}],
|
||||||
['isVisible', this._onApiIsVisibleAsync.bind(this)],
|
['setCustomCss', {async: false, handler: this._onApiSetCustomCss.bind(this)}],
|
||||||
['setVisibleOverride', this._onApiSetVisibleOverride.bind(this)],
|
['clearAutoPlayTimer', {async: false, handler: this._onApiClearAutoPlayTimer.bind(this)}],
|
||||||
['containsPoint', this._onApiContainsPoint.bind(this)],
|
['setContentScale', {async: false, handler: this._onApiSetContentScale.bind(this)}],
|
||||||
['showContent', this._onApiShowContent.bind(this)],
|
['getUrl', {async: false, handler: this._onApiGetUrl.bind(this)}]
|
||||||
['setCustomCss', this._onApiSetCustomCss.bind(this)],
|
|
||||||
['clearAutoPlayTimer', this._onApiClearAutoPlayTimer.bind(this)],
|
|
||||||
['setContentScale', this._onApiSetContentScale.bind(this)],
|
|
||||||
['getHostUrl', this._onApiGetHostUrl.bind(this)]
|
|
||||||
]));
|
]));
|
||||||
apiReceiver.prepare();
|
apiReceiver.prepare();
|
||||||
}
|
}
|
||||||
@ -97,7 +92,7 @@ class PopupProxyHost {
|
|||||||
|
|
||||||
// API message handlers
|
// API message handlers
|
||||||
|
|
||||||
async _onApiGetOrCreatePopup({id, parentId}) {
|
_onApiGetOrCreatePopup({id, parentId}) {
|
||||||
const popup = this.getOrCreatePopup(id, parentId);
|
const popup = this.getOrCreatePopup(id, parentId);
|
||||||
return {
|
return {
|
||||||
id: popup.id
|
id: popup.id
|
||||||
@ -109,7 +104,7 @@ class PopupProxyHost {
|
|||||||
return await popup.setOptionsContext(optionsContext, source);
|
return await popup.setOptionsContext(optionsContext, source);
|
||||||
}
|
}
|
||||||
|
|
||||||
async _onApiHide({id, changeFocus}) {
|
_onApiHide({id, changeFocus}) {
|
||||||
const popup = this._getPopup(id);
|
const popup = this._getPopup(id);
|
||||||
return popup.hide(changeFocus);
|
return popup.hide(changeFocus);
|
||||||
}
|
}
|
||||||
@ -126,33 +121,33 @@ class PopupProxyHost {
|
|||||||
|
|
||||||
async _onApiContainsPoint({id, x, y}) {
|
async _onApiContainsPoint({id, x, y}) {
|
||||||
const popup = this._getPopup(id);
|
const popup = this._getPopup(id);
|
||||||
[x, y] = PopupProxyHost._convertPopupPointToRootPagePoint(popup, x, y);
|
[x, y] = this._convertPopupPointToRootPagePoint(popup, x, y);
|
||||||
return await popup.containsPoint(x, y);
|
return await popup.containsPoint(x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
async _onApiShowContent({id, elementRect, writingMode, type, details, context}) {
|
async _onApiShowContent({id, elementRect, writingMode, type, details, context}) {
|
||||||
const popup = this._getPopup(id);
|
const popup = this._getPopup(id);
|
||||||
elementRect = PopupProxyHost._convertJsonRectToDOMRect(popup, elementRect);
|
elementRect = this._convertJsonRectToDOMRect(popup, elementRect);
|
||||||
if (!PopupProxyHost._popupCanShow(popup)) { return; }
|
if (!this._popupCanShow(popup)) { return; }
|
||||||
return await popup.showContent(elementRect, writingMode, type, details, context);
|
return await popup.showContent(elementRect, writingMode, type, details, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
async _onApiSetCustomCss({id, css}) {
|
_onApiSetCustomCss({id, css}) {
|
||||||
const popup = this._getPopup(id);
|
const popup = this._getPopup(id);
|
||||||
return popup.setCustomCss(css);
|
return popup.setCustomCss(css);
|
||||||
}
|
}
|
||||||
|
|
||||||
async _onApiClearAutoPlayTimer({id}) {
|
_onApiClearAutoPlayTimer({id}) {
|
||||||
const popup = this._getPopup(id);
|
const popup = this._getPopup(id);
|
||||||
return popup.clearAutoPlayTimer();
|
return popup.clearAutoPlayTimer();
|
||||||
}
|
}
|
||||||
|
|
||||||
async _onApiSetContentScale({id, scale}) {
|
_onApiSetContentScale({id, scale}) {
|
||||||
const popup = this._getPopup(id);
|
const popup = this._getPopup(id);
|
||||||
return popup.setContentScale(scale);
|
return popup.setContentScale(scale);
|
||||||
}
|
}
|
||||||
|
|
||||||
async _onApiGetHostUrl() {
|
_onApiGetUrl() {
|
||||||
return window.location.href;
|
return window.location.href;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -166,12 +161,12 @@ class PopupProxyHost {
|
|||||||
return popup;
|
return popup;
|
||||||
}
|
}
|
||||||
|
|
||||||
static _convertJsonRectToDOMRect(popup, jsonRect) {
|
_convertJsonRectToDOMRect(popup, jsonRect) {
|
||||||
const [x, y] = PopupProxyHost._convertPopupPointToRootPagePoint(popup, jsonRect.x, jsonRect.y);
|
const [x, y] = this._convertPopupPointToRootPagePoint(popup, jsonRect.x, jsonRect.y);
|
||||||
return new DOMRect(x, y, jsonRect.width, jsonRect.height);
|
return new DOMRect(x, y, jsonRect.width, jsonRect.height);
|
||||||
}
|
}
|
||||||
|
|
||||||
static _convertPopupPointToRootPagePoint(popup, x, y) {
|
_convertPopupPointToRootPagePoint(popup, x, y) {
|
||||||
if (popup.parent !== null) {
|
if (popup.parent !== null) {
|
||||||
const popupRect = popup.parent.getContainerRect();
|
const popupRect = popup.parent.getContainerRect();
|
||||||
x += popupRect.x;
|
x += popupRect.x;
|
||||||
@ -180,7 +175,7 @@ class PopupProxyHost {
|
|||||||
return [x, y];
|
return [x, y];
|
||||||
}
|
}
|
||||||
|
|
||||||
static _popupCanShow(popup) {
|
_popupCanShow(popup) {
|
||||||
return popup.parent === null || popup.parent.isVisibleSync();
|
return popup.parent === null || popup.parent.isVisibleSync();
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -51,7 +51,7 @@ class PopupProxy {
|
|||||||
// Public functions
|
// Public functions
|
||||||
|
|
||||||
async prepare() {
|
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;
|
this._id = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,19 +60,19 @@ class PopupProxy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async setOptionsContext(optionsContext, source) {
|
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) {
|
hide(changeFocus) {
|
||||||
this._invokeHostApi('hide', {id: this._id, changeFocus});
|
this._invoke('hide', {id: this._id, changeFocus});
|
||||||
}
|
}
|
||||||
|
|
||||||
async isVisible() {
|
async isVisible() {
|
||||||
return await this._invokeHostApi('isVisible', {id: this._id});
|
return await this._invoke('isVisible', {id: this._id});
|
||||||
}
|
}
|
||||||
|
|
||||||
setVisibleOverride(visible) {
|
setVisibleOverride(visible) {
|
||||||
this._invokeHostApi('setVisibleOverride', {id: this._id, visible});
|
this._invoke('setVisibleOverride', {id: this._id, visible});
|
||||||
}
|
}
|
||||||
|
|
||||||
async containsPoint(x, y) {
|
async containsPoint(x, y) {
|
||||||
@ -80,7 +80,7 @@ class PopupProxy {
|
|||||||
await this._updateFrameOffset();
|
await this._updateFrameOffset();
|
||||||
[x, y] = this._applyFrameOffset(x, y);
|
[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) {
|
async showContent(elementRect, writingMode, type, details, context) {
|
||||||
@ -90,32 +90,32 @@ class PopupProxy {
|
|||||||
[x, y] = this._applyFrameOffset(x, y);
|
[x, y] = this._applyFrameOffset(x, y);
|
||||||
}
|
}
|
||||||
elementRect = {x, y, width, height};
|
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) {
|
setCustomCss(css) {
|
||||||
return await this._invokeHostApi('setCustomCss', {id: this._id, css});
|
this._invoke('setCustomCss', {id: this._id, css});
|
||||||
}
|
}
|
||||||
|
|
||||||
clearAutoPlayTimer() {
|
clearAutoPlayTimer() {
|
||||||
this._invokeHostApi('clearAutoPlayTimer', {id: this._id});
|
this._invoke('clearAutoPlayTimer', {id: this._id});
|
||||||
}
|
}
|
||||||
|
|
||||||
async setContentScale(scale) {
|
setContentScale(scale) {
|
||||||
this._invokeHostApi('setContentScale', {id: this._id, scale});
|
this._invoke('setContentScale', {id: this._id, scale});
|
||||||
}
|
}
|
||||||
|
|
||||||
async getHostUrl() {
|
async getUrl() {
|
||||||
return await this._invokeHostApi('getHostUrl', {});
|
return await this._invoke('getUrl', {});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Private
|
// Private
|
||||||
|
|
||||||
_invokeHostApi(action, params={}) {
|
_invoke(action, params={}) {
|
||||||
if (typeof this._parentFrameId !== 'number') {
|
if (typeof this._parentFrameId !== 'number') {
|
||||||
return Promise.reject(new Error('Invalid frame'));
|
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() {
|
async _updateFrameOffset() {
|
||||||
|
@ -139,7 +139,7 @@ class Popup {
|
|||||||
this._invokeApi('setContent', {type, details});
|
this._invokeApi('setContent', {type, details});
|
||||||
}
|
}
|
||||||
|
|
||||||
async setCustomCss(css) {
|
setCustomCss(css) {
|
||||||
this._invokeApi('setCustomCss', {css});
|
this._invokeApi('setCustomCss', {css});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,9 +44,9 @@
|
|||||||
"fg/js/frontend-api-receiver.js",
|
"fg/js/frontend-api-receiver.js",
|
||||||
"fg/js/popup.js",
|
"fg/js/popup.js",
|
||||||
"fg/js/source.js",
|
"fg/js/source.js",
|
||||||
|
"fg/js/popup-factory.js",
|
||||||
"fg/js/frame-offset-forwarder.js",
|
"fg/js/frame-offset-forwarder.js",
|
||||||
"fg/js/popup-proxy.js",
|
"fg/js/popup-proxy.js",
|
||||||
"fg/js/popup-proxy-host.js",
|
|
||||||
"fg/js/frontend.js",
|
"fg/js/frontend.js",
|
||||||
"fg/js/content-script-main.js"
|
"fg/js/content-script-main.js"
|
||||||
],
|
],
|
||||||
|
Loading…
Reference in New Issue
Block a user