dom.pp 91 KB

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