7
7
* @flow
8
8
*/
9
9
10
- import type { Instance } from './ReactDOMHostConfig' ;
10
+ import type { Instance , Container } from './ReactDOMHostConfig' ;
11
+
11
12
import ReactDOMSharedInternals from 'shared/ReactDOMSharedInternals.js' ;
12
13
const { Dispatcher} = ReactDOMSharedInternals ;
14
+ import { DOCUMENT_NODE } from '../shared/HTMLNodeType' ;
13
15
import {
14
16
validateUnmatchedLinkResourceProps ,
15
17
validatePreloadResourceDifference ,
@@ -21,7 +23,9 @@ import {
21
23
validatePreinitArguments ,
22
24
} from '../shared/ReactDOMResourceValidation' ;
23
25
import { createElement , setInitialProperties } from './ReactDOMComponent' ;
26
+ import { getStylesFromRoot } from './ReactDOMComponentTree' ;
24
27
import { HTML_NAMESPACE } from '../shared/DOMNamespaces' ;
28
+ import { getCurrentRootHostContainer } from 'react-reconciler/src/ReactFiberHostContext' ;
25
29
26
30
// The resource types we support. currently they match the form for the as argument.
27
31
// In the future this may need to change, especially when modules / scripts are supported
@@ -47,7 +51,7 @@ type StyleProps = {
47
51
'data-rprec' : string ,
48
52
[ string ] : mixed ,
49
53
} ;
50
- type StyleResource = {
54
+ export type StyleResource = {
51
55
type : 'style' ,
52
56
53
57
// Ref count for resource
@@ -66,7 +70,7 @@ type StyleResource = {
66
70
loaded : boolean ,
67
71
error : mixed ,
68
72
instance : ?Element ,
69
- ownerDocument : Document ,
73
+ root : FloatRoot ,
70
74
} ;
71
75
72
76
type Props = { [ string ] : mixed } ;
@@ -79,11 +83,6 @@ type Resource = StyleResource | PreloadResource;
79
83
// e = errored
80
84
type StyleResourceLoadingState = Promise < mixed > & { s ?: 'l' | 'e' } ;
81
85
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
-
87
86
// It is valid to preload even when we aren't actively rendering. For cases where Float functions are
88
87
// called when there is no rendering we track the last used document. It is not safe to insert
89
88
// arbitrary resources into the lastCurrentDocument b/c it may not actually be the document
@@ -93,14 +92,17 @@ let currentDocument: ?Document = null;
93
92
let lastCurrentDocument : ?Document = null ;
94
93
95
94
let 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
+
98
101
previousDispatcher = Dispatcher . current ;
99
102
Dispatcher . current = ReactDOMClientDispatcher ;
100
103
}
101
104
102
105
export function cleanupAfterRenderResources ( ) {
103
- currentDocument = null ;
104
106
Dispatcher . current = previousDispatcher ;
105
107
previousDispatcher = null ;
106
108
}
@@ -110,9 +112,16 @@ export function cleanupAfterRenderResources() {
110
112
// from Internals -> ReactDOM -> FloatClient -> Internals so this doesn't introduce a new one.
111
113
export const ReactDOMClientDispatcher = { preload, preinit} ;
112
114
115
+ export type FloatRoot = Document | ShadowRoot ;
116
+
113
117
// global maps of Resources
114
118
const 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
+ }
116
125
117
126
// Preloads are somewhat special. Even if we don't have the Document
118
127
// 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();
121
130
// lastCurrentDocument if that exists. As a fallback we will use the window.document
122
131
// if available.
123
132
function 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
+ }
128
142
}
129
143
}
130
144
145
+ function getDocumentFromRoot ( root : FloatRoot ) : Document {
146
+ return root . ownerDocument || root ;
147
+ }
148
+
131
149
// --------------------------------------
132
150
// ReactDOM.Preload
133
151
// --------------------------------------
@@ -200,8 +218,9 @@ function preinit(href: string, options: PreinitOptions) {
200
218
typeof options === 'object' &&
201
219
options !== null
202
220
) {
221
+ const resourceRoot = getCurrentResourceRoot ( ) ;
203
222
const as = options . as ;
204
- if ( ! currentDocument ) {
223
+ if ( ! resourceRoot ) {
205
224
// We are going to emit a preload as a best effort fallback since this preinit
206
225
// was called outside of a render. Given the passive nature of this fallback
207
226
// 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) {
223
242
224
243
switch ( as ) {
225
244
case 'style' : {
245
+ const styleResources = getStylesFromRoot ( resourceRoot ) ;
226
246
const precedence = options . precedence || 'default' ;
227
247
let resource = styleResources . get ( href ) ;
228
248
if ( resource ) {
@@ -241,8 +261,8 @@ function preinit(href: string, options: PreinitOptions) {
241
261
options ,
242
262
) ;
243
263
resource = createStyleResource (
244
- // $FlowFixMe[incompatible-call] found when upgrading Flow
245
- currentDocument ,
264
+ styleResources ,
265
+ resourceRoot ,
246
266
href ,
247
267
precedence ,
248
268
resourceProps ,
@@ -303,16 +323,18 @@ export function getResource(
303
323
pendingProps : Props ,
304
324
currentProps : null | Props ,
305
325
) : null | Resource {
306
- if ( ! currentDocument ) {
326
+ const resourceRoot = getCurrentResourceRoot ( ) ;
327
+ if ( ! resourceRoot ) {
307
328
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.' ,
309
330
) ;
310
331
}
311
332
switch ( type ) {
312
333
case 'link' : {
313
334
const { rel} = pendingProps ;
314
335
switch ( rel ) {
315
336
case 'stylesheet' : {
337
+ const styleResources = getStylesFromRoot ( resourceRoot ) ;
316
338
let didWarn ;
317
339
if ( __DEV__ ) {
318
340
if ( currentProps ) {
@@ -348,8 +370,8 @@ export function getResource(
348
370
} else {
349
371
const resourceProps = stylePropsFromRawProps ( styleRawProps ) ;
350
372
resource = createStyleResource (
351
- // $FlowFixMe[incompatible-call] found when upgrading Flow
352
- currentDocument ,
373
+ styleResources ,
374
+ resourceRoot ,
353
375
href ,
354
376
precedence ,
355
377
resourceProps ,
@@ -384,8 +406,7 @@ export function getResource(
384
406
} else {
385
407
const resourceProps = preloadPropsFromRawProps ( preloadRawProps ) ;
386
408
resource = createPreloadResource (
387
- // $FlowFixMe[incompatible-call] found when upgrading Flow
388
- currentDocument ,
409
+ getDocumentFromRoot ( resourceRoot ) ,
389
410
href ,
390
411
resourceProps ,
391
412
) ;
@@ -463,7 +484,8 @@ function createResourceInstance(
463
484
}
464
485
465
486
function createStyleResource (
466
- ownerDocument : Document ,
487
+ styleResources : Map < string , StyleResource > ,
488
+ root : FloatRoot ,
467
489
href : string ,
468
490
precedence : string ,
469
491
props : StyleProps ,
@@ -479,7 +501,7 @@ function createStyleResource(
479
501
const limitedEscapedHref = escapeSelectorAttributeValueInsideDoubleQuotes (
480
502
href ,
481
503
) ;
482
- const existingEl = ownerDocument . querySelector (
504
+ const existingEl = root . querySelector (
483
505
`link[rel="stylesheet"][href="${ limitedEscapedHref } "]` ,
484
506
) ;
485
507
const resource = {
@@ -492,7 +514,7 @@ function createStyleResource(
492
514
preloaded : false ,
493
515
loaded : false ,
494
516
error : false ,
495
- ownerDocument ,
517
+ root ,
496
518
instance : null ,
497
519
} ;
498
520
styleResources . set ( href , resource ) ;
@@ -567,7 +589,7 @@ function immediatelyPreloadStyleResource(resource: StyleResource) {
567
589
const { href, props} = resource ;
568
590
const preloadProps = preloadPropsFromStyleProps ( props ) ;
569
591
resource . hint = createPreloadResource (
570
- resource . ownerDocument ,
592
+ getDocumentFromRoot ( resource . root ) ,
571
593
href ,
572
594
preloadProps ,
573
595
) ;
@@ -613,11 +635,11 @@ function createPreloadResource(
613
635
614
636
function acquireStyleResource ( resource : StyleResource ) : Instance {
615
637
if ( ! resource . instance ) {
616
- const { props, ownerDocument , precedence} = resource ;
638
+ const { props, root , precedence} = resource ;
617
639
const limitedEscapedHref = escapeSelectorAttributeValueInsideDoubleQuotes (
618
640
props . href ,
619
641
) ;
620
- const existingEl = ownerDocument . querySelector (
642
+ const existingEl = root . querySelector (
621
643
`link[rel="stylesheet"][data-rprec][href="${ limitedEscapedHref } "]` ,
622
644
) ;
623
645
if ( existingEl ) {
@@ -649,11 +671,11 @@ function acquireStyleResource(resource: StyleResource): Instance {
649
671
const instance = createResourceInstance (
650
672
'link' ,
651
673
resource . props ,
652
- ownerDocument ,
674
+ getDocumentFromRoot ( root ) ,
653
675
) ;
654
676
655
677
attachLoadListeners ( instance , resource ) ;
656
- insertStyleInstance ( instance , precedence , ownerDocument ) ;
678
+ insertStyleInstance ( instance , precedence , root ) ;
657
679
resource . instance = instance ;
658
680
}
659
681
}
@@ -724,11 +746,9 @@ function onResourceError(
724
746
function insertStyleInstance (
725
747
instance : Instance ,
726
748
precedence : string ,
727
- ownerDocument : Document ,
749
+ root : FloatRoot ,
728
750
) : void {
729
- const nodes = ownerDocument . querySelectorAll (
730
- 'link[rel="stylesheet"][data-rprec]' ,
731
- ) ;
751
+ const nodes = root . querySelectorAll ( 'link[rel="stylesheet"][data-rprec]' ) ;
732
752
const last = nodes . length ? nodes [ nodes . length - 1 ] : null ;
733
753
let prior = last ;
734
754
for ( let i = 0 ; i < nodes . length ; i ++ ) {
@@ -746,9 +766,8 @@ function insertStyleInstance(
746
766
// must exist.
747
767
( ( prior . parentNode : any ) : Node ) . insertBefore ( instance , prior . nextSibling ) ;
748
768
} 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 ;
752
771
if ( parent ) {
753
772
parent . insertBefore ( instance , parent . firstChild ) ;
754
773
} else {
0 commit comments