Skip to content

Commit

Permalink
Look for tagged ancestors for cross-frame messages (#234)
Browse files Browse the repository at this point in the history
  • Loading branch information
dgirardi authored Jul 1, 2024
1 parent a5cb58d commit 1c32e8b
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 4 deletions.
12 changes: 10 additions & 2 deletions src/messaging.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,28 @@ export function prebidMessenger(publisherURL, win = window) {
return parsedUrl.protocol + '://' + parsedUrl.host;
})();

function isPrebidWindow(win) {
return win && win.frames && win.frames.__pb_locator__;
}

let target = win.parent;
while (target != null && target !== win.top && !isPrebidWindow(target)) target = target.parent;
if (!isPrebidWindow(target)) target = win.parent;

return function sendMessage(message, onResponse) {
if (prebidDomain == null) {
throw new Error('Missing pubUrl')
}
message = JSON.stringify(message);
let messagePort;
if (onResponse == null) {
win.parent.postMessage(message, prebidDomain);
target.postMessage(message, prebidDomain);
} else {
const channel = new MessageChannel();
messagePort = channel.port1;
messagePort.onmessage = onResponse;
win.addEventListener('message', windowListener);
win.parent.postMessage(message, prebidDomain, [channel.port2]);
target.postMessage(message, prebidDomain, [channel.port2]);
}

return function stopListening() {
Expand Down
1 change: 1 addition & 0 deletions test/helpers/mocks.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export const mocks = {
},
parent: {},
top: {},
frames: {},
};
}
}
46 changes: 44 additions & 2 deletions test/spec/messaging_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,23 +25,65 @@ describe('prebidMessenger',() => {
describe('when publisher URL is available', () => {
const URL = 'https://www.publisher.com/page.html';
const ORIGIN = 'https://www.publisher.com'
let sendMessage;
let callback, handler;

beforeEach(() => {
win.addEventListener = function (_, h) {
handler = h;
}
win.removeEventListener = sinon.spy();
sendMessage = prebidMessenger(URL, win);
callback = sinon.spy();
})

function sendMessage(...args) {
return prebidMessenger(URL, win)(...args);
}

it('should use origin for postMessage', () => {
sendMessage('test');
sinon.assert.calledWith(win.parent.postMessage, JSON.stringify('test'), ORIGIN);
});

describe('when window has multiple ancestors', () => {
let target;
beforeEach(() => {
const top = mocks.createFakeWindow('top');
target = {
...win.parent,
frames: {},
parent: {
top,
frames: {},
parent: top
}
};
win = {
top,
frames: {},
parent: {
top,
frames: {},
parent: target
}
};
})
it('should post to first ancestor that has a __pb_locator__ child', () => {
[target, target.parent].forEach(win => {
win.frames = {
__pb_locator__: {}
};
})
sendMessage('test');
sinon.assert.calledWith(target.postMessage);
});
it('should post to immediate parent when no ancestor has __pb_locator__', () => {
win.parent.postMessage = sinon.spy();
delete target.postMessage;
sendMessage('test');
sinon.assert.calledWith(win.parent.postMessage);
});
});

it('should not run callback on response if origin does not mach', ()=> {
sendMessage('test', callback);
handler({origin: 'different'});
Expand Down

0 comments on commit 1c32e8b

Please sign in to comment.