dom.pp 101 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587
  1. {
  2. This file is part of the Free Component Library
  3. Implementation of DOM interfaces
  4. Copyright (c) 1999-2000 by Sebastian Guenther, [email protected]
  5. Modified in 2006 by Sergei Gorelkin, [email protected]
  6. See the file COPYING.FPC, included in this distribution,
  7. for details about the copyright.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  11. **********************************************************************}
  12. {
  13. This unit provides classes which implement the interfaces defined in the
  14. DOM (Document Object Model) specification.
  15. The current state is:
  16. DOM Levels 1 and 2 - Completely implemented
  17. DOM Level 3 - Partially implemented
  18. Specification used for this implementation:
  19. "Document Object Model (DOM) Level 2 Specification Version 1.0
  20. W3C Recommendation 11 November, 2000"
  21. http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113
  22. }
  23. unit DOM;
  24. {$ifdef fpc}
  25. {$MODE objfpc}{$H+}
  26. {$endif}
  27. interface
  28. uses
  29. SysUtils, Classes, xmlutils, dtdmodel;
  30. // -------------------------------------------------------
  31. // DOMException
  32. // -------------------------------------------------------
  33. const
  34. // DOM Level 1 exception codes:
  35. INDEX_SIZE_ERR = 1; // index or size is negative, or greater than the allowed value
  36. DOMSTRING_SIZE_ERR = 2; // Specified range of text does not fit into a DOMString
  37. HIERARCHY_REQUEST_ERR = 3; // node is inserted somewhere it does not belong
  38. WRONG_DOCUMENT_ERR = 4; // node is used in a different document than the one that created it (that does not support it)
  39. INVALID_CHARACTER_ERR = 5; // invalid or illegal character is specified, such as in a name
  40. NO_DATA_ALLOWED_ERR = 6; // data is specified for a node which does not support data
  41. NO_MODIFICATION_ALLOWED_ERR = 7; // an attempt is made to modify an object where modifications are not allowed
  42. NOT_FOUND_ERR = 8; // an attempt is made to reference a node in a context where it does not exist
  43. NOT_SUPPORTED_ERR = 9; // implementation does not support the type of object requested
  44. INUSE_ATTRIBUTE_ERR = 10; // an attempt is made to add an attribute that is already in use elsewhere
  45. // DOM Level 2 exception codes:
  46. INVALID_STATE_ERR = 11; // an attempt is made to use an object that is not, or is no longer, usable
  47. SYNTAX_ERR = 12; // invalid or illegal string specified
  48. INVALID_MODIFICATION_ERR = 13; // an attempt is made to modify the type of the underlying object
  49. NAMESPACE_ERR = 14; // an attempt is made to create or change an object in a way which is incorrect with regard to namespaces
  50. INVALID_ACCESS_ERR = 15; // parameter or operation is not supported by the underlying object
  51. // -------------------------------------------------------
  52. // Node
  53. // -------------------------------------------------------
  54. const
  55. ELEMENT_NODE = 1;
  56. ATTRIBUTE_NODE = 2;
  57. TEXT_NODE = 3;
  58. CDATA_SECTION_NODE = 4;
  59. ENTITY_REFERENCE_NODE = 5;
  60. ENTITY_NODE = 6;
  61. PROCESSING_INSTRUCTION_NODE = 7;
  62. COMMENT_NODE = 8;
  63. DOCUMENT_NODE = 9;
  64. DOCUMENT_TYPE_NODE = 10;
  65. DOCUMENT_FRAGMENT_NODE = 11;
  66. NOTATION_NODE = 12;
  67. type
  68. TDOMDocument = class;
  69. TDOMNodeList = class;
  70. TDOMNamedNodeMap = class;
  71. TDOMAttr = class;
  72. TDOMElement = class;
  73. TDOMText = class;
  74. TDOMComment = class;
  75. TDOMCDATASection = class;
  76. TDOMDocumentType = class;
  77. TDOMEntityReference = class;
  78. TDOMProcessingInstruction = class;
  79. TNodePool = class;
  80. PNodePoolArray = ^TNodePoolArray;
  81. {$ifdef CPU16}
  82. TNodePoolArray = array[0..MaxSmallInt div sizeof(Pointer)-1] of TNodePool;
  83. {$else CPU16}
  84. TNodePoolArray = array[0..MaxInt div sizeof(Pointer)-1] of TNodePool;
  85. {$endif CPU16}
  86. {$ifndef fpc}
  87. TFPList = TList;
  88. {$endif}
  89. // -------------------------------------------------------
  90. // DOMString
  91. // -------------------------------------------------------
  92. TSetOfChar = xmlutils.TSetOfChar; { to be removed: not used in DOM unit }
  93. DOMString = XMLString;
  94. DOMPChar = PXMLChar;
  95. PDOMString = ^DOMString;
  96. EDOMError = class(Exception)
  97. public
  98. Code: Integer;
  99. constructor Create(ACode: Integer; const ASituation: String);
  100. end;
  101. EDOMIndexSize = class(EDOMError)
  102. public
  103. constructor Create(const ASituation: String);
  104. end;
  105. EDOMHierarchyRequest = class(EDOMError)
  106. public
  107. constructor Create(const ASituation: String);
  108. end;
  109. EDOMWrongDocument = class(EDOMError)
  110. public
  111. constructor Create(const ASituation: String);
  112. end;
  113. EDOMNotFound = class(EDOMError)
  114. public
  115. constructor Create(const ASituation: String);
  116. end;
  117. EDOMNotSupported = class(EDOMError)
  118. public
  119. constructor Create(const ASituation: String);
  120. end;
  121. EDOMInUseAttribute = class(EDOMError)
  122. public
  123. constructor Create(const ASituation: String);
  124. end;
  125. EDOMInvalidState = class(EDOMError)
  126. public
  127. constructor Create(const ASituation: String);
  128. end;
  129. EDOMSyntax = class(EDOMError)
  130. public
  131. constructor Create(const ASituation: String);
  132. end;
  133. EDOMInvalidModification = class(EDOMError)
  134. public
  135. constructor Create(const ASituation: String);
  136. end;
  137. EDOMNamespace = class(EDOMError)
  138. public
  139. constructor Create(const ASituation: String);
  140. end;
  141. EDOMInvalidAccess = class(EDOMError)
  142. public
  143. constructor Create(const ASituation: String);
  144. end;
  145. { NodeType, NodeName and NodeValue had been moved from fields to functions.
  146. This lowers memory usage and also obsoletes most constructors,
  147. at a slight performance penalty. However, NodeName and NodeValue are
  148. accessible via fields using specialized properties of descendant classes,
  149. e.g. TDOMElement.TagName, TDOMCharacterData.Data etc.}
  150. TNodeFlagEnum = (
  151. nfReadonly,
  152. nfRecycled,
  153. nfLevel2,
  154. nfIgnorableWS,
  155. nfSpecified,
  156. nfDestroying,
  157. nfFirstChild
  158. );
  159. TNodeFlags = set of TNodeFlagEnum;
  160. TDOMNode = class
  161. protected
  162. FPool: TObject;
  163. FFlags: TNodeFlags;
  164. FParentNode: TDOMNode;
  165. FPreviousSibling, FNextSibling: TDOMNode;
  166. FOwnerDocument: TDOMDocument;
  167. function GetNodeName: DOMString; virtual; abstract;
  168. function GetNodeValue: DOMString; virtual;
  169. function GetParentNode: TDOMNode; virtual;
  170. procedure SetNodeValue(const AValue: DOMString); virtual;
  171. function GetFirstChild: TDOMNode; virtual;
  172. function GetLastChild: TDOMNode; virtual;
  173. function GetPreviousSibling: TDOMNode; virtual;
  174. function GetAttributes: TDOMNamedNodeMap; virtual;
  175. function GetRevision: Integer;
  176. function GetNodeType: Integer; virtual; abstract;
  177. function GetTextContent: DOMString; virtual;
  178. procedure SetTextContent(const AValue: DOMString); virtual;
  179. function GetLocalName: DOMString; virtual;
  180. function GetNamespaceURI: DOMString; virtual;
  181. function GetPrefix: DOMString; virtual;
  182. procedure SetPrefix(const Value: DOMString); virtual;
  183. function GetOwnerDocument: TDOMDocument; virtual;
  184. function GetBaseURI: DOMString;
  185. procedure SetReadOnly(Value: Boolean);
  186. procedure Changing;
  187. public
  188. constructor Create(AOwner: TDOMDocument);
  189. destructor Destroy; override;
  190. procedure FreeInstance; override;
  191. function GetChildNodes: TDOMNodeList;
  192. property NodeName: DOMString read GetNodeName;
  193. property NodeValue: DOMString read GetNodeValue write SetNodeValue;
  194. property NodeType: Integer read GetNodeType;
  195. property ParentNode: TDOMNode read GetParentNode;
  196. property FirstChild: TDOMNode read GetFirstChild;
  197. property LastChild: TDOMNode read GetLastChild;
  198. property ChildNodes: TDOMNodeList read GetChildNodes;
  199. property PreviousSibling: TDOMNode read GetPreviousSibling;
  200. property NextSibling: TDOMNode read FNextSibling;
  201. property Attributes: TDOMNamedNodeMap read GetAttributes;
  202. property OwnerDocument: TDOMDocument read GetOwnerDocument;
  203. function InsertBefore(NewChild, RefChild: TDOMNode): TDOMNode; virtual;
  204. function ReplaceChild(NewChild, OldChild: TDOMNode): TDOMNode; virtual;
  205. function DetachChild(OldChild: TDOMNode): TDOMNode; virtual;
  206. function RemoveChild(OldChild: TDOMNode): TDOMNode;
  207. function AppendChild(NewChild: TDOMNode): TDOMNode;
  208. function HasChildNodes: Boolean; virtual;
  209. function CloneNode(deep: Boolean): TDOMNode; overload; virtual;
  210. // DOM level 2
  211. function IsSupported(const Feature, Version: DOMString): Boolean;
  212. function HasAttributes: Boolean; virtual;
  213. procedure Normalize; virtual;
  214. property NamespaceURI: DOMString read GetNamespaceURI;
  215. property LocalName: DOMString read GetLocalName;
  216. property Prefix: DOMString read GetPrefix write SetPrefix;
  217. // DOM level 3
  218. property TextContent: DOMString read GetTextContent write SetTextContent;
  219. function LookupPrefix(const nsURI: DOMString): DOMString;
  220. function LookupNamespaceURI(const APrefix: DOMString): DOMString;
  221. function IsDefaultNamespace(const nsURI: DOMString): Boolean;
  222. property baseURI: DOMString read GetBaseURI;
  223. // Extensions to DOM interface:
  224. function CloneNode(deep: Boolean; ACloneOwner: TDOMDocument): TDOMNode; overload; virtual;
  225. function FindNode(const ANodeName: DOMString): TDOMNode; virtual;
  226. function CompareName(const name: DOMString): Integer; virtual;
  227. property Flags: TNodeFlags read FFlags;
  228. end;
  229. TDOMNodeClass = class of TDOMNode;
  230. TDOMElementClass = class of TDOMElement;
  231. { The following class is an implementation specific extension, it is just an
  232. extended implementation of TDOMNode, the generic DOM::Node interface
  233. implementation. (Its main purpose is to save memory in a big node tree) }
  234. TDOMNode_WithChildren = class(TDOMNode)
  235. protected
  236. FFirstChild: TDOMNode;
  237. FChildNodes: TDOMNodeList;
  238. function GetFirstChild: TDOMNode; override;
  239. function GetLastChild: TDOMNode; override;
  240. procedure CloneChildren(ACopy: TDOMNode; ACloneOwner: TDOMDocument);
  241. procedure FreeChildren;
  242. function GetTextContent: DOMString; override;
  243. procedure SetTextContent(const AValue: DOMString); override;
  244. public
  245. destructor Destroy; override;
  246. function InsertBefore(NewChild, RefChild: TDOMNode): TDOMNode; override;
  247. function ReplaceChild(NewChild, OldChild: TDOMNode): TDOMNode; override;
  248. function DetachChild(OldChild: TDOMNode): TDOMNode; override;
  249. function HasChildNodes: Boolean; override;
  250. function FindNode(const ANodeName: DOMString): TDOMNode; override;
  251. procedure InternalAppend(NewChild: TDOMNode);
  252. end;
  253. { A common ancestor for Document and Entity nodes. }
  254. TDOMNode_TopLevel = class(TDOMNode_WithChildren)
  255. protected
  256. FInputEncoding: DOMString;
  257. FXMLEncoding: DOMString;
  258. FURI: DOMString;
  259. FXMLVersion: TXMLVersion;
  260. function GetXMLVersion: DOMString;
  261. public
  262. property InputEncoding: DOMString read FInputEncoding;
  263. property XMLEncoding: DOMString read FXMLEncoding;
  264. end;
  265. // -------------------------------------------------------
  266. // NodeList
  267. // -------------------------------------------------------
  268. TFilterResult = (frFalse, frNorecurseFalse, frTrue, frNorecurseTrue);
  269. TDOMNodeList = class(TObject)
  270. protected
  271. FNode: TDOMNode;
  272. FRevision: Integer;
  273. FList: TFPList;
  274. function GetCount: LongWord;
  275. function GetItem(index: LongWord): TDOMNode;
  276. function NodeFilter(aNode: TDOMNode): TFilterResult; virtual;
  277. // now deprecated in favor of NodeFilter
  278. procedure BuildList; virtual;
  279. public
  280. constructor Create(ANode: TDOMNode);
  281. destructor Destroy; override;
  282. property Item[index: LongWord]: TDOMNode read GetItem; default;
  283. property Count: LongWord read GetCount;
  284. property Length: LongWord read GetCount;
  285. end;
  286. { an extension to DOM interface, used to build recursive lists of elements }
  287. TDOMElementList = class(TDOMNodeList)
  288. protected
  289. filter: DOMString;
  290. FNSIndexFilter: Integer;
  291. localNameFilter: DOMString;
  292. FMatchNS: Boolean;
  293. FMatchAnyNS: Boolean;
  294. UseFilter: Boolean;
  295. function NodeFilter(aNode: TDOMNode): TFilterResult; override;
  296. public
  297. constructor Create(ANode: TDOMNode; const AFilter: DOMString); overload;
  298. constructor Create(ANode: TDOMNode; const nsURI, localName: DOMString); overload;
  299. end;
  300. // -------------------------------------------------------
  301. // NamedNodeMap
  302. // -------------------------------------------------------
  303. TDOMNamedNodeMap = class(TObject)
  304. protected
  305. FOwner: TDOMNode;
  306. FList: TFPList;
  307. function GetItem(index: LongWord): TDOMNode;
  308. function GetLength: LongWord;
  309. function Find(const name: DOMString; out Index: LongWord): Boolean;
  310. function Delete(index: LongWord): TDOMNode; virtual;
  311. function InternalRemove(const name: DOMString): TDOMNode;
  312. function ValidateInsert(arg: TDOMNode): Integer; virtual;
  313. public
  314. constructor Create(AOwner: TDOMNode);
  315. destructor Destroy; override;
  316. function GetNamedItem(const name: DOMString): TDOMNode;
  317. function SetNamedItem(arg: TDOMNode): TDOMNode; virtual;
  318. function RemoveNamedItem(const name: DOMString): TDOMNode;
  319. // Introduced in DOM Level 2:
  320. function getNamedItemNS(const namespaceURI, localName: DOMString): TDOMNode; virtual;
  321. function setNamedItemNS(arg: TDOMNode): TDOMNode; virtual;
  322. function removeNamedItemNS(const namespaceURI,localName: DOMString): TDOMNode; virtual;
  323. property Item[index: LongWord]: TDOMNode read GetItem; default;
  324. property Length: LongWord read GetLength;
  325. end;
  326. // -------------------------------------------------------
  327. // CharacterData
  328. // -------------------------------------------------------
  329. TDOMCharacterData = class(TDOMNode)
  330. private
  331. FNodeValue: DOMString;
  332. protected
  333. function GetLength: LongWord;
  334. function GetNodeValue: DOMString; override;
  335. procedure SetNodeValue(const AValue: DOMString); override;
  336. public
  337. property Data: DOMString read FNodeValue write SetNodeValue;
  338. property Length: LongWord read GetLength;
  339. function SubstringData(offset, count: LongWord): DOMString;
  340. procedure AppendData(const arg: DOMString);
  341. procedure InsertData(offset: LongWord; const arg: DOMString);
  342. procedure DeleteData(offset, count: LongWord);
  343. procedure ReplaceData(offset, count: LongWord; const arg: DOMString);
  344. end;
  345. // -------------------------------------------------------
  346. // DOMImplementation
  347. // -------------------------------------------------------
  348. TDOMImplementation = class
  349. public
  350. function HasFeature(const feature, version: DOMString): Boolean;
  351. // Introduced in DOM Level 2:
  352. function CreateDocumentType(const QualifiedName, PublicID,
  353. SystemID: DOMString): TDOMDocumentType;
  354. function CreateDocument(const NamespaceURI, QualifiedName: DOMString;
  355. doctype: TDOMDocumentType): TDOMDocument;
  356. end;
  357. // -------------------------------------------------------
  358. // DocumentFragment
  359. // -------------------------------------------------------
  360. TDOMDocumentFragment = class(TDOMNode_WithChildren)
  361. protected
  362. function GetNodeType: Integer; override;
  363. function GetNodeName: DOMString; override;
  364. public
  365. function CloneNode(deep: Boolean; ACloneOwner: TDOMDocument): TDOMNode; overload; override;
  366. end;
  367. // -------------------------------------------------------
  368. // Document
  369. // -------------------------------------------------------
  370. // TODO: to be replaced by more suitable container
  371. TNamespaces = array of DOMString;
  372. TDOMDocument = class(TDOMNode_TopLevel)
  373. protected
  374. FIDList: THashTable;
  375. FRevision: Integer;
  376. FImplementation: TDOMImplementation;
  377. FNamespaces: TNamespaces;
  378. FNames: THashTable;
  379. FEmptyNode: TDOMElement;
  380. FNodeLists: THashTable;
  381. FMaxPoolSize: Integer;
  382. FPools: PNodePoolArray;
  383. FXmlStandalone: Boolean;
  384. FStdUri_xml: PHashItem;
  385. FStdUri_xmlns: PHashItem;
  386. function GetDocumentElement: TDOMElement;
  387. function GetDocType: TDOMDocumentType;
  388. function GetNodeType: Integer; override;
  389. function GetNodeName: DOMString; override;
  390. function GetTextContent: DOMString; override;
  391. function GetOwnerDocument: TDOMDocument; override;
  392. procedure SetTextContent(const value: DOMString); override;
  393. procedure RemoveID(Elem: TDOMElement);
  394. function GetChildNodeList(aNode: TDOMNode): TDOMNodeList;
  395. function GetElementList(aNode: TDOMNode; const nsURI, aLocalName: DOMString; UseNS: Boolean): TDOMNodeList;
  396. procedure NodeListDestroyed(aList: TDOMNodeList);
  397. function Alloc(AClass: TDOMNodeClass): TDOMNode;
  398. procedure SetXMLVersion(const aValue: DOMString); virtual;
  399. procedure SetXMLStandalone(aValue: Boolean); virtual;
  400. function ValidateQName(const nsUri, qName: DOMString; out nsidx: PHashItem): Integer;
  401. public
  402. function IndexOfNS(const nsURI: DOMString; AddIfAbsent: Boolean = False): Integer;
  403. function InsertBefore(NewChild, RefChild: TDOMNode): TDOMNode; override;
  404. function ReplaceChild(NewChild, OldChild: TDOMNode): TDOMNode; override;
  405. property DocType: TDOMDocumentType read GetDocType;
  406. property Impl: TDOMImplementation read FImplementation;
  407. property DocumentElement: TDOMElement read GetDocumentElement;
  408. function CreateElement(const tagName: DOMString): TDOMElement; virtual;
  409. function CreateElementBuf(Buf: DOMPChar; Length: Integer): TDOMElement;
  410. function CreateDocumentFragment: TDOMDocumentFragment;
  411. function CreateTextNode(const data: DOMString): TDOMText;
  412. function CreateTextNodeBuf(Buf: DOMPChar; Length: Integer; IgnWS: Boolean): TDOMText;
  413. function CreateComment(const data: DOMString): TDOMComment;
  414. function CreateCommentBuf(Buf: DOMPChar; Length: Integer): TDOMComment;
  415. function CreateCDATASection(const data: DOMString): TDOMCDATASection;
  416. virtual;
  417. function CreateProcessingInstruction(const target, data: DOMString):
  418. TDOMProcessingInstruction; virtual;
  419. function CreateAttribute(const name: DOMString): TDOMAttr;
  420. function CreateAttributeBuf(Buf: DOMPChar; Length: Integer): TDOMAttr;
  421. function CreateEntityReference(const name: DOMString): TDOMEntityReference;
  422. virtual;
  423. function GetElementsByTagName(const tagname: DOMString): TDOMNodeList;
  424. // DOM level 2 methods
  425. function ImportNode(ImportedNode: TDOMNode; Deep: Boolean): TDOMNode;
  426. function CreateElementNS(const nsURI, QualifiedName: DOMString): TDOMElement; overload;
  427. function CreateElementNS(const nsURI, QualifiedName: DOMString;
  428. AClass: TDOMElementClass): TDOMElement; overload;
  429. function CreateAttributeNS(const nsURI, QualifiedName: DOMString): TDOMAttr;
  430. function GetElementsByTagNameNS(const nsURI, alocalName: DOMString): TDOMNodeList;
  431. function GetElementById(const ElementID: DOMString): TDOMElement;
  432. // DOM level 3:
  433. property documentURI: DOMString read FURI write FURI;
  434. property XMLVersion: DOMString read GetXMLVersion write SetXMLVersion;
  435. property XMLStandalone: Boolean read FXmlStandalone write SetXmlStandalone;
  436. // Extensions to DOM interface:
  437. constructor Create; virtual;
  438. destructor Destroy; override;
  439. function CloneNode(deep: Boolean): TDOMNode; overload; override;
  440. property Names: THashTable read FNames;
  441. property IDs: THashTable read FIDList write FIDList;
  442. end;
  443. TXMLDocument = class(TDOMDocument)
  444. protected
  445. procedure SetXMLVersion(const aValue: DOMString); override;
  446. procedure SetXMLStandalone(aValue: Boolean); override;
  447. public
  448. // These fields are extensions to the DOM interface:
  449. StylesheetType, StylesheetHRef: DOMString;
  450. constructor Create; override;
  451. function CreateCDATASection(const data: DOMString): TDOMCDATASection; override;
  452. function CreateProcessingInstruction(const target, data: DOMString):
  453. TDOMProcessingInstruction; override;
  454. function CreateEntityReference(const name: DOMString): TDOMEntityReference; override;
  455. end;
  456. // This limits number of namespaces per document to 65535,
  457. // and prefix length to 65535, too.
  458. // I believe that higher values may only be found in deliberately malformed documents.
  459. TNamespaceInfo = packed record
  460. NSIndex: Word;
  461. PrefixLen: Word;
  462. QName: PHashItem;
  463. end;
  464. // -------------------------------------------------------
  465. // Attr
  466. // -------------------------------------------------------
  467. TAttrDataType = xmlutils.TAttrDataType;
  468. TDOMNode_NS = class(TDOMNode_WithChildren)
  469. protected
  470. FNSI: TNamespaceInfo;
  471. function GetNodeName: DOMString; override;
  472. function GetLocalName: DOMString; override;
  473. function GetNamespaceURI: DOMString; override;
  474. function GetPrefix: DOMString; override;
  475. procedure SetPrefix(const Value: DOMString); override;
  476. public
  477. { Used by parser }
  478. procedure SetNSI(const nsUri: DOMString; ColonPos: Integer);
  479. function CompareName(const AName: DOMString): Integer; override;
  480. property NSI: TNamespaceInfo read FNSI;
  481. end;
  482. TDOMAttr = class(TDOMNode_NS)
  483. protected
  484. FDataType: TAttrDataType;
  485. function GetNodeValue: DOMString; override;
  486. function GetNodeType: Integer; override;
  487. function GetParentNode: TDOMNode; override;
  488. function GetSpecified: Boolean;
  489. function GetIsID: Boolean;
  490. function GetOwnerElement: TDOMElement;
  491. procedure SetNodeValue(const AValue: DOMString); override;
  492. public
  493. destructor Destroy; override;
  494. function CloneNode(deep: Boolean; ACloneOwner: TDOMDocument): TDOMNode; overload; override;
  495. property Name: DOMString read GetNodeName;
  496. property Specified: Boolean read GetSpecified;
  497. property Value: DOMString read GetNodeValue write SetNodeValue;
  498. property OwnerElement: TDOMElement read GetOwnerElement;
  499. property IsID: Boolean read GetIsID;
  500. // extensions
  501. // TODO: this is to be replaced with DOM 3 TypeInfo
  502. property DataType: TAttrDataType read FDataType write FDataType;
  503. end;
  504. // -------------------------------------------------------
  505. // Element
  506. // -------------------------------------------------------
  507. TDOMElement = class(TDOMNode_NS)
  508. protected
  509. FAttributes: TDOMNamedNodeMap;
  510. function GetNodeType: Integer; override;
  511. function GetAttributes: TDOMNamedNodeMap; override;
  512. procedure AttachDefaultAttrs;
  513. function InternalLookupPrefix(const nsURI: DOMString; Original: TDOMElement): DOMString;
  514. procedure RestoreDefaultAttr(AttrDef: TAttributeDef);
  515. public
  516. destructor Destroy; override;
  517. function CloneNode(deep: Boolean; ACloneOwner: TDOMDocument): TDOMNode; overload; override;
  518. procedure Normalize; override;
  519. property TagName: DOMString read GetNodeName;
  520. function GetAttribute(const name: DOMString): DOMString;
  521. procedure SetAttribute(const name, value: DOMString);
  522. procedure RemoveAttribute(const name: DOMString);
  523. function GetAttributeNode(const name: DOMString): TDOMAttr;
  524. function SetAttributeNode(NewAttr: TDOMAttr): TDOMAttr;
  525. function RemoveAttributeNode(OldAttr: TDOMAttr): TDOMAttr;
  526. function GetElementsByTagName(const name: DOMString): TDOMNodeList;
  527. // Introduced in DOM Level 2:
  528. function GetAttributeNS(const nsURI, aLocalName: DOMString): DOMString;
  529. procedure SetAttributeNS(const nsURI, qualifiedName, value: DOMString);
  530. procedure RemoveAttributeNS(const nsURI, aLocalName: DOMString);
  531. function GetAttributeNodeNS(const nsURI, aLocalName: DOMString): TDOMAttr;
  532. function SetAttributeNodeNS(newAttr: TDOMAttr): TDOMAttr;
  533. function GetElementsByTagNameNS(const nsURI, aLocalName: DOMString): TDOMNodeList;
  534. function hasAttribute(const name: DOMString): Boolean;
  535. function hasAttributeNS(const nsURI, aLocalName: DOMString): Boolean;
  536. function HasAttributes: Boolean; override;
  537. // extension
  538. property AttribStrings[const Name: DOMString]: DOMString
  539. read GetAttribute write SetAttribute; default;
  540. end;
  541. // -------------------------------------------------------
  542. // Text
  543. // -------------------------------------------------------
  544. TDOMText = class(TDOMCharacterData)
  545. protected
  546. function GetNodeType: Integer; override;
  547. function GetNodeName: DOMString; override;
  548. procedure SetNodeValue(const aValue: DOMString); override;
  549. public
  550. function CloneNode(deep: Boolean; ACloneOwner: TDOMDocument): TDOMNode; overload; override;
  551. function SplitText(offset: LongWord): TDOMText;
  552. function IsElementContentWhitespace: Boolean;
  553. end;
  554. // -------------------------------------------------------
  555. // Comment
  556. // -------------------------------------------------------
  557. TDOMComment = class(TDOMCharacterData)
  558. protected
  559. function GetNodeType: Integer; override;
  560. function GetNodeName: DOMString; override;
  561. public
  562. function CloneNode(deep: Boolean; ACloneOwner: TDOMDocument): TDOMNode; overload; override;
  563. end;
  564. // -------------------------------------------------------
  565. // CDATASection
  566. // -------------------------------------------------------
  567. TDOMCDATASection = class(TDOMText)
  568. protected
  569. function GetNodeType: Integer; override;
  570. function GetNodeName: DOMString; override;
  571. public
  572. function CloneNode(deep: Boolean; ACloneOwner: TDOMDocument): TDOMNode; overload; override;
  573. end;
  574. // -------------------------------------------------------
  575. // DocumentType
  576. // -------------------------------------------------------
  577. TDOMDocumentType = class(TDOMNode)
  578. protected
  579. FModel: TDTDModel;
  580. FEntities, FNotations: TDOMNamedNodeMap;
  581. function GetEntities: TDOMNamedNodeMap;
  582. function GetNotations: TDOMNamedNodeMap;
  583. function GetNodeType: Integer; override;
  584. function GetNodeName: DOMString; override;
  585. function GetPublicID: DOMString;
  586. function GetSystemID: DOMString;
  587. function GetInternalSubset: DOMString;
  588. public
  589. constructor Create(aOwner: TDOMDocument; aModel: TDTDModel);
  590. destructor Destroy; override;
  591. property Name: DOMString read GetNodeName;
  592. property Entities: TDOMNamedNodeMap read GetEntities;
  593. property Notations: TDOMNamedNodeMap read GetNotations;
  594. // Introduced in DOM Level 2:
  595. property PublicID: DOMString read GetPublicID;
  596. property SystemID: DOMString read GetSystemID;
  597. property InternalSubset: DOMString read GetInternalSubset;
  598. end;
  599. // -------------------------------------------------------
  600. // Notation
  601. // -------------------------------------------------------
  602. TDOMNotation = class(TDOMNode)
  603. protected
  604. FDecl: TNotationDecl;
  605. FBaseURI: DOMString;
  606. function GetNodeType: Integer; override;
  607. function GetNodeName: DOMString; override;
  608. function GetPublicID: DOMString;
  609. function GetSystemID: DOMString;
  610. public
  611. function CloneNode(deep: Boolean; ACloneOwner: TDOMDocument): TDOMNode; overload; override;
  612. property PublicID: DOMString read GetPublicID;
  613. property SystemID: DOMString read GetSystemID;
  614. end;
  615. // -------------------------------------------------------
  616. // Entity
  617. // -------------------------------------------------------
  618. TDOMEntity = class(TDOMNode_TopLevel)
  619. protected
  620. FDecl: TEntityDecl;
  621. FBaseURI: DOMString;
  622. function GetNodeType: Integer; override;
  623. function GetNodeName: DOMString; override;
  624. function GetPublicID: DOMString;
  625. function GetSystemID: DOMString;
  626. function GetNotationName: DOMString;
  627. public
  628. function CloneNode(deep: Boolean; aCloneOwner: TDOMDocument): TDOMNode; override;
  629. property PublicID: DOMString read GetPublicID;
  630. property SystemID: DOMString read GetSystemID;
  631. property NotationName: DOMString read GetNotationName;
  632. property XMLVersion: DOMString read GetXMLVersion;
  633. end;
  634. // -------------------------------------------------------
  635. // EntityReference
  636. // -------------------------------------------------------
  637. TDOMEntityReference = class(TDOMNode_WithChildren)
  638. protected
  639. FName: DOMString;
  640. function GetNodeType: Integer; override;
  641. function GetNodeName: DOMString; override;
  642. public
  643. function CloneNode(deep: Boolean; ACloneOwner: TDOMDocument): TDOMNode; overload; override;
  644. end;
  645. // -------------------------------------------------------
  646. // ProcessingInstruction
  647. // -------------------------------------------------------
  648. TDOMProcessingInstruction = class(TDOMNode)
  649. private
  650. FTarget: DOMString;
  651. FNodeValue: DOMString;
  652. protected
  653. function GetNodeType: Integer; override;
  654. function GetNodeName: DOMString; override;
  655. function GetNodeValue: DOMString; override;
  656. procedure SetNodeValue(const AValue: DOMString); override;
  657. public
  658. function CloneNode(deep: Boolean; ACloneOwner: TDOMDocument): TDOMNode; overload; override;
  659. property Target: DOMString read FTarget;
  660. property Data: DOMString read FNodeValue write SetNodeValue;
  661. end;
  662. // TNodePool - custom memory management for TDOMNode's
  663. // One pool manages objects of the same InstanceSize (may be of various classes)
  664. PExtent = ^TExtent;
  665. TExtent = record
  666. Next: PExtent;
  667. // following: array of TDOMNode instances
  668. end;
  669. TNodePool = class(TObject)
  670. private
  671. FCurrExtent: PExtent;
  672. FCurrExtentSize: Integer;
  673. FElementSize: Integer;
  674. FCurrBlock: TDOMNode;
  675. FFirstFree: TDOMNode;
  676. procedure AddExtent(AElemCount: Integer);
  677. public
  678. constructor Create(AElementSize: Integer; AElementCount: Integer = 32);
  679. destructor Destroy; override;
  680. function AllocNode(AClass: TDOMNodeClass): TDOMNode;
  681. procedure FreeNode(ANode: TDOMNode);
  682. end;
  683. // temporary until things are settled
  684. function LoadElement(doc: TDOMDocument; src: PNodeData; attrCount: Integer): TDOMElement;
  685. // =======================================================
  686. // =======================================================
  687. implementation
  688. uses
  689. UriParser;
  690. { a namespace-enabled NamedNodeMap }
  691. type
  692. TAttributeMap = class(TDOMNamedNodeMap)
  693. private
  694. function FindNS(nsIndex: Integer; const aLocalName: DOMString;
  695. out Index: LongWord): Boolean;
  696. function InternalRemoveNS(const nsURI, aLocalName: DOMString): TDOMNode;
  697. procedure RestoreDefault(aName: PHashItem);
  698. protected
  699. function Delete(index: LongWord): TDOMNode; override;
  700. function ValidateInsert(arg: TDOMNode): Integer; override;
  701. public
  702. function setNamedItem(arg: TDOMNode): TDOMNode; override;
  703. function getNamedItemNS(const namespaceURI, localName: DOMString): TDOMNode; override;
  704. function setNamedItemNS(arg: TDOMNode): TDOMNode; override;
  705. function removeNamedItemNS(const namespaceURI,localName: DOMString): TDOMNode; override;
  706. end;
  707. // -------------------------------------------------------
  708. // DOM Exception
  709. // -------------------------------------------------------
  710. constructor EDOMError.Create(ACode: Integer; const ASituation: String);
  711. begin
  712. Code := ACode;
  713. inherited Create(Self.ClassName + ' in ' + ASituation);
  714. end;
  715. constructor EDOMIndexSize.Create(const ASituation: String); // 1
  716. begin
  717. inherited Create(INDEX_SIZE_ERR, ASituation);
  718. end;
  719. constructor EDOMHierarchyRequest.Create(const ASituation: String); // 3
  720. begin
  721. inherited Create(HIERARCHY_REQUEST_ERR, ASituation);
  722. end;
  723. constructor EDOMWrongDocument.Create(const ASituation: String); // 4
  724. begin
  725. inherited Create(WRONG_DOCUMENT_ERR, ASituation);
  726. end;
  727. constructor EDOMNotFound.Create(const ASituation: String); // 8
  728. begin
  729. inherited Create(NOT_FOUND_ERR, ASituation);
  730. end;
  731. constructor EDOMNotSupported.Create(const ASituation: String); // 9
  732. begin
  733. inherited Create(NOT_SUPPORTED_ERR, ASituation);
  734. end;
  735. constructor EDOMInUseAttribute.Create(const ASituation: String); // 10
  736. begin
  737. inherited Create(INUSE_ATTRIBUTE_ERR, ASituation);
  738. end;
  739. constructor EDOMInvalidState.Create(const ASituation: String); // 11
  740. begin
  741. inherited Create(INVALID_STATE_ERR, ASituation);
  742. end;
  743. constructor EDOMSyntax.Create(const ASituation: String); // 12
  744. begin
  745. inherited Create(SYNTAX_ERR, ASituation);
  746. end;
  747. constructor EDOMInvalidModification.Create(const ASituation: String); // 13
  748. begin
  749. inherited Create(INVALID_MODIFICATION_ERR, ASituation);
  750. end;
  751. constructor EDOMNamespace.Create(const ASituation: String); // 14
  752. begin
  753. inherited Create(NAMESPACE_ERR, ASituation);
  754. end;
  755. constructor EDOMInvalidAccess.Create(const ASituation: String); // 15
  756. begin
  757. inherited Create(INVALID_ACCESS_ERR, ASituation);
  758. end;
  759. // -------------------------------------------------------
  760. // Node
  761. // -------------------------------------------------------
  762. constructor TDOMNode.Create(AOwner: TDOMDocument);
  763. begin
  764. FOwnerDocument := AOwner;
  765. inherited Create;
  766. end;
  767. destructor TDOMNode.Destroy;
  768. begin
  769. if Assigned(FParentNode) then
  770. FParentNode.DetachChild(Self);
  771. inherited Destroy;
  772. end;
  773. procedure TDOMNode.FreeInstance;
  774. begin
  775. if Assigned(FPool) then
  776. begin
  777. CleanupInstance;
  778. TNodePool(FPool).FreeNode(Self);
  779. end
  780. else
  781. inherited FreeInstance;
  782. end;
  783. function TDOMNode.GetNodeValue: DOMString;
  784. begin
  785. Result := '';
  786. end;
  787. function TDOMNode.GetParentNode: TDOMNode;
  788. begin
  789. Result := FParentNode;
  790. end;
  791. procedure TDOMNode.SetNodeValue(const AValue: DOMString);
  792. begin
  793. // do nothing
  794. end;
  795. function TDOMNode.GetChildNodes: TDOMNodeList;
  796. begin
  797. Result := FOwnerDocument.GetChildNodeList(Self);
  798. end;
  799. function TDOMNode.GetFirstChild: TDOMNode;
  800. begin
  801. Result := nil;
  802. end;
  803. function TDOMNode.GetLastChild: TDOMNode;
  804. begin
  805. Result := nil;
  806. end;
  807. function TDOMNode.GetPreviousSibling: TDOMNode;
  808. begin
  809. if nfFirstChild in FFlags then
  810. Result := nil
  811. else
  812. Result := FPreviousSibling;
  813. end;
  814. function TDOMNode.GetAttributes: TDOMNamedNodeMap;
  815. begin
  816. Result := nil;
  817. end;
  818. function TDOMNode.InsertBefore(NewChild, RefChild: TDOMNode): TDOMNode;
  819. begin
  820. Changing; // merely to comply with core3/nodeinsertbefore14
  821. raise EDOMHierarchyRequest.Create('Node.InsertBefore');
  822. Result:=nil;
  823. end;
  824. function TDOMNode.ReplaceChild(NewChild, OldChild: TDOMNode): TDOMNode;
  825. begin
  826. Changing; // merely to comply with core3/nodereplacechild21
  827. raise EDOMHierarchyRequest.Create('Node.ReplaceChild');
  828. Result:=nil;
  829. end;
  830. function TDOMNode.DetachChild(OldChild: TDOMNode): TDOMNode;
  831. begin
  832. // OldChild isn't in our child list
  833. raise EDOMNotFound.Create('Node.RemoveChild');
  834. Result:=nil;
  835. end;
  836. function TDOMNode.RemoveChild(OldChild: TDOMNode): TDOMNode;
  837. begin
  838. Result := DetachChild(OldChild);
  839. end;
  840. function TDOMNode.AppendChild(NewChild: TDOMNode): TDOMNode;
  841. begin
  842. Result := InsertBefore(NewChild, nil);
  843. end;
  844. function TDOMNode.HasChildNodes: Boolean;
  845. begin
  846. Result := False;
  847. end;
  848. function TDOMNode.CloneNode(deep: Boolean): TDOMNode;
  849. begin
  850. Result := CloneNode(deep, FOwnerDocument);
  851. end;
  852. function TDOMNode.CloneNode(deep: Boolean; ACloneOwner: TDOMDocument): TDOMNode;
  853. begin
  854. // !! CreateFmt() does not set Code property !!
  855. raise EDOMNotSupported.Create(Format('Cloning/importing of %s is not supported', [ClassName]));
  856. Result:=nil;
  857. end;
  858. function TDOMNode.FindNode(const ANodeName: DOMString): TDOMNode;
  859. begin
  860. // FIX: we have no children, hence cannot find anything
  861. Result := nil;
  862. end;
  863. function TDOMNode.GetRevision: Integer;
  864. begin
  865. Result := FOwnerDocument.FRevision;
  866. end;
  867. function TDOMNode.IsSupported(const Feature, Version: DOMString): Boolean;
  868. begin
  869. Result := FOwnerDocument.Impl.HasFeature(Feature, Version);
  870. end;
  871. function TDOMNode.HasAttributes: Boolean;
  872. begin
  873. Result := False;
  874. end;
  875. procedure TDOMNode.Normalize;
  876. var
  877. Child, tmp: TDOMNode;
  878. Txt: TDOMText;
  879. begin
  880. Child := FirstChild;
  881. Txt := nil;
  882. while Assigned(Child) do
  883. begin
  884. if Child.NodeType = TEXT_NODE then
  885. begin
  886. tmp := Child.NextSibling;
  887. if TDOMText(Child).Data <> '' then
  888. begin
  889. if Assigned(Txt) then
  890. begin
  891. Txt.AppendData(TDOMText(Child).Data);
  892. // TODO: maybe should be smarter
  893. Exclude(Txt.FFlags, nfIgnorableWS);
  894. end
  895. else
  896. begin
  897. Txt := TDOMText(Child);
  898. Child := Child.NextSibling;
  899. Continue;
  900. end;
  901. end;
  902. Child.Free;
  903. Child := tmp;
  904. end
  905. else
  906. begin
  907. Child.Normalize; // should be recursive!
  908. Child := Child.NextSibling;
  909. Txt := nil;
  910. end;
  911. end;
  912. end;
  913. function TDOMNode.GetTextContent: DOMString;
  914. begin
  915. Result := NodeValue;
  916. end;
  917. procedure TDOMNode.SetTextContent(const AValue: DOMString);
  918. begin
  919. SetNodeValue(AValue);
  920. end;
  921. function TDOMNode.GetNamespaceURI: DOMString;
  922. begin
  923. Result := '';
  924. end;
  925. function TDOMNode.GetLocalName: DOMString;
  926. begin
  927. Result := '';
  928. end;
  929. function TDOMNode.GetPrefix: DOMString;
  930. begin
  931. Result := '';
  932. end;
  933. procedure TDOMNode.SetPrefix(const Value: DOMString);
  934. begin
  935. // do nothing, override for Elements and Attributes
  936. end;
  937. function TDOMNode.GetOwnerDocument: TDOMDocument;
  938. begin
  939. Result := FOwnerDocument;
  940. end;
  941. procedure TDOMNode.SetReadOnly(Value: Boolean);
  942. var
  943. child: TDOMNode;
  944. attrs: TDOMNamedNodeMap;
  945. I: Integer;
  946. begin
  947. if Value then
  948. Include(FFlags, nfReadOnly)
  949. else
  950. Exclude(FFlags, nfReadOnly);
  951. child := FirstChild;
  952. while Assigned(child) do
  953. begin
  954. child.SetReadOnly(Value);
  955. child := child.NextSibling;
  956. end;
  957. if HasAttributes then
  958. begin
  959. attrs := Attributes;
  960. for I := 0 to attrs.Length-1 do
  961. attrs[I].SetReadOnly(Value);
  962. end;
  963. end;
  964. procedure TDOMNode.Changing;
  965. begin
  966. if (nfReadOnly in FFlags) and not (nfDestroying in FOwnerDocument.FFlags) then
  967. raise EDOMError.Create(NO_MODIFICATION_ALLOWED_ERR, 'Node.CheckReadOnly');
  968. end;
  969. function CompareDOMStrings(const s1, s2: DOMPChar; l1, l2: integer): integer;
  970. var i: integer;
  971. begin
  972. Result:=l1-l2;
  973. i:=0;
  974. while (i<l1) and (Result=0) do begin
  975. Result:=ord(s1[i])-ord(s2[i]);
  976. inc(i);
  977. end;
  978. end;
  979. // generic version (slow)
  980. function TDOMNode.CompareName(const name: DOMString): Integer;
  981. var
  982. SelfName: DOMString;
  983. begin
  984. SelfName := NodeName;
  985. Result := CompareDOMStrings(DOMPChar(name), DOMPChar(SelfName), Length(name), Length(SelfName));
  986. end;
  987. // This will return nil for Entity, Notation, DocType and DocFragment's
  988. function GetAncestorElement(n: TDOMNode): TDOMElement;
  989. var
  990. parent: TDOMNode;
  991. begin
  992. case n.nodeType of
  993. DOCUMENT_NODE:
  994. result := TDOMDocument(n).documentElement;
  995. ATTRIBUTE_NODE:
  996. result := TDOMAttr(n).OwnerElement;
  997. else
  998. parent := n.ParentNode;
  999. while Assigned(parent) and (parent.NodeType <> ELEMENT_NODE) do
  1000. parent := parent.ParentNode;
  1001. Result := TDOMElement(parent);
  1002. end;
  1003. end;
  1004. // TODO: specs prescribe to return default namespace if APrefix=null,
  1005. // but we aren't able to distinguish null from an empty string.
  1006. // This breaks level3/nodelookupnamespaceuri08 which passes an empty string.
  1007. function TDOMNode.LookupNamespaceURI(const APrefix: DOMString): DOMString;
  1008. var
  1009. Attr: TDOMAttr;
  1010. Map: TDOMNamedNodeMap;
  1011. I: Integer;
  1012. begin
  1013. Result := '';
  1014. if Self = nil then
  1015. Exit;
  1016. if nodeType = ELEMENT_NODE then
  1017. begin
  1018. if (nfLevel2 in FFlags) and (TDOMElement(Self).Prefix = APrefix) then
  1019. begin
  1020. result := Self.NamespaceURI;
  1021. Exit;
  1022. end;
  1023. if HasAttributes then
  1024. begin
  1025. Map := Attributes;
  1026. for I := 0 to Map.Length-1 do
  1027. begin
  1028. Attr := TDOMAttr(Map[I]);
  1029. // should ignore level 1 atts here
  1030. if ((Attr.Prefix = 'xmlns') and (Attr.localName = APrefix)) or
  1031. ((Attr.localName = 'xmlns') and (APrefix = '')) then
  1032. begin
  1033. result := Attr.NodeValue;
  1034. Exit;
  1035. end;
  1036. end
  1037. end;
  1038. end;
  1039. result := GetAncestorElement(Self).LookupNamespaceURI(APrefix);
  1040. end;
  1041. function TDOMNode.LookupPrefix(const nsURI: DOMString): DOMString;
  1042. begin
  1043. Result := '';
  1044. if (nsURI = '') or (Self = nil) then
  1045. Exit;
  1046. if nodeType = ELEMENT_NODE then
  1047. result := TDOMElement(Self).InternalLookupPrefix(nsURI, TDOMElement(Self))
  1048. else
  1049. result := GetAncestorElement(Self).LookupPrefix(nsURI);
  1050. end;
  1051. function TDOMNode.IsDefaultNamespace(const nsURI: DOMString): Boolean;
  1052. var
  1053. Attr: TDOMAttr;
  1054. Map: TDOMNamedNodeMap;
  1055. I: Integer;
  1056. begin
  1057. Result := False;
  1058. if Self = nil then
  1059. Exit;
  1060. if nodeType = ELEMENT_NODE then
  1061. begin
  1062. if TDOMElement(Self).FNSI.PrefixLen = 0 then
  1063. begin
  1064. result := (nsURI = namespaceURI);
  1065. Exit;
  1066. end
  1067. else if HasAttributes then
  1068. begin
  1069. Map := Attributes;
  1070. for I := 0 to Map.Length-1 do
  1071. begin
  1072. Attr := TDOMAttr(Map[I]);
  1073. if Attr.LocalName = 'xmlns' then
  1074. begin
  1075. result := (Attr.Value = nsURI);
  1076. Exit;
  1077. end;
  1078. end;
  1079. end;
  1080. end;
  1081. result := GetAncestorElement(Self).IsDefaultNamespace(nsURI);
  1082. end;
  1083. function GetParentURI(n: TDOMNode): DOMString;
  1084. var
  1085. entity, parent: TDOMNode;
  1086. begin
  1087. parent := n.ParentNode;
  1088. if Assigned(parent) then
  1089. begin
  1090. entity := nil;
  1091. case parent.nodeType of
  1092. ENTITY_NODE:
  1093. entity := parent;
  1094. ENTITY_REFERENCE_NODE:
  1095. if Assigned(n.OwnerDocument.DocType) then
  1096. entity := n.OwnerDocument.DocType.Entities.GetNamedItem(parent.NodeName);
  1097. end;
  1098. if entity = nil then
  1099. result := parent.BaseURI
  1100. else
  1101. { TODO: this will need fix when resource resolving is implemented;
  1102. it should return the URI of actually fetched entity. }
  1103. ResolveRelativeURI(TDOMEntity(entity).FDecl.FURI, TDOMEntity(entity).SystemID, result);
  1104. end
  1105. else
  1106. result := n.OwnerDocument.DocumentURI;
  1107. end;
  1108. function TDOMNode.GetBaseURI: DOMString;
  1109. var
  1110. base: DOMString;
  1111. dtype: TDOMDocumentType;
  1112. ent: TDOMEntity;
  1113. begin
  1114. case NodeType of
  1115. ELEMENT_NODE:
  1116. begin
  1117. result := GetParentURI(Self);
  1118. { 'xml' prefix is restricted to xml namespace, so this will work
  1119. regardless of namespace processing enabled }
  1120. base := TDOMElement(Self).GetAttribute('xml:base');
  1121. if base <> '' then
  1122. begin
  1123. ResolveRelativeUri(result, base, result);
  1124. end;
  1125. end;
  1126. DOCUMENT_NODE:
  1127. result := TDOMDocument(Self).FURI;
  1128. PROCESSING_INSTRUCTION_NODE:
  1129. result := GetParentURI(Self);
  1130. { BaseUri of entities and notations is the URI where they're defined;
  1131. cloning should cause this property to get lost. }
  1132. ENTITY_NODE:
  1133. result := TDOMEntity(Self).FBaseURI;
  1134. NOTATION_NODE:
  1135. result := TDOMNotation(Self).FBaseURI;
  1136. ENTITY_REFERENCE_NODE:
  1137. begin
  1138. result := '';
  1139. dtype := OwnerDocument.DocType;
  1140. if Assigned(dtype) then
  1141. begin
  1142. ent := TDOMEntity(dtype.Entities.GetNamedItem(NodeName));
  1143. if Assigned(ent) then
  1144. result := ent.FDecl.FURI;
  1145. end;
  1146. end
  1147. else
  1148. result := '';
  1149. end;
  1150. end;
  1151. //------------------------------------------------------------------------------
  1152. type
  1153. TNodeTypeEnum = ELEMENT_NODE..NOTATION_NODE;
  1154. TNodeTypeSet = set of TNodeTypeEnum;
  1155. const
  1156. stdChildren = [TEXT_NODE, ENTITY_REFERENCE_NODE, PROCESSING_INSTRUCTION_NODE,
  1157. COMMENT_NODE, CDATA_SECTION_NODE, ELEMENT_NODE];
  1158. ValidChildren: array [TNodeTypeEnum] of TNodeTypeSet = (
  1159. stdChildren, { element }
  1160. [TEXT_NODE, ENTITY_REFERENCE_NODE], { attribute }
  1161. [], { text }
  1162. [], { cdata }
  1163. stdChildren, { ent ref }
  1164. stdChildren, { entity }
  1165. [], { pi }
  1166. [], { comment }
  1167. [ELEMENT_NODE, DOCUMENT_TYPE_NODE, PROCESSING_INSTRUCTION_NODE, COMMENT_NODE], { document }
  1168. [], { doctype }
  1169. stdChildren, { fragment }
  1170. [] { notation }
  1171. );
  1172. function TDOMNode_WithChildren.GetFirstChild: TDOMNode;
  1173. begin
  1174. Result := FFirstChild;
  1175. end;
  1176. function TDOMNode_WithChildren.GetLastChild: TDOMNode;
  1177. begin
  1178. if FFirstChild = nil then
  1179. Result := nil
  1180. else
  1181. Result := FFirstChild.FPreviousSibling;
  1182. end;
  1183. destructor TDOMNode_WithChildren.Destroy;
  1184. begin
  1185. FreeChildren;
  1186. FChildNodes.Free; // its destructor will zero the field
  1187. inherited Destroy;
  1188. end;
  1189. function TDOMNode_WithChildren.InsertBefore(NewChild, RefChild: TDOMNode):
  1190. TDOMNode;
  1191. var
  1192. Tmp: TDOMNode;
  1193. NewChildType: Integer;
  1194. begin
  1195. Result := NewChild;
  1196. NewChildType := NewChild.NodeType;
  1197. Changing;
  1198. if NewChild.FOwnerDocument <> FOwnerDocument then
  1199. begin
  1200. if (NewChildType <> DOCUMENT_TYPE_NODE) or
  1201. (NewChild.FOwnerDocument <> nil) then
  1202. raise EDOMWrongDocument.Create('NodeWC.InsertBefore');
  1203. end;
  1204. if Assigned(RefChild) and (RefChild.ParentNode <> Self) then
  1205. raise EDOMNotFound.Create('NodeWC.InsertBefore');
  1206. // TODO: skip checking Fragments as well? (Fragment itself cannot be in the tree)
  1207. if not (NewChildType in [TEXT_NODE, CDATA_SECTION_NODE, COMMENT_NODE, PROCESSING_INSTRUCTION_NODE]) and (NewChild.FirstChild <> nil) then
  1208. begin
  1209. Tmp := Self;
  1210. while Assigned(Tmp) do
  1211. begin
  1212. if Tmp = NewChild then
  1213. raise EDOMHierarchyRequest.Create('NodeWC.InsertBefore (cycle in tree)');
  1214. Tmp := Tmp.ParentNode;
  1215. end;
  1216. end;
  1217. if NewChild = RefChild then // inserting node before itself is a no-op
  1218. Exit;
  1219. Inc(FOwnerDocument.FRevision); // invalidate nodelists
  1220. if NewChildType = DOCUMENT_FRAGMENT_NODE then
  1221. begin
  1222. Tmp := NewChild.FirstChild;
  1223. if Assigned(Tmp) then
  1224. begin
  1225. while Assigned(Tmp) do
  1226. begin
  1227. if not (Tmp.NodeType in ValidChildren[NodeType]) then
  1228. raise EDOMHierarchyRequest.Create('NodeWC.InsertBefore');
  1229. Tmp := Tmp.NextSibling;
  1230. end;
  1231. while Assigned(TDOMDocumentFragment(NewChild).FFirstChild) do
  1232. InsertBefore(TDOMDocumentFragment(NewChild).FFirstChild, RefChild);
  1233. end;
  1234. Exit;
  1235. end;
  1236. if not (NewChildType in ValidChildren[NodeType]) then
  1237. raise EDOMHierarchyRequest.Create('NodeWC.InsertBefore');
  1238. if Assigned(NewChild.FParentNode) then
  1239. NewChild.FParentNode.DetachChild(NewChild);
  1240. NewChild.FNextSibling := RefChild;
  1241. if RefChild = nil then // append to the end
  1242. begin
  1243. if Assigned(FFirstChild) then
  1244. begin
  1245. Tmp := FFirstChild.FPreviousSibling; { our last child }
  1246. Tmp.FNextSibling := NewChild;
  1247. NewChild.FPreviousSibling := Tmp;
  1248. end
  1249. else
  1250. begin
  1251. FFirstChild := NewChild;
  1252. Include(NewChild.FFlags, nfFirstChild);
  1253. end;
  1254. FFirstChild.FPreviousSibling := NewChild; { becomes our last child }
  1255. end
  1256. else // insert before RefChild
  1257. begin
  1258. NewChild.FPreviousSibling := RefChild.FPreviousSibling;
  1259. if RefChild = FFirstChild then
  1260. begin
  1261. Exclude(RefChild.FFlags, nfFirstChild);
  1262. FFirstChild := NewChild;
  1263. Include(NewChild.FFlags, nfFirstChild);
  1264. end
  1265. else
  1266. RefChild.FPreviousSibling.FNextSibling := NewChild;
  1267. RefChild.FPreviousSibling := NewChild;
  1268. end;
  1269. NewChild.FParentNode := Self;
  1270. end;
  1271. function TDOMNode_WithChildren.ReplaceChild(NewChild, OldChild: TDOMNode):
  1272. TDOMNode;
  1273. begin
  1274. InsertBefore(NewChild, OldChild);
  1275. if Assigned(OldChild) and (OldChild <> NewChild) then
  1276. RemoveChild(OldChild);
  1277. Result := OldChild;
  1278. end;
  1279. function TDOMNode_WithChildren.DetachChild(OldChild: TDOMNode): TDOMNode;
  1280. var
  1281. prev, next: TDOMNode;
  1282. begin
  1283. Changing;
  1284. if OldChild.ParentNode <> Self then
  1285. raise EDOMNotFound.Create('NodeWC.RemoveChild');
  1286. Inc(FOwnerDocument.FRevision); // invalidate nodelists
  1287. if OldChild = FFirstChild then
  1288. begin
  1289. Exclude(OldChild.FFlags, nfFirstChild);
  1290. FFirstChild := FFirstChild.FNextSibling;
  1291. if Assigned(FFirstChild) then
  1292. begin
  1293. { maintain lastChild }
  1294. Include(FFirstChild.FFlags, nfFirstChild);
  1295. FFirstChild.FPreviousSibling := OldChild.FPreviousSibling;
  1296. end;
  1297. end
  1298. else
  1299. begin
  1300. prev := OldChild.FPreviousSibling;
  1301. next := OldChild.FNextSibling;
  1302. prev.FNextSibling := next;
  1303. if Assigned(next) then { removing node in the middle }
  1304. next.FPreviousSibling := prev
  1305. else { removing the last child }
  1306. FFirstChild.FPreviousSibling := prev;
  1307. end;
  1308. // Make sure removed child does not contain references to nowhere
  1309. OldChild.FPreviousSibling := nil;
  1310. OldChild.FNextSibling := nil;
  1311. OldChild.FParentNode := nil;
  1312. Result := OldChild;
  1313. end;
  1314. procedure TDOMNode_WithChildren.InternalAppend(NewChild: TDOMNode);
  1315. var
  1316. Tmp: TDOMNode;
  1317. begin
  1318. if Assigned(FFirstChild) then
  1319. begin
  1320. Tmp := FFirstChild.FPreviousSibling; { our last child }
  1321. Tmp.FNextSibling := NewChild;
  1322. NewChild.FPreviousSibling := Tmp;
  1323. end
  1324. else
  1325. begin
  1326. FFirstChild := NewChild;
  1327. Include(NewChild.FFlags, nfFirstChild);
  1328. end;
  1329. FFirstChild.FPreviousSibling := NewChild; { becomes our last child }
  1330. NewChild.FParentNode := Self;
  1331. end;
  1332. function TDOMNode_WithChildren.HasChildNodes: Boolean;
  1333. begin
  1334. Result := Assigned(FFirstChild);
  1335. end;
  1336. function TDOMNode_WithChildren.FindNode(const ANodeName: DOMString): TDOMNode;
  1337. begin
  1338. Result := FFirstChild;
  1339. while Assigned(Result) do
  1340. begin
  1341. if Result.CompareName(ANodeName)=0 then
  1342. Exit;
  1343. Result := Result.NextSibling;
  1344. end;
  1345. end;
  1346. procedure TDOMNode_WithChildren.CloneChildren(ACopy: TDOMNode;
  1347. ACloneOwner: TDOMDocument);
  1348. var
  1349. node: TDOMNode;
  1350. begin
  1351. node := FirstChild;
  1352. while Assigned(node) do
  1353. begin
  1354. TDOMNode_WithChildren(ACopy).InternalAppend(node.CloneNode(True, ACloneOwner));
  1355. node := node.NextSibling;
  1356. end;
  1357. end;
  1358. procedure TDOMNode_WithChildren.FreeChildren;
  1359. var
  1360. child, next: TDOMNode;
  1361. begin
  1362. child := FFirstChild;
  1363. while Assigned(child) do
  1364. begin
  1365. next := child.NextSibling;
  1366. child.FParentNode := nil;
  1367. child.Destroy; // we know it's not nil, so save a call
  1368. child := next;
  1369. end;
  1370. FFirstChild := nil;
  1371. end;
  1372. function TDOMNode_WithChildren.GetTextContent: DOMString;
  1373. var
  1374. child: TDOMNode;
  1375. begin
  1376. Result := '';
  1377. child := FFirstChild;
  1378. // TODO: probably very slow, optimization needed
  1379. while Assigned(child) do
  1380. begin
  1381. case child.NodeType of
  1382. TEXT_NODE: if not (nfIgnorableWS in child.FFlags) then
  1383. Result := Result + TDOMText(child).Data;
  1384. COMMENT_NODE, PROCESSING_INSTRUCTION_NODE: ; // ignored
  1385. else
  1386. Result := Result + child.TextContent;
  1387. end;
  1388. child := child.NextSibling;
  1389. end;
  1390. end;
  1391. procedure TDOMNode_WithChildren.SetTextContent(const AValue: DOMString);
  1392. begin
  1393. Changing;
  1394. while Assigned(FFirstChild) do
  1395. DetachChild(FFirstChild);
  1396. if AValue <> '' then
  1397. AppendChild(FOwnerDocument.CreateTextNode(AValue));
  1398. end;
  1399. // -------------------------------------------------------
  1400. // NodeList
  1401. // -------------------------------------------------------
  1402. constructor TDOMNodeList.Create(ANode: TDOMNode);
  1403. begin
  1404. inherited Create;
  1405. FNode := ANode;
  1406. FRevision := ANode.GetRevision-1; // force BuildList at first access
  1407. FList := TFPList.Create;
  1408. end;
  1409. destructor TDOMNodeList.Destroy;
  1410. begin
  1411. if (FNode is TDOMNode_WithChildren) and
  1412. (TDOMNode_WithChildren(FNode).FChildNodes = Self) then
  1413. TDOMNode_WithChildren(FNode).FChildNodes := nil
  1414. else
  1415. FNode.FOwnerDocument.NodeListDestroyed(Self);
  1416. FList.Free;
  1417. inherited Destroy;
  1418. end;
  1419. function TDOMNodeList.NodeFilter(aNode: TDOMNode): TFilterResult;
  1420. begin
  1421. // accept all nodes but don't allow recursion
  1422. Result := frNorecurseTrue;
  1423. end;
  1424. procedure TDOMNodeList.BuildList;
  1425. var
  1426. current, next: TDOMNode;
  1427. res: TFilterResult;
  1428. begin
  1429. FList.Clear;
  1430. FRevision := FNode.GetRevision; // refresh
  1431. current := FNode.FirstChild;
  1432. while Assigned(current) do
  1433. begin
  1434. res := NodeFilter(current);
  1435. if res in [frTrue, frNorecurseTrue] then
  1436. FList.Add(current);
  1437. next := nil;
  1438. if res in [frTrue, frFalse] then
  1439. next := current.FirstChild;
  1440. if next = nil then
  1441. begin
  1442. while current <> FNode do
  1443. begin
  1444. next := current.NextSibling;
  1445. if Assigned(next) then
  1446. Break;
  1447. current := current.ParentNode;
  1448. end;
  1449. end;
  1450. current := next;
  1451. end;
  1452. end;
  1453. function TDOMNodeList.GetCount: LongWord;
  1454. begin
  1455. if FRevision <> FNode.GetRevision then
  1456. BuildList;
  1457. Result := FList.Count;
  1458. end;
  1459. function TDOMNodeList.GetItem(index: LongWord): TDOMNode;
  1460. begin
  1461. if FRevision <> FNode.GetRevision then
  1462. BuildList;
  1463. if index < LongWord(FList.Count) then
  1464. Result := TDOMNode(FList.List^[index])
  1465. else
  1466. Result := nil;
  1467. end;
  1468. { TDOMElementList }
  1469. constructor TDOMElementList.Create(ANode: TDOMNode; const AFilter: DOMString);
  1470. begin
  1471. inherited Create(ANode);
  1472. filter := AFilter;
  1473. UseFilter := filter <> '*';
  1474. end;
  1475. constructor TDOMElementList.Create(ANode: TDOMNode; const nsURI, localName: DOMString);
  1476. begin
  1477. inherited Create(ANode);
  1478. localNameFilter := localName;
  1479. FMatchNS := True;
  1480. FMatchAnyNS := (nsURI = '*');
  1481. if not FMatchAnyNS then
  1482. FNSIndexFilter := ANode.FOwnerDocument.IndexOfNS(nsURI);
  1483. UseFilter := (localName <> '*');
  1484. end;
  1485. function TDOMElementList.NodeFilter(aNode: TDOMNode): TFilterResult;
  1486. var
  1487. I, L: Integer;
  1488. begin
  1489. Result := frFalse;
  1490. if aNode.NodeType = ELEMENT_NODE then with TDOMElement(aNode) do
  1491. begin
  1492. if FMatchNS then
  1493. begin
  1494. if (FMatchAnyNS or (FNSI.NSIndex = Word(FNSIndexFilter))) then
  1495. begin
  1496. I := FNSI.PrefixLen;
  1497. L := system.Length(FNSI.QName^.Key);
  1498. if (not UseFilter or ((L-I = system.Length(localNameFilter)) and
  1499. CompareMem(@FNSI.QName^.Key[I+1], DOMPChar(localNameFilter), system.Length(localNameFilter)*sizeof(WideChar)))) then
  1500. Result := frTrue;
  1501. end;
  1502. end
  1503. else if (not UseFilter or (TagName = Filter)) then
  1504. Result := frTrue;
  1505. end;
  1506. end;
  1507. // -------------------------------------------------------
  1508. // NamedNodeMap
  1509. // -------------------------------------------------------
  1510. constructor TDOMNamedNodeMap.Create(AOwner: TDOMNode);
  1511. begin
  1512. inherited Create;
  1513. FOwner := AOwner;
  1514. FList := TFPList.Create;
  1515. end;
  1516. destructor TDOMNamedNodeMap.Destroy;
  1517. var
  1518. I: Integer;
  1519. begin
  1520. for I := FList.Count-1 downto 0 do
  1521. TDOMNode(FList.List^[I]).Free;
  1522. FList.Free;
  1523. inherited Destroy;
  1524. end;
  1525. function TDOMNamedNodeMap.GetItem(index: LongWord): TDOMNode;
  1526. begin
  1527. if index < LongWord(FList.Count) then
  1528. Result := TDOMNode(FList.List^[index])
  1529. else
  1530. Result := nil;
  1531. end;
  1532. function TDOMNamedNodeMap.GetLength: LongWord;
  1533. begin
  1534. Result := FList.Count;
  1535. end;
  1536. function TDOMNamedNodeMap.Find(const name: DOMString; out Index: LongWord): Boolean;
  1537. var
  1538. L, H, I, C: Integer;
  1539. begin
  1540. Result := False;
  1541. L := 0;
  1542. H := FList.Count - 1;
  1543. while L <= H do
  1544. begin
  1545. I := (L + H) shr 1;
  1546. C := TDOMNode(FList.List^[I]).CompareName(name);
  1547. if C > 0 then L := I + 1 else
  1548. begin
  1549. H := I - 1;
  1550. if C = 0 then
  1551. begin
  1552. Result := True;
  1553. L := I;
  1554. end;
  1555. end;
  1556. end;
  1557. Index := L;
  1558. end;
  1559. function TDOMNamedNodeMap.GetNamedItem(const name: DOMString): TDOMNode;
  1560. var
  1561. i: Cardinal;
  1562. begin
  1563. if Find(name, i) then
  1564. Result := TDOMNode(FList.List^[i])
  1565. else
  1566. Result := nil;
  1567. end;
  1568. // Note: this *may* raise NOT_SUPPORTED_ERR if the document is e.g. HTML.
  1569. // This isn't checked now.
  1570. function TDOMNamedNodeMap.GetNamedItemNS(const namespaceURI, localName: DOMString): TDOMNode;
  1571. begin
  1572. Result := nil;
  1573. end;
  1574. function TDOMNamedNodeMap.ValidateInsert(arg: TDOMNode): Integer;
  1575. begin
  1576. Result := 0;
  1577. if nfReadOnly in FOwner.FFlags then
  1578. Result := NO_MODIFICATION_ALLOWED_ERR
  1579. else if arg.FOwnerDocument <> FOwner.FOwnerDocument then
  1580. Result := WRONG_DOCUMENT_ERR;
  1581. { Note: Since Entity and Notation maps are always read-only, and the AttributeMap
  1582. overrides this method and does its own check for correct arg.NodeType, there's
  1583. no point in checking NodeType here. }
  1584. end;
  1585. function TDOMNamedNodeMap.SetNamedItem(arg: TDOMNode): TDOMNode;
  1586. var
  1587. i: Cardinal;
  1588. Exists: Boolean;
  1589. res: Integer;
  1590. begin
  1591. res := ValidateInsert(arg);
  1592. if res <> 0 then
  1593. raise EDOMError.Create(res, 'NamedNodeMap.SetNamedItem');
  1594. Exists := Find(arg.NodeName, i);
  1595. if Exists then
  1596. begin
  1597. Result := TDOMNode(FList.List^[i]);
  1598. FList.List^[i] := arg;
  1599. exit;
  1600. end;
  1601. FList.Insert(i, arg);
  1602. Result := nil;
  1603. end;
  1604. function TDOMNamedNodeMap.SetNamedItemNS(arg: TDOMNode): TDOMNode;
  1605. begin
  1606. { Since the map contains only namespaceless nodes (all having empty
  1607. localName and namespaceURI properties), a namespaced arg won't match
  1608. any of them. Therefore, add it using nodeName as key.
  1609. Note: a namespaceless arg is another story, as it will match *any* node
  1610. in the map. This can be considered as a flaw in specs. }
  1611. Result := SetNamedItem(arg);
  1612. end;
  1613. function TDOMNamedNodeMap.Delete(index: LongWord): TDOMNode;
  1614. begin
  1615. Result := TDOMNode(FList.List^[index]);
  1616. FList.Delete(index);
  1617. end;
  1618. function TDOMNamedNodeMap.InternalRemove(const name: DOMString): TDOMNode;
  1619. var
  1620. i: Cardinal;
  1621. begin
  1622. if Find(name, i) then
  1623. Result := Delete(I)
  1624. else
  1625. Result := nil;
  1626. end;
  1627. function TDOMNamedNodeMap.RemoveNamedItem(const name: DOMString): TDOMNode;
  1628. begin
  1629. if nfReadOnly in FOwner.FFlags then
  1630. raise EDOMError.Create(NO_MODIFICATION_ALLOWED_ERR, 'NamedNodeMap.RemoveNamedItem');
  1631. Result := InternalRemove(name);
  1632. if Result = nil then
  1633. raise EDOMNotFound.Create('NamedNodeMap.RemoveNamedItem');
  1634. end;
  1635. function TDOMNamedNodeMap.RemoveNamedItemNS(const namespaceURI, localName: DOMString): TDOMNode;
  1636. begin
  1637. // see comments to SetNamedItemNS. Related tests are written clever enough
  1638. // in the sense they don't expect NO_MODIFICATION_ERR in first place.
  1639. raise EDOMNotFound.Create('NamedNodeMap.RemoveNamedItemNS');
  1640. Result := nil;
  1641. end;
  1642. { TAttributeMap }
  1643. function TAttributeMap.Delete(index: LongWord): TDOMNode;
  1644. begin
  1645. Result := inherited Delete(index);
  1646. if Assigned(Result) then
  1647. begin
  1648. Result.FParentNode := nil;
  1649. if Assigned(TDOMAttr(Result).FNSI.QName) then
  1650. RestoreDefault(TDOMAttr(Result).FNSI.QName);
  1651. end;
  1652. end;
  1653. function TAttributeMap.ValidateInsert(arg: TDOMNode): Integer;
  1654. begin
  1655. Result := inherited ValidateInsert(arg);
  1656. if Result = 0 then
  1657. begin
  1658. if arg.NodeType <> ATTRIBUTE_NODE then
  1659. Result := HIERARCHY_REQUEST_ERR
  1660. else if Assigned(arg.FParentNode) and (arg.FParentNode <> FOwner) then
  1661. Result := INUSE_ATTRIBUTE_ERR;
  1662. end;
  1663. end;
  1664. procedure TAttributeMap.RestoreDefault(aName: PHashItem);
  1665. var
  1666. eldef: TElementDecl;
  1667. attrdef: TAttributeDef;
  1668. begin
  1669. if not Assigned(TDOMElement(FOwner).FNSI.QName) then // safeguard
  1670. Exit;
  1671. eldef := TElementDecl(TDOMElement(FOwner).FNSI.QName^.Data);
  1672. if Assigned(eldef) then
  1673. begin
  1674. // TODO: can be avoided by linking attributes directly to their defs
  1675. attrdef := eldef.GetAttrDef(aName);
  1676. if Assigned(attrdef) and (attrdef.Default in [adDefault, adFixed]) then
  1677. TDOMElement(FOwner).RestoreDefaultAttr(attrdef);
  1678. end;
  1679. end;
  1680. // Since list is kept sorted by nodeName, we must use linear search here.
  1681. // This routine is not called while parsing, so parsing speed is not lowered.
  1682. function TAttributeMap.FindNS(nsIndex: Integer; const aLocalName: DOMString;
  1683. out Index: LongWord): Boolean;
  1684. var
  1685. I: Integer;
  1686. P: DOMPChar;
  1687. begin
  1688. for I := 0 to FList.Count-1 do
  1689. begin
  1690. with TDOMAttr(FList.List^[I]) do
  1691. begin
  1692. if nsIndex = FNSI.NSIndex then
  1693. begin
  1694. P := DOMPChar(FNSI.QName^.Key);
  1695. if FNSI.PrefixLen > 1 then
  1696. Inc(P, FNSI.PrefixLen);
  1697. if CompareDOMStrings(DOMPChar(aLocalName), P, System.Length(aLocalName), System.Length(FNSI.QName^.Key) - FNSI.PrefixLen) = 0 then
  1698. begin
  1699. Index := I;
  1700. Result := True;
  1701. Exit;
  1702. end;
  1703. end;
  1704. end;
  1705. end;
  1706. Result := False;
  1707. end;
  1708. function TAttributeMap.InternalRemoveNS(const nsURI, aLocalName: DOMString): TDOMNode;
  1709. var
  1710. i: Cardinal;
  1711. nsIndex: Integer;
  1712. begin
  1713. Result := nil;
  1714. nsIndex := FOwner.FOwnerDocument.IndexOfNS(nsURI);
  1715. if (nsIndex >= 0) and FindNS(nsIndex, aLocalName, i) then
  1716. Result := Delete(I);
  1717. end;
  1718. function TAttributeMap.getNamedItemNS(const namespaceURI, localName: DOMString): TDOMNode;
  1719. var
  1720. nsIndex: Integer;
  1721. i: LongWord;
  1722. begin
  1723. nsIndex := FOwner.FOwnerDocument.IndexOfNS(namespaceURI);
  1724. if (nsIndex >= 0) and FindNS(nsIndex, localName, i) then
  1725. Result := TDOMNode(FList.List^[i])
  1726. else
  1727. Result := nil;
  1728. end;
  1729. function TAttributeMap.setNamedItem(arg: TDOMNode): TDOMNode;
  1730. begin
  1731. Result := inherited setNamedItem(arg);
  1732. if Assigned(Result) then
  1733. Result.FParentNode := nil;
  1734. arg.FParentNode := FOwner;
  1735. end;
  1736. function TAttributeMap.setNamedItemNS(arg: TDOMNode): TDOMNode;
  1737. var
  1738. i: LongWord;
  1739. res: Integer;
  1740. Exists: Boolean;
  1741. begin
  1742. res := ValidateInsert(arg);
  1743. if res <> 0 then
  1744. raise EDOMError.Create(res, 'NamedNodeMap.SetNamedItemNS');
  1745. Result := nil;
  1746. with TDOMAttr(arg) do
  1747. begin
  1748. // calling LocalName is no good... but it is done once
  1749. if FindNS(FNSI.NSIndex, localName, i) then
  1750. begin
  1751. Result := TDOMNode(FList.List^[i]);
  1752. FList.Delete(i);
  1753. end;
  1754. // Do a non-namespace search in order to keep the list sorted on nodeName
  1755. Exists := Find(FNSI.QName^.Key, i);
  1756. if Exists and (Result = nil) then // case when arg has no namespace
  1757. begin
  1758. Result := TDOMNode(FList.List^[i]);
  1759. FList.List^[i] := arg;
  1760. end
  1761. else
  1762. FList.Insert(i, arg);
  1763. end;
  1764. if Assigned(Result) then
  1765. Result.FParentNode := nil;
  1766. arg.FParentNode := FOwner;
  1767. end;
  1768. function TAttributeMap.removeNamedItemNS(const namespaceURI,
  1769. localName: DOMString): TDOMNode;
  1770. begin
  1771. if nfReadOnly in FOwner.FFlags then
  1772. raise EDOMError.Create(NO_MODIFICATION_ALLOWED_ERR, 'NamedNodeMap.RemoveNamedItemNS');
  1773. Result := InternalRemoveNS(namespaceURI, localName);
  1774. if Result = nil then
  1775. raise EDOMNotFound.Create('NamedNodeMap.RemoveNamedItemNS');
  1776. end;
  1777. // -------------------------------------------------------
  1778. // CharacterData
  1779. // -------------------------------------------------------
  1780. function TDOMCharacterData.GetLength: LongWord;
  1781. begin
  1782. Result := system.Length(FNodeValue);
  1783. end;
  1784. function TDOMCharacterData.GetNodeValue: DOMString;
  1785. begin
  1786. Result := FNodeValue;
  1787. end;
  1788. procedure TDOMCharacterData.SetNodeValue(const AValue: DOMString);
  1789. begin
  1790. Changing;
  1791. FNodeValue := AValue;
  1792. end;
  1793. function TDOMCharacterData.SubstringData(offset, count: LongWord): DOMString;
  1794. begin
  1795. if offset > Length then
  1796. raise EDOMIndexSize.Create('CharacterData.SubstringData');
  1797. Result := Copy(FNodeValue, offset + 1, count);
  1798. end;
  1799. procedure TDOMCharacterData.AppendData(const arg: DOMString);
  1800. begin
  1801. Changing;
  1802. FNodeValue := FNodeValue + arg;
  1803. end;
  1804. procedure TDOMCharacterData.InsertData(offset: LongWord; const arg: DOMString);
  1805. begin
  1806. Changing;
  1807. if offset > Length then
  1808. raise EDOMIndexSize.Create('CharacterData.InsertData');
  1809. Insert(arg, FNodeValue, offset+1);
  1810. end;
  1811. procedure TDOMCharacterData.DeleteData(offset, count: LongWord);
  1812. begin
  1813. Changing;
  1814. if offset > Length then
  1815. raise EDOMIndexSize.Create('CharacterData.DeleteData');
  1816. Delete(FNodeValue, offset+1, count);
  1817. end;
  1818. procedure TDOMCharacterData.ReplaceData(offset, count: LongWord; const arg: DOMString);
  1819. begin
  1820. DeleteData(offset, count);
  1821. InsertData(offset, arg);
  1822. end;
  1823. // -------------------------------------------------------
  1824. // DocumentFragmet
  1825. // -------------------------------------------------------
  1826. function TDOMDocumentFragment.GetNodeType: Integer;
  1827. begin
  1828. Result := DOCUMENT_FRAGMENT_NODE;
  1829. end;
  1830. function TDOMDocumentFragment.GetNodeName: DOMString;
  1831. begin
  1832. Result := '#document-fragment';
  1833. end;
  1834. function TDOMDocumentFragment.CloneNode(deep: Boolean; aCloneOwner: TDOMDocument): TDOMNode;
  1835. begin
  1836. Result := aCloneOwner.CreateDocumentFragment;
  1837. if deep then
  1838. CloneChildren(Result, aCloneOwner);
  1839. end;
  1840. // -------------------------------------------------------
  1841. // Top-level node
  1842. // -------------------------------------------------------
  1843. function TDOMNode_TopLevel.GetXMLVersion: DOMString;
  1844. begin
  1845. Result := xmlVersionStr[FXMLVersion];
  1846. end;
  1847. // -------------------------------------------------------
  1848. // DOMImplementation
  1849. // -------------------------------------------------------
  1850. { Non-negative return value is Pos(':', QName), negative is DOM error code. }
  1851. function CheckQName(const QName: DOMString): Integer;
  1852. var
  1853. I, L: Integer;
  1854. begin
  1855. if not IsXmlName(QName) then
  1856. begin
  1857. Result := -INVALID_CHARACTER_ERR;
  1858. Exit;
  1859. end;
  1860. L := Length(QName);
  1861. Result := Pos(WideChar(':'), QName);
  1862. if Result > 0 then
  1863. begin
  1864. for I := Result+1 to L-1 do // check for second colon (Use IndexWord?)
  1865. if QName[I] = ':' then
  1866. begin
  1867. Result := -NAMESPACE_ERR;
  1868. Exit;
  1869. end;
  1870. // Name validity has already been checked by IsXmlName() call above.
  1871. // So just check that colon isn't first or last char, and that it is follwed by NameStartChar.
  1872. if ((Result = 1) or (Result = L) or not IsXmlName(@QName[Result+1], 1)) then
  1873. begin
  1874. Result := -NAMESPACE_ERR;
  1875. Exit;
  1876. end;
  1877. end;
  1878. end;
  1879. function TDOMImplementation.HasFeature(const feature, version: DOMString):
  1880. Boolean;
  1881. var
  1882. s: string;
  1883. begin
  1884. s := feature; // force Ansi, features do not contain non-ASCII chars
  1885. Result := (SameText(s, 'XML') and ((version = '') or (version = '1.0') or (version = '2.0'))) or
  1886. (SameText(s, 'Core') and ((version = '') or (version = '2.0')));
  1887. end;
  1888. function TDOMImplementation.CreateDocumentType(const QualifiedName, PublicID,
  1889. SystemID: DOMString): TDOMDocumentType;
  1890. var
  1891. res: Integer;
  1892. model: TDTDModel;
  1893. begin
  1894. res := CheckQName(QualifiedName);
  1895. if res < 0 then
  1896. raise EDOMError.Create(-res, 'Implementation.CreateDocumentType');
  1897. model := TDTDModel.Create(nil); // !!nowhere to get nametable from at this time
  1898. model.FName := QualifiedName;
  1899. // DOM does not restrict PublicID without SystemID (unlike XML spec)
  1900. model.FPublicID := PublicID;
  1901. model.FSystemID := SystemID;
  1902. Result := TDOMDocumentType.Create(nil, model);
  1903. model.Release; // now Result remains a sole owner of model
  1904. end;
  1905. function TDOMImplementation.CreateDocument(const NamespaceURI,
  1906. QualifiedName: DOMString; doctype: TDOMDocumentType): TDOMDocument;
  1907. var
  1908. Root: TDOMNode;
  1909. begin
  1910. if Assigned(doctype) and Assigned(doctype.OwnerDocument) then
  1911. raise EDOMWrongDocument.Create('Implementation.CreateDocument');
  1912. Result := TXMLDocument.Create;
  1913. Result.FImplementation := Self;
  1914. try
  1915. if Assigned(doctype) then
  1916. begin
  1917. Doctype.FOwnerDocument := Result;
  1918. Result.AppendChild(doctype);
  1919. end;
  1920. Root := Result.CreateElementNS(NamespaceURI, QualifiedName);
  1921. Result.AppendChild(Root);
  1922. except
  1923. Result.Free;
  1924. raise;
  1925. end;
  1926. end;
  1927. // -------------------------------------------------------
  1928. // Document
  1929. // -------------------------------------------------------
  1930. constructor TDOMDocument.Create;
  1931. begin
  1932. inherited Create(nil);
  1933. FOwnerDocument := Self;
  1934. FMaxPoolSize := (TDOMEntity.InstanceSize + sizeof(Pointer)-1) and not (sizeof(Pointer)-1) + sizeof(Pointer);
  1935. FPools := AllocMem(FMaxPoolSize);
  1936. FNames := THashTable.Create(256, True);
  1937. SetLength(FNamespaces, 3);
  1938. // Namespace #0 should always be an empty string
  1939. FNamespaces[1] := stduri_xml;
  1940. FNamespaces[2] := stduri_xmlns;
  1941. FEmptyNode := TDOMElement.Create(Self);
  1942. FStdUri_xml := FNames.FindOrAdd(stduri_xml);
  1943. FStdUri_xmlns := FNames.FindOrAdd(stduri_xmlns);
  1944. end;
  1945. destructor TDOMDocument.Destroy;
  1946. var
  1947. i: Integer;
  1948. begin
  1949. Include(FFlags, nfDestroying);
  1950. FreeAndNil(FIDList); // set to nil before starting destroying children
  1951. FNodeLists.Free;
  1952. FEmptyNode.Free;
  1953. inherited Destroy;
  1954. for i := 0 to (FMaxPoolSize div sizeof(TNodePool))-1 do
  1955. FPools^[i].Free;
  1956. FreeMem(FPools);
  1957. FNames.Free; // free the nametable after inherited has destroyed the children
  1958. // (because children reference the nametable)
  1959. end;
  1960. function TDOMDocument.CloneNode(deep: Boolean): TDOMNode;
  1961. type
  1962. TDOMDocumentClass = class of TDOMDocument;
  1963. var
  1964. Clone: TDOMDocument;
  1965. node, doctypenode: TDOMNode;
  1966. begin
  1967. Clone := TDOMDocumentClass(ClassType).Create;
  1968. Clone.FInputEncoding := FInputEncoding;
  1969. Clone.FXMLEncoding := FXMLEncoding;
  1970. Clone.FXMLVersion := FXMLVersion;
  1971. Clone.FXMLStandalone := FXMLStandalone;
  1972. Clone.FURI := FURI;
  1973. if deep then
  1974. begin
  1975. node := FirstChild;
  1976. doctypenode := DocType;
  1977. while Assigned(node) do
  1978. begin
  1979. {TODO: now just skip doctype, a better solution is to be found.}
  1980. if node <> doctypenode then
  1981. Clone.InternalAppend(node.CloneNode(True, Clone));
  1982. node := node.NextSibling;
  1983. end;
  1984. end;
  1985. Result := Clone;
  1986. end;
  1987. function TDOMDocument.Alloc(AClass: TDOMNodeClass): TDOMNode;
  1988. var
  1989. pp: TNodePool;
  1990. size: Integer;
  1991. begin
  1992. if nfDestroying in FFlags then
  1993. raise EDOMError.Create(INVALID_ACCESS_ERR, 'Attempt to allocate node memory while destroying');
  1994. size := (AClass.InstanceSize + sizeof(Pointer)-1) and not (sizeof(Pointer)-1);
  1995. if size > FMaxPoolSize then
  1996. begin
  1997. Result := TDOMNode(AClass.NewInstance);
  1998. Exit;
  1999. end;
  2000. pp := FPools^[size div sizeof(TNodePool)];
  2001. if pp = nil then
  2002. begin
  2003. pp := TNodePool.Create(size);
  2004. FPools^[size div sizeof(TNodePool)] := pp;
  2005. end;
  2006. Result := pp.AllocNode(AClass);
  2007. end;
  2008. // This shouldn't be called if document has no IDs,
  2009. // or when it is being destroyed
  2010. // TODO: This could be much faster if removing ID happens
  2011. // upon modification of corresponding attribute value.
  2012. procedure TDOMDocument.RemoveID(Elem: TDOMElement);
  2013. begin
  2014. FIDList.RemoveData(Elem);
  2015. end;
  2016. function TDOMDocument.GetNodeType: Integer;
  2017. begin
  2018. Result := DOCUMENT_NODE;
  2019. end;
  2020. function TDOMDocument.GetNodeName: DOMString;
  2021. begin
  2022. Result := '#document';
  2023. end;
  2024. function TDOMDocument.GetTextContent: DOMString;
  2025. begin
  2026. Result := '';
  2027. end;
  2028. procedure TDOMDocument.SetTextContent(const value: DOMString);
  2029. begin
  2030. // Document ignores setting TextContent
  2031. end;
  2032. function TDOMDocument.GetOwnerDocument: TDOMDocument;
  2033. begin
  2034. Result := nil;
  2035. end;
  2036. function TDOMDocument.InsertBefore(NewChild, RefChild: TDOMNode): TDOMNode;
  2037. var
  2038. nType: Integer;
  2039. begin
  2040. nType := NewChild.NodeType;
  2041. if ((nType = ELEMENT_NODE) and Assigned(DocumentElement)) or
  2042. ((nType = DOCUMENT_TYPE_NODE) and Assigned(DocType)) then
  2043. raise EDOMHierarchyRequest.Create('Document.InsertBefore');
  2044. Result := inherited InsertBefore(NewChild, RefChild);
  2045. end;
  2046. function TDOMDocument.ReplaceChild(NewChild, OldChild: TDOMNode): TDOMNode;
  2047. var
  2048. nType: Integer;
  2049. begin
  2050. nType := NewChild.NodeType;
  2051. if ((nType = ELEMENT_NODE) and (OldChild = DocumentElement)) or // root can be replaced by another element
  2052. ((nType = DOCUMENT_TYPE_NODE) and (OldChild = DocType)) then // and so can be DTD
  2053. begin
  2054. inherited InsertBefore(NewChild, OldChild);
  2055. Result := OldChild;
  2056. if OldChild <> NewChild then
  2057. RemoveChild(OldChild);
  2058. end
  2059. else
  2060. Result := inherited ReplaceChild(NewChild, OldChild);
  2061. end;
  2062. function TDOMDocument.GetDocumentElement: TDOMElement;
  2063. var
  2064. node: TDOMNode;
  2065. begin
  2066. node := FFirstChild;
  2067. while Assigned(node) and (node.NodeType <> ELEMENT_NODE) do
  2068. node := node.NextSibling;
  2069. Result := TDOMElement(node);
  2070. end;
  2071. function TDOMDocument.GetDocType: TDOMDocumentType;
  2072. var
  2073. node: TDOMNode;
  2074. begin
  2075. node := FFirstChild;
  2076. while Assigned(node) and (node.NodeType <> DOCUMENT_TYPE_NODE) do
  2077. node := node.NextSibling;
  2078. Result := TDOMDocumentType(node);
  2079. end;
  2080. function TDOMDocument.CreateElement(const tagName: DOMString): TDOMElement;
  2081. begin
  2082. if not IsXmlName(tagName) then
  2083. raise EDOMError.Create(INVALID_CHARACTER_ERR, 'DOMDocument.CreateElement');
  2084. TDOMNode(Result) := Alloc(TDOMElement);
  2085. Result.Create(Self);
  2086. Result.FNSI.QName := FNames.FindOrAdd(DOMPChar(tagName), Length(tagName));
  2087. Result.AttachDefaultAttrs;
  2088. end;
  2089. function TDOMDocument.CreateElementBuf(Buf: DOMPChar; Length: Integer): TDOMElement;
  2090. begin
  2091. TDOMNode(Result) := Alloc(TDOMElement);
  2092. Result.Create(Self);
  2093. Result.FNSI.QName := FNames.FindOrAdd(Buf, Length);
  2094. end;
  2095. function TDOMDocument.CreateDocumentFragment: TDOMDocumentFragment;
  2096. begin
  2097. TDOMNode(Result) := Alloc(TDOMDocumentFragment);
  2098. Result.Create(Self);
  2099. end;
  2100. function TDOMDocument.CreateTextNode(const data: DOMString): TDOMText;
  2101. begin
  2102. TDOMNode(Result) := Alloc(TDOMText);
  2103. Result.Create(Self);
  2104. Result.FNodeValue := data;
  2105. end;
  2106. function TDOMDocument.CreateTextNodeBuf(Buf: DOMPChar; Length: Integer; IgnWS: Boolean): TDOMText;
  2107. begin
  2108. TDOMNode(Result) := Alloc(TDOMText);
  2109. Result.Create(Self);
  2110. SetString(Result.FNodeValue, Buf, Length);
  2111. if IgnWS then
  2112. Include(Result.FFlags, nfIgnorableWS);
  2113. end;
  2114. function TDOMDocument.CreateComment(const data: DOMString): TDOMComment;
  2115. begin
  2116. TDOMNode(Result) := Alloc(TDOMComment);
  2117. Result.Create(Self);
  2118. Result.FNodeValue := data;
  2119. end;
  2120. function TDOMDocument.CreateCommentBuf(Buf: DOMPChar; Length: Integer): TDOMComment;
  2121. begin
  2122. TDOMNode(Result) := Alloc(TDOMComment);
  2123. Result.Create(Self);
  2124. SetString(Result.FNodeValue, Buf, Length);
  2125. end;
  2126. function TDOMDocument.CreateCDATASection(const data: DOMString):
  2127. TDOMCDATASection;
  2128. begin
  2129. raise EDOMNotSupported.Create('DOMDocument.CreateCDATASection');
  2130. Result:=nil;
  2131. end;
  2132. function TDOMDocument.CreateProcessingInstruction(const target,
  2133. data: DOMString): TDOMProcessingInstruction;
  2134. begin
  2135. raise EDOMNotSupported.Create('DOMDocument.CreateProcessingInstruction');
  2136. Result:=nil;
  2137. end;
  2138. function TDOMDocument.CreateAttribute(const name: DOMString): TDOMAttr;
  2139. begin
  2140. if not IsXmlName(name) then
  2141. raise EDOMError.Create(INVALID_CHARACTER_ERR, 'DOMDocument.CreateAttribute');
  2142. TDOMNode(Result) := Alloc(TDOMAttr);
  2143. Result.Create(Self);
  2144. Result.FNSI.QName := FNames.FindOrAdd(DOMPChar(name), Length(name));
  2145. Include(Result.FFlags, nfSpecified);
  2146. end;
  2147. function TDOMDocument.CreateAttributeBuf(Buf: DOMPChar; Length: Integer): TDOMAttr;
  2148. begin
  2149. TDOMNode(Result) := Alloc(TDOMAttr);
  2150. Result.Create(Self);
  2151. Result.FNSI.QName := FNames.FindOrAdd(buf, Length);
  2152. Include(Result.FFlags, nfSpecified);
  2153. end;
  2154. function TDOMDocument.CreateEntityReference(const name: DOMString):
  2155. TDOMEntityReference;
  2156. begin
  2157. raise EDOMNotSupported.Create('DOMDocument.CreateEntityReference');
  2158. Result:=nil;
  2159. end;
  2160. function TDOMDocument.GetChildNodeList(aNode: TDOMNode): TDOMNodeList;
  2161. begin
  2162. if not (aNode is TDOMNode_WithChildren) then
  2163. aNode := FEmptyNode;
  2164. Result := TDOMNode_WithChildren(aNode).FChildNodes;
  2165. if Result = nil then
  2166. begin
  2167. Result := TDOMNodeList.Create(aNode);
  2168. TDOMNode_WithChildren(aNode).FChildNodes := Result;
  2169. end;
  2170. end;
  2171. function TDOMDocument.GetElementList(aNode: TDOMNode; const nsURI, aLocalName: DOMString;
  2172. UseNS: Boolean): TDOMNodeList;
  2173. var
  2174. L: Integer;
  2175. Key, P: DOMPChar;
  2176. Item: PHashItem;
  2177. begin
  2178. if FNodeLists = nil then
  2179. FNodeLists := THashTable.Create(32, True);
  2180. L := (sizeof(Pointer) div sizeof(WideChar)) + Length(aLocalName);
  2181. if UseNS then
  2182. Inc(L, Length(nsURI)+1);
  2183. GetMem(Key, L*sizeof(WideChar));
  2184. try
  2185. // compose the key for hashing
  2186. P := Key;
  2187. PPointer(P)^ := aNode;
  2188. Inc(PPointer(P));
  2189. Move(DOMPChar(aLocalName)^, P^, Length(aLocalName)*sizeof(WideChar));
  2190. if UseNS then
  2191. begin
  2192. Inc(P, Length(aLocalName));
  2193. P^ := #12; Inc(P); // separator -- diff ('foo','bar') from 'foobar'
  2194. Move(DOMPChar(nsURI)^, P^, Length(nsURI)*sizeof(WideChar));
  2195. end;
  2196. // try finding in the hashtable
  2197. Item := FNodeLists.FindOrAdd(Key, L);
  2198. Result := TDOMNodeList(Item^.Data);
  2199. if Result = nil then
  2200. begin
  2201. if UseNS then
  2202. Result := TDOMElementList.Create(aNode, nsURI, aLocalName)
  2203. else
  2204. Result := TDOMElementList.Create(aNode, aLocalName);
  2205. Item^.Data := Result;
  2206. end;
  2207. finally
  2208. FreeMem(Key);
  2209. end;
  2210. end;
  2211. function TDOMDocument.GetElementsByTagName(const tagname: DOMString): TDOMNodeList;
  2212. begin
  2213. Result := GetElementList(Self, '', tagname, False);
  2214. end;
  2215. function TDOMDocument.GetElementsByTagNameNS(const nsURI, aLocalName: DOMString): TDOMNodeList;
  2216. begin
  2217. Result := GetElementList(Self, nsURI, aLocalName, True);
  2218. end;
  2219. { This is linear hence slow. However:
  2220. - if user code frees each nodelist ASAP, there are only few items in the hashtable
  2221. - if user code does not free nodelists, this is not called at all.
  2222. }
  2223. procedure TDOMDocument.NodeListDestroyed(aList: TDOMNodeList);
  2224. begin
  2225. if (not (nfDestroying in FFlags)) and (FNodeLists <> nil) then
  2226. FNodeLists.RemoveData(aList);
  2227. end;
  2228. function TDOMDocument.ValidateQName(const nsUri, qName: DOMString;
  2229. out nsidx: PHashItem): Integer;
  2230. begin
  2231. nsidx := FNames.FindOrAdd(DOMPChar(nsUri), Length(nsUri));
  2232. Result := CheckQName(qName);
  2233. if Result >= 0 then
  2234. begin
  2235. // QName contains prefix, but no namespace
  2236. if ((nsUri = '') and (Result > 0)) or
  2237. // Bad usage of 'http://www.w3.org/2000/xmlns/'
  2238. ((((Length(QName) = 5) or (Result = 6)) and (Pos(DOMString('xmlns'), QName) = 1)) <> (nsIdx = FStdUri_xmlns)) or
  2239. // Bad usage of 'http://www.w3.org/XML/1998/namespace'
  2240. ((Result = 4) and (Pos(DOMString('xml'), QName) = 1) and (nsIdx <> FStdUri_xml)) then
  2241. Result := -NAMESPACE_ERR;
  2242. end;
  2243. end;
  2244. function TDOMDocument.CreateAttributeNS(const nsURI,
  2245. QualifiedName: DOMString): TDOMAttr;
  2246. var
  2247. PrefIdx: Integer;
  2248. nsidx: PHashItem;
  2249. begin
  2250. PrefIdx := ValidateQName(nsURI, QualifiedName, nsidx);
  2251. if PrefIdx < 0 then
  2252. raise EDOMError.Create(-PrefIdx, 'Document.CreateAttributeNS');
  2253. TDOMNode(Result) := Alloc(TDOMAttr);
  2254. Result.Create(Self);
  2255. Result.FNSI.QName := FNames.FindOrAdd(DOMPChar(QualifiedName), Length(QualifiedName));
  2256. Result.FNSI.NSIndex := Word(IndexOfNS(nsURI, True));
  2257. Result.FNSI.PrefixLen := Word(PrefIdx);
  2258. Include(Result.FFlags, nfLevel2);
  2259. Include(Result.FFlags, nfSpecified);
  2260. end;
  2261. function TDOMDocument.CreateElementNS(const nsURI,
  2262. QualifiedName: DOMString): TDOMElement;
  2263. begin
  2264. result:=CreateElementNS(nsURI, QualifiedName, TDOMElement);
  2265. end;
  2266. function TDOMDocument.CreateElementNS(const nsURI, QualifiedName: DOMString;
  2267. AClass: TDOMElementClass): TDOMElement; overload;
  2268. var
  2269. PrefIdx: Integer;
  2270. nsidx: PHashItem;
  2271. begin
  2272. PrefIdx := ValidateQName(nsURI, QualifiedName, nsidx);
  2273. if PrefIdx < 0 then
  2274. raise EDOMError.Create(-PrefIdx, 'Document.CreateElementNS');
  2275. TDOMNode(Result) := Alloc(AClass);
  2276. Result.Create(Self);
  2277. Result.FNSI.QName := FNames.FindOrAdd(DOMPChar(QualifiedName), Length(QualifiedName));
  2278. Result.FNSI.NSIndex := Word(IndexOfNS(nsURI, True));
  2279. Result.FNSI.PrefixLen := Word(PrefIdx);
  2280. Include(Result.FFlags, nfLevel2);
  2281. Result.AttachDefaultAttrs;
  2282. end;
  2283. function TDOMDocument.GetElementById(const ElementID: DOMString): TDOMElement;
  2284. begin
  2285. Result := nil;
  2286. if Assigned(FIDList) then
  2287. Result := TDOMElement(FIDList.Get(DOMPChar(ElementID), Length(ElementID)));
  2288. end;
  2289. function TDOMDocument.ImportNode(ImportedNode: TDOMNode;
  2290. Deep: Boolean): TDOMNode;
  2291. begin
  2292. Result := ImportedNode.CloneNode(Deep, Self);
  2293. end;
  2294. function TDOMDocument.IndexOfNS(const nsURI: DOMString; AddIfAbsent: Boolean): Integer;
  2295. var
  2296. I: Integer;
  2297. begin
  2298. // TODO: elaborate implementation
  2299. for I := 0 to Length(FNamespaces)-1 do
  2300. if FNamespaces[I] = nsURI then
  2301. begin
  2302. Result := I;
  2303. Exit;
  2304. end;
  2305. if AddIfAbsent then
  2306. begin
  2307. Result := Length(FNamespaces);
  2308. SetLength(FNamespaces, Result+1);
  2309. FNamespaces[Result] := nsURI;
  2310. end
  2311. else
  2312. Result := -1;
  2313. end;
  2314. procedure TDOMDocument.SetXMLVersion(const aValue: DOMString);
  2315. begin
  2316. raise EDOMNotSupported.Create('DOMDocument.SetXMLVersion');
  2317. end;
  2318. procedure TDOMDocument.SetXMLStandalone(aValue: Boolean);
  2319. begin
  2320. raise EDOMNotSupported.Create('DOMDocument.SetXMLStandalone');
  2321. end;
  2322. constructor TXMLDocument.Create;
  2323. begin
  2324. inherited Create;
  2325. FXMLVersion := xmlVersion10;
  2326. end;
  2327. function TXMLDocument.CreateCDATASection(const data: DOMString):
  2328. TDOMCDATASection;
  2329. begin
  2330. TDOMNode(Result) := Alloc(TDOMCDATASection);
  2331. Result.Create(Self);
  2332. Result.FNodeValue := data;
  2333. end;
  2334. function TXMLDocument.CreateProcessingInstruction(const target,
  2335. data: DOMString): TDOMProcessingInstruction;
  2336. begin
  2337. if not IsXmlName(target) then
  2338. raise EDOMError.Create(INVALID_CHARACTER_ERR, 'XMLDocument.CreateProcessingInstruction');
  2339. TDOMNode(Result) := Alloc(TDOMProcessingInstruction);
  2340. Result.Create(Self);
  2341. Result.FTarget := target;
  2342. Result.FNodeValue := data;
  2343. end;
  2344. function TXMLDocument.CreateEntityReference(const name: DOMString):
  2345. TDOMEntityReference;
  2346. var
  2347. dType: TDOMDocumentType;
  2348. ent: TDOMEntity;
  2349. begin
  2350. if not IsXmlName(name) then
  2351. raise EDOMError.Create(INVALID_CHARACTER_ERR, 'XMLDocument.CreateEntityReference');
  2352. TDOMNode(Result) := Alloc(TDOMEntityReference);
  2353. Result.Create(Self);
  2354. Result.FName := name;
  2355. dType := DocType;
  2356. if Assigned(dType) then
  2357. begin
  2358. TDOMNode(ent) := dType.Entities.GetNamedItem(name);
  2359. if Assigned(ent) then
  2360. ent.CloneChildren(Result, Self);
  2361. end;
  2362. Result.SetReadOnly(True);
  2363. end;
  2364. procedure TXMLDocument.SetXMLVersion(const aValue: DOMString);
  2365. begin
  2366. if aValue = '1.0' then
  2367. FXMLVersion := xmlVersion10
  2368. else if aValue = '1.1' then
  2369. FXMLVersion := xmlVersion11
  2370. else
  2371. raise EDOMNotSupported.Create('XMLDocument.SetXMLVersion');
  2372. end;
  2373. procedure TXMLDocument.SetXMLStandalone(aValue: Boolean);
  2374. begin
  2375. FXmlStandalone := aValue;
  2376. end;
  2377. { TDOMNode_NS }
  2378. function TDOMNode_NS.GetNodeName: DOMString;
  2379. begin
  2380. // Because FNSI.QName is not set by the TDOMNode itself, but is set by
  2381. // other classes/functions, it is necessary to check if FNSQ.QName is
  2382. // assigned.
  2383. if assigned(FNSI.QName) then
  2384. Result := FNSI.QName^.Key
  2385. else
  2386. Result := '';
  2387. end;
  2388. function TDOMNode_NS.GetLocalName: DOMString;
  2389. begin
  2390. if nfLevel2 in FFlags then
  2391. Result := Copy(FNSI.QName^.Key, FNSI.PrefixLen+1, MaxInt)
  2392. else
  2393. Result := '';
  2394. end;
  2395. function TDOMNode_NS.GetNamespaceURI: DOMString;
  2396. begin
  2397. Result := FOwnerDocument.FNamespaces[FNSI.NSIndex];
  2398. end;
  2399. function TDOMNode_NS.GetPrefix: DOMString;
  2400. begin
  2401. if FNSI.PrefixLen < 2 then
  2402. Result := ''
  2403. else
  2404. Result := Copy(FNSI.QName^.Key, 1, FNSI.PrefixLen-1);
  2405. end;
  2406. procedure TDOMNode_NS.SetPrefix(const Value: DOMString);
  2407. var
  2408. NewName: DOMString;
  2409. begin
  2410. Changing;
  2411. if not IsXmlName(Value) then
  2412. raise EDOMError.Create(INVALID_CHARACTER_ERR, 'Node.SetPrefix');
  2413. if (Pos(WideChar(':'), Value) > 0) or ((FNSI.NSIndex = 0) and (Value <> '')) or
  2414. ((Value = 'xml') and (FNSI.NSIndex <> 1)) or
  2415. ((ClassType = TDOMAttr) and // BAD!
  2416. ((Value = 'xmlns') and (FNSI.NSIndex <> 2)) or (FNSI.QName^.Key = 'xmlns')) then
  2417. raise EDOMNamespace.Create('Node.SetPrefix');
  2418. // TODO: rehash properly
  2419. NewName := Value + ':' + Copy(FNSI.QName^.Key, FNSI.PrefixLen+1, MaxInt);
  2420. FNSI.QName := FOwnerDocument.FNames.FindOrAdd(DOMPChar(NewName), Length(NewName));
  2421. FNSI.PrefixLen := Length(Value)+1;
  2422. end;
  2423. function TDOMNode_NS.CompareName(const AName: DOMString): Integer;
  2424. begin
  2425. Result := CompareDOMStrings(DOMPChar(AName), DOMPChar(NodeName), Length(AName), Length(NodeName));
  2426. end;
  2427. procedure TDOMNode_NS.SetNSI(const nsUri: DOMString; ColonPos: Integer);
  2428. begin
  2429. FNSI.NSIndex := FOwnerDocument.IndexOfNS(nsURI, True);
  2430. FNSI.PrefixLen := ColonPos;
  2431. Include(FFlags, nfLevel2);
  2432. end;
  2433. // -------------------------------------------------------
  2434. // Attr
  2435. // -------------------------------------------------------
  2436. function TDOMAttr.GetNodeType: Integer;
  2437. begin
  2438. Result := ATTRIBUTE_NODE;
  2439. end;
  2440. function TDOMAttr.GetParentNode: TDOMNode;
  2441. begin
  2442. Result := nil;
  2443. end;
  2444. destructor TDOMAttr.Destroy;
  2445. begin
  2446. if Assigned(FParentNode) and not (nfDestroying in FParentNode.FFlags) then
  2447. // TODO: This may raise NOT_FOUND_ERR in case something's really wrong
  2448. TDOMElement(FParentNode).RemoveAttributeNode(Self);
  2449. FParentNode := nil;
  2450. inherited Destroy;
  2451. end;
  2452. function TDOMAttr.CloneNode(deep: Boolean; ACloneOwner: TDOMDocument): TDOMNode;
  2453. begin
  2454. // Cloned attribute is always specified and carries its children
  2455. if nfLevel2 in FFlags then
  2456. Result := ACloneOwner.CreateAttributeNS(namespaceURI, NodeName)
  2457. else
  2458. Result := ACloneOwner.CreateAttribute(NodeName);
  2459. TDOMAttr(Result).FDataType := FDataType;
  2460. CloneChildren(Result, ACloneOwner);
  2461. end;
  2462. function TDOMAttr.GetNodeValue: DOMString;
  2463. begin
  2464. Result := GetTextContent;
  2465. if FDataType <> dtCdata then
  2466. NormalizeSpaces(Result);
  2467. end;
  2468. procedure TDOMAttr.SetNodeValue(const AValue: DOMString);
  2469. begin
  2470. SetTextContent(AValue);
  2471. Include(FFlags, nfSpecified);
  2472. end;
  2473. function TDOMAttr.GetSpecified: Boolean;
  2474. begin
  2475. Result := nfSpecified in FFlags;
  2476. end;
  2477. function TDOMAttr.GetIsID: Boolean;
  2478. begin
  2479. Result := FDataType = dtID;
  2480. end;
  2481. function TDOMAttr.GetOwnerElement: TDOMElement;
  2482. begin
  2483. Result := TDOMElement(FParentNode);
  2484. end;
  2485. // -------------------------------------------------------
  2486. // Element
  2487. // -------------------------------------------------------
  2488. function TDOMElement.GetNodeType: Integer;
  2489. begin
  2490. Result := ELEMENT_NODE;
  2491. end;
  2492. destructor TDOMElement.Destroy;
  2493. begin
  2494. Include(FFlags, nfDestroying);
  2495. if Assigned(FOwnerDocument.FIDList) then
  2496. FOwnerDocument.RemoveID(Self);
  2497. FAttributes.Free;
  2498. FAttributes := nil;
  2499. inherited Destroy;
  2500. end;
  2501. function TDOMElement.CloneNode(deep: Boolean; ACloneOwner: TDOMDocument): TDOMNode;
  2502. var
  2503. i: Integer;
  2504. Attr, AttrClone: TDOMAttr;
  2505. begin
  2506. if ACloneOwner <> FOwnerDocument then
  2507. begin
  2508. // Importing has to go the hard way...
  2509. if nfLevel2 in FFlags then
  2510. Result := ACloneOwner.CreateElementNS(NamespaceURI, NodeName)
  2511. else
  2512. Result := ACloneOwner.CreateElement(NodeName);
  2513. if Assigned(FAttributes) then
  2514. begin
  2515. for i := 0 to FAttributes.Length - 1 do
  2516. begin
  2517. Attr := TDOMAttr(FAttributes[i]);
  2518. // destroy defaulted attributes (if any), it is safe because caller had not seen them yet
  2519. if Attr.Specified then
  2520. TDOMElement(Result).SetAttributeNode(TDOMAttr(Attr.CloneNode(True, ACloneOwner))).Free;
  2521. end;
  2522. end;
  2523. end
  2524. else // Cloning may cheat a little bit.
  2525. begin
  2526. Result := FOwnerDocument.Alloc(TDOMElement);
  2527. TDOMElement(Result).Create(FOwnerDocument);
  2528. TDOMElement(Result).FNSI := FNSI;
  2529. if nfLevel2 in FFlags then
  2530. Include(Result.FFlags, nfLevel2);
  2531. if Assigned(FAttributes) then
  2532. begin
  2533. // clone all attributes, but preserve nfSpecified flag
  2534. for i := 0 to FAttributes.Length - 1 do
  2535. begin
  2536. Attr := TDOMAttr(FAttributes[i]);
  2537. AttrClone := TDOMAttr(Attr.CloneNode(True, ACloneOwner));
  2538. if not Attr.Specified then
  2539. Exclude(AttrClone.FFlags, nfSpecified);
  2540. TDOMElement(Result).SetAttributeNode(AttrClone);
  2541. end;
  2542. end;
  2543. end;
  2544. if deep then
  2545. CloneChildren(Result, ACloneOwner);
  2546. end;
  2547. procedure TDOMElement.AttachDefaultAttrs;
  2548. var
  2549. eldef: TElementDecl;
  2550. attrdef: TAttributeDef;
  2551. I: Integer;
  2552. begin
  2553. if not Assigned(FNSI.QName) then // safeguard
  2554. Exit;
  2555. eldef := TElementDecl(FNSI.QName^.Data);
  2556. if Assigned(eldef) and eldef.NeedsDefaultPass then
  2557. begin
  2558. for I := 0 to eldef.AttrDefCount-1 do
  2559. begin
  2560. attrdef := eldef.AttrDefs[I];
  2561. if attrdef.Default in [adDefault, adFixed] then
  2562. RestoreDefaultAttr(attrdef);
  2563. end;
  2564. end;
  2565. end;
  2566. function TDOMElement.InternalLookupPrefix(const nsURI: DOMString; Original: TDOMElement): DOMString;
  2567. var
  2568. I: Integer;
  2569. Attr: TDOMAttr;
  2570. begin
  2571. result := '';
  2572. if Self = nil then
  2573. Exit;
  2574. if (nfLevel2 in FFlags) and (namespaceURI = nsURI) and (FNSI.PrefixLen > 0) then
  2575. begin
  2576. Result := Prefix;
  2577. if Original.LookupNamespaceURI(result) = nsURI then
  2578. Exit;
  2579. end;
  2580. if Assigned(FAttributes) then
  2581. begin
  2582. for I := 0 to FAttributes.Length-1 do
  2583. begin
  2584. Attr := TDOMAttr(FAttributes[I]);
  2585. if (Attr.Prefix = 'xmlns') and (Attr.Value = nsURI) then
  2586. begin
  2587. result := Attr.LocalName;
  2588. if Original.LookupNamespaceURI(result) = nsURI then
  2589. Exit;
  2590. end;
  2591. end;
  2592. end;
  2593. result := GetAncestorElement(Self).InternalLookupPrefix(nsURI, Original);
  2594. end;
  2595. function LoadAttribute(doc: TDOMDocument; src: PNodeData): TDOMAttr;
  2596. var
  2597. curr: PNodeData;
  2598. begin
  2599. TDOMNode(result) := doc.Alloc(TDOMAttr);
  2600. result.Create(doc);
  2601. result.FNSI.QName := src^.FQName;
  2602. if not src^.FIsDefault then
  2603. Include(result.FFlags, nfSpecified);
  2604. if Assigned(src^.FTypeInfo) then
  2605. result.FDataType := TAttributeDef(src^.FTypeInfo).DataType;
  2606. if Assigned(src^.FNsUri) then
  2607. result.SetNSI(src^.FNsUri^.Key, src^.FColonPos+1);
  2608. if Assigned(src^.FNext) then
  2609. begin
  2610. curr := src^.FNext;
  2611. while Assigned(curr) do
  2612. begin
  2613. case curr^.FNodeType of
  2614. ntText: result.InternalAppend(doc.CreateTextNode(curr^.FValueStr));
  2615. ntEntityReference: result.InternalAppend(doc.CreateEntityReference(curr^.FQName^.Key));
  2616. end;
  2617. curr := curr^.FNext;
  2618. end;
  2619. end
  2620. else if src^.FValueStr <> '' then
  2621. result.InternalAppend(doc.CreateTextNode(src^.FValueStr));
  2622. end;
  2623. function LoadElement(doc: TDOMDocument; src: PNodeData; attrCount: Integer): TDOMElement;
  2624. var
  2625. i: Integer;
  2626. begin
  2627. TDOMNode(result) := doc.Alloc(TDOMElement);
  2628. result.Create(doc);
  2629. result.FNSI.QName := src^.FQName;
  2630. if Assigned(src^.FNsUri) then
  2631. result.SetNSI(src^.FNsUri^.Key, src^.FColonPos+1);
  2632. for i := 0 to attrCount-1 do
  2633. begin
  2634. Inc(src);
  2635. result.SetAttributeNode(LoadAttribute(doc, src));
  2636. // Attach element to ID map entry if necessary
  2637. if Assigned(src^.FIDEntry) then
  2638. src^.FIDEntry^.Data := Result;
  2639. end;
  2640. end;
  2641. procedure TDOMElement.RestoreDefaultAttr(AttrDef: TAttributeDef);
  2642. var
  2643. Attr: TDOMAttr;
  2644. AttrData: TNodeData;
  2645. nsuri: DOMString;
  2646. begin
  2647. if nfDestroying in FOwnerDocument.FFlags then
  2648. Exit;
  2649. { Copy data and maybe fixup namespace fields }
  2650. AttrData := AttrDef.Data^;
  2651. if AttrDef.IsNamespaceDecl then
  2652. AttrData.FNsUri := FOwnerDocument.FStdUri_xmlns
  2653. else if AttrData.FColonPos > 0 then
  2654. begin
  2655. if (AttrData.FColonPos = 3) and (Pos(DOMString('xml'), AttrData.FQName^.Key) = 1) then
  2656. AttrData.FNsUri := FOwnerDocument.FStdUri_xml
  2657. else
  2658. begin
  2659. nsuri := LookupNamespaceURI(Copy(AttrData.FQName^.Key, 1, AttrData.FColonPos));
  2660. // TODO: what if prefix isn't defined?
  2661. AttrData.FNsUri := FOwnerDocument.FNames.FindOrAdd(nsuri);
  2662. end;
  2663. end;
  2664. Attr := LoadAttribute(FOwnerDocument, @AttrData);
  2665. // TODO: this is cheat, should look at config['namespaces'] instead.
  2666. // revisit when it is implemented.
  2667. if nfLevel2 in FFlags then
  2668. Include(Attr.FFlags, nfLevel2);
  2669. // There should be no matching attribute at this point, so non-namespace method is ok
  2670. SetAttributeNode(Attr);
  2671. end;
  2672. procedure TDOMElement.Normalize;
  2673. var
  2674. I: Integer;
  2675. begin
  2676. if Assigned(FAttributes) then
  2677. for I := 0 to FAttributes.Length - 1 do
  2678. FAttributes[I].Normalize;
  2679. inherited Normalize;
  2680. end;
  2681. function TDOMElement.GetAttributes: TDOMNamedNodeMap;
  2682. begin
  2683. if FAttributes=nil then
  2684. FAttributes := TAttributeMap.Create(Self);
  2685. Result := FAttributes;
  2686. end;
  2687. function TDOMElement.GetAttribute(const name: DOMString): DOMString;
  2688. var
  2689. Attr: TDOMNode;
  2690. begin
  2691. SetLength(Result, 0);
  2692. if Assigned(FAttributes) then
  2693. begin
  2694. Attr := FAttributes.GetNamedItem(name);
  2695. if Assigned(Attr) then
  2696. Result := Attr.NodeValue;
  2697. end;
  2698. end;
  2699. function TDOMElement.GetAttributeNS(const nsURI, aLocalName: DOMString): DOMString;
  2700. var
  2701. Attr: TDOMNode;
  2702. begin
  2703. SetLength(Result, 0);
  2704. if Assigned(FAttributes) then
  2705. begin
  2706. Attr := FAttributes.GetNamedItemNS(nsURI, aLocalName);
  2707. if Assigned(Attr) then
  2708. Result := Attr.NodeValue;
  2709. end;
  2710. end;
  2711. procedure TDOMElement.SetAttribute(const name, value: DOMString);
  2712. var
  2713. I: Cardinal;
  2714. attr: TDOMAttr;
  2715. begin
  2716. Changing;
  2717. if Attributes.Find(name, I) then
  2718. Attr := FAttributes[I] as TDOMAttr
  2719. else
  2720. begin
  2721. Attr := FOwnerDocument.CreateAttribute(name);
  2722. Attr.FParentNode := Self;
  2723. FAttributes.FList.Insert(I, Attr);
  2724. end;
  2725. attr.NodeValue := value;
  2726. end;
  2727. procedure TDOMElement.RemoveAttribute(const name: DOMString);
  2728. begin
  2729. Changing;
  2730. // (note) NamedNodeMap.RemoveNamedItem can raise NOT_FOUND_ERR and we should not.
  2731. if Assigned(FAttributes) then
  2732. FAttributes.InternalRemove(name).Free;
  2733. end;
  2734. procedure TDOMElement.RemoveAttributeNS(const nsURI,
  2735. aLocalName: DOMString);
  2736. begin
  2737. Changing;
  2738. if Assigned(FAttributes) then
  2739. TAttributeMap(FAttributes).InternalRemoveNS(nsURI, aLocalName).Free;
  2740. end;
  2741. procedure TDOMElement.SetAttributeNS(const nsURI, qualifiedName,
  2742. value: DOMString);
  2743. var
  2744. I: Cardinal;
  2745. Attr: TDOMAttr;
  2746. idx, prefIdx: Integer;
  2747. nsidx: PHashItem;
  2748. begin
  2749. Changing;
  2750. idx := FOwnerDocument.IndexOfNS(nsURI, True);
  2751. prefIdx := FOwnerDocument.ValidateQName(nsURI, qualifiedName, nsidx);
  2752. if prefIdx < 0 then
  2753. raise EDOMError.Create(-prefIdx, 'Element.SetAttributeNS');
  2754. if TAttributeMap(Attributes).FindNS(idx, Copy(qualifiedName, prefIdx+1, MaxInt), I) then
  2755. begin
  2756. Attr := TDOMAttr(FAttributes[I]);
  2757. // need to reinsert because the nodeName may change
  2758. FAttributes.FList.Delete(I);
  2759. end
  2760. else
  2761. begin
  2762. TDOMNode(Attr) := FOwnerDocument.Alloc(TDOMAttr);
  2763. Attr.Create(FOwnerDocument);
  2764. Attr.FParentNode := Self;
  2765. Attr.FNSI.NSIndex := Word(idx);
  2766. Include(Attr.FFlags, nfLevel2);
  2767. end;
  2768. // keep list sorted by DOM Level 1 name
  2769. FAttributes.Find(qualifiedName, I);
  2770. FAttributes.FList.Insert(I, Attr);
  2771. // TODO: rehash properly, same issue as with Node.SetPrefix()
  2772. Attr.FNSI.QName := FOwnerDocument.FNames.FindOrAdd(DOMPChar(qualifiedName), Length(qualifiedName));
  2773. Attr.FNSI.PrefixLen := Word(prefIdx);
  2774. attr.NodeValue := value;
  2775. end;
  2776. function TDOMElement.GetAttributeNode(const name: DOMString): TDOMAttr;
  2777. begin
  2778. if Assigned(FAttributes) then
  2779. Result := FAttributes.GetNamedItem(name) as TDOMAttr
  2780. else
  2781. Result := nil;
  2782. end;
  2783. function TDOMElement.GetAttributeNodeNS(const nsURI, aLocalName: DOMString): TDOMAttr;
  2784. begin
  2785. if Assigned(FAttributes) then
  2786. Result := FAttributes.GetNamedItemNS(nsURI, aLocalName) as TDOMAttr
  2787. else
  2788. Result := nil;
  2789. end;
  2790. function TDOMElement.SetAttributeNode(NewAttr: TDOMAttr): TDOMAttr;
  2791. begin
  2792. Result := Attributes.SetNamedItem(NewAttr) as TDOMAttr;
  2793. end;
  2794. function TDOMElement.SetAttributeNodeNS(NewAttr: TDOMAttr): TDOMAttr;
  2795. begin
  2796. Result := Attributes.SetNamedItemNS(NewAttr) as TDOMAttr;
  2797. end;
  2798. function TDOMElement.RemoveAttributeNode(OldAttr: TDOMAttr): TDOMAttr;
  2799. var
  2800. Index: Integer;
  2801. begin
  2802. Changing;
  2803. Result := OldAttr;
  2804. if Assigned(FAttributes) then
  2805. begin
  2806. Index := FAttributes.FList.IndexOf(OldAttr);
  2807. if Index > -1 then
  2808. begin
  2809. FAttributes.Delete(Index);
  2810. Exit;
  2811. end;
  2812. end;
  2813. raise EDOMNotFound.Create('Element.RemoveAttributeNode');
  2814. end;
  2815. function TDOMElement.GetElementsByTagName(const name: DOMString): TDOMNodeList;
  2816. begin
  2817. Result := FOwnerDocument.GetElementList(Self, '', name, False);
  2818. end;
  2819. function TDOMElement.GetElementsByTagNameNS(const nsURI, aLocalName: DOMString): TDOMNodeList;
  2820. begin
  2821. Result := FOwnerDocument.GetElementList(Self, nsURI, aLocalName, True);
  2822. end;
  2823. function TDOMElement.hasAttribute(const name: DOMString): Boolean;
  2824. begin
  2825. Result := Assigned(FAttributes) and
  2826. Assigned(FAttributes.GetNamedItem(name));
  2827. end;
  2828. function TDOMElement.hasAttributeNS(const nsURI, aLocalName: DOMString): Boolean;
  2829. begin
  2830. Result := Assigned(FAttributes) and
  2831. Assigned(FAttributes.getNamedItemNS(nsURI, aLocalName));
  2832. end;
  2833. function TDOMElement.HasAttributes: Boolean;
  2834. begin
  2835. Result := Assigned(FAttributes) and (FAttributes.Length > 0);
  2836. end;
  2837. // -------------------------------------------------------
  2838. // Text
  2839. // -------------------------------------------------------
  2840. function TDOMText.GetNodeType: Integer;
  2841. begin
  2842. Result := TEXT_NODE;
  2843. end;
  2844. function TDOMText.GetNodeName: DOMString;
  2845. begin
  2846. Result := '#text';
  2847. end;
  2848. procedure TDOMText.SetNodeValue(const aValue: DOMString);
  2849. begin
  2850. inherited SetNodeValue(aValue);
  2851. // TODO: may analyze aValue, but this will slow things down...
  2852. Exclude(FFlags, nfIgnorableWS);
  2853. end;
  2854. function TDOMText.CloneNode(deep: Boolean; ACloneOwner: TDOMDocument): TDOMNode;
  2855. begin
  2856. Result := ACloneOwner.CreateTextNode(FNodeValue);
  2857. end;
  2858. function TDOMText.SplitText(offset: LongWord): TDOMText;
  2859. var
  2860. L: LongWord;
  2861. begin
  2862. Changing;
  2863. L := Length;
  2864. if offset > L then
  2865. raise EDOMIndexSize.Create('Text.SplitText');
  2866. Result := FOwnerDocument.CreateTextNodeBuf(@FNodeValue[offset+1], L-offset, False);
  2867. Result.FFlags := FFlags * [nfIgnorableWS];
  2868. FNodeValue := Copy(FNodeValue, 1, offset);
  2869. if Assigned(FParentNode) then
  2870. FParentNode.InsertBefore(Result, FNextSibling);
  2871. end;
  2872. function TDOMText.IsElementContentWhitespace: Boolean;
  2873. begin
  2874. Result := nfIgnorableWS in FFlags;
  2875. end;
  2876. // -------------------------------------------------------
  2877. // Comment
  2878. // -------------------------------------------------------
  2879. function TDOMComment.GetNodeType: Integer;
  2880. begin
  2881. Result := COMMENT_NODE;
  2882. end;
  2883. function TDOMComment.GetNodeName: DOMString;
  2884. begin
  2885. Result := '#comment';
  2886. end;
  2887. function TDOMComment.CloneNode(deep: Boolean; ACloneOwner: TDOMDocument): TDOMNode;
  2888. begin
  2889. Result := ACloneOwner.CreateComment(FNodeValue);
  2890. end;
  2891. // -------------------------------------------------------
  2892. // CDATASection
  2893. // -------------------------------------------------------
  2894. function TDOMCDATASection.GetNodeType: Integer;
  2895. begin
  2896. Result := CDATA_SECTION_NODE;
  2897. end;
  2898. function TDOMCDATASection.GetNodeName: DOMString;
  2899. begin
  2900. Result := '#cdata-section';
  2901. end;
  2902. function TDOMCDATASection.CloneNode(deep: Boolean; ACloneOwner: TDOMDocument): TDOMNode;
  2903. begin
  2904. Result := ACloneOwner.CreateCDATASection(FNodeValue);
  2905. end;
  2906. // -------------------------------------------------------
  2907. // DocumentType
  2908. // -------------------------------------------------------
  2909. function TDOMDocumentType.GetNodeType: Integer;
  2910. begin
  2911. Result := DOCUMENT_TYPE_NODE;
  2912. end;
  2913. function TDOMDocumentType.GetNodeName: DOMString;
  2914. begin
  2915. Result := FModel.FName;
  2916. end;
  2917. function TDOMDocumentType.GetPublicID: DOMString;
  2918. begin
  2919. Result := FModel.FPublicID;
  2920. end;
  2921. function TDOMDocumentType.GetSystemID: DOMString;
  2922. begin
  2923. Result := FModel.FSystemID;
  2924. end;
  2925. function TDOMDocumentType.GetInternalSubset: DOMString;
  2926. begin
  2927. Result := FModel.FInternalSubset;
  2928. end;
  2929. function ConvertEntity(Entry: PHashItem; arg: Pointer): Boolean;
  2930. var
  2931. this: TDOMDocumentType absolute arg;
  2932. node: TDOMEntity;
  2933. begin
  2934. node := TDOMEntity.Create(this.ownerDocument);
  2935. node.FDecl := TEntityDecl(Entry^.Data);
  2936. node.FBaseURI := node.FDecl.FURI;
  2937. node.SetReadOnly(True);
  2938. this.Entities.SetNamedItem(node);
  2939. Result := True;
  2940. end;
  2941. function ConvertNotation(Entry: PHashItem; arg: Pointer): Boolean;
  2942. var
  2943. this: TDOMDocumentType absolute arg;
  2944. node: TDOMNotation;
  2945. begin
  2946. node := TDOMNotation.Create(this.ownerDocument);
  2947. node.FDecl := TNotationDecl(Entry^.Data);
  2948. node.FBaseURI := node.FDecl.FURI;
  2949. node.SetReadOnly(True);
  2950. this.Notations.SetNamedItem(node);
  2951. Result := True;
  2952. end;
  2953. constructor TDOMDocumentType.Create(aOwner: TDOMDocument; aModel: TDTDModel);
  2954. begin
  2955. inherited Create(aOwner);
  2956. FModel := aModel.Reference;
  2957. FModel.Entities.ForEach(@ConvertEntity, Self);
  2958. FModel.Notations.ForEach(@ConvertNotation, Self);
  2959. SetReadOnly(True);
  2960. end;
  2961. destructor TDOMDocumentType.Destroy;
  2962. begin
  2963. FModel.Release;
  2964. FEntities.Free;
  2965. FNotations.Free;
  2966. inherited Destroy;
  2967. end;
  2968. function TDOMDocumentType.GetEntities: TDOMNamedNodeMap;
  2969. begin
  2970. if FEntities = nil then
  2971. FEntities := TDOMNamedNodeMap.Create(Self);
  2972. Result := FEntities;
  2973. end;
  2974. function TDOMDocumentType.GetNotations: TDOMNamedNodeMap;
  2975. begin
  2976. if FNotations = nil then
  2977. FNotations := TDOMNamedNodeMap.Create(Self);
  2978. Result := FNotations;
  2979. end;
  2980. // -------------------------------------------------------
  2981. // Notation
  2982. // -------------------------------------------------------
  2983. function TDOMNotation.GetNodeType: Integer;
  2984. begin
  2985. Result := NOTATION_NODE;
  2986. end;
  2987. function TDOMNotation.GetNodeName: DOMString;
  2988. begin
  2989. Result := FDecl.FName;
  2990. end;
  2991. function TDOMNotation.GetPublicID: DOMString;
  2992. begin
  2993. Result := FDecl.FPublicID;
  2994. end;
  2995. function TDOMNotation.GetSystemID: DOMString;
  2996. begin
  2997. Result := FDecl.FSystemID;
  2998. end;
  2999. function TDOMNotation.CloneNode(deep: Boolean; ACloneOwner: TDOMDocument): TDOMNode;
  3000. begin
  3001. Result := ACloneOwner.Alloc(TDOMNotation);
  3002. TDOMNotation(Result).Create(ACloneOwner);
  3003. TDOMNotation(Result).FDecl := FDecl;
  3004. // notation cannot have children, ignore Deep
  3005. end;
  3006. // -------------------------------------------------------
  3007. // Entity
  3008. // -------------------------------------------------------
  3009. function TDOMEntity.GetNodeType: Integer;
  3010. begin
  3011. Result := ENTITY_NODE;
  3012. end;
  3013. function TDOMEntity.GetNodeName: DOMString;
  3014. begin
  3015. Result := FDecl.FName;
  3016. end;
  3017. function TDOMEntity.GetPublicID: DOMString;
  3018. begin
  3019. Result := FDecl.FPublicID;
  3020. end;
  3021. function TDOMEntity.GetSystemID: DOMString;
  3022. begin
  3023. Result := FDecl.FSystemID;
  3024. end;
  3025. function TDOMEntity.GetNotationName: DOMString;
  3026. begin
  3027. Result := FDecl.FNotationName;
  3028. end;
  3029. function TDOMEntity.CloneNode(deep: Boolean; aCloneOwner: TDOMDocument): TDOMNode;
  3030. begin
  3031. Result := aCloneOwner.Alloc(TDOMEntity);
  3032. TDOMEntity(Result).Create(aCloneOwner);
  3033. TDOMEntity(Result).FDecl := FDecl;
  3034. if deep then
  3035. CloneChildren(Result, aCloneOwner);
  3036. Result.SetReadOnly(True);
  3037. end;
  3038. // -------------------------------------------------------
  3039. // EntityReference
  3040. // -------------------------------------------------------
  3041. function TDOMEntityReference.GetNodeType: Integer;
  3042. begin
  3043. Result := ENTITY_REFERENCE_NODE;
  3044. end;
  3045. function TDOMEntityReference.GetNodeName: DOMString;
  3046. begin
  3047. Result := FName;
  3048. end;
  3049. function TDOMEntityReference.CloneNode(deep: Boolean; ACloneOwner: TDOMDocument): TDOMNode;
  3050. begin
  3051. Result := ACloneOwner.CreateEntityReference(FName);
  3052. end;
  3053. // -------------------------------------------------------
  3054. // ProcessingInstruction
  3055. // -------------------------------------------------------
  3056. function TDOMProcessingInstruction.CloneNode(deep: Boolean;
  3057. ACloneOwner: TDOMDocument): TDOMNode;
  3058. begin
  3059. Result := ACloneOwner.CreateProcessingInstruction(Target, Data);
  3060. end;
  3061. function TDOMProcessingInstruction.GetNodeType: Integer;
  3062. begin
  3063. Result := PROCESSING_INSTRUCTION_NODE;
  3064. end;
  3065. function TDOMProcessingInstruction.GetNodeName: DOMString;
  3066. begin
  3067. Result := FTarget;
  3068. end;
  3069. function TDOMProcessingInstruction.GetNodeValue: DOMString;
  3070. begin
  3071. Result := FNodeValue;
  3072. end;
  3073. procedure TDOMProcessingInstruction.SetNodeValue(const AValue: DOMString);
  3074. begin
  3075. Changing;
  3076. FNodeValue := AValue;
  3077. end;
  3078. { TNodePool }
  3079. constructor TNodePool.Create(AElementSize: Integer; AElementCount: Integer);
  3080. begin
  3081. FElementSize := AElementSize;
  3082. AddExtent(AElementCount);
  3083. end;
  3084. destructor TNodePool.Destroy;
  3085. var
  3086. ext, next: PExtent;
  3087. ptr, ptr_end: PAnsiChar;
  3088. sz: Integer;
  3089. begin
  3090. ext := FCurrExtent;
  3091. ptr := PAnsiChar(FCurrBlock) + FElementSize;
  3092. sz := FCurrExtentSize;
  3093. while Assigned(ext) do
  3094. begin
  3095. // call destructors for everyone still there
  3096. ptr_end := PAnsiChar(ext) + sizeof(TExtent) + (sz - 1) * FElementSize;
  3097. while ptr <= ptr_end do
  3098. begin
  3099. if TDOMNode(ptr).FPool = Self then
  3100. TObject(ptr).Destroy;
  3101. Inc(ptr, FElementSize);
  3102. end;
  3103. // dispose the extent and pass to the next one
  3104. next := ext^.Next;
  3105. FreeMem(ext);
  3106. ext := next;
  3107. sz := sz div 2;
  3108. ptr := PAnsiChar(ext) + sizeof(TExtent);
  3109. end;
  3110. inherited Destroy;
  3111. end;
  3112. procedure TNodePool.AddExtent(AElemCount: Integer);
  3113. var
  3114. ext: PExtent;
  3115. begin
  3116. Assert((FCurrExtent = nil) or
  3117. (PAnsiChar(FCurrBlock) < PAnsiChar(FCurrExtent) + sizeof(TExtent)));
  3118. Assert(AElemCount > 0);
  3119. GetMem(ext, sizeof(TExtent) + AElemCount * FElementSize);
  3120. ext^.Next := FCurrExtent;
  3121. // point to the beginning of the last block of extent
  3122. FCurrBlock := TDOMNode(PAnsiChar(ext) + sizeof(TExtent) + (AElemCount - 1) * FElementSize);
  3123. FCurrExtent := ext;
  3124. FCurrExtentSize := AElemCount;
  3125. end;
  3126. function TNodePool.AllocNode(AClass: TDOMNodeClass): TDOMNode;
  3127. begin
  3128. if Assigned(FFirstFree) then
  3129. begin
  3130. Result := FFirstFree; // remove from free list
  3131. FFirstFree := TDOMNode(Result.FPool);
  3132. end
  3133. else
  3134. begin
  3135. if PAnsiChar(FCurrBlock) < PAnsiChar(FCurrExtent) + sizeof(TExtent) then
  3136. AddExtent(FCurrExtentSize * 2);
  3137. Result := FCurrBlock;
  3138. Dec(PAnsiChar(FCurrBlock), FElementSize);
  3139. end;
  3140. AClass.InitInstance(Result);
  3141. Result.FPool := Self; // mark as used
  3142. end;
  3143. procedure TNodePool.FreeNode(ANode: TDOMNode);
  3144. begin
  3145. ANode.FPool := FFirstFree;
  3146. FFirstFree := ANode;
  3147. end;
  3148. end.