Fix messaging issues when iframes are present in the document
This commit is contained in:
parent
42ec3e2a43
commit
53aad0bef6
@ -205,3 +205,8 @@ function apiForward(action, params, sender) {
|
|||||||
chrome.tabs.sendMessage(tabId, {action, params}, (response) => resolve(response));
|
chrome.tabs.sendMessage(tabId, {action, params}, (response) => resolve(response));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function apiFrameInformationGet(sender) {
|
||||||
|
const frameId = sender.frameId;
|
||||||
|
return Promise.resolve({frameId});
|
||||||
|
}
|
||||||
|
@ -127,6 +127,10 @@ class Backend {
|
|||||||
|
|
||||||
forward: ({action, params}) => {
|
forward: ({action, params}) => {
|
||||||
forward(apiForward(action, params, sender), callback);
|
forward(apiForward(action, params, sender), callback);
|
||||||
|
},
|
||||||
|
|
||||||
|
frameInformationGet: () => {
|
||||||
|
forward(apiFrameInformationGet(sender), callback);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -25,8 +25,8 @@ async function searchFrontendSetup() {
|
|||||||
'/fg/js/api.js',
|
'/fg/js/api.js',
|
||||||
'/fg/js/frontend-api-receiver.js',
|
'/fg/js/frontend-api-receiver.js',
|
||||||
'/fg/js/popup.js',
|
'/fg/js/popup.js',
|
||||||
'/fg/js/popup-proxy-host.js',
|
|
||||||
'/fg/js/util.js',
|
'/fg/js/util.js',
|
||||||
|
'/fg/js/popup-proxy-host.js',
|
||||||
'/fg/js/frontend.js'
|
'/fg/js/frontend.js'
|
||||||
];
|
];
|
||||||
for (const src of scriptSrcs) {
|
for (const src of scriptSrcs) {
|
||||||
|
@ -64,3 +64,7 @@ function apiScreenshotGet(options) {
|
|||||||
function apiForward(action, params) {
|
function apiForward(action, params) {
|
||||||
return utilInvoke('forward', {action, params});
|
return utilInvoke('forward', {action, params});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function apiFrameInformationGet() {
|
||||||
|
return utilInvoke('frameInformationGet');
|
||||||
|
}
|
||||||
|
@ -31,7 +31,7 @@ class FrontendApiReceiver {
|
|||||||
port.onMessage.addListener(this.onMessage.bind(this, port));
|
port.onMessage.addListener(this.onMessage.bind(this, port));
|
||||||
}
|
}
|
||||||
|
|
||||||
onMessage(port, {id, action, params, target}) {
|
onMessage(port, {id, action, params, target, senderId}) {
|
||||||
if (
|
if (
|
||||||
target !== this.source ||
|
target !== this.source ||
|
||||||
!this.handlers.hasOwnProperty(action)
|
!this.handlers.hasOwnProperty(action)
|
||||||
@ -39,24 +39,24 @@ class FrontendApiReceiver {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.sendAck(port, id);
|
this.sendAck(port, id, senderId);
|
||||||
|
|
||||||
const handler = this.handlers[action];
|
const handler = this.handlers[action];
|
||||||
handler(params).then(
|
handler(params).then(
|
||||||
result => {
|
result => {
|
||||||
this.sendResult(port, id, {result});
|
this.sendResult(port, id, senderId, {result});
|
||||||
},
|
},
|
||||||
e => {
|
e => {
|
||||||
const error = typeof e.toString === 'function' ? e.toString() : e;
|
const error = typeof e.toString === 'function' ? e.toString() : e;
|
||||||
this.sendResult(port, id, {error});
|
this.sendResult(port, id, senderId, {error});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
sendAck(port, id) {
|
sendAck(port, id, senderId) {
|
||||||
port.postMessage({type: 'ack', id});
|
port.postMessage({type: 'ack', id, senderId});
|
||||||
}
|
}
|
||||||
|
|
||||||
sendResult(port, id, data) {
|
sendResult(port, id, senderId, data) {
|
||||||
port.postMessage({type: 'result', id, data});
|
port.postMessage({type: 'result', id, senderId, data});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
class FrontendApiSender {
|
class FrontendApiSender {
|
||||||
constructor() {
|
constructor() {
|
||||||
|
this.senderId = FrontendApiSender.generateId(16);
|
||||||
this.ackTimeout = 3000; // 3 seconds
|
this.ackTimeout = 3000; // 3 seconds
|
||||||
this.responseTimeout = 10000; // 10 seconds
|
this.responseTimeout = 10000; // 10 seconds
|
||||||
this.callbacks = {};
|
this.callbacks = {};
|
||||||
@ -43,11 +44,12 @@ class FrontendApiSender {
|
|||||||
this.callbacks[id] = info;
|
this.callbacks[id] = info;
|
||||||
info.timer = setTimeout(() => this.onError(id, 'Timeout (ack)'), this.ackTimeout);
|
info.timer = setTimeout(() => this.onError(id, 'Timeout (ack)'), this.ackTimeout);
|
||||||
|
|
||||||
this.port.postMessage({id, action, params, target});
|
this.port.postMessage({id, action, params, target, senderId: this.senderId});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onMessage({type, id, data}) {
|
onMessage({type, id, data, senderId}) {
|
||||||
|
if (senderId !== this.senderId) { return; }
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'ack':
|
case 'ack':
|
||||||
this.onAck(id);
|
this.onAck(id);
|
||||||
@ -69,7 +71,7 @@ class FrontendApiSender {
|
|||||||
|
|
||||||
onAck(id) {
|
onAck(id) {
|
||||||
if (!this.callbacks.hasOwnProperty(id)) {
|
if (!this.callbacks.hasOwnProperty(id)) {
|
||||||
console.warn(`ID ${id} not found`);
|
console.warn(`ID ${id} not found for ack`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,14 +42,20 @@ class Frontend {
|
|||||||
const isNested = (currentUrl === floatUrl);
|
const isNested = (currentUrl === floatUrl);
|
||||||
|
|
||||||
let id = null;
|
let id = null;
|
||||||
|
let parentFrameId = null;
|
||||||
if (isNested) {
|
if (isNested) {
|
||||||
const match = /[&?]id=([^&]*?)(?:&|$)/.exec(location.href);
|
let match = /[&?]id=([^&]*?)(?:&|$)/.exec(location.href);
|
||||||
if (match !== null) {
|
if (match !== null) {
|
||||||
id = match[1];
|
id = match[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
match = /[&?]parent=(\d+)(?:&|$)/.exec(location.href);
|
||||||
|
if (match !== null) {
|
||||||
|
parentFrameId = parseInt(match[1], 10);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const popup = isNested ? new PopupProxy(id) : PopupProxyHost.instance.createPopup();
|
const popup = isNested ? new PopupProxy(id, parentFrameId) : PopupProxyHost.instance.createPopup(null);
|
||||||
const frontend = new Frontend(popup);
|
const frontend = new Frontend(popup);
|
||||||
frontend.prepare();
|
frontend.prepare();
|
||||||
return frontend;
|
return frontend;
|
||||||
|
@ -21,7 +21,22 @@ class PopupProxyHost {
|
|||||||
constructor() {
|
constructor() {
|
||||||
this.popups = {};
|
this.popups = {};
|
||||||
this.nextId = 0;
|
this.nextId = 0;
|
||||||
this.apiReceiver = new FrontendApiReceiver('popup-proxy-host', {
|
this.apiReceiver = null;
|
||||||
|
this.frameIdPromise = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static create() {
|
||||||
|
const popupProxyHost = new PopupProxyHost();
|
||||||
|
popupProxyHost.prepare();
|
||||||
|
return popupProxyHost;
|
||||||
|
}
|
||||||
|
|
||||||
|
async prepare() {
|
||||||
|
this.frameIdPromise = apiFrameInformationGet();
|
||||||
|
const {frameId} = await this.frameIdPromise;
|
||||||
|
if (typeof frameId !== 'number') { return; }
|
||||||
|
|
||||||
|
this.apiReceiver = new FrontendApiReceiver(`popup-proxy-host#${frameId}`, {
|
||||||
createNestedPopup: ({parentId}) => this.createNestedPopup(parentId),
|
createNestedPopup: ({parentId}) => this.createNestedPopup(parentId),
|
||||||
show: ({id, elementRect, options}) => this.show(id, elementRect, options),
|
show: ({id, elementRect, options}) => this.show(id, elementRect, options),
|
||||||
showOrphaned: ({id, elementRect, options}) => this.show(id, elementRect, options),
|
showOrphaned: ({id, elementRect, options}) => this.show(id, elementRect, options),
|
||||||
@ -39,7 +54,7 @@ class PopupProxyHost {
|
|||||||
const depth = (parent !== null ? parent.depth + 1 : 0);
|
const depth = (parent !== null ? parent.depth + 1 : 0);
|
||||||
const id = `${this.nextId}`;
|
const id = `${this.nextId}`;
|
||||||
++this.nextId;
|
++this.nextId;
|
||||||
const popup = new Popup(id, depth);
|
const popup = new Popup(id, depth, this.frameIdPromise);
|
||||||
if (parent !== null) {
|
if (parent !== null) {
|
||||||
popup.parent = parent;
|
popup.parent = parent;
|
||||||
parent.children.push(popup);
|
parent.children.push(popup);
|
||||||
@ -116,4 +131,4 @@ class PopupProxyHost {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PopupProxyHost.instance = new PopupProxyHost();
|
PopupProxyHost.instance = PopupProxyHost.create();
|
||||||
|
@ -18,12 +18,14 @@
|
|||||||
|
|
||||||
|
|
||||||
class PopupProxy {
|
class PopupProxy {
|
||||||
constructor(parentId) {
|
constructor(parentId, parentFrameId) {
|
||||||
this.parentId = parentId;
|
this.parentId = parentId;
|
||||||
|
this.parentFrameId = parentFrameId;
|
||||||
this.id = null;
|
this.id = null;
|
||||||
this.idPromise = null;
|
this.idPromise = null;
|
||||||
this.parent = null;
|
this.parent = null;
|
||||||
this.children = [];
|
this.children = [];
|
||||||
|
this.depth = 0;
|
||||||
|
|
||||||
this.container = null;
|
this.container = null;
|
||||||
|
|
||||||
@ -102,7 +104,10 @@ class PopupProxy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
invokeHostApi(action, params={}) {
|
invokeHostApi(action, params={}) {
|
||||||
return this.apiSender.invoke(action, params, 'popup-proxy-host');
|
if (typeof this.parentFrameId !== 'number') {
|
||||||
|
return Promise.reject('Invalid frame');
|
||||||
|
}
|
||||||
|
return this.apiSender.invoke(action, params, `popup-proxy-host#${this.parentFrameId}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
static DOMRectToJson(domRect) {
|
static DOMRectToJson(domRect) {
|
||||||
|
@ -18,24 +18,43 @@
|
|||||||
|
|
||||||
|
|
||||||
class Popup {
|
class Popup {
|
||||||
constructor(id, depth) {
|
constructor(id, depth, frameIdPromise) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.depth = depth;
|
this.depth = depth;
|
||||||
|
this.frameIdPromise = frameIdPromise;
|
||||||
|
this.frameId = null;
|
||||||
this.parent = null;
|
this.parent = null;
|
||||||
this.children = [];
|
this.children = [];
|
||||||
this.container = document.createElement('iframe');
|
this.container = document.createElement('iframe');
|
||||||
this.container.id = 'yomichan-float';
|
this.container.id = 'yomichan-float';
|
||||||
this.container.addEventListener('mousedown', e => e.stopPropagation());
|
this.container.addEventListener('mousedown', e => e.stopPropagation());
|
||||||
this.container.addEventListener('scroll', e => e.stopPropagation());
|
this.container.addEventListener('scroll', e => e.stopPropagation());
|
||||||
this.container.setAttribute('src', chrome.extension.getURL(`/fg/float.html?id=${id}&depth=${depth}`));
|
|
||||||
this.container.style.width = '0px';
|
this.container.style.width = '0px';
|
||||||
this.container.style.height = '0px';
|
this.container.style.height = '0px';
|
||||||
this.injected = null;
|
this.injectPromise = null;
|
||||||
|
this.isInjected = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
inject(options) {
|
inject(options) {
|
||||||
if (!this.injected) {
|
if (this.injectPromise === null) {
|
||||||
this.injected = new Promise((resolve, reject) => {
|
this.injectPromise = this.createInjectPromise(options);
|
||||||
|
}
|
||||||
|
return this.injectPromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
async createInjectPromise(options) {
|
||||||
|
try {
|
||||||
|
const {frameId} = await this.frameIdPromise;
|
||||||
|
if (typeof frameId === 'number') {
|
||||||
|
this.frameId = frameId;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// NOP
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
const parent = (typeof this.frameId === 'number' ? this.frameId : '');
|
||||||
|
this.container.setAttribute('src', chrome.extension.getURL(`/fg/float.html?id=${this.id}&depth=${this.depth}&parent=${parent}`));
|
||||||
this.container.addEventListener('load', () => {
|
this.container.addEventListener('load', () => {
|
||||||
this.invokeApi('setOptions', {
|
this.invokeApi('setOptions', {
|
||||||
general: {
|
general: {
|
||||||
@ -46,12 +65,10 @@ class Popup {
|
|||||||
});
|
});
|
||||||
this.observeFullscreen();
|
this.observeFullscreen();
|
||||||
this.onFullscreenChanged();
|
this.onFullscreenChanged();
|
||||||
|
this.isInjected = true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.injected;
|
|
||||||
}
|
|
||||||
|
|
||||||
async show(elementRect, writingMode, options) {
|
async show(elementRect, writingMode, options) {
|
||||||
await this.inject(options);
|
await this.inject(options);
|
||||||
|
|
||||||
@ -215,7 +232,7 @@ class Popup {
|
|||||||
}
|
}
|
||||||
|
|
||||||
isVisible() {
|
isVisible() {
|
||||||
return this.injected && this.container.style.visibility !== 'hidden';
|
return this.isInjected && this.container.style.visibility !== 'hidden';
|
||||||
}
|
}
|
||||||
|
|
||||||
setVisible(visible) {
|
setVisible(visible) {
|
||||||
@ -260,7 +277,7 @@ class Popup {
|
|||||||
}
|
}
|
||||||
|
|
||||||
clearAutoPlayTimer() {
|
clearAutoPlayTimer() {
|
||||||
if (this.injected) {
|
if (this.isInjected) {
|
||||||
this.invokeApi('clearAutoPlayTimer');
|
this.invokeApi('clearAutoPlayTimer');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,9 +23,9 @@
|
|||||||
"fg/js/document.js",
|
"fg/js/document.js",
|
||||||
"fg/js/frontend-api-receiver.js",
|
"fg/js/frontend-api-receiver.js",
|
||||||
"fg/js/popup.js",
|
"fg/js/popup.js",
|
||||||
"fg/js/popup-proxy-host.js",
|
|
||||||
"fg/js/source.js",
|
"fg/js/source.js",
|
||||||
"fg/js/util.js",
|
"fg/js/util.js",
|
||||||
|
"fg/js/popup-proxy-host.js",
|
||||||
"fg/js/frontend.js"
|
"fg/js/frontend.js"
|
||||||
],
|
],
|
||||||
"css": ["fg/css/client.css"],
|
"css": ["fg/css/client.css"],
|
||||||
|
Loading…
Reference in New Issue
Block a user