Frame ancestry handler refactor (#1352)
* Validate source window before handling messages * Add unregisterHandler to CrossFrameAPI * Refactor the process FrameAncestryHandler uses to get ancestor frame IDs * Store a mapping of child frame information * Update getFrameAncestryInfo to only run once
This commit is contained in:
parent
356e7f5274
commit
9f5cbaac5a
@ -33,7 +33,9 @@ class FrameAncestryHandler {
|
|||||||
this._frameId = frameId;
|
this._frameId = frameId;
|
||||||
this._isPrepared = false;
|
this._isPrepared = false;
|
||||||
this._requestMessageId = 'FrameAncestryHandler.requestFrameInfo';
|
this._requestMessageId = 'FrameAncestryHandler.requestFrameInfo';
|
||||||
this._responseMessageId = `${this._requestMessageId}.response`;
|
this._responseMessageIdBase = `${this._requestMessageId}.response.`;
|
||||||
|
this._getFrameAncestryInfoPromise = null;
|
||||||
|
this._childFrameMap = new Map();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -59,7 +61,16 @@ class FrameAncestryHandler {
|
|||||||
* @param timeout The maximum time to wait to receive a response to frame information requests.
|
* @param timeout The maximum time to wait to receive a response to frame information requests.
|
||||||
* @returns An array of frame IDs corresponding to the ancestors of the current frame.
|
* @returns An array of frame IDs corresponding to the ancestors of the current frame.
|
||||||
*/
|
*/
|
||||||
getFrameAncestryInfo(timeout=5000) {
|
async getFrameAncestryInfo() {
|
||||||
|
if (this._getFrameAncestryInfoPromise === null) {
|
||||||
|
this._getFrameAncestryInfoPromise = this._getFrameAncestryInfo(5000);
|
||||||
|
}
|
||||||
|
return await this._getFrameAncestryInfoPromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Private
|
||||||
|
|
||||||
|
_getFrameAncestryInfo(timeout=5000) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const targetWindow = window.parent;
|
const targetWindow = window.parent;
|
||||||
if (window === targetWindow) {
|
if (window === targetWindow) {
|
||||||
@ -68,10 +79,9 @@ class FrameAncestryHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const uniqueId = generateId(16);
|
const uniqueId = generateId(16);
|
||||||
const responseMessageId = this._responseMessageId;
|
let nonce = generateId(16);
|
||||||
|
const responseMessageId = `${this._responseMessageIdBase}${uniqueId}`;
|
||||||
const results = [];
|
const results = [];
|
||||||
let resultsExpectedCount = null;
|
|
||||||
let resultsCount = 0;
|
|
||||||
let timer = null;
|
let timer = null;
|
||||||
|
|
||||||
const cleanup = () => {
|
const cleanup = () => {
|
||||||
@ -79,42 +89,24 @@ class FrameAncestryHandler {
|
|||||||
clearTimeout(timer);
|
clearTimeout(timer);
|
||||||
timer = null;
|
timer = null;
|
||||||
}
|
}
|
||||||
chrome.runtime.onMessage.removeListener(onMessage);
|
api.crossFrame.unregisterHandler(responseMessageId);
|
||||||
};
|
};
|
||||||
const onMessage = (message, sender, sendResponse) => {
|
const onMessage = (params) => {
|
||||||
// Validate message
|
if (params.nonce !== nonce) { return null; }
|
||||||
if (
|
|
||||||
typeof message !== 'object' ||
|
|
||||||
message === null ||
|
|
||||||
message.action !== responseMessageId
|
|
||||||
) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const {params} = message;
|
|
||||||
if (params.uniqueId !== uniqueId) { return; } // Wrong ID
|
|
||||||
|
|
||||||
const {frameId, index, more} = params;
|
|
||||||
console.log({frameId, index, more});
|
|
||||||
if (typeof results[index] !== 'undefined') { return; } // Invalid repeat
|
|
||||||
|
|
||||||
// Add result
|
// Add result
|
||||||
results[index] = frameId;
|
const {frameId, more} = params;
|
||||||
++resultsCount;
|
results.push(frameId);
|
||||||
if (!more) {
|
nonce = generateId(16);
|
||||||
resultsExpectedCount = index + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (resultsExpectedCount !== null && resultsCount >= resultsExpectedCount) {
|
if (!more) {
|
||||||
// Cleanup
|
// Cleanup
|
||||||
cleanup();
|
cleanup();
|
||||||
sendResponse();
|
|
||||||
|
|
||||||
// Finish
|
// Finish
|
||||||
resolve(results);
|
resolve(results);
|
||||||
} else {
|
|
||||||
resetTimeout();
|
|
||||||
}
|
}
|
||||||
|
return {nonce};
|
||||||
};
|
};
|
||||||
const onTimeout = () => {
|
const onTimeout = () => {
|
||||||
timer = null;
|
timer = null;
|
||||||
@ -127,61 +119,68 @@ class FrameAncestryHandler {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Start
|
// Start
|
||||||
chrome.runtime.onMessage.addListener(onMessage);
|
api.crossFrame.registerHandlers([[responseMessageId, {async: false, handler: onMessage}]]);
|
||||||
resetTimeout();
|
resetTimeout();
|
||||||
this._requestFrameInfo(targetWindow, uniqueId, this._frameId, 0);
|
const frameId = this._frameId;
|
||||||
|
this._requestFrameInfo(targetWindow, frameId, frameId, uniqueId, nonce);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Private
|
|
||||||
|
|
||||||
_onWindowMessage(event) {
|
_onWindowMessage(event) {
|
||||||
|
const {source} = event;
|
||||||
|
if (source === window || source.parent !== window) { return; }
|
||||||
|
|
||||||
const {data} = event;
|
const {data} = event;
|
||||||
if (
|
if (
|
||||||
typeof data === 'object' &&
|
typeof data === 'object' &&
|
||||||
data !== null &&
|
data !== null &&
|
||||||
data.action === this._requestMessageId
|
data.action === this._requestMessageId
|
||||||
) {
|
) {
|
||||||
try {
|
this._onRequestFrameInfo(data.params, source);
|
||||||
this._onRequestFrameInfo(data.params);
|
|
||||||
} catch (e) {
|
|
||||||
// NOP
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_onRequestFrameInfo({uniqueId, originFrameId, index}) {
|
async _onRequestFrameInfo(params, source) {
|
||||||
|
try {
|
||||||
|
let {originFrameId, childFrameId, uniqueId, nonce} = params;
|
||||||
if (
|
if (
|
||||||
|
!this._isNonNegativeInteger(originFrameId) ||
|
||||||
typeof uniqueId !== 'string' ||
|
typeof uniqueId !== 'string' ||
|
||||||
typeof originFrameId !== 'number' ||
|
typeof nonce !== 'string'
|
||||||
!this._isNonNegativeInteger(index)
|
|
||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const frameId = this._frameId;
|
||||||
const {parent} = window;
|
const {parent} = window;
|
||||||
const more = (window !== parent);
|
const more = (window !== parent);
|
||||||
|
const responseParams = {frameId, nonce, more};
|
||||||
|
const responseMessageId = `${this._responseMessageIdBase}${uniqueId}`;
|
||||||
|
|
||||||
const responseParams = {uniqueId, frameId: this._frameId, index, more};
|
try {
|
||||||
this._safeSendMessageToFrame(originFrameId, this._responseMessageId, responseParams);
|
const response = await api.crossFrame.invoke(originFrameId, responseMessageId, responseParams);
|
||||||
|
if (response === null) { return; }
|
||||||
|
nonce = response.nonce;
|
||||||
|
} catch (e) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this._childFrameMap.has(childFrameId)) {
|
||||||
|
this._childFrameMap.set(childFrameId, {window: source});
|
||||||
|
}
|
||||||
|
|
||||||
if (more) {
|
if (more) {
|
||||||
this._requestFrameInfo(parent, uniqueId, originFrameId, index + 1);
|
this._requestFrameInfo(parent, originFrameId, frameId, uniqueId, nonce);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
async _safeSendMessageToFrame(frameId, action, params) {
|
|
||||||
try {
|
|
||||||
await api.sendMessageToFrame(frameId, action, params);
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// NOP
|
// NOP
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_requestFrameInfo(targetWindow, uniqueId, originFrameId, index) {
|
_requestFrameInfo(targetWindow, originFrameId, childFrameId, uniqueId, nonce) {
|
||||||
targetWindow.postMessage({
|
targetWindow.postMessage({
|
||||||
action: this._requestMessageId,
|
action: this._requestMessageId,
|
||||||
params: {uniqueId, originFrameId, index}
|
params: {originFrameId, childFrameId, uniqueId, nonce}
|
||||||
}, '*');
|
}, '*');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -248,6 +248,12 @@ class CrossFrameAPI {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unregisterHandler(key) {
|
||||||
|
return this._messageHandlers.delete(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Private
|
||||||
|
|
||||||
_onConnect(port) {
|
_onConnect(port) {
|
||||||
try {
|
try {
|
||||||
let details;
|
let details;
|
||||||
|
Loading…
Reference in New Issue
Block a user