Permalink
Browse files

Supporting mounting into iframes

Sencha says that separating big components into their own iframes was important for performance:
http://www.sencha.com/blog/the-making-of-fastbook-an-html5-love-story.

Today the only thing stopping us is that events don't bubble to our events system from an iframe. This diff
looks at the owning document of the container and adds top-level listeners to it. It should not change
existing behavior and should improve our support for this.
  • Loading branch information...
1 parent cd7d863 commit 647731e399fc824c02fdf851d91ce4477958e7fe @petehunt petehunt committed with zpao Sep 9, 2013
@@ -152,8 +152,9 @@ var ReactEventEmitter = merge(ReactEventEmitterMixin, {
* reason, and only in some cases).
*
* @param {boolean} touchNotMouse Listen to touch events instead of mouse.
+ * @param {DOMDocument} contentDocument DOM document to listen on
*/
- ensureListening: function(touchNotMouse) {
+ ensureListening: function(touchNotMouse, contentDocument) {
invariant(
ExecutionEnvironment.canUseDOM,
'ensureListening(...): Cannot toggle event listening in a Worker ' +
@@ -168,7 +169,10 @@ var ReactEventEmitter = merge(ReactEventEmitterMixin, {
// Call out to base implementation.
ReactEventEmitterMixin.ensureListening.call(
ReactEventEmitter,
- touchNotMouse
+ {
+ touchNotMouse: touchNotMouse,
+ contentDocument: contentDocument
+ }
);
},
@@ -217,16 +221,17 @@ var ReactEventEmitter = merge(ReactEventEmitterMixin, {
* they bubble to document.
*
* @param {boolean} touchNotMouse Listen to touch events instead of mouse.
+ * @param {DOMDocument} contentDocument Document which owns the container
* @private
* @see http://www.quirksmode.org/dom/events/keys.html.
*/
- listenAtTopLevel: function(touchNotMouse) {
+ listenAtTopLevel: function(touchNotMouse, contentDocument) {
invariant(
- !this._isListening,
+ !contentDocument._isListening,
'listenAtTopLevel(...): Cannot setup top-level listener more than once.'
);
var topLevelTypes = EventConstants.topLevelTypes;
- var mountAt = document;
+ var mountAt = contentDocument;
registerScrollValueMonitoring();
trapBubbledEvent(topLevelTypes.topMouseOver, 'mouseover', mountAt);
@@ -54,9 +54,9 @@ var ReactEventEmitterMixin = {
* @param {*} config Configuration passed through to `listenAtTopLevel`.
*/
ensureListening: function(config) {
- if (!this._isListening) {
- this.listenAtTopLevel(config);
- this._isListening = true;
+ if (!config.contentDocument._reactIsListening) {
+ this.listenAtTopLevel(config.touchNotMouse, config.contentDocument);
+ config.contentDocument._reactIsListening = true;
}
},
@@ -229,11 +229,19 @@ var ReactMount = {
/**
* Ensures that the top-level event delegation listener is set up. This will
* be invoked some time before the first time any React component is rendered.
+ * @param {DOMElement} container container we're rendering into
*
* @private
*/
- prepareEnvironmentForDOM: function() {
- ReactEventEmitter.ensureListening(ReactMount.useTouchEvents);
+ prepareEnvironmentForDOM: function(container) {
+ invariant(
+ container && container.nodeType === 1,
@kassens
kassens Sep 10, 2013 Member

This invariant broke react-page which calls this with document (nodeType === 9)

+ 'prepareEnvironmentForDOM(...): Target container is not a DOM element.'
+ );
+ ReactEventEmitter.ensureListening(
+ ReactMount.useTouchEvents,
+ container.ownerDocument
+ );
},
/**
@@ -269,7 +277,7 @@ var ReactMount = {
* @return {string} reactRoot ID prefix
*/
_registerComponent: function(nextComponent, container) {
- ReactMount.prepareEnvironmentForDOM();
+ ReactMount.prepareEnvironmentForDOM(container);
var reactRootID = ReactMount.registerContainer(container);
instancesByReactRootID[reactRootID] = nextComponent;
@@ -37,15 +37,15 @@ describe('ReactComponent', function() {
expect(function() {
React.renderComponent(<div></div>, [container]);
}).toThrow(
- 'Invariant Violation: mountComponentIntoNode(...): Target container is ' +
- 'not valid.'
+ 'Invariant Violation: prepareEnvironmentForDOM(...): Target container ' +
+ 'is not a DOM element.'
);
expect(function() {
React.renderComponent(<div></div>, null);
}).toThrow(
- 'Invariant Violation: mountComponentIntoNode(...): Target container is ' +
- 'not valid.'
+ 'Invariant Violation: prepareEnvironmentForDOM(...): Target container ' +
+ 'is not a DOM element.'
);
});
@@ -95,7 +95,7 @@ describe('ReactEventEmitter', function() {
ReactTestUtils = require('ReactTestUtils');
idCallOrder = [];
tapMoveThreshold = TapEventPlugin.tapMoveThreshold;
- ReactEventEmitter.ensureListening(false);
+ ReactEventEmitter.ensureListening(false, document);
EventPluginHub.injection.injectEventPluginsByName({
TapEventPlugin: TapEventPlugin
});
@@ -61,7 +61,7 @@ describe('AnalyticsEventPlugin', function() {
'ChangeEventPlugin': ChangeEventPlugin
});
- ReactEventEmitter.ensureListening(false);
+ ReactEventEmitter.ensureListening(false, document);
});
it('should count events correctly', function() {

0 comments on commit 647731e

Please sign in to comment.