Open kunaakos opened 6 years ago
I'm not sure I understand the issue correctly. Can you not pass (...args) => window.parent.postMessage(...args)
as the postMessage
option to caller
?
https://github.com/tableflip/postmsg-rpc#callerfuncname-options
Yeah, but to get the return value, I need to pass window.parent.addEventListener
as the addListener
option to caller
- and creating event listeners on the parent window is not allowed from cross-domain iframes.
This is what I'm currently doing, exposing functions with hack()
instead of expose()
:
const exposeOptions = {
postMessage: (returnValue, targetOrigin) => {
returnValue.res.source.postMessage({
...returnValue,
res: returnValue.res.data
}, targetOrigin);
},
getMessageData: (event) => {
return {
...event.data,
args: event.data.args
? [event.source, ...event.data.args]
: [event.source]
};
}
};
function hack(fnName, fn) {
const wrappedFn = async (...args) => {
return {
source: args[0],
data: await fn(...args.slice(1))
};
};
expose(fnName, wrappedFn, exposeOptions);
}
If I'm missing something, and there's a better way, lemme know.
Yeah, but to get the return value, I need to pass
window.parent.addEventListener
as theaddListener
option tocaller
- and creating event listeners on the parent window is allowed from cross-domain iframes.
You should use the default - window.addEventListener
?
correction: *not allowed from cross-domain iframes - that's a confusing typo to make, sry
If I use the default, I need the above hack. That works.
But if window.postMessage()
is called in the parent, and an event listener is created using window.addEventlistener()
in the child (iframe) context, it doesn't, because events are dispatched on the parent window object, not the child.
In the parent can you pass postMessage
option to expose
as (...args) => document.querySelector('iframe').contentWindow.postMessage(...args)
I have several, dynamically inserted iframes on the page, and several of those will call the same exposed method, so that approach doesn't work :(
Gotcha, I think your hack is ok for now. I wanted to add this a while ago but my primary use for this library is in a webextension context where the postMessage API is similar but not the same. I'd like to add it in a way that allows it to be used in a web page but also in a web extension.
FYI the web extension API https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/runtime/onMessage
I was thinking about sth like:
export default function expose (funcName, func, opts) {
opts = opts || {}
// ...
const replyToEmitter = opts.replyToEmitter || false
// ...
const handler = function (event) {
// ...
const dispatch = replyToEmitter
? (msg) => event.source.postMessage(msg, targetOrigin)
: (msg) => postMessage(msg, targetOrigin)
// ...
const onSuccess = (res) => {
msg.res = res
dispatch(msg)
}
const onError = (err) => {
// ...
dispatch(msg)
}
// ...
}
This doesn't break compatibility AFAIK, but makes the config a bit more confusing, since setting opts.replyToEmitter
to true would make opts.postMessage
unnecessary. Or maybe adding something like 'emitter'
as a possible value to opts.postMessage
would be cleaner?
Either way, happy to submit a PR, but wasn't sure about how to approach this.
Hi!
I ran into an issue with multiple iframes from different domains communicating with the main window. I cannot create listeners on the main window from the iframes' contexts because of cross-domain restrictions. The way this was handled before using postmsg-rpc, was by dispatching an event on the window we got the original message from:
Would it be possible to add this as an option to postmsg-rpc? Something like
opts.replyToSender
Happy to submit a PR!