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 ;
@@ -2355,10 +2350,6 @@ public final void startTag(ElementName elementName,
23552350 if (selectedContentPointer != null ) {
23562351 if (hasSelected ) {
23572352 // Option with selected attr becomes active
2358- // Clear previous selectedcontent content if we had a different active option
2359- if (hadActiveOption && !seenSelectedOption ) {
2360- clearSelectedContentChildren ();
2361- }
23622353 seenSelectedOption = true ;
23632354 shouldBeActive = true ;
23642355 } else if (!seenSelectedOption && !hadActiveOption ) {
@@ -2372,9 +2363,6 @@ public final void startTag(ElementName elementName,
23722363 if (shouldBeActive ) {
23732364 activeOptionStackPos = currentPtr ;
23742365 hadActiveOption = true ;
2375- // Initialize the clone stack with selectedcontent as the root parent
2376- selectedContentCloneStack .clear ();
2377- selectedContentCloneStack .add (selectedContentPointer );
23782366 }
23792367 attributes = null ; // CPP
23802368 break starttagloop ;
@@ -5338,14 +5326,9 @@ private void pop() throws SAXException {
53385326 // This handles adoption agency restructuring correctly
53395327 if (currentPtr == activeOptionStackPos ) {
53405328 if (selectedContentPointer != null ) {
5341- clearSelectedContentChildren ();
5342- deepCloneChildren (node .node , selectedContentPointer );
5329+ cloneOptionContentToSelectedContent (node .node , selectedContentPointer );
53435330 }
53445331 activeOptionStackPos = -1 ;
5345- selectedContentCloneStack .clear ();
5346- } else if (activeOptionStackPos >= 0 && currentPtr > activeOptionStackPos && selectedContentCloneStack .size () > 1 ) {
5347- // Pop from clone stack when popping an element inside active option
5348- selectedContentCloneStack .remove (selectedContentCloneStack .size () - 1 );
53495332 }
53505333 // Clear selectedcontent tracking if we're popping the select element
53515334 // (not when popping selectedcontent itself - the DOM node is still valid)
@@ -5355,7 +5338,6 @@ private void pop() throws SAXException {
53555338 seenSelectedOption = false ;
53565339 activeOptionStackPos = -1 ;
53575340 hadActiveOption = false ;
5358- selectedContentCloneStack .clear ();
53595341 }
53605342 currentPtr --;
53615343 elementPopped (node .ns , node .popName , node .node );
@@ -5371,14 +5353,9 @@ private void popForeign(int origPos, int eltPos) throws SAXException {
53715353 // When active option closes, deep-clone its content to selectedcontent
53725354 if (currentPtr == activeOptionStackPos ) {
53735355 if (selectedContentPointer != null ) {
5374- clearSelectedContentChildren ();
5375- deepCloneChildren (node .node , selectedContentPointer );
5356+ cloneOptionContentToSelectedContent (node .node , selectedContentPointer );
53765357 }
53775358 activeOptionStackPos = -1 ;
5378- selectedContentCloneStack .clear ();
5379- } else if (activeOptionStackPos >= 0 && currentPtr > activeOptionStackPos && selectedContentCloneStack .size () > 1 ) {
5380- // Pop from clone stack when popping an element inside active option
5381- selectedContentCloneStack .remove (selectedContentCloneStack .size () - 1 );
53825359 }
53835360 // Clear selectedcontent tracking if we're popping the select element
53845361 if (node .getGroup () == SELECT ) {
@@ -5387,7 +5364,6 @@ private void popForeign(int origPos, int eltPos) throws SAXException {
53875364 seenSelectedOption = false ;
53885365 activeOptionStackPos = -1 ;
53895366 hadActiveOption = false ;
5390- selectedContentCloneStack .clear ();
53915367 }
53925368 currentPtr --;
53935369 elementPopped (node .ns , node .popName , node .node );
@@ -5400,14 +5376,9 @@ private void silentPop() throws SAXException {
54005376 // When active option closes, deep-clone its content to selectedcontent
54015377 if (currentPtr == activeOptionStackPos ) {
54025378 if (selectedContentPointer != null ) {
5403- clearSelectedContentChildren ();
5404- deepCloneChildren (node .node , selectedContentPointer );
5379+ cloneOptionContentToSelectedContent (node .node , selectedContentPointer );
54055380 }
54065381 activeOptionStackPos = -1 ;
5407- selectedContentCloneStack .clear ();
5408- } else if (activeOptionStackPos >= 0 && currentPtr > activeOptionStackPos && selectedContentCloneStack .size () > 1 ) {
5409- // Pop from clone stack when popping an element inside active option
5410- selectedContentCloneStack .remove (selectedContentCloneStack .size () - 1 );
54115382 }
54125383 // Clear selectedcontent tracking if we're popping the select element
54135384 if (node .getGroup () == SELECT ) {
@@ -5416,7 +5387,6 @@ private void silentPop() throws SAXException {
54165387 seenSelectedOption = false ;
54175388 activeOptionStackPos = -1 ;
54185389 hadActiveOption = false ;
5419- selectedContentCloneStack .clear ();
54205390 }
54215391 currentPtr --;
54225392 node .release (this );
@@ -5428,14 +5398,9 @@ private void popOnEof() throws SAXException {
54285398 // When active option closes, deep-clone its content to selectedcontent
54295399 if (currentPtr == activeOptionStackPos ) {
54305400 if (selectedContentPointer != null ) {
5431- clearSelectedContentChildren ();
5432- deepCloneChildren (node .node , selectedContentPointer );
5401+ cloneOptionContentToSelectedContent (node .node , selectedContentPointer );
54335402 }
54345403 activeOptionStackPos = -1 ;
5435- selectedContentCloneStack .clear ();
5436- } else if (activeOptionStackPos >= 0 && currentPtr > activeOptionStackPos && selectedContentCloneStack .size () > 1 ) {
5437- // Pop from clone stack when popping an element inside active option
5438- selectedContentCloneStack .remove (selectedContentCloneStack .size () - 1 );
54395404 }
54405405 // Clear selectedcontent tracking if we're popping the select element
54415406 if (node .getGroup () == SELECT ) {
@@ -5444,7 +5409,6 @@ private void popOnEof() throws SAXException {
54445409 seenSelectedOption = false ;
54455410 activeOptionStackPos = -1 ;
54465411 hadActiveOption = false ;
5447- selectedContentCloneStack .clear ();
54485412 }
54495413 currentPtr --;
54505414 markMalformedIfScript (node .node );
@@ -5635,8 +5599,6 @@ private void appendToCurrentNodeAndPushFormattingElementMayFoster(
56355599 // ]NOCPP]
56365600 // This method can't be called for custom elements
56375601 HtmlAttributes clone = attributes .cloneAttributes ();
5638- // Clone to selectedcontent if inside active option (must be before createElement due to C++ attribute ownership)
5639- cloneElementToSelectedContent ("http://www.w3.org/1999/xhtml" , elementName .getName (), attributes );
56405602 // Attributes must not be read after calling createElement, because
56415603 // createElement may delete attributes in C++.
56425604 T elt ;
@@ -5686,8 +5648,6 @@ private void appendToCurrentNodeAndPushElement(ElementName elementName,
56865648 } else {
56875649 appendElement (elt , currentNode );
56885650 }
5689- // Clone to selectedcontent if inside active option
5690- cloneElementToSelectedContent ("http://www.w3.org/1999/xhtml" , elementName .getName (), attributes );
56915651 StackNode <T > node = createStackNode (elementName , elt
56925652 // [NOCPP[
56935653 , errorHandler == null ? null : new TaintableLocatorImpl (tokenizer )
@@ -5720,8 +5680,6 @@ private void appendToCurrentNodeAndPushElementMayFoster(ElementName elementName,
57205680 );
57215681 appendElement (elt , currentNode );
57225682 }
5723- // Clone to selectedcontent if inside active option
5724- cloneElementToSelectedContent ("http://www.w3.org/1999/xhtml" , popName , attributes );
57255683 StackNode <T > node = createStackNode (elementName , elt , popName
57265684 // [NOCPP[
57275685 , errorHandler == null ? null : new TaintableLocatorImpl (tokenizer )
@@ -5745,8 +5703,6 @@ private void appendToCurrentNodeAndPushElementMayFosterMathML(
57455703 && annotationXmlEncodingPermitsHtml (attributes )) {
57465704 markAsHtmlIntegrationPoint = true ;
57475705 }
5748- // Clone to selectedcontent if inside active option (must be before createElement due to C++ attribute ownership)
5749- cloneElementToSelectedContent ("http://www.w3.org/1998/Math/MathML" , popName , attributes );
57505706 // Attributes must not be read after calling createElement(), since
57515707 // createElement may delete the object in C++.
57525708 T elt ;
@@ -5812,8 +5768,6 @@ private void appendToCurrentNodeAndPushElementMayFosterSVG(
58125768 popName = checkPopName (popName );
58135769 }
58145770 // ]NOCPP]
5815- // Clone to selectedcontent if inside active option
5816- cloneElementToSelectedContent ("http://www.w3.org/2000/svg" , popName , attributes );
58175771 T elt ;
58185772 StackNode <T > current = stack [currentPtr ];
58195773 if (current .isFosterParenting ()) {
@@ -5843,8 +5797,6 @@ private void appendToCurrentNodeAndPushElementMayFoster(ElementName elementName,
58435797 checkAttributes (attributes , "http://www.w3.org/1999/xhtml" );
58445798 // ]NOCPP]
58455799 // Can't be called for custom elements
5846- // Clone to selectedcontent if inside active option
5847- cloneElementToSelectedContent ("http://www.w3.org/1999/xhtml" , elementName .getName (), attributes );
58485800 T elt ;
58495801 T formOwner = form == null || fragment || isTemplateContents () ? null : form ;
58505802 StackNode <T > current = stack [currentPtr ];
@@ -6033,55 +5985,6 @@ private void appendVoidFormToCurrent(HtmlAttributes attributes) throws SAXExcept
60335985 elementPopped ("http://www.w3.org/1999/xhtml" , "form" , elt );
60345986 }
60355987
6036- /**
6037- * Clones an element to the selectedcontent hierarchy when inside an active option.
6038- * This is used for the customizable select feature.
6039- *
6040- * @param ns The namespace URI
6041- * @param name The element name
6042- * @param attributes The attributes (will be cloned)
6043- */
6044- private void cloneElementToSelectedContent (@ NsUri String ns , @ Local String name ,
6045- HtmlAttributes attributes ) throws SAXException {
6046- if (activeOptionStackPos < 0 || selectedContentCloneStack .isEmpty ()) {
6047- return ;
6048- }
6049- // Clone the attributes
6050- HtmlAttributes clonedAttrs = attributes .cloneAttributes ();
6051- // Get the current parent in the selectedcontent hierarchy
6052- T selectedContentParent = selectedContentCloneStack .get (selectedContentCloneStack .size () - 1 );
6053- // Create a clone element
6054- T clone = createElement (ns , name , clonedAttrs , selectedContentParent
6055- // CPPONLY: , htmlCreator(null)
6056- );
6057- // Append the clone to the selectedcontent parent
6058- appendElement (clone , selectedContentParent );
6059- // Push the clone to the stack so nested content goes into it
6060- selectedContentCloneStack .add (clone );
6061- }
6062-
6063- /**
6064- * Clones a void element to the selectedcontent hierarchy when inside an active option.
6065- * Void elements don't need to be pushed to the stack since they have no children.
6066- */
6067- private void cloneVoidElementToSelectedContent (@ NsUri String ns , @ Local String name ,
6068- HtmlAttributes attributes ) throws SAXException {
6069- if (activeOptionStackPos < 0 || selectedContentCloneStack .isEmpty ()) {
6070- return ;
6071- }
6072- // Clone the attributes
6073- HtmlAttributes clonedAttrs = attributes .cloneAttributes ();
6074- // Get the current parent in the selectedcontent hierarchy
6075- T selectedContentParent = selectedContentCloneStack .get (selectedContentCloneStack .size () - 1 );
6076- // Create a clone element
6077- T clone = createElement (ns , name , clonedAttrs , selectedContentParent
6078- // CPPONLY: , htmlCreator(null)
6079- );
6080- // Append the clone to the selectedcontent parent
6081- appendElement (clone , selectedContentParent );
6082- // Void elements don't need to be pushed to the stack
6083- }
6084-
60855988 // [NOCPP[
60865989
60875990 private final void accumulateCharactersForced (@ Const @ NoLength char [] buf ,
@@ -6117,10 +6020,6 @@ private final void accumulateCharactersForced(@Const @NoLength char[] buf,
61176020 protected void accumulateCharacters (@ Const @ NoLength char [] buf , int start ,
61186021 int length ) throws SAXException {
61196022 appendCharacters (stack [currentPtr ].node , buf , start , length );
6120- // Also clone to selectedcontent if in active option
6121- if (activeOptionStackPos >= 0 && !selectedContentCloneStack .isEmpty ()) {
6122- appendCharacters (selectedContentCloneStack .get (selectedContentCloneStack .size () - 1 ), buf , start , length );
6123- }
61246023 }
61256024
61266025 // ------------------------------- //
@@ -6149,12 +6048,14 @@ protected abstract T createHtmlElementSetAsRoot(HtmlAttributes attributes)
61496048 protected abstract void detachFromParent (T element ) throws SAXException ;
61506049
61516050 /**
6152- * Deep clones the children of the source element to the destination element.
6153- * Used for cloning option content to selectedcontent.
6154- * Default implementation does nothing. Subclasses should override.
6051+ * Called when the active option is popped from the stack, to clone
6052+ * the option's children into selectedcontent. Subclasses that support
6053+ * DOM operations should override this to clear selectedcontent and
6054+ * deep-clone the option's children into it.
61556055 */
6156- protected void deepCloneChildren (T source , T destination ) throws SAXException {
6157- // Default implementation does nothing
6056+ protected void cloneOptionContentToSelectedContent (T option , T selectedContent )
6057+ throws SAXException {
6058+ // Default: no-op (streaming SAX mode ignores cloning)
61586059 }
61596060
61606061 protected abstract boolean hasChildren (T element ) throws SAXException ;
@@ -6200,16 +6101,6 @@ protected abstract void appendCommentToDocument(@NoLength char[] buf,
62006101 protected abstract void addAttributesToElement (T element ,
62016102 HtmlAttributes attributes ) throws SAXException ;
62026103
6203- /**
6204- * Clears all children from the selectedcontent element.
6205- * Used when an option with 'selected' attribute is seen after
6206- * content has already been cloned from a previous option.
6207- */
6208- protected void clearSelectedContentChildren () throws SAXException {
6209- // Default implementation does nothing. Subclasses that support
6210- // selectedcontent cloning can override this.
6211- }
6212-
62136104 protected void markMalformedIfScript (T elt ) throws SAXException {
62146105
62156106 }
@@ -6415,10 +6306,6 @@ && charBufferContainsNonWhitespace()) {
64156306 // reconstructing gave us a new current node
64166307 appendCharacters (currentNode (), charBuffer , 0 ,
64176308 charBufferLen );
6418- // Also clone to selectedcontent if in active option
6419- if (activeOptionStackPos >= 0 && !selectedContentCloneStack .isEmpty ()) {
6420- appendCharacters (selectedContentCloneStack .get (selectedContentCloneStack .size () - 1 ), charBuffer , 0 , charBufferLen );
6421- }
64226309 charBufferLen = 0 ;
64236310 return ;
64246311 }
@@ -6428,29 +6315,17 @@ && charBufferContainsNonWhitespace()) {
64286315
64296316 if (templatePos >= tablePos ) {
64306317 appendCharacters (stack [templatePos ].node , charBuffer , 0 , charBufferLen );
6431- // Also clone to selectedcontent if in active option
6432- if (activeOptionStackPos >= 0 && !selectedContentCloneStack .isEmpty ()) {
6433- appendCharacters (selectedContentCloneStack .get (selectedContentCloneStack .size () - 1 ), charBuffer , 0 , charBufferLen );
6434- }
64356318 charBufferLen = 0 ;
64366319 return ;
64376320 }
64386321
64396322 StackNode <T > tableElt = stack [tablePos ];
64406323 insertFosterParentedCharacters (charBuffer , 0 , charBufferLen ,
64416324 tableElt .node , stack [tablePos - 1 ].node );
6442- // Also clone to selectedcontent if in active option
6443- if (activeOptionStackPos >= 0 && !selectedContentCloneStack .isEmpty ()) {
6444- appendCharacters (selectedContentCloneStack .get (selectedContentCloneStack .size () - 1 ), charBuffer , 0 , charBufferLen );
6445- }
64466325 charBufferLen = 0 ;
64476326 return ;
64486327 }
64496328 appendCharacters (currentNode (), charBuffer , 0 , charBufferLen );
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- }
64546329 charBufferLen = 0 ;
64556330 }
64566331 }
0 commit comments