Popup crossFrame communication (#658)
* Add support for dynamic message handlers * Pass messages using crossFrame.invoke instead of contentWindow.postMessage * Set up async handlers * Simplify configure call and response
This commit is contained in:
parent
964f011409
commit
8389cd8ba2
@ -32,12 +32,12 @@ class DisplayFloat extends Display {
|
|||||||
this._ownerFrameId = null;
|
this._ownerFrameId = null;
|
||||||
this._frameEndpoint = new FrameEndpoint();
|
this._frameEndpoint = new FrameEndpoint();
|
||||||
this._windowMessageHandlers = new Map([
|
this._windowMessageHandlers = new Map([
|
||||||
['configure', {handler: this._onMessageConfigure.bind(this)}],
|
['configure', {async: true, handler: this._onMessageConfigure.bind(this)}],
|
||||||
['setOptionsContext', {handler: this._onMessageSetOptionsContext.bind(this)}],
|
['setOptionsContext', {async: false, handler: this._onMessageSetOptionsContext.bind(this)}],
|
||||||
['setContent', {handler: this._onMessageSetContent.bind(this)}],
|
['setContent', {async: false, handler: this._onMessageSetContent.bind(this)}],
|
||||||
['clearAutoPlayTimer', {handler: this._onMessageClearAutoPlayTimer.bind(this)}],
|
['clearAutoPlayTimer', {async: false, handler: this._onMessageClearAutoPlayTimer.bind(this)}],
|
||||||
['setCustomCss', {handler: this._onMessageSetCustomCss.bind(this)}],
|
['setCustomCss', {async: false, handler: this._onMessageSetCustomCss.bind(this)}],
|
||||||
['setContentScale', {handler: this._onMessageSetContentScale.bind(this)}]
|
['setContentScale', {async: false, handler: this._onMessageSetContentScale.bind(this)}]
|
||||||
]);
|
]);
|
||||||
|
|
||||||
this.registerActions([
|
this.registerActions([
|
||||||
@ -51,7 +51,9 @@ class DisplayFloat extends Display {
|
|||||||
async prepare() {
|
async prepare() {
|
||||||
await super.prepare();
|
await super.prepare();
|
||||||
|
|
||||||
window.addEventListener('message', this._onMessage.bind(this), false);
|
api.crossFrame.registerHandlers([
|
||||||
|
['popupMessage', {async: 'dynamic', handler: this._onMessage.bind(this)}]
|
||||||
|
]);
|
||||||
|
|
||||||
this._frameEndpoint.signal();
|
this._frameEndpoint.signal();
|
||||||
}
|
}
|
||||||
@ -99,33 +101,23 @@ class DisplayFloat extends Display {
|
|||||||
|
|
||||||
// Message handling
|
// Message handling
|
||||||
|
|
||||||
_onMessage(e) {
|
_onMessage(data) {
|
||||||
let data = e.data;
|
if (!this._frameEndpoint.authenticate(data)) {
|
||||||
if (!this._frameEndpoint.authenticate(data)) { return; }
|
throw new Error('Invalid authentication');
|
||||||
data = data.data;
|
|
||||||
|
|
||||||
if (typeof data !== 'object' || data === null) {
|
|
||||||
this._logMessageError(e, 'Invalid data');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const action = data.action;
|
|
||||||
if (typeof action !== 'string') {
|
|
||||||
this._logMessageError(e, 'Invalid data');
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const {action, params} = data.data;
|
||||||
const handlerInfo = this._windowMessageHandlers.get(action);
|
const handlerInfo = this._windowMessageHandlers.get(action);
|
||||||
if (typeof handlerInfo === 'undefined') {
|
if (typeof handlerInfo === 'undefined') {
|
||||||
this._logMessageError(e, `Invalid action: ${JSON.stringify(action)}`);
|
throw new Error(`Invalid action: ${action}`);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const handler = handlerInfo.handler;
|
const {async, handler} = handlerInfo;
|
||||||
handler(data.params);
|
const result = handler(params);
|
||||||
|
return {async, result};
|
||||||
}
|
}
|
||||||
|
|
||||||
async _onMessageConfigure({messageId, frameId, ownerFrameId, popupId, optionsContext, childrenSupported, scale}) {
|
async _onMessageConfigure({frameId, ownerFrameId, popupId, optionsContext, childrenSupported, scale}) {
|
||||||
this._ownerFrameId = ownerFrameId;
|
this._ownerFrameId = ownerFrameId;
|
||||||
this.setOptionsContext(optionsContext);
|
this.setOptionsContext(optionsContext);
|
||||||
|
|
||||||
@ -138,8 +130,6 @@ class DisplayFloat extends Display {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this._setContentScale(scale);
|
this._setContentScale(scale);
|
||||||
|
|
||||||
api.sendMessageToFrame(frameId, 'popupConfigured', {messageId});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_onMessageSetOptionsContext({optionsContext}) {
|
_onMessageSetOptionsContext({optionsContext}) {
|
||||||
@ -183,10 +173,6 @@ class DisplayFloat extends Display {
|
|||||||
body.style.fontSize = `${scale}em`;
|
body.style.fontSize = `${scale}em`;
|
||||||
}
|
}
|
||||||
|
|
||||||
_logMessageError(event, type) {
|
|
||||||
yomichan.logWarning(new Error(`Popup received invalid message from origin ${JSON.stringify(event.origin)}: ${type}`));
|
|
||||||
}
|
|
||||||
|
|
||||||
async _prepareNestedPopups(id, depth, parentFrameId, url) {
|
async _prepareNestedPopups(id, depth, parentFrameId, url) {
|
||||||
let complete = false;
|
let complete = false;
|
||||||
|
|
||||||
|
@ -257,22 +257,7 @@ class Popup {
|
|||||||
await frameClient.connect(this._frame, this._targetOrigin, this._frameId, setupFrame);
|
await frameClient.connect(this._frame, this._targetOrigin, this._frameId, setupFrame);
|
||||||
|
|
||||||
// Configure
|
// Configure
|
||||||
const messageId = yomichan.generateId(16);
|
await this._invokeApi('configure', {
|
||||||
const popupPreparedPromise = yomichan.getTemporaryListenerResult(
|
|
||||||
chrome.runtime.onMessage,
|
|
||||||
(message, {resolve}) => {
|
|
||||||
if (
|
|
||||||
isObject(message) &&
|
|
||||||
message.action === 'popupConfigured' &&
|
|
||||||
isObject(message.params) &&
|
|
||||||
message.params.messageId === messageId
|
|
||||||
) {
|
|
||||||
resolve();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
this._invokeApi('configure', {
|
|
||||||
messageId,
|
|
||||||
frameId: this._frameId,
|
frameId: this._frameId,
|
||||||
ownerFrameId: this._ownerFrameId,
|
ownerFrameId: this._ownerFrameId,
|
||||||
popupId: this._id,
|
popupId: this._id,
|
||||||
@ -280,8 +265,6 @@ class Popup {
|
|||||||
childrenSupported: this._childrenSupported,
|
childrenSupported: this._childrenSupported,
|
||||||
scale: this._contentScale
|
scale: this._contentScale
|
||||||
});
|
});
|
||||||
|
|
||||||
return popupPreparedPromise;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_onFrameLoad() {
|
_onFrameLoad() {
|
||||||
@ -456,12 +439,12 @@ class Popup {
|
|||||||
return dark ? 'dark' : 'light';
|
return dark ? 'dark' : 'light';
|
||||||
}
|
}
|
||||||
|
|
||||||
_invokeApi(action, params={}) {
|
async _invokeApi(action, params={}) {
|
||||||
const contentWindow = this._frame.contentWindow;
|
const contentWindow = this._frame.contentWindow;
|
||||||
if (this._frameClient === null || !this._frameClient.isConnected() || contentWindow === null) { return; }
|
if (this._frameClient === null || !this._frameClient.isConnected() || contentWindow === null) { return; }
|
||||||
|
|
||||||
const message = this._frameClient.createMessage({action, params});
|
const message = this._frameClient.createMessage({action, params});
|
||||||
contentWindow.postMessage(message, this._targetOrigin);
|
return await api.crossFrame.invoke(this._frameClient.frameId, 'popupMessage', message);
|
||||||
}
|
}
|
||||||
|
|
||||||
_getFrameParentElement() {
|
_getFrameParentElement() {
|
||||||
|
@ -172,29 +172,22 @@ class CrossFrameAPIPort extends EventDispatcher {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const {handler, async} = messageHandler;
|
let {handler, async} = messageHandler;
|
||||||
|
|
||||||
this._sendAck(id);
|
this._sendAck(id);
|
||||||
|
try {
|
||||||
|
let result = handler(params);
|
||||||
|
if (async === 'dynamic') {
|
||||||
|
({async, result} = result);
|
||||||
|
}
|
||||||
if (async) {
|
if (async) {
|
||||||
this._invokeHandlerAsync(id, handler, params);
|
result.then(
|
||||||
|
(result2) => this._sendResult(id, result2),
|
||||||
|
(error2) => this._sendError(id, error2)
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
this._invokeHandler(id, handler, params);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_invokeHandler(id, handler, params) {
|
|
||||||
try {
|
|
||||||
const result = handler(params);
|
|
||||||
this._sendResult(id, result);
|
this._sendResult(id, result);
|
||||||
} catch (error) {
|
|
||||||
this._sendError(id, error);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
async _invokeHandlerAsync(id, handler, params) {
|
|
||||||
try {
|
|
||||||
const result = await handler(params);
|
|
||||||
this._sendResult(id, result);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this._sendError(id, error);
|
this._sendError(id, error);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user