3535
3636package nu .validator .htmlparser .impl ;
3737
38- import java .util .ArrayList ;
3938import java .util .Arrays ;
4039import java .util .HashMap ;
4140import java .util .Map ;
@@ -444,10 +443,6 @@ public abstract class TreeBuilder<T> implements TokenHandler,
444443 // Tracks if we've already had an active option (first option was selected for cloning)
445444 private boolean hadActiveOption = false ;
446445
447- // Stack to track the current parent in selectedcontent for element cloning
448- // When we're inside an active option, we push cloned elements here
449- private ArrayList <T > selectedContentCloneStack = new ArrayList <T >();
450-
451446 protected @ Auto char [] charBuffer ;
452447
453448 protected int charBufferLen = 0 ;
@@ -2362,10 +2357,6 @@ public final void startTag(ElementName elementName,
23622357 if (selectedContentPointer != null ) {
23632358 if (hasSelected ) {
23642359 // Option with selected attr becomes active
2365- // Clear previous selectedcontent content if we had a different active option
2366- if (hadActiveOption && !seenSelectedOption ) {
2367- clearSelectedContentChildren ();
2368- }
23692360 seenSelectedOption = true ;
23702361 shouldBeActive = true ;
23712362 } else if (!seenSelectedOption && !hadActiveOption ) {
@@ -2379,9 +2370,6 @@ public final void startTag(ElementName elementName,
23792370 if (shouldBeActive ) {
23802371 activeOptionStackPos = currentPtr ;
23812372 hadActiveOption = true ;
2382- // Initialize the clone stack with selectedcontent as the root parent
2383- selectedContentCloneStack .clear ();
2384- selectedContentCloneStack .add (selectedContentPointer );
23852373 }
23862374 attributes = null ; // CPP
23872375 break starttagloop ;
@@ -5346,14 +5334,9 @@ private void pop() throws SAXException {
53465334 // This handles adoption agency restructuring correctly
53475335 if (currentPtr == activeOptionStackPos ) {
53485336 if (selectedContentPointer != null ) {
5349- clearSelectedContentChildren ();
5350- deepCloneChildren (node .node , selectedContentPointer );
5337+ cloneOptionContentToSelectedContent (node .node , selectedContentPointer );
53515338 }
53525339 activeOptionStackPos = -1 ;
5353- selectedContentCloneStack .clear ();
5354- } else if (activeOptionStackPos >= 0 && currentPtr > activeOptionStackPos && selectedContentCloneStack .size () > 1 ) {
5355- // Pop from clone stack when popping an element inside active option
5356- selectedContentCloneStack .remove (selectedContentCloneStack .size () - 1 );
53575340 }
53585341 // Clear selectedcontent tracking if we're popping the select element
53595342 // (not when popping selectedcontent itself - the DOM node is still valid)
@@ -5363,7 +5346,6 @@ private void pop() throws SAXException {
53635346 seenSelectedOption = false ;
53645347 activeOptionStackPos = -1 ;
53655348 hadActiveOption = false ;
5366- selectedContentCloneStack .clear ();
53675349 }
53685350 currentPtr --;
53695351 elementPopped (node .ns , node .popName , node .node );
@@ -5379,14 +5361,9 @@ private void popForeign(int origPos, int eltPos) throws SAXException {
53795361 // When active option closes, deep-clone its content to selectedcontent
53805362 if (currentPtr == activeOptionStackPos ) {
53815363 if (selectedContentPointer != null ) {
5382- clearSelectedContentChildren ();
5383- deepCloneChildren (node .node , selectedContentPointer );
5364+ cloneOptionContentToSelectedContent (node .node , selectedContentPointer );
53845365 }
53855366 activeOptionStackPos = -1 ;
5386- selectedContentCloneStack .clear ();
5387- } else if (activeOptionStackPos >= 0 && currentPtr > activeOptionStackPos && selectedContentCloneStack .size () > 1 ) {
5388- // Pop from clone stack when popping an element inside active option
5389- selectedContentCloneStack .remove (selectedContentCloneStack .size () - 1 );
53905367 }
53915368 // Clear selectedcontent tracking if we're popping the select element
53925369 if (node .getGroup () == SELECT ) {
@@ -5395,7 +5372,6 @@ private void popForeign(int origPos, int eltPos) throws SAXException {
53955372 seenSelectedOption = false ;
53965373 activeOptionStackPos = -1 ;
53975374 hadActiveOption = false ;
5398- selectedContentCloneStack .clear ();
53995375 }
54005376 currentPtr --;
54015377 elementPopped (node .ns , node .popName , node .node );
@@ -5408,14 +5384,9 @@ private void silentPop() throws SAXException {
54085384 // When active option closes, deep-clone its content to selectedcontent
54095385 if (currentPtr == activeOptionStackPos ) {
54105386 if (selectedContentPointer != null ) {
5411- clearSelectedContentChildren ();
5412- deepCloneChildren (node .node , selectedContentPointer );
5387+ cloneOptionContentToSelectedContent (node .node , selectedContentPointer );
54135388 }
54145389 activeOptionStackPos = -1 ;
5415- selectedContentCloneStack .clear ();
5416- } else if (activeOptionStackPos >= 0 && currentPtr > activeOptionStackPos && selectedContentCloneStack .size () > 1 ) {
5417- // Pop from clone stack when popping an element inside active option
5418- selectedContentCloneStack .remove (selectedContentCloneStack .size () - 1 );
54195390 }
54205391 // Clear selectedcontent tracking if we're popping the select element
54215392 if (node .getGroup () == SELECT ) {
@@ -5424,7 +5395,6 @@ private void silentPop() throws SAXException {
54245395 seenSelectedOption = false ;
54255396 activeOptionStackPos = -1 ;
54265397 hadActiveOption = false ;
5427- selectedContentCloneStack .clear ();
54285398 }
54295399 currentPtr --;
54305400 node .release (this );
@@ -5436,14 +5406,9 @@ private void popOnEof() throws SAXException {
54365406 // When active option closes, deep-clone its content to selectedcontent
54375407 if (currentPtr == activeOptionStackPos ) {
54385408 if (selectedContentPointer != null ) {
5439- clearSelectedContentChildren ();
5440- deepCloneChildren (node .node , selectedContentPointer );
5409+ cloneOptionContentToSelectedContent (node .node , selectedContentPointer );
54415410 }
54425411 activeOptionStackPos = -1 ;
5443- selectedContentCloneStack .clear ();
5444- } else if (activeOptionStackPos >= 0 && currentPtr > activeOptionStackPos && selectedContentCloneStack .size () > 1 ) {
5445- // Pop from clone stack when popping an element inside active option
5446- selectedContentCloneStack .remove (selectedContentCloneStack .size () - 1 );
54475412 }
54485413 // Clear selectedcontent tracking if we're popping the select element
54495414 if (node .getGroup () == SELECT ) {
@@ -5452,7 +5417,6 @@ private void popOnEof() throws SAXException {
54525417 seenSelectedOption = false ;
54535418 activeOptionStackPos = -1 ;
54545419 hadActiveOption = false ;
5455- selectedContentCloneStack .clear ();
54565420 }
54575421 currentPtr --;
54585422 markMalformedIfScript (node .node );
@@ -5643,8 +5607,6 @@ private void appendToCurrentNodeAndPushFormattingElementMayFoster(
56435607 // ]NOCPP]
56445608 // This method can't be called for custom elements
56455609 HtmlAttributes clone = attributes .cloneAttributes ();
5646- // Clone to selectedcontent if inside active option (must be before createElement due to C++ attribute ownership)
5647- cloneElementToSelectedContent ("http://www.w3.org/1999/xhtml" , elementName .getName (), attributes );
56485610 // Attributes must not be read after calling createElement, because
56495611 // createElement may delete attributes in C++.
56505612 T elt ;
@@ -5694,8 +5656,6 @@ private void appendToCurrentNodeAndPushElement(ElementName elementName,
56945656 } else {
56955657 appendElement (elt , currentNode );
56965658 }
5697- // Clone to selectedcontent if inside active option
5698- cloneElementToSelectedContent ("http://www.w3.org/1999/xhtml" , elementName .getName (), attributes );
56995659 StackNode <T > node = createStackNode (elementName , elt
57005660 // [NOCPP[
57015661 , errorHandler == null ? null : new TaintableLocatorImpl (tokenizer )
@@ -5728,8 +5688,6 @@ private void appendToCurrentNodeAndPushElementMayFoster(ElementName elementName,
57285688 );
57295689 appendElement (elt , currentNode );
57305690 }
5731- // Clone to selectedcontent if inside active option
5732- cloneElementToSelectedContent ("http://www.w3.org/1999/xhtml" , popName , attributes );
57335691 StackNode <T > node = createStackNode (elementName , elt , popName
57345692 // [NOCPP[
57355693 , errorHandler == null ? null : new TaintableLocatorImpl (tokenizer )
@@ -5753,8 +5711,6 @@ private void appendToCurrentNodeAndPushElementMayFosterMathML(
57535711 && annotationXmlEncodingPermitsHtml (attributes )) {
57545712 markAsHtmlIntegrationPoint = true ;
57555713 }
5756- // Clone to selectedcontent if inside active option (must be before createElement due to C++ attribute ownership)
5757- cloneElementToSelectedContent ("http://www.w3.org/1998/Math/MathML" , popName , attributes );
57585714 // Attributes must not be read after calling createElement(), since
57595715 // createElement may delete the object in C++.
57605716 T elt ;
@@ -5820,8 +5776,6 @@ private void appendToCurrentNodeAndPushElementMayFosterSVG(
58205776 popName = checkPopName (popName );
58215777 }
58225778 // ]NOCPP]
5823- // Clone to selectedcontent if inside active option
5824- cloneElementToSelectedContent ("http://www.w3.org/2000/svg" , popName , attributes );
58255779 T elt ;
58265780 StackNode <T > current = stack [currentPtr ];
58275781 if (current .isFosterParenting ()) {
@@ -5851,8 +5805,6 @@ private void appendToCurrentNodeAndPushElementMayFoster(ElementName elementName,
58515805 checkAttributes (attributes , "http://www.w3.org/1999/xhtml" );
58525806 // ]NOCPP]
58535807 // Can't be called for custom elements
5854- // Clone to selectedcontent if inside active option
5855- cloneElementToSelectedContent ("http://www.w3.org/1999/xhtml" , elementName .getName (), attributes );
58565808 T elt ;
58575809 T formOwner = form == null || fragment || isTemplateContents () ? null : form ;
58585810 StackNode <T > current = stack [currentPtr ];
@@ -6041,55 +5993,6 @@ private void appendVoidFormToCurrent(HtmlAttributes attributes) throws SAXExcept
60415993 elementPopped ("http://www.w3.org/1999/xhtml" , "form" , elt );
60425994 }
60435995
6044- /**
6045- * Clones an element to the selectedcontent hierarchy when inside an active option.
6046- * This is used for the customizable select feature.
6047- *
6048- * @param ns The namespace URI
6049- * @param name The element name
6050- * @param attributes The attributes (will be cloned)
6051- */
6052- private void cloneElementToSelectedContent (@ NsUri String ns , @ Local String name ,
6053- HtmlAttributes attributes ) throws SAXException {
6054- if (activeOptionStackPos < 0 || selectedContentCloneStack .isEmpty ()) {
6055- return ;
6056- }
6057- // Clone the attributes
6058- HtmlAttributes clonedAttrs = attributes .cloneAttributes ();
6059- // Get the current parent in the selectedcontent hierarchy
6060- T selectedContentParent = selectedContentCloneStack .get (selectedContentCloneStack .size () - 1 );
6061- // Create a clone element
6062- T clone = createElement (ns , name , clonedAttrs , selectedContentParent
6063- // CPPONLY: , htmlCreator(null)
6064- );
6065- // Append the clone to the selectedcontent parent
6066- appendElement (clone , selectedContentParent );
6067- // Push the clone to the stack so nested content goes into it
6068- selectedContentCloneStack .add (clone );
6069- }
6070-
6071- /**
6072- * Clones a void element to the selectedcontent hierarchy when inside an active option.
6073- * Void elements don't need to be pushed to the stack since they have no children.
6074- */
6075- private void cloneVoidElementToSelectedContent (@ NsUri String ns , @ Local String name ,
6076- HtmlAttributes attributes ) throws SAXException {
6077- if (activeOptionStackPos < 0 || selectedContentCloneStack .isEmpty ()) {
6078- return ;
6079- }
6080- // Clone the attributes
6081- HtmlAttributes clonedAttrs = attributes .cloneAttributes ();
6082- // Get the current parent in the selectedcontent hierarchy
6083- T selectedContentParent = selectedContentCloneStack .get (selectedContentCloneStack .size () - 1 );
6084- // Create a clone element
6085- T clone = createElement (ns , name , clonedAttrs , selectedContentParent
6086- // CPPONLY: , htmlCreator(null)
6087- );
6088- // Append the clone to the selectedcontent parent
6089- appendElement (clone , selectedContentParent );
6090- // Void elements don't need to be pushed to the stack
6091- }
6092-
60935996 // [NOCPP[
60945997
60955998 private final void accumulateCharactersForced (@ Const @ NoLength char [] buf ,
@@ -6125,10 +6028,6 @@ private final void accumulateCharactersForced(@Const @NoLength char[] buf,
61256028 protected void accumulateCharacters (@ Const @ NoLength char [] buf , int start ,
61266029 int length ) throws SAXException {
61276030 appendCharacters (stack [currentPtr ].node , buf , start , length );
6128- // Also clone to selectedcontent if in active option
6129- if (activeOptionStackPos >= 0 && !selectedContentCloneStack .isEmpty ()) {
6130- appendCharacters (selectedContentCloneStack .get (selectedContentCloneStack .size () - 1 ), buf , start , length );
6131- }
61326031 }
61336032
61346033 // ------------------------------- //
@@ -6157,12 +6056,14 @@ protected abstract T createHtmlElementSetAsRoot(HtmlAttributes attributes)
61576056 protected abstract void detachFromParent (T element ) throws SAXException ;
61586057
61596058 /**
6160- * Deep clones the children of the source element to the destination element.
6161- * Used for cloning option content to selectedcontent.
6162- * Default implementation does nothing. Subclasses should override.
6059+ * Called when the active option is popped from the stack, to clone
6060+ * the option's children into selectedcontent. Subclasses that support
6061+ * DOM operations should override this to clear selectedcontent and
6062+ * deep-clone the option's children into it.
61636063 */
6164- protected void deepCloneChildren (T source , T destination ) throws SAXException {
6165- // Default implementation does nothing
6064+ protected void cloneOptionContentToSelectedContent (T option , T selectedContent )
6065+ throws SAXException {
6066+ // Default: no-op (streaming SAX mode ignores cloning)
61666067 }
61676068
61686069 protected abstract boolean hasChildren (T element ) throws SAXException ;
@@ -6208,16 +6109,6 @@ protected abstract void appendCommentToDocument(@NoLength char[] buf,
62086109 protected abstract void addAttributesToElement (T element ,
62096110 HtmlAttributes attributes ) throws SAXException ;
62106111
6211- /**
6212- * Clears all children from the selectedcontent element.
6213- * Used when an option with 'selected' attribute is seen after
6214- * content has already been cloned from a previous option.
6215- */
6216- protected void clearSelectedContentChildren () throws SAXException {
6217- // Default implementation does nothing. Subclasses that support
6218- // selectedcontent cloning can override this.
6219- }
6220-
62216112 protected void markMalformedIfScript (T elt ) throws SAXException {
62226113
62236114 }
@@ -6423,10 +6314,6 @@ && charBufferContainsNonWhitespace()) {
64236314 // reconstructing gave us a new current node
64246315 appendCharacters (currentNode (), charBuffer , 0 ,
64256316 charBufferLen );
6426- // Also clone to selectedcontent if in active option
6427- if (activeOptionStackPos >= 0 && !selectedContentCloneStack .isEmpty ()) {
6428- appendCharacters (selectedContentCloneStack .get (selectedContentCloneStack .size () - 1 ), charBuffer , 0 , charBufferLen );
6429- }
64306317 charBufferLen = 0 ;
64316318 return ;
64326319 }
@@ -6436,29 +6323,17 @@ && charBufferContainsNonWhitespace()) {
64366323
64376324 if (templatePos >= tablePos ) {
64386325 appendCharacters (stack [templatePos ].node , charBuffer , 0 , charBufferLen );
6439- // Also clone to selectedcontent if in active option
6440- if (activeOptionStackPos >= 0 && !selectedContentCloneStack .isEmpty ()) {
6441- appendCharacters (selectedContentCloneStack .get (selectedContentCloneStack .size () - 1 ), charBuffer , 0 , charBufferLen );
6442- }
64436326 charBufferLen = 0 ;
64446327 return ;
64456328 }
64466329
64476330 StackNode <T > tableElt = stack [tablePos ];
64486331 insertFosterParentedCharacters (charBuffer , 0 , charBufferLen ,
64496332 tableElt .node , stack [tablePos - 1 ].node );
6450- // Also clone to selectedcontent if in active option
6451- if (activeOptionStackPos >= 0 && !selectedContentCloneStack .isEmpty ()) {
6452- appendCharacters (selectedContentCloneStack .get (selectedContentCloneStack .size () - 1 ), charBuffer , 0 , charBufferLen );
6453- }
64546333 charBufferLen = 0 ;
64556334 return ;
64566335 }
64576336 appendCharacters (currentNode (), charBuffer , 0 , charBufferLen );
6458- // Also clone to selectedcontent if in active option
6459- if (activeOptionStackPos >= 0 && !selectedContentCloneStack .isEmpty ()) {
6460- appendCharacters (selectedContentCloneStack .get (selectedContentCloneStack .size () - 1 ), charBuffer , 0 , charBufferLen );
6461- }
64626337 charBufferLen = 0 ;
64636338 }
64646339 }
0 commit comments