Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for iFrame #236

Open
rmdort opened this issue Oct 19, 2017 · 15 comments
Open

Support for iFrame #236

rmdort opened this issue Oct 19, 2017 · 15 comments

Comments

@rmdort
Copy link
Contributor

rmdort commented Oct 19, 2017

If the react component is in an iframe, onclickoutside doesnt really work. You have to click on the parent document to trigger handleClickOutside

This module binds all click events to document. If you can expose the document prop, i can bind events to an iframe document instead. What do you think?

@Andarist
Copy link
Collaborator

Could you describe your issue in more detail? Im using this library in an iframe and it work just fine (within iframe ofc)

@rmdort
Copy link
Contributor Author

rmdort commented Oct 20, 2017

Here is a plunkr demo https://plnkr.co/edit/1DwrgrAqkakmGMamJrXr?p=preview

When you click inside the iframe, handleClickOutside doesnt get called.

https://www.evernote.com/l/ABot4Su8DvBOSruUVClLgGWoUmq3Aywe5Aw

Note: I am using react-iframe-component - https://github.com/ryanseddon/react-frame-component

@Pomax
Copy link
Owner

Pomax commented Oct 20, 2017

This is how browsers work: parent pages do not get notified of clicks inside iframes because those are literally different pages with different security domains, and knowing anything about what happens inside of them means leaking information that can be used for exploits.

Aside from obviously not using iframes (except for maybe editor live-views, they're almost never a good thing), the only signal you get when you click inside the iframe is that the parent document is blurred. However, as the parent can get blurred for a variety of reasons (for instance, clicking outside the browser) that is not a signal that can be used to determine that an outside click inside the same document (as far as the user experiences documents) occurs.

What you can do, if you control both the parent and the iframed content, and you absolutely need this, is write your some code that makes sure that your parent page and iframed page know how to communicate using postMessage or the like, so that iframed page(s) can signal "something happened inside of me that you wanted to hear about", and then make the parent manually trigger the outside click when it hears about something that you deem should trigger outside-click behaviour.

@Andarist
Copy link
Collaborator

Aside from obviously not using iframes (except for maybe editor live-views, they're almost never a good thing)

actually iframes are still the safest bet for installable 3rd party widgets too

I think @rmdort might have something else on mind - I think he might be in control of both parent document and iframe and he renders into the iframe from the parent document, so the outside click handler gets registered at the moment in the parent document, while he wants it to be registered inside. Is that right, @rmdort ?

@rmdort
Copy link
Contributor Author

rmdort commented Oct 21, 2017

@Andarist Yes. We are using iframes for widgets. I will have control of the parent page as the Javascript to render the widget has to be in the parent page.

@Pomax I will have multiple onclickOutside wrapped react components in the Widget. So even if i use postMessage or add a click listener on the parent page, how do i get the correct handleClickOutside handler to call ?

Since you are binding all touch, mousedown events on the document, why cant i instead attach the events to IFRAME.documentElement . Wouldnt it work?

Cant we expose the document prop so i can pass any element for binding ?

@Pomax
Copy link
Owner

Pomax commented Oct 23, 2017

the postmessage design is fully up to you, so one way would be to make sure all your widgets post a message that is JSON serialized data like:

JSON.stringify({
  type: "mynamespace::clickedOnWidget",
  source: "iframe identifier of some sort that the iframe knows about because you passed it in through the iframe's href as a query argument"
})

That way the parent document can check incoming post messages, and if it's the right type, it can simulate a click event with mouse coordinates (or touch coordiantes) that correspond to the iframe's document coordinates and no additional work on your end would be required: the click event would get seen by the HOC and because the source is not whatever menu is open right now, that menu will close.

(you can also do source binding by adding a negotiation step to the postmessage communication system, where you bootstrap your iframe with a one-time-pad password as url argument that it can send back to the parent so that the parent can go "I just got a postmessage from source X that uses the right type, and uses the random password I made up for it: I will add this soucrce to my list of whitelisted sources and now I will allow it to send "real" messages to me")

@Andarist
Copy link
Collaborator

He would still have to detect somehow clicks inside the iframe, but outside of the wrapped component (the one in the iframe) - I think thats why configurable documentElement is requested here.

@Pomax
Copy link
Owner

Pomax commented Oct 25, 2017

the idea is that the iframe listens for clicks at its own document level, then sends a postMessage signal to the parent that "a click has occurred inside of me", so that the parent can then dispatch an artificial click event on its own document object, which'll automatically trigger the outside click handler.

If a document needs to do anything based on something happening inside of an iframe embedded in the document, then a mutually agreed communication channel needs to be set up, and agreed upon signals need to be sent from one to the other (you could probably even use a websocket connection, although that's a little heavier-handed than just using a decent set of messages through postmessage)

@rmdort
Copy link
Contributor Author

rmdort commented Oct 25, 2017

I am not sure we are on the same page here.

In my example, the iframe is not listening for click in the document level. Instead the parent page is listening for clicks. The plugin is attaching event handlers to the parent document.

Instead if the plugin attaches click event to iframe's document, it will all just work.

Note: I don't want to trigger handleClickOutside when the parent page is clicked. I am expecting to trigger the handler when user clicks on the iframe. ie anywhere outside the element, but still inside the iframe

@Pomax
Copy link
Owner

Pomax commented Nov 1, 2017

No, we're talking about the same thing: if you have a setup with page X containing an iFrame Y, then for security reasons X is never notified of events originating in Y. When you click anywhere inside the clientRect for Y, Y sees that click and is allowed to handle it, but X will never be notified of that click because that would be leaking potentially exploitable information.

Specifically to this HOC, mouse events that occur because of something you do while your cursor (or finger) is over the iFrame will never trigger event handling in the parent (whether you use React or plain Javascript), and the only way around that is for you to intentionally set up a communication channel between X and Y (postmessage, websocket, polling localStorage or sessionStorage, a nasty bit of sneaky ninja Flash for out-of-browser storage, what have you), so that you can make sure that there is code in place that effects events in Y turn into signals that are sent to X for proper handling.

@Andarist
Copy link
Collaborator

Andarist commented Nov 1, 2017

@Pomax I think @rmdort is right here - he describes a little bit different scenario than you.

He has page X which creates components (including the one wrapped in our HOC) and which creates src-less iframe Y - there are no security concerns here and page X can manipulate iframe's Y content and the opposite it true too.

Actually he doesn't want any support for listening to click documents from both documents - he just want simply be able to listen to click events of frame Y where he renders his wrapped component.

At the moment component rendered into frame Y is listening to page's X events.

@rmdort
Copy link
Contributor Author

rmdort commented Nov 2, 2017

Yes :+1

@rmdort
Copy link
Contributor Author

rmdort commented Jan 11, 2018

Where i have gotten so far is add document variable to my context, which points to my iframe document

const MyComponent = () => {}
MyComponent.contextTypes = {
   document: PropTypes.object
}

var clickConfig = {
  getDocument: function (instance) {
    return instance.context.document // This can be any user defined function
  }
}
export default listensToClickOutside(MyComponent, clickConfig)

And replace document references in the plugin to getDocument function, if exists, else fallbacks to window.document. If you are fine with this, i can submit a pull request.

@rmdort
Copy link
Contributor Author

rmdort commented Jan 11, 2018

Added a pull request #255 . Can you review it

@rosa-nugget
Copy link

Is there somethings in react-onclickoutside to support what @Pomax is talking about? Page X have a React component wrapped in react-onclickoutside HOC and a iframe Y, the React component is not inside of the iframe, and obviously the React component did not respond to the onclickoutside event when click on iframe. Do you recommend using some type of communication(postmessage) with the iframe? I am looking for a solution and I found 2 other different solutions like creating a modal-backdrop or adding $(window).on('blur',function() { ... } ); , I am trying to analyze this different options.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants