dom.pp 102 KB

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