Bug 1342297 - Remove React DOM event system patching. r=ochameau draft
authorTim Nguyen <ntim.bugs@gmail.com>
Sat, 25 Feb 2017 13:12:39 +0000
changeset 489662 31fcecf320f528a0f56f1c05b18afede7780cd73
parent 489661 94e9bdb6ee05875eaea715d64a237cbfce501abc
child 489663 209ac747ca55f2a72270cc37a8a3672ea7ddd2fd
push id46875
push userbmo:ntim.bugs@gmail.com
push dateSat, 25 Feb 2017 13:15:07 +0000
reviewersochameau
bugs1342297
milestone54.0a1
Bug 1342297 - Remove React DOM event system patching. r=ochameau MozReview-Commit-ID: 2O474CTeqes
devtools/client/shared/vendor/REACT_UPGRADING
devtools/client/shared/vendor/react-dom.js
--- a/devtools/client/shared/vendor/REACT_UPGRADING
+++ b/devtools/client/shared/vendor/REACT_UPGRADING
@@ -48,45 +48,14 @@ MOVE OFF XUL and we don't need to do thi
 
 After patching `build/react-with-addons.js` again, copy the production
 version over:
 
 * cp build/react-with-addons.js <gecko-dev>/devtools/client/shared/vendor/react.js
 
 You also need to copy the ReactDOM and ReactDOMServer package. It requires React, so
 right now we are just manually changing the path from `react` to
-`devtools/client/shared/vendor/react`. Also, to have React's event system working
-correctly in certain XUL situations, ReactDOM must be monkey patched with a fix. This
-fix is currently applied in devtools/client/shared/vendor/react-dom.js. When upgrading,
-copy and paste the existing block of code into the new file in the same location. It is
-delimited by a header and footer, and then the monkeyPatchReactDOM() needs to be applied
-to the returned value.
-
-e.g. Turn this:
-
-```
-})(function(React) {
-  return React.__SECRET_DOM_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
-});
-```
-
-Into this:
-
-```
-})(function(React) {
-  //--------------------------------------------------------------------------------------
-  // START MONKEY PATCH
-
-  ...
-
-  // END MONKEY PATCH
-  //--------------------------------------------------------------------------------------
-
-  return monkeyPatchReactDOM(React.__SECRET_DOM_DO_NOT_USE_OR_YOU_WILL_BE_FIRED);
-});
-```
+`devtools/client/shared/vendor/react`.
 
 * cp build/react-dom.js <gecko-dev>/devtools/client/shared/vendor/react-dom.js
 * (change `require('react')` at the top of the file to the right path)
-* Copy/paste existing monkey patch
-* Apply monkeyPatchReactDOM() to the returned object ReactDOM object.
 * cp build/react-dom.js <gecko-dev>/devtools/client/shared/vendor/react-dom-server.js
 * (change `require('react')` at the top of the file to the right path)
--- a/devtools/client/shared/vendor/react-dom.js
+++ b/devtools/client/shared/vendor/react-dom.js
@@ -33,164 +33,10 @@
       // needed for Java 8 Nashorn
       // see https://github.com/facebook/react/issues/3037
       g = this;
     }
     g.ReactDOM = f(g.React);
   }
 
 })(function(React) {
-  //--------------------------------------------------------------------------------------
-  // START MONKEY PATCH
-  /**
-   * This section contains a monkey patch for React DOM, so that it functions correctly in
-   * certain XUL situations. React centralizes events to specific DOM nodes by only
-   * binding a single listener to the document of the page. It then captures these events,
-   * and then uses a SyntheticEvent system to dispatch these throughout the page.
-   *
-   * In privileged XUL with a XUL iframe, and React in both documents, this system breaks.
-   * By design, these XUL frames can still talk to each other, while in a normal HTML
-   * situation, they would not be able to. The events from the XUL iframe propagate to the
-   * parent document as well. This leads to the React event system incorrectly dispatching
-   * TWO SyntheticEvents for for every ONE action.
-   *
-   * The fix here is trick React into thinking that the owning document for every node in
-   * a XUL iframe to be the toolbox.xul. This is done by creating a Proxy object that
-   * captures any usage of HTMLElement.ownerDocument, and then passing in the toolbox.xul
-   * document rather than (for example) the netmonitor.xul document. React will then hook
-   * up the event system correctly on the top level controlling document.
-   *
-   * @return {object} The proxied and monkey patched ReactDOM
-   */
-  function monkeyPatchReactDOM(ReactDOM) {
-    // This is the actual monkey patched function.
-    const reactDomRender = monkeyPatchRender(ReactDOM);
-
-    // Proxied method calls might need to be bound, but do this lazily with caching.
-    const lazyFunctionBinding = functionLazyBinder();
-
-    // Create a proxy, but the render property is not writable on the ReactDOM object, so
-    // a direct proxy will fail with an error. Instead, create a proxy on a a blank object.
-    // Pass on getting and setting behaviors.
-    return new Proxy({}, {
-      get: (target, name) => {
-        return name === "render"
-          ? reactDomRender
-          : lazyFunctionBinding(ReactDOM, name);
-      },
-      set: (target, name, value) => {
-        ReactDOM[name] = value;
-      }
-    });
-  };
-
-  /**
-   * Creates a function that replaces the ReactDOM.render method. It does this by
-   * creating a proxy for the dom node being mounted. This proxy rewrites the
-   * "ownerDocument" property to point to the toolbox.xul document. This function
-   * is only used for XUL iframes inside of a XUL document.
-   *
-   * @param {object} ReactDOM
-   * @return {function} The patched ReactDOM.render function.
-   */
-  function monkeyPatchRender(ReactDOM) {
-    const elementProxyCache = new WeakMap();
-
-    return (...args) => {
-      const container = args[1];
-      const toolboxDoc = getToolboxDocIfXulOnly(container);
-
-      if (toolboxDoc) {
-        // Re-use any existing cached HTMLElement proxies.
-        let proxy = elementProxyCache.get(container);
-
-        if (!proxy) {
-          // Proxied method calls need to be bound, but do this lazily.
-          const lazyFunctionBinding = functionLazyBinder();
-
-          // Create a proxy to the container HTMLElement. If React tries to access the
-          // ownerDocument, pass in the toolbox's document, as the event dispatching system
-          // is rooted from the toolbox document.
-          proxy = new Proxy(container, {
-            get: function (target, name) {
-              if (name === "ownerDocument") {
-                return toolboxDoc;
-              }
-              return lazyFunctionBinding(target, name);
-            }
-          });
-
-          elementProxyCache.set(container, proxy);
-        }
-
-        // Update the args passed to ReactDOM.render.
-        args[1] = proxy;
-      }
-
-      return ReactDOM.render.apply(this, args);
-    };
-  }
-
-  /**
-   * Try to access the containing toolbox XUL document, but only if all of the iframes
-   * in the heirarchy are XUL documents. Events dispatch differently in the case of all
-   * privileged XUL documents. Events that fire in an iframe propagate up to the parent
-   * frame. This does not happen when HTML is in the mix. Only return the toolbox if
-   * it matches the proper case of a XUL iframe inside of a XUL document.
-   *
-   * In addition to the XUL case, if the panel uses the toolbox's ReactDOM instance,
-   * this patch needs to be applied as well. This is the case for the inspector.
-   *
-   * @param {HTMLElement} node - The DOM node inside of an iframe.
-   * @return {XULDocument|null} The toolbox.xul document, or null.
-   */
-  function getToolboxDocIfXulOnly(node) {
-    // This execution context doesn't know about XULDocuments, so don't get the toolbox.
-    if (typeof XULDocument !== "function") {
-      return null;
-    }
-
-    let doc = node.ownerDocument;
-    const inspectorUrl = "chrome://devtools/content/inspector/inspector.xhtml";
-
-    while (doc instanceof XULDocument || doc.location.href === inspectorUrl) {
-      const {frameElement} = doc.defaultView;
-
-      if (!frameElement) {
-        // We're at the root element, and no toolbox was found.
-        return null;
-      }
-
-      doc = frameElement.parentElement.ownerDocument;
-      if (doc.documentURI === "about:devtools-toolbox") {
-        return doc;
-      }
-    }
-
-    return null;
-  }
-
-  /**
-   * When accessing proxied functions, the instance becomes unbound to the method. This
-   * utility either passes a value through if it's not a function, or automatically binds a
-   * function and caches that bound function for repeated calls.
-   */
-  function functionLazyBinder() {
-    const boundFunctions = {};
-
-    return (target, name) => {
-      if (typeof target[name] === "function") {
-        // Lazily cache function bindings.
-        if (boundFunctions[name]) {
-          return boundFunctions[name];
-        }
-        boundFunctions[name] = target[name].bind(target);
-        return boundFunctions[name];
-      }
-      return target[name];
-    };
-  }
-
-  // END MONKEY PATCH
-  //--------------------------------------------------------------------------------------
-
-  return monkeyPatchReactDOM(React.__SECRET_DOM_DO_NOT_USE_OR_YOU_WILL_BE_FIRED);
+  return React.__SECRET_DOM_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
 });