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

Fixes #9667: Updated createTextInstance to create the text node on correct document #10723

Merged
merged 10 commits into from Sep 22, 2017

Conversation

kenotron
Copy link
Contributor

Second try: this time update the Fiber reconciler to create the text node on the correct ownerDocument so that when it is being appendChild()'ed / insertBefore()'ed, it will succeed on IE / Edge.

@kenotron
Copy link
Contributor Author

@gaearon Alright, second try! I think this fixes up the issue on Edge / IE...

@kenotron
Copy link
Contributor Author

Related to #9667

@@ -16,6 +16,7 @@ var ReactDOM = require('react-dom');
var ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
var ReactTestUtils = require('react-dom/test-utils');
var PropTypes = require('prop-types');
var JSDom = require('jsdom');
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

jsdom is needed here to create a brand new document because by default jest-jsdom assumes every test is on the same document.

@@ -1117,5 +1118,22 @@ describe('ReactDOMFiber', () => {
'to empty a container.',
);
});

it('should render a text component with a text DOM node on the same document as the container', () => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've verified that this test case fails in the current master.

@kenotron
Copy link
Contributor Author

@gaearon all green!

var textNode: TextInstance = document.createTextNode(text);

var doc = rootContainerInstance;
if (rootContainerInstance.nodeType !== DOCUMENT_NODE && doc.ownerDocument) {
Copy link
Contributor

@syranide syranide Sep 18, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can rootContainerInstance even be a document node?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

line 101: type Container = Element | Document;

I think so.

var textNode: TextInstance = document.createTextNode(text);

var doc = rootContainerInstance;
if (rootContainerInstance.nodeType !== DOCUMENT_NODE && doc.ownerDocument) {
Copy link
Contributor

@syranide syranide Sep 18, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why test for doc.ownerDocument? I don't know why it would ever be falsy and if it is then your code would break anyway, you would be trying to call createTextNode on some random element.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't have to, I guess from the same flow typing info, it can only be an Element (which HAS ownerDocument) or it's a Document which is the document itself.

var textNode: TextInstance = document.createTextNode(text);

var doc = rootContainerInstance;
if (rootContainerInstance.nodeType !== DOCUMENT_NODE && doc.ownerDocument) {
Copy link
Contributor

@syranide syranide Sep 18, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PS. rootContainerInstance should be changed to doc for consistency.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good call out, but I'm actually removing that ownerDocument and changing the name for short.

var textNode: TextInstance = document.createTextNode(text);

var doc = rootContainerInstance;
if (rootContainerInstance.nodeType !== DOCUMENT_NODE && doc.ownerDocument) {
Copy link
Contributor

@syranide syranide Sep 18, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, is it really guaranteed that rootContainerInstance belongs to the same document you're trying to render the TextNode into? I would think that rendering an iframe and into it would break this assumption... or has this changed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From the way it WAS written before, Fiber would have broken the iframe case. If you looked at the ReactDOMFiberComponent's createElement(), you'd see that the semantics there for retrieving "ownerDocument" is the same as what I have written here:

 var ownerDocument: Document = rootContainerElement.nodeType ===
      DOCUMENT_NODE
      ? (rootContainerElement: any)
      : rootContainerElement.ownerDocument;

I'm going to reuse this bit of code for clarity inside fiberentry

Copy link
Collaborator

@gaearon gaearon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this looks good and consistent with what we do in ReactDOMFiberComponent.createElement. Maybe we can even get it into 16.

@gaearon
Copy link
Collaborator

gaearon commented Sep 18, 2017

@kenotron Is there a way to rewrite the test to not use jsdom APIs directly? Maybe there’s some cases with an iframe that would reproduce it?

Copy link
Collaborator

@sophiebits sophiebits left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we make a helper function ReactDOMFiberComponent.createTextNode like how createElement works? I think the parallel structure would be clearer.

cc @acdlite for conflicts with #10624

@acdlite
Copy link
Collaborator

acdlite commented Sep 19, 2017

Conflicts are fine, I've already accepted I'll have to rebase/rewrite a lot of my work it before it lands :D

@kenotron
Copy link
Contributor Author

kenotron commented Sep 19, 2017

@gaearon I have attempted to do this with iframe, but it didn't fail in master. I don't think jsdom truly emulates the edge case behavior of the browser in this case. I have to force the behavior via separate documents as coded.

@sophiebits
Copy link
Collaborator

I think you can copy what src/renderers/dom/shared/eventPlugins/tests/EnterLeaveEventPlugin-test.js does.

@kenotron
Copy link
Contributor Author

@sophiebits I've tried this route:

it('should render a text component with a text DOM node on the same document as the container', () => {
      // Arrange
      var textContent = ' world ';

      var iframe = document.createElement('iframe');
      document.body.appendChild(iframe);

      var iframeDocument = iframe.contentDocument;

      iframeDocument.write(
        '<!DOCTYPE html><html><head></head><body><div></div></body></html>',
      );
      iframeDocument.close();

      // Act
      ReactDOM.render(
        <div><span id="pre">hello</span>{textContent}<span>again</span></div>,
        iframeDocument.body.getElementsByTagName('div')[0]
      );

      // Assert
      var textNode = iframeDocument.getElementById('pre').nextSibling;

      expect(textNode.textContent).toBe(textContent);
      expect(textNode.ownerDocument).toBe(iframeDocument);
      expect(textNode.ownerDocument).not.toBe(document);
    });

But I noticed that when I ran this test, it didn't fail for react master. It can only mean that jsdom is doing the "right thing" like many all non-MS browsers (sigh!!). So, to simulate the effect of actually unrelated documents not able to appendNode() / insertBefore() from another (related) document, I had to drop down to the level of jsdom directly creating an unrelated document.

@sophiebits
Copy link
Collaborator

Can you do ReactDOM.render(text, container) and mock out container.appendChild and assert on its argument's .ownerDocument?

@gaearon
Copy link
Collaborator

gaearon commented Sep 20, 2017

Thanks for the updated test. Do you mind also addressing this comment from @sophiebits?

Can we make a helper function ReactDOMFiberComponent.createTextNode like how createElement works? I think the parallel structure would be clearer.

textNode = node;
});

// Act
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: we can remove Arrange/Act/Assert comments as we don't use them in other tests.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've also addressed the parallel structure comment from @sophiebits. Since now we have repeated code to do that same thing, I've also factored that out into a function.

Copy link
Collaborator

@sophiebits sophiebits left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great, just a few more small comments and then I think we can merge this.

DOCUMENT_NODE
? (rootContainerElement: any)
: rootContainerElement.ownerDocument;
var ownerDocument: Document = getOwnerDocumentFromRootContainer(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you put the annotation on the function getOwnerDocumentFromRootContainer's definition? It's necessary to "cancel out" the : any cast.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would I still need this Document type? I think flow should be able to infer, right?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, we don't need this one any more.


var actualDocument;
var textNode;
iframeContainer.appendChildBackup = iframeContainer.appendChild;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: remove

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good catch - goober from a previous attempt at it.


ReactDOM.render(textContent, iframeContainer);

expect(textNode.textContent).toBe(textContent);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add an assertion on the number of calls for iframeContainer.appendChild here?

Copy link
Contributor Author

@kenotron kenotron Sep 21, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sophiebits I've added this check.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sophiebits ping?

Copy link
Collaborator

@sophiebits sophiebits left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good, thanks!

DOCUMENT_NODE
? (rootContainerElement: any)
: rootContainerElement.ownerDocument;
var ownerDocument: Document = getOwnerDocumentFromRootContainer(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, we don't need this one any more.

@sophiebits sophiebits merged commit a160f3e into facebook:master Sep 22, 2017
@sophiebits
Copy link
Collaborator

Thanks!

@kenotron
Copy link
Contributor Author

YAY!

bvaughn pushed a commit that referenced this pull request Sep 27, 2017
* Update changelog for unreleased 16.0 changes (#10730)

* First shot at updating changelog for 16.0

**what is the change?:**
Added an 'unreleased' section to the changelog with info from #10294

**why make this change?:**
To get things set for the 16.0 release.

**test plan:**
Visual inspection

**issue:**
#8854

* Fix typos and formatting errors in changelog

* Add requestAnimationFrame and remove "New helpful warnings"

**what is the change?:**
In response to helpful code review -
- Add mention of dependency on `requestAnimationFrame` and need to
  polyfill that as well as `Map` and `Set`
- Remove "New helpful warnings" section; it was incomplete, and there
  are so many new and updated warnings that it might not be reasonable
  to cover them in the changelog.

**why make this change?:**
Accuracy

**test plan:**
Visual inspection

**issue:**
issue #8854

* Improve wording

* Improve wording and fix missing links

* Add backticks to file names & code; wording tweak

* Break "Major Changes" into "New Feature" and "Breaking Changes"

* Add server side render changes to 16.0 changelog

* Change gist link from mine to gaearons

* Add note about returning fragments

* fix misc nits

* Misc. formatting/wording fixes to changelog

**what is the change?:**
Thanks to the kind code review comments of @gaearon and @nhunzaker we
have
- removed the non-deterministic bold styling on some bullet points
- improved wording of the bullet points for portals, attribute whitelist
  changes, and server rendering changes
- Add note about error boundaries including a breaking change to error
  handling behavior.
- punctuation and capitalization fixes

**why make this change?:**
Clarity and correctness

**test plan:**
Visual inspection

**issue:**
#8854

* fix broken link

* Fixes #9667: Updated createTextInstance to create the text node on correct document (#10723)

* Record sizes

*  Add a changelog for elements having the same key (#10811)

*  Add a changelog for elements having the same key

* Reword

* Markdown fixs on "DOM Attributes in React 16" post (#10816)

* Include tag name into the table snapshot (#10818)

* Update DOM warning wording and link (#10819)

* Update DOM warning wording and link

* Consistently use "Invalid" for known misspellings

* Update license headers BSD+Patents -> MIT

Did find and replace in TextMate.

```
find: (?:( \*)( ))?Copyright (?:\(c\) )?(\d{4})\b.+Facebook[\s\S]+(?:this source tree|the same directory)\.$
replace: $1$2Copyright (c) $3-present, Facebook, Inc.\n$1\n$1$2This source code is licensed under the MIT license found in the\n$1$2LICENSE file in the root directory of this source tree.
```

* Change license and remove references to PATENTS

Only remaining references:

```
docs/_posts/2014-10-28-react-v0.12.md
51:You can read the full text of the [LICENSE](https://github.com/facebook/react/blob/master/LICENSE) and [`PATENTS`](https://github.com/facebook/react/blob/master/PATENTS) files on GitHub.

docs/_posts/2015-04-17-react-native-v0.4.md
20:* **Patent Grant**: Many of you had concerns and questions around the PATENTS file. We pushed [a new version of the grant](https://code.facebook.com/posts/1639473982937255/updating-our-open-source-patent-grant/).

src/__mocks__/vendor/third_party/WebComponents.js
8: * subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
```

* Version bumps to use MIT license

* Add ReactTestRenderer documentations (#10692)

* Add ReactTestRenderer documentations

* Add TestRenderer documentations

* TestRenderer is not experiment

* Add a link for jsdom

* Use ES Modules syntax

* Twaek

* Add a Link component

* Use Jest assertions

* Move a documentation for createNodeMock to Idea section

* Renamed

* Tweak

* Rename

* More explicit

* Add a usecase for createNodeMock

* Add changelog for 15.6.2

* Add 15.6.2 blog post to master

* Add Nate to authors on master

* Bump object-assign patch range to match main package.json

* Flow should ignore node_modules/create-react-class

* Update error codes

* Update CHANGELOG for React 16

* v16.0.0

* Doc updates for React 16 + blog post (#10824)

* Doc updates for React 16 + blog post

* Add link to Sophie's post

* Fix React links on the website (#10837)

* Fix React links on the website

* Fix code editor

* Fix code editor, attempt 2

* Doc change for prevContext removal in CDU (#10836)

* Doc change for prevContext removal in CDU

Ref: #8631

* Minor rewording

* Fix note formatting

* React.createPortal is not a function (#10843)

* Update Portals Documentation (#10840)

* Update Portals Documentation

Correct some grammar to be more explicit and clear. Update example CodePen to better match code found in documentation. Update code example styles to match other code examples (ie. 'State and Lifecycle', 'Handling Events').

* Clean up comment to be accurate to example

There was a small comment overlooked when reviewing the documentation. This fixes it to be accurate to the example as well as grammatically correct.

* Update portals.md

* More fixes

* Update name of property initializer proposal (#10812)

The proposal for property initializers is called [Public Class Fields](https://tc39.github.io/proposal-class-public-fields/) now (part of the combined [Class Fields](https://github.com/tc39/proposal-class-fields) proposal).

* Fix portal link (#10845)

* Update docs for React 16 (#10846)

* Minor doc edit

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

Successfully merging this pull request may close these issues.

None yet

6 participants