77 * @flow
88 */
99
10- import type { Instance } from './ReactDOMHostConfig' ;
10+ import type { Instance , Container } from './ReactDOMHostConfig' ;
11+
1112import ReactDOMSharedInternals from 'shared/ReactDOMSharedInternals.js' ;
1213const { Dispatcher} = ReactDOMSharedInternals ;
14+ import { DOCUMENT_NODE } from '../shared/HTMLNodeType' ;
1315import {
1416 validateUnmatchedLinkResourceProps ,
1517 validatePreloadResourceDifference ,
@@ -21,7 +23,9 @@ import {
2123 validatePreinitArguments ,
2224} from '../shared/ReactDOMResourceValidation' ;
2325import { createElement , setInitialProperties } from './ReactDOMComponent' ;
26+ import { getStylesFromRoot } from './ReactDOMComponentTree' ;
2427import { HTML_NAMESPACE } from '../shared/DOMNamespaces' ;
28+ import { getCurrentRootHostContainer } from 'react-reconciler/src/ReactFiberHostContext' ;
2529
2630// The resource types we support. currently they match the form for the as argument.
2731// In the future this may need to change, especially when modules / scripts are supported
@@ -47,7 +51,7 @@ type StyleProps = {
4751 'data-rprec' : string ,
4852 [ string ] : mixed ,
4953} ;
50- type StyleResource = {
54+ export type StyleResource = {
5155 type : 'style' ,
5256
5357 // Ref count for resource
@@ -66,7 +70,7 @@ type StyleResource = {
6670 loaded : boolean ,
6771 error : mixed ,
6872 instance : ?Element ,
69- ownerDocument : Document ,
73+ root : FloatRoot ,
7074} ;
7175
7276type Props = { [ string ] : mixed } ;
@@ -79,11 +83,6 @@ type Resource = StyleResource | PreloadResource;
7983// e = errored
8084type StyleResourceLoadingState = Promise < mixed > & { s ?: 'l' | 'e' } ;
8185
82- // When rendering we set the currentDocument if one exists. we use this for Resources
83- // we encounter during render. If this is null and we are dispatching preloads and
84- // other calls on the ReactDOM module we look for the window global and get the document from there
85- let currentDocument : ?Document = null ;
86-
8786// It is valid to preload even when we aren't actively rendering. For cases where Float functions are
8887// called when there is no rendering we track the last used document. It is not safe to insert
8988// arbitrary resources into the lastCurrentDocument b/c it may not actually be the document
@@ -93,14 +92,17 @@ let currentDocument: ?Document = null;
9392let lastCurrentDocument : ?Document = null ;
9493
9594let previousDispatcher = null ;
96- export function prepareToRenderResources ( ownerDocument : Document ) {
97- currentDocument = lastCurrentDocument = ownerDocument ;
95+ export function prepareToRenderResources ( rootContainer : Container ) {
96+ // Flot thinks that getRootNode returns a Node but it actually returns a
97+ // Document or ShadowRoot
98+ const rootNode : FloatRoot = ( rootContainer . getRootNode ( ) : any ) ;
99+ lastCurrentDocument = getDocumentFromRoot ( rootNode ) ;
100+
98101 previousDispatcher = Dispatcher . current ;
99102 Dispatcher . current = ReactDOMClientDispatcher ;
100103}
101104
102105export function cleanupAfterRenderResources ( ) {
103- currentDocument = null ;
104106 Dispatcher . current = previousDispatcher ;
105107 previousDispatcher = null ;
106108}
@@ -110,9 +112,16 @@ export function cleanupAfterRenderResources() {
110112// from Internals -> ReactDOM -> FloatClient -> Internals so this doesn't introduce a new one.
111113export const ReactDOMClientDispatcher = { preload, preinit} ;
112114
115+ export type FloatRoot = Document | ShadowRoot ;
116+
113117// global maps of Resources
114118const preloadResources : Map < string , PreloadResource > = new Map ( ) ;
115- const styleResources : Map < string , StyleResource > = new Map ( ) ;
119+
120+ function getCurrentResourceRoot ( ) : null | FloatRoot {
121+ const currentContainer = getCurrentRootHostContainer ( ) ;
122+ // $FlowFixMe flow should know currentContainer is a Node and has getRootNode
123+ return currentContainer ? currentContainer . getRootNode ( ) : null ;
124+ }
116125
117126// Preloads are somewhat special. Even if we don't have the Document
118127// used by the root that is rendering a component trying to insert a preload
@@ -121,13 +130,22 @@ const styleResources: Map<string, StyleResource> = new Map();
121130// lastCurrentDocument if that exists. As a fallback we will use the window.document
122131// if available.
123132function getDocumentForPreloads ( ) : ?Document {
124- try {
125- return currentDocument || lastCurrentDocument || window . document ;
126- } catch ( error ) {
127- return null ;
133+ const root = getCurrentResourceRoot ( ) ;
134+ if ( root ) {
135+ return root . ownerDocument || root ;
136+ } else {
137+ try {
138+ return lastCurrentDocument || window . document ;
139+ } catch ( error ) {
140+ return null ;
141+ }
128142 }
129143}
130144
145+ function getDocumentFromRoot ( root : FloatRoot ) : Document {
146+ return root . ownerDocument || root ;
147+ }
148+
131149// --------------------------------------
132150// ReactDOM.Preload
133151// --------------------------------------
@@ -200,8 +218,9 @@ function preinit(href: string, options: PreinitOptions) {
200218 typeof options === 'object' &&
201219 options !== null
202220 ) {
221+ const resourceRoot = getCurrentResourceRoot ( ) ;
203222 const as = options . as ;
204- if ( ! currentDocument ) {
223+ if ( ! resourceRoot ) {
205224 // We are going to emit a preload as a best effort fallback since this preinit
206225 // was called outside of a render. Given the passive nature of this fallback
207226 // we do not warn in dev when props disagree if there happens to already be a
@@ -223,6 +242,7 @@ function preinit(href: string, options: PreinitOptions) {
223242
224243 switch ( as ) {
225244 case 'style' : {
245+ const styleResources = getStylesFromRoot ( resourceRoot ) ;
226246 const precedence = options . precedence || 'default' ;
227247 let resource = styleResources . get ( href ) ;
228248 if ( resource ) {
@@ -241,8 +261,8 @@ function preinit(href: string, options: PreinitOptions) {
241261 options ,
242262 ) ;
243263 resource = createStyleResource (
244- // $FlowFixMe[incompatible-call] found when upgrading Flow
245- currentDocument ,
264+ styleResources ,
265+ resourceRoot ,
246266 href ,
247267 precedence ,
248268 resourceProps ,
@@ -303,16 +323,18 @@ export function getResource(
303323 pendingProps : Props ,
304324 currentProps : null | Props ,
305325) : null | Resource {
306- if ( ! currentDocument ) {
326+ const resourceRoot = getCurrentResourceRoot ( ) ;
327+ if ( ! resourceRoot ) {
307328 throw new Error (
308- '"currentDocument " was expected to exist. This is a bug in React.' ,
329+ '"resourceRoot " was expected to exist. This is a bug in React.' ,
309330 ) ;
310331 }
311332 switch ( type ) {
312333 case 'link' : {
313334 const { rel} = pendingProps ;
314335 switch ( rel ) {
315336 case 'stylesheet' : {
337+ const styleResources = getStylesFromRoot ( resourceRoot ) ;
316338 let didWarn ;
317339 if ( __DEV__ ) {
318340 if ( currentProps ) {
@@ -348,8 +370,8 @@ export function getResource(
348370 } else {
349371 const resourceProps = stylePropsFromRawProps ( styleRawProps ) ;
350372 resource = createStyleResource (
351- // $FlowFixMe[incompatible-call] found when upgrading Flow
352- currentDocument ,
373+ styleResources ,
374+ resourceRoot ,
353375 href ,
354376 precedence ,
355377 resourceProps ,
@@ -384,8 +406,7 @@ export function getResource(
384406 } else {
385407 const resourceProps = preloadPropsFromRawProps ( preloadRawProps ) ;
386408 resource = createPreloadResource (
387- // $FlowFixMe[incompatible-call] found when upgrading Flow
388- currentDocument ,
409+ getDocumentFromRoot ( resourceRoot ) ,
389410 href ,
390411 resourceProps ,
391412 ) ;
@@ -463,7 +484,8 @@ function createResourceInstance(
463484}
464485
465486function createStyleResource (
466- ownerDocument : Document ,
487+ styleResources : Map < string , StyleResource > ,
488+ root : FloatRoot ,
467489 href : string ,
468490 precedence : string ,
469491 props : StyleProps ,
@@ -479,7 +501,7 @@ function createStyleResource(
479501 const limitedEscapedHref = escapeSelectorAttributeValueInsideDoubleQuotes (
480502 href ,
481503 ) ;
482- const existingEl = ownerDocument . querySelector (
504+ const existingEl = root . querySelector (
483505 `link[rel="stylesheet"][href="${ limitedEscapedHref } "]` ,
484506 ) ;
485507 const resource = {
@@ -492,7 +514,7 @@ function createStyleResource(
492514 preloaded : false ,
493515 loaded : false ,
494516 error : false ,
495- ownerDocument ,
517+ root ,
496518 instance : null ,
497519 } ;
498520 styleResources . set ( href , resource ) ;
@@ -567,7 +589,7 @@ function immediatelyPreloadStyleResource(resource: StyleResource) {
567589 const { href, props} = resource ;
568590 const preloadProps = preloadPropsFromStyleProps ( props ) ;
569591 resource . hint = createPreloadResource (
570- resource . ownerDocument ,
592+ getDocumentFromRoot ( resource . root ) ,
571593 href ,
572594 preloadProps ,
573595 ) ;
@@ -613,11 +635,11 @@ function createPreloadResource(
613635
614636function acquireStyleResource ( resource : StyleResource ) : Instance {
615637 if ( ! resource . instance ) {
616- const { props, ownerDocument , precedence} = resource ;
638+ const { props, root , precedence} = resource ;
617639 const limitedEscapedHref = escapeSelectorAttributeValueInsideDoubleQuotes (
618640 props . href ,
619641 ) ;
620- const existingEl = ownerDocument . querySelector (
642+ const existingEl = root . querySelector (
621643 `link[rel="stylesheet"][data-rprec][href="${ limitedEscapedHref } "]` ,
622644 ) ;
623645 if ( existingEl ) {
@@ -649,11 +671,11 @@ function acquireStyleResource(resource: StyleResource): Instance {
649671 const instance = createResourceInstance (
650672 'link' ,
651673 resource . props ,
652- ownerDocument ,
674+ getDocumentFromRoot ( root ) ,
653675 ) ;
654676
655677 attachLoadListeners ( instance , resource ) ;
656- insertStyleInstance ( instance , precedence , ownerDocument ) ;
678+ insertStyleInstance ( instance , precedence , root ) ;
657679 resource . instance = instance ;
658680 }
659681 }
@@ -724,11 +746,9 @@ function onResourceError(
724746function insertStyleInstance (
725747 instance : Instance ,
726748 precedence : string ,
727- ownerDocument : Document ,
749+ root : FloatRoot ,
728750) : void {
729- const nodes = ownerDocument . querySelectorAll (
730- 'link[rel="stylesheet"][data-rprec]' ,
731- ) ;
751+ const nodes = root . querySelectorAll ( 'link[rel="stylesheet"][data-rprec]' ) ;
732752 const last = nodes . length ? nodes [ nodes . length - 1 ] : null ;
733753 let prior = last ;
734754 for ( let i = 0 ; i < nodes . length ; i ++ ) {
@@ -746,9 +766,8 @@ function insertStyleInstance(
746766 // must exist.
747767 ( ( prior . parentNode : any ) : Node ) . insertBefore ( instance , prior . nextSibling ) ;
748768 } else {
749- // @TODO call getRootNode on root.container. if it is a Document, insert into head
750- // if it is a ShadowRoot insert it into the root node.
751- const parent = ownerDocument . head ;
769+ const parent =
770+ root . nodeType === DOCUMENT_NODE ? ( ( root : any ) : Document ) . head : root ;
752771 if ( parent ) {
753772 parent . insertBefore ( instance , parent . firstChild ) ;
754773 } else {
0 commit comments