@@ -64,6 +64,22 @@ let eventsEnabled : ?boolean = null;
6464let selectionInformation : ?mixed = null ;
6565let currentNamespaceURI : null | SVG_NAMESPACE | MATH_NAMESPACE = null ;
6666
67+ // How many <svg>s have we entered so far.
68+ // We increment or decrement the last array item when pushing and popping <svg>.
69+ // A new counter is appended to the end whenever we enter a <foreignObject>.
70+ let svgDepthByForeignObjectDepth : Array < number > = [ 0 ] ;
71+
72+ // How many <foreignObject>s we have entered so far.
73+ // We increment and decrement it when pushing and popping <foreignObject>.
74+ // We use this counter as the current index for accessing the array above.
75+ let foreignObjectDepth : number = 0 ;
76+
77+ // For example, given this structure, `svgDepthByForeignObjectDepth` would be:
78+ // <svg><foreignObject><svg><svg><svg><foreignObject><svg>
79+ // ^^^ ^^^^^^^^^^^^^ ^^^^^^^^^^^^^ ^^^^^^^^^^^^^ ^^^]
80+ // [ 1 , 3 , 1 ]
81+ // `foreignObjectDepth` would be 2.
82+
6783function getIntrinsicNamespaceURI ( type : string ) {
6884 switch ( type ) {
6985 case 'svg' :
@@ -80,14 +96,32 @@ var DOMRenderer = ReactFiberReconciler({
8096 pushHostContext ( type : string ) {
8197 switch ( type ) {
8298 case 'svg' :
99+ if ( currentNamespaceURI == null ) {
100+ // We are entering an <svg> for the first time.
101+ currentNamespaceURI = SVG_NAMESPACE ;
102+ svgDepthByForeignObjectDepth [ foreignObjectDepth ] = 1 ;
103+ } else if ( currentNamespaceURI === SVG_NAMESPACE ) {
104+ // We are entering an <svg> inside <svg>.
105+ // We record this fact so that when we pop this <svg>, we stay in the
106+ // SVG mode instead of switching to HTML mode.
107+ svgDepthByForeignObjectDepth [ foreignObjectDepth ] ++ ;
108+ }
109+ break ;
83110 case 'math' :
84111 if ( currentNamespaceURI == null ) {
85- currentNamespaceURI = getIntrinsicNamespaceURI ( type ) ;
112+ currentNamespaceURI = MATH_NAMESPACE ;
86113 }
87114 break ;
88115 case 'foreignObject' :
89116 if ( currentNamespaceURI === SVG_NAMESPACE ) {
90117 currentNamespaceURI = null ;
118+ // We are in HTML mode again, so current <svg> nesting counter needs
119+ // to be reset. However we still need to remember its value when we
120+ // pop this <foreignObject>. So instead of resetting the counter, we
121+ // advance the pointer, and start a new independent <svg> depth
122+ // counter at the next array index.
123+ foreignObjectDepth ++ ;
124+ svgDepthByForeignObjectDepth [ foreignObjectDepth ] = 0 ;
91125 }
92126 break ;
93127 }
@@ -97,7 +131,16 @@ var DOMRenderer = ReactFiberReconciler({
97131 switch ( type ) {
98132 case 'svg' :
99133 if ( currentNamespaceURI === SVG_NAMESPACE ) {
100- currentNamespaceURI = null ;
134+ if ( svgDepthByForeignObjectDepth [ foreignObjectDepth ] === 1 ) {
135+ // We exited all nested <svg> nodes.
136+ // We can switch to HTML mode.
137+ currentNamespaceURI = null ;
138+ } else {
139+ // There is still an <svg> above so we stay in SVG mode.
140+ // We decrease the counter so that next time we leave <svg>
141+ // we will be able to switch to HTML mode.
142+ svgDepthByForeignObjectDepth [ foreignObjectDepth ] -- ;
143+ }
101144 }
102145 break ;
103146 case 'math' :
@@ -107,6 +150,9 @@ var DOMRenderer = ReactFiberReconciler({
107150 break ;
108151 case 'foreignObject' :
109152 if ( currentNamespaceURI == null ) {
153+ // We are exiting <foreignObject> and nested <svg>s may exist above.
154+ // Switch to the previous <svg> depth counter by decreasing the index.
155+ foreignObjectDepth -- ;
110156 currentNamespaceURI = SVG_NAMESPACE ;
111157 }
112158 break ;
0 commit comments