Use a token to ensure that messages are coming from Yomichan

This commit is contained in:
toasted-nutbread 2020-02-17 11:02:21 -05:00
parent aee16c4431
commit 0f46e3a093
4 changed files with 66 additions and 10 deletions

View File

@ -46,6 +46,8 @@ class Backend {
this.popupWindow = null;
this.apiForwarder = new BackendApiForwarder();
this.messageToken = yomichan.generateId(16);
}
async prepare() {
@ -614,6 +616,10 @@ class Backend {
});
}
async _onApiGetMessageToken() {
return this.messageToken;
}
// Command handlers
async _onCommandSearch(params) {
@ -875,7 +881,8 @@ Backend._messageHandlers = new Map([
['clipboardGet', (self, ...args) => self._onApiClipboardGet(...args)],
['getDisplayTemplatesHtml', (self, ...args) => self._onApiGetDisplayTemplatesHtml(...args)],
['getQueryParserTemplatesHtml', (self, ...args) => self._onApiGetQueryParserTemplatesHtml(...args)],
['getZoom', (self, ...args) => self._onApiGetZoom(...args)]
['getZoom', (self, ...args) => self._onApiGetZoom(...args)],
['getMessageToken', (self, ...args) => self._onApiGetMessageToken(...args)]
]);
Backend._commandHandlers = new Map([

View File

@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
/*global popupNestedInitialize, apiForward, Display*/
/*global popupNestedInitialize, apiForward, apiGetMessageToken, Display*/
class DisplayFloat extends Display {
constructor() {
@ -30,6 +30,8 @@ class DisplayFloat extends Display {
this._orphaned = false;
this._prepareInvoked = false;
this._messageToken = null;
this._messageTokenPromise = null;
yomichan.on('orphaned', () => this.onOrphaned());
window.addEventListener('message', (e) => this.onMessage(e), false);
@ -75,11 +77,23 @@ class DisplayFloat extends Display {
}
onMessage(e) {
const {action, params} = e.data;
const handler = DisplayFloat._messageHandlers.get(action);
if (typeof handler !== 'function') { return; }
const data = e.data;
if (typeof data !== 'object' || data === null) { return; } // Invalid data
handler(this, params);
const token = data.token;
if (typeof token !== 'string') { return; } // Invalid data
if (this._messageToken === null) {
// Async
this.getMessageToken()
.then(
() => { this.handleAction(token, data); },
() => {}
);
} else {
// Sync
this.handleAction(token, data);
}
}
onKeyDown(e) {
@ -94,6 +108,30 @@ class DisplayFloat extends Display {
return super.onKeyDown(e);
}
async getMessageToken() {
// this._messageTokenPromise is used to ensure that only one call to apiGetMessageToken is made.
if (this._messageTokenPromise === null) {
this._messageTokenPromise = apiGetMessageToken();
}
const messageToken = await this._messageTokenPromise;
if (this._messageToken === null) {
this._messageToken = messageToken;
}
this._messageTokenPromise = null;
}
handleAction(token, {action, params}) {
if (token !== this._messageToken) {
// Invalid token
return;
}
const handler = DisplayFloat._messageHandlers.get(action);
if (typeof handler !== 'function') { return; }
handler(this, params);
}
getOptionsContext() {
return this.optionsContext;
}

View File

@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
/*global apiInjectStylesheet*/
/*global apiInjectStylesheet, apiGetMessageToken*/
class Popup {
constructor(id, depth, frameIdPromise) {
@ -34,6 +34,7 @@ class Popup {
this._contentScale = 1.0;
this._containerSizeContentScale = null;
this._targetOrigin = chrome.runtime.getURL('/').replace(/\/$/, '');
this._messageToken = null;
this._container = document.createElement('iframe');
this._container.className = 'yomichan-float';
@ -198,6 +199,10 @@ class Popup {
// NOP
}
if (this._messageToken === null) {
this._messageToken = await apiGetMessageToken();
}
return new Promise((resolve) => {
const parentFrameId = (typeof this._frameId === 'number' ? this._frameId : null);
this._container.setAttribute('src', chrome.runtime.getURL('/fg/float.html'));
@ -349,9 +354,11 @@ class Popup {
}
_invokeApi(action, params={}) {
if (this._container.contentWindow) {
this._container.contentWindow.postMessage({action, params}, this._targetOrigin);
}
const token = this._messageToken;
const contentWindow = this._container.contentWindow;
if (token === null || contentWindow === null) { return; }
contentWindow.postMessage({action, params, token}, this._targetOrigin);
}
static _getFullscreenElement() {

View File

@ -113,6 +113,10 @@ function apiGetZoom() {
return _apiInvoke('getZoom');
}
function apiGetMessageToken() {
return _apiInvoke('getMessageToken');
}
function _apiInvoke(action, params={}) {
const data = {action, params};
return new Promise((resolve, reject) => {