2
This file is based on the FCL unit dom svn revision 15251.
3
Converted to use UTF8 instead of widestrings by Mattias Gaertner.
6
This file is part of the Free Component Library
8
Implementation of DOM interfaces
9
Copyright (c) 1999-2000 by Sebastian Guenther, sg@freepascal.org
10
Modified in 2006 by Sergei Gorelkin, sergei_gorelkin@mail.ru
12
See the file COPYING.FPC, included in this distribution,
13
for details about the copyright.
15
This program is distributed in the hope that it will be useful,
16
but WITHOUT ANY WARRANTY; without even the implied warranty of
17
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
19
**********************************************************************}
22
This unit provides classes which implement the interfaces defined in the
23
DOM (Document Object Model) specification.
25
DOM Levels 1 and 2 - Completely implemented
26
DOM Level 3 - Partially implemented
28
Specification used for this implementation:
30
"Document Object Model (DOM) Level 2 Specification Version 1.0
31
W3C Recommendation 11 November, 2000"
32
http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113
45
SysUtils, Classes, laz2_xmlutils;
47
// -------------------------------------------------------
49
// -------------------------------------------------------
53
// DOM Level 1 exception codes:
55
INDEX_SIZE_ERR = 1; // index or size is negative, or greater than the allowed value
56
DOMSTRING_SIZE_ERR = 2; // Specified range of text does not fit into a DOMString
57
HIERARCHY_REQUEST_ERR = 3; // node is inserted somewhere it does not belong
58
WRONG_DOCUMENT_ERR = 4; // node is used in a different document than the one that created it (that does not support it)
59
INVALID_CHARACTER_ERR = 5; // invalid or illegal character is specified, such as in a name
60
NO_DATA_ALLOWED_ERR = 6; // data is specified for a node which does not support data
61
NO_MODIFICATION_ALLOWED_ERR = 7; // an attempt is made to modify an object where modifications are not allowed
62
NOT_FOUND_ERR = 8; // an attempt is made to reference a node in a context where it does not exist
63
NOT_SUPPORTED_ERR = 9; // implementation does not support the type of object requested
64
INUSE_ATTRIBUTE_ERR = 10; // an attempt is made to add an attribute that is already in use elsewhere
66
// DOM Level 2 exception codes:
68
INVALID_STATE_ERR = 11; // an attempt is made to use an object that is not, or is no longer, usable
69
SYNTAX_ERR = 12; // invalid or illegal string specified
70
INVALID_MODIFICATION_ERR = 13; // an attempt is made to modify the type of the underlying object
71
NAMESPACE_ERR = 14; // an attempt is made to create or change an object in a way which is incorrect with regard to namespaces
72
INVALID_ACCESS_ERR = 15; // parameter or operation is not supported by the underlying object
74
// -------------------------------------------------------
76
// -------------------------------------------------------
82
CDATA_SECTION_NODE = 4;
83
ENTITY_REFERENCE_NODE = 5;
85
PROCESSING_INSTRUCTION_NODE = 7;
88
DOCUMENT_TYPE_NODE = 10;
89
DOCUMENT_FRAGMENT_NODE = 11;
95
TDOMNamedNodeMap = class;
101
TDOMCDATASection = class;
102
TDOMDocumentType = class;
103
TDOMEntityReference = class;
104
TDOMProcessingInstruction = class;
108
PNodePoolArray = ^TNodePoolArray;
109
TNodePoolArray = array[0..MaxInt div sizeof(Pointer)-1] of TNodePool;
115
// -------------------------------------------------------
117
// -------------------------------------------------------
119
TSetOfChar = set of Char;
120
DOMString = AnsiString;
123
PDOMString = ^DOMString;
125
EDOMError = class(Exception)
128
constructor Create(ACode: Integer; const ASituation: String);
131
EDOMIndexSize = class(EDOMError)
133
constructor Create(const ASituation: String);
136
EDOMHierarchyRequest = class(EDOMError)
138
constructor Create(const ASituation: String);
141
EDOMWrongDocument = class(EDOMError)
143
constructor Create(const ASituation: String);
146
EDOMNotFound = class(EDOMError)
148
constructor Create(const ASituation: String);
151
EDOMNotSupported = class(EDOMError)
153
constructor Create(const ASituation: String);
156
EDOMInUseAttribute = class(EDOMError)
158
constructor Create(const ASituation: String);
161
EDOMInvalidState = class(EDOMError)
163
constructor Create(const ASituation: String);
166
EDOMSyntax = class(EDOMError)
168
constructor Create(const ASituation: String);
171
EDOMInvalidModification = class(EDOMError)
173
constructor Create(const ASituation: String);
176
EDOMNamespace = class(EDOMError)
178
constructor Create(const ASituation: String);
181
EDOMInvalidAccess = class(EDOMError)
183
constructor Create(const ASituation: String);
186
{ NodeType, NodeName and NodeValue had been moved from fields to functions.
187
This lowers memory usage and also obsoletes most constructors,
188
at a slight performance penalty. However, NodeName and NodeValue are
189
accessible via fields using specialized properties of descendant classes,
190
e.g. TDOMElement.TagName, TDOMCharacterData.Data etc.}
200
TNodeFlags = set of TNodeFlagEnum;
202
{ TDOMNodeEnumerator }
204
TDOMNodeEnumerator = class
209
constructor Create(Node: TDOMNode);
210
function MoveNext: boolean;
211
property Current: TDOMNode read FCurrent;
214
{ TDOMNodeAllChildEnumerator }
216
TDOMNodeAllChildEnumerator = class
222
constructor Create(Node: TDOMNode);
223
function MoveNext: boolean;
224
property Current: TDOMNode read FCurrent;
225
function GetEnumerator: TDOMNodeAllChildEnumerator; // including grand children
234
FParentNode: TDOMNode;
235
FPreviousSibling, FNextSibling: TDOMNode;
236
FOwnerDocument: TDOMDocument;
238
function GetNodeName: DOMString; virtual; abstract;
239
function GetNodeValue: DOMString; virtual;
240
procedure SetNodeValue(const {%H-}AValue: DOMString); virtual;
241
function GetFirstChild: TDOMNode; virtual;
242
function GetLastChild: TDOMNode; virtual;
243
function GetAttributes: TDOMNamedNodeMap; virtual;
244
function GetRevision: Integer;
245
function GetNodeType: Integer; virtual; abstract;
246
function GetTextContent: DOMString; virtual;
247
procedure SetTextContent(const AValue: DOMString); virtual;
248
function GetLocalName: DOMString; virtual;
249
function GetNamespaceURI: DOMString; virtual;
250
function GetPrefix: DOMString; virtual;
251
procedure SetPrefix(const {%H-}Value: DOMString); virtual;
252
function GetOwnerDocument: TDOMDocument; virtual;
253
function GetBaseURI: DOMString;
254
procedure SetReadOnly(Value: Boolean);
257
constructor Create(AOwner: TDOMDocument);
258
destructor Destroy; override;
259
procedure FreeInstance; override;
261
function GetChildNodes: TDOMNodeList;
262
function GetChildCount: SizeInt; virtual;
264
property NodeName: DOMString read GetNodeName;
265
property NodeValue: DOMString read GetNodeValue write SetNodeValue;
266
property NodeType: Integer read GetNodeType;
267
property ParentNode: TDOMNode read FParentNode;
268
property FirstChild: TDOMNode read GetFirstChild;
269
property LastChild: TDOMNode read GetLastChild;
270
property ChildNodes: TDOMNodeList read GetChildNodes;
271
property PreviousSibling: TDOMNode read FPreviousSibling;
272
property NextSibling: TDOMNode read FNextSibling;
273
property Attributes: TDOMNamedNodeMap read GetAttributes;
274
property OwnerDocument: TDOMDocument read GetOwnerDocument;
275
function GetEnumerator: TDOMNodeEnumerator; // all children including grand children
276
function GetEnumeratorAllChildren: TDOMNodeAllChildEnumerator; // all children including grand children
277
function GetNextNode: TDOMNode; // first child, then next sibling, then next sibling of parent, ...
278
function GetNextNodeSkipChildren: TDOMNode; // first next sibling, then next sibling of parent, ...
279
function GetPreviousNode: TDOMNode; // the reverse of GetNext
280
function GetLastLeaf: TDOMNode; // get last child of last child of ...
281
function GetLevel: SizeInt; // root node has 0
283
function InsertBefore({%H-}NewChild, {%H-}RefChild: TDOMNode): TDOMNode; virtual;
284
function ReplaceChild({%H-}NewChild, {%H-}OldChild: TDOMNode): TDOMNode; virtual;
285
function DetachChild({%H-}OldChild: TDOMNode): TDOMNode; virtual;
286
function RemoveChild(OldChild: TDOMNode): TDOMNode;
287
function AppendChild(NewChild: TDOMNode): TDOMNode;
288
function HasChildNodes: Boolean; virtual;
289
function CloneNode(deep: Boolean): TDOMNode; overload;
292
function IsSupported(const Feature, Version: DOMString): Boolean;
293
function HasAttributes: Boolean; virtual;
294
procedure Normalize; virtual;
296
property NamespaceURI: DOMString read GetNamespaceURI;
297
property LocalName: DOMString read GetLocalName;
298
property Prefix: DOMString read GetPrefix write SetPrefix;
300
property TextContent: DOMString read GetTextContent write SetTextContent;
301
function LookupPrefix(const nsURI: DOMString): DOMString;
302
function LookupNamespaceURI(const APrefix: DOMString): DOMString;
303
function IsDefaultNamespace(const nsURI: DOMString): Boolean;
304
property baseURI: DOMString read GetBaseURI;
305
// Extensions to DOM interface:
306
function CloneNode({%H-}deep: Boolean; {%H-}ACloneOwner: TDOMDocument): TDOMNode; overload; virtual;
307
function FindNode(const {%H-}ANodeName: DOMString): TDOMNode; virtual;
308
function CompareName(const name: DOMString): Integer; virtual;
309
property Flags: TNodeFlags read FFlags;
312
TDOMNodeClass = class of TDOMNode;
314
{ The following class is an implementation specific extension, it is just an
315
extended implementation of TDOMNode, the generic DOM::Node interface
316
implementation. (Its main purpose is to save memory in a big node tree) }
318
{ TDOMNode_WithChildren }
320
TDOMNode_WithChildren = class(TDOMNode)
322
FFirstChild, FLastChild: TDOMNode;
323
FChildNodes: TDOMNodeList;
324
function GetFirstChild: TDOMNode; override;
325
function GetLastChild: TDOMNode; override;
326
procedure CloneChildren(ACopy: TDOMNode; ACloneOwner: TDOMDocument);
327
procedure FreeChildren;
328
function GetTextContent: DOMString; override;
329
procedure SetTextContent(const AValue: DOMString); override;
331
destructor Destroy; override;
332
function InsertBefore(NewChild, RefChild: TDOMNode): TDOMNode; override;
333
function ReplaceChild(NewChild, OldChild: TDOMNode): TDOMNode; override;
334
function DetachChild(OldChild: TDOMNode): TDOMNode; override;
335
function HasChildNodes: Boolean; override;
336
function GetChildCount: SizeInt; override;
337
function FindNode(const ANodeName: DOMString): TDOMNode; override;
338
procedure InternalAppend(NewChild: TDOMNode);
342
// -------------------------------------------------------
344
// -------------------------------------------------------
346
TFilterResult = (frFalse, frNorecurseFalse, frTrue, frNorecurseTrue);
348
TDOMNodeList = class(TObject)
353
function GetCount: LongWord;
354
function GetItem(index: LongWord): TDOMNode;
355
function NodeFilter({%H-}aNode: TDOMNode): TFilterResult; virtual;
356
// now deprecated in favor of NodeFilter
357
procedure BuildList; virtual;
359
constructor Create(ANode: TDOMNode);
360
destructor Destroy; override;
361
property Item[index: LongWord]: TDOMNode read GetItem; default;
362
property Count: LongWord read GetCount;
363
property Length: LongWord read GetCount;
366
{ an extension to DOM interface, used to build recursive lists of elements }
368
TDOMElementList = class(TDOMNodeList)
371
FNSIndexFilter: Integer;
372
localNameFilter: DOMString;
374
FMatchAnyNS: Boolean;
376
function NodeFilter(aNode: TDOMNode): TFilterResult; override;
378
constructor Create(ANode: TDOMNode; const AFilter: DOMString); overload;
379
constructor Create(ANode: TDOMNode; const nsURI, localName: DOMString); overload;
383
// -------------------------------------------------------
385
// -------------------------------------------------------
389
TDOMNamedNodeMap = class(TObject)
393
FSortedList: TFPList; // list of TDOMNode sorted via CompareName
394
FPosList: TFPList; // list of TDOMNode not sorted
395
function GetPosItem(index: LongWord): TDOMNode;
396
function GetSortedItem(index: LongWord): TDOMNode;
397
function GetLength: LongWord;
398
function FindSorted(const name: DOMString; out Index: LongWord): Boolean;
399
function DeleteSorted(index: LongWord): TDOMNode;
400
procedure RestoreDefault(const name: DOMString);
401
function InternalRemove(const name: DOMString): TDOMNode;
402
function ValidateInsert(arg: TDOMNode): Integer;
404
constructor Create(AOwner: TDOMNode; ANodeType: Integer);
405
destructor Destroy; override;
407
function GetNamedItem(const name: DOMString): TDOMNode;
408
function SetNamedItem(arg: TDOMNode): TDOMNode;
409
function RemoveNamedItem(const name: DOMString): TDOMNode;
410
// Introduced in DOM Level 2:
411
function getNamedItemNS(const {%H-}namespaceURI, {%H-}localName: DOMString): TDOMNode; virtual;
412
function setNamedItemNS(arg: TDOMNode): TDOMNode; virtual;
413
function removeNamedItemNS(const {%H-}namespaceURI,{%H-}localName: DOMString): TDOMNode; virtual;
415
property Item[index: LongWord]: TDOMNode read GetPosItem; default;
416
property SortedItem[index: LongWord]: TDOMNode read GetSortedItem;
417
property Length: LongWord read GetLength;
421
// -------------------------------------------------------
423
// -------------------------------------------------------
425
TDOMCharacterData = class(TDOMNode)
427
FNodeValue: DOMString;
429
function GetLength: LongWord;
430
function GetNodeValue: DOMString; override;
431
procedure SetNodeValue(const AValue: DOMString); override;
433
property Data: DOMString read FNodeValue write SetNodeValue;
434
property Length: LongWord read GetLength;
435
function SubstringData(offset, count: LongWord): DOMString;
436
procedure AppendData(const arg: DOMString);
437
procedure InsertData(offset: LongWord; const arg: DOMString);
438
procedure DeleteData(offset, count: LongWord);
439
procedure ReplaceData(offset, count: LongWord; const arg: DOMString);
443
// -------------------------------------------------------
445
// -------------------------------------------------------
447
TDOMImplementation = class
449
function HasFeature(const feature, version: DOMString): Boolean;
451
// Introduced in DOM Level 2:
453
function CreateDocumentType(const QualifiedName, PublicID,
454
SystemID: DOMString): TDOMDocumentType;
455
function CreateDocument(const NamespaceURI, QualifiedName: DOMString;
456
doctype: TDOMDocumentType): TDOMDocument;
460
// -------------------------------------------------------
462
// -------------------------------------------------------
464
TDOMDocumentFragment = class(TDOMNode_WithChildren)
466
function GetNodeType: Integer; override;
467
function GetNodeName: DOMString; override;
469
function CloneNode(deep: Boolean; ACloneOwner: TDOMDocument): TDOMNode; overload; override;
473
// -------------------------------------------------------
475
// -------------------------------------------------------
476
// TODO: to be replaced by more suitable container
477
TNamespaces = array of DOMString;
479
TDOMDocument = class(TDOMNode_WithChildren)
484
FImplementation: TDOMImplementation;
485
FNamespaces: TNamespaces;
487
FEmptyNode: TDOMElement;
488
FNodeLists: THashTable;
489
FMaxPoolSize: Integer;
490
FPools: PNodePoolArray;
491
FDocumentURI: DOMString;
492
function GetDocumentElement: TDOMElement;
493
function GetDocType: TDOMDocumentType;
494
function GetNodeType: Integer; override;
495
function GetNodeName: DOMString; override;
496
function GetTextContent: DOMString; override;
497
function GetOwnerDocument: TDOMDocument; override;
498
procedure SetTextContent(const {%H-}value: DOMString); override;
499
procedure RemoveID(Elem: TDOMElement);
500
function GetChildNodeList(aNode: TDOMNode): TDOMNodeList;
501
function GetElementList(aNode: TDOMNode; const nsURI, aLocalName: DOMString; UseNS: Boolean): TDOMNodeList;
502
procedure NodeListDestroyed(aList: TDOMNodeList);
503
function Alloc(AClass: TDOMNodeClass): TDOMNode;
505
function IndexOfNS(const nsURI: DOMString; AddIfAbsent: Boolean = False): Integer;
506
function InsertBefore(NewChild, RefChild: TDOMNode): TDOMNode; override;
507
function ReplaceChild(NewChild, OldChild: TDOMNode): TDOMNode; override;
508
property DocType: TDOMDocumentType read GetDocType;
509
property Impl: TDOMImplementation read FImplementation;
510
property DocumentElement: TDOMElement read GetDocumentElement;
512
function CreateElement(const tagName: DOMString): TDOMElement; virtual;
513
function CreateElementBuf(Buf: DOMPChar; Length: Integer): TDOMElement;
514
function CreateDocumentFragment: TDOMDocumentFragment;
515
function CreateTextNode(const data: DOMString): TDOMText;
516
function CreateTextNodeBuf(Buf: DOMPChar; Length: Integer; IgnWS: Boolean): TDOMText;
517
function CreateComment(const data: DOMString): TDOMComment;
518
function CreateCommentBuf(Buf: DOMPChar; Length: Integer): TDOMComment;
519
function CreateCDATASection(const {%H-}data: DOMString): TDOMCDATASection;
521
function CreateProcessingInstruction(const {%H-}target, {%H-}data: DOMString):
522
TDOMProcessingInstruction; virtual;
523
function CreateAttribute(const name: DOMString): TDOMAttr;
524
function CreateAttributeBuf(Buf: DOMPChar; Length: Integer): TDOMAttr;
525
function CreateAttributeDef(Buf: DOMPChar; Length: Integer): TDOMAttrDef;
526
function CreateEntityReference(const {%H-}name: DOMString): TDOMEntityReference;
528
function GetElementsByTagName(const tagname: DOMString): TDOMNodeList;
530
// DOM level 2 methods
531
function ImportNode(ImportedNode: TDOMNode; Deep: Boolean): TDOMNode;
532
function CreateElementNS(const nsURI, QualifiedName: DOMString): TDOMElement;
533
function CreateAttributeNS(const nsURI, QualifiedName: DOMString): TDOMAttr;
534
function GetElementsByTagNameNS(const nsURI, alocalName: DOMString): TDOMNodeList;
535
function GetElementById(const ElementID: DOMString): TDOMElement;
537
property documentURI: DOMString read FDocumentURI write FDocumentURI;
538
// Extensions to DOM interface:
540
destructor Destroy; override;
541
function AddID(Attr: TDOMAttr): Boolean;
542
property Names: THashTable read FNames;
545
TXMLDocument = class(TDOMDocument)
547
FXMLVersion: DOMString;
548
procedure SetXMLVersion(const aValue: DOMString);
550
// These fields are extensions to the DOM interface:
551
Encoding, StylesheetType, StylesheetHRef: DOMString;
553
function CreateCDATASection(const data: DOMString): TDOMCDATASection; override;
554
function CreateProcessingInstruction(const target, data: DOMString):
555
TDOMProcessingInstruction; override;
556
function CreateEntityReference(const name: DOMString): TDOMEntityReference; override;
557
property XMLVersion: DOMString read FXMLVersion write SetXMLVersion;
560
// This limits number of namespaces per document to 65535,
561
// and prefix length to 65535, too.
562
// I believe that higher values may only be found in deliberately malformed documents.
563
TNamespaceInfo = packed record
569
// -------------------------------------------------------
571
// -------------------------------------------------------
585
TDOMNode_NS = class(TDOMNode_WithChildren)
587
FNSI: TNamespaceInfo;
588
function GetNodeName: DOMString; override;
589
function GetLocalName: DOMString; override;
590
function GetNamespaceURI: DOMString; override;
591
function GetPrefix: DOMString; override;
592
procedure SetPrefix(const Value: DOMString); override;
595
procedure SetNSI(const nsUri: DOMString; ColonPos: Integer);
596
function CompareName(const AName: DOMString): Integer; override;
597
property NSI: TNamespaceInfo read FNSI;
600
TDOMAttr = class(TDOMNode_NS)
602
FOwnerElement: TDOMElement;
603
FDataType: TAttrDataType;
604
function GetNodeValue: DOMString; override;
605
function GetNodeType: Integer; override;
606
function GetSpecified: Boolean;
607
function GetIsID: Boolean;
608
procedure SetNodeValue(const AValue: DOMString); override;
610
destructor Destroy; override;
611
function CloneNode({%H-}deep: Boolean; ACloneOwner: TDOMDocument): TDOMNode; overload; override;
612
property Name: DOMString read GetNodeName;
613
property Specified: Boolean read GetSpecified;
614
property Value: DOMString read GetNodeValue write SetNodeValue;
615
property OwnerElement: TDOMElement read FOwnerElement;
616
property IsID: Boolean read GetIsID;
618
// TODO: this is to be replaced with DOM 3 TypeInfo
619
property DataType: TAttrDataType read FDataType write FDataType;
623
// -------------------------------------------------------
625
// -------------------------------------------------------
627
TDOMElement = class(TDOMNode_NS)
629
FAttributes: TDOMNamedNodeMap;
630
function GetNodeType: Integer; override;
631
function GetAttributes: TDOMNamedNodeMap; override;
632
procedure AttachDefaultAttrs;
633
function InternalLookupPrefix(const nsURI: DOMString; Original: TDOMElement): DOMString;
634
procedure RestoreDefaultAttr(AttrDef: TDOMAttr);
636
destructor Destroy; override;
637
function CloneNode(deep: Boolean; ACloneOwner: TDOMDocument): TDOMNode; overload; override;
638
function IsEmpty: Boolean; virtual;
639
procedure Normalize; override;
640
property TagName: DOMString read GetNodeName;
641
function GetAttribute(const name: DOMString): DOMString;
642
procedure SetAttribute(const name, value: DOMString);
643
procedure RemoveAttribute(const name: DOMString);
644
function GetAttributeNode(const name: DOMString): TDOMAttr;
645
function SetAttributeNode(NewAttr: TDOMAttr): TDOMAttr;
646
function RemoveAttributeNode(OldAttr: TDOMAttr): TDOMAttr;
647
function GetElementsByTagName(const name: DOMString): TDOMNodeList;
649
// Introduced in DOM Level 2:
650
function GetAttributeNS(const nsURI, aLocalName: DOMString): DOMString;
651
procedure SetAttributeNS(const nsURI, qualifiedName, value: DOMString);
652
procedure RemoveAttributeNS(const nsURI, aLocalName: DOMString);
653
function GetAttributeNodeNS(const nsURI, aLocalName: DOMString): TDOMAttr;
654
function SetAttributeNodeNS(newAttr: TDOMAttr): TDOMAttr;
655
function GetElementsByTagNameNS(const nsURI, aLocalName: DOMString): TDOMNodeList;
656
function hasAttribute(const name: DOMString): Boolean;
657
function hasAttributeNS(const nsURI, aLocalName: DOMString): Boolean;
658
function HasAttributes: Boolean; override;
660
property AttribStrings[const Name: DOMString]: DOMString
661
read GetAttribute write SetAttribute; default;
665
// -------------------------------------------------------
667
// -------------------------------------------------------
669
TDOMText = class(TDOMCharacterData)
671
function GetNodeType: Integer; override;
672
function GetNodeName: DOMString; override;
673
procedure SetNodeValue(const aValue: DOMString); override;
675
function CloneNode({%H-}deep: Boolean; ACloneOwner: TDOMDocument): TDOMNode; overload; override;
676
function SplitText(offset: LongWord): TDOMText;
677
function IsElementContentWhitespace: Boolean;
681
// -------------------------------------------------------
683
// -------------------------------------------------------
685
TDOMComment = class(TDOMCharacterData)
687
function GetNodeType: Integer; override;
688
function GetNodeName: DOMString; override;
690
function CloneNode({%H-}deep: Boolean; ACloneOwner: TDOMDocument): TDOMNode; overload; override;
694
// -------------------------------------------------------
696
// -------------------------------------------------------
698
TDOMCDATASection = class(TDOMText)
700
function GetNodeType: Integer; override;
701
function GetNodeName: DOMString; override;
703
function CloneNode({%H-}deep: Boolean; ACloneOwner: TDOMDocument): TDOMNode; overload; override;
707
// -------------------------------------------------------
709
// -------------------------------------------------------
711
TDOMDocumentType = class(TDOMNode)
714
FPublicID: DOMString;
715
FSystemID: DOMString;
716
FInternalSubset: DOMString;
717
FEntities, FNotations: TDOMNamedNodeMap;
718
function GetEntities: TDOMNamedNodeMap;
719
function GetNotations: TDOMNamedNodeMap;
720
function GetNodeType: Integer; override;
721
function GetNodeName: DOMString; override;
723
destructor Destroy; override;
724
property Name: DOMString read FName;
725
property Entities: TDOMNamedNodeMap read GetEntities;
726
property Notations: TDOMNamedNodeMap read GetNotations;
727
// Introduced in DOM Level 2:
728
property PublicID: DOMString read FPublicID;
729
property SystemID: DOMString read FSystemID;
730
property InternalSubset: DOMString read FInternalSubset;
734
// -------------------------------------------------------
736
// -------------------------------------------------------
738
TDOMNotation = class(TDOMNode)
741
FPublicID, FSystemID: DOMString;
742
function GetNodeType: Integer; override;
743
function GetNodeName: DOMString; override;
745
function CloneNode({%H-}deep: Boolean; ACloneOwner: TDOMDocument): TDOMNode; overload; override;
746
property PublicID: DOMString read FPublicID;
747
property SystemID: DOMString read FSystemID;
751
// -------------------------------------------------------
753
// -------------------------------------------------------
755
TDOMEntity = class(TDOMNode_WithChildren)
758
FPublicID, FSystemID, FNotationName: DOMString;
759
function GetNodeType: Integer; override;
760
function GetNodeName: DOMString; override;
762
function CloneNode(deep: Boolean; aCloneOwner: TDOMDocument): TDOMNode; override;
763
property PublicID: DOMString read FPublicID;
764
property SystemID: DOMString read FSystemID;
765
property NotationName: DOMString read FNotationName;
769
// -------------------------------------------------------
771
// -------------------------------------------------------
773
TDOMEntityReference = class(TDOMNode_WithChildren)
776
function GetNodeType: Integer; override;
777
function GetNodeName: DOMString; override;
779
function CloneNode({%H-}deep: Boolean; ACloneOwner: TDOMDocument): TDOMNode; overload; override;
783
// -------------------------------------------------------
784
// ProcessingInstruction
785
// -------------------------------------------------------
787
TDOMProcessingInstruction = class(TDOMNode)
790
FNodeValue: DOMString;
792
function GetNodeType: Integer; override;
793
function GetNodeName: DOMString; override;
794
function GetNodeValue: DOMString; override;
795
procedure SetNodeValue(const AValue: DOMString); override;
797
function CloneNode({%H-}deep: Boolean; ACloneOwner: TDOMDocument): TDOMNode; overload; override;
798
property Target: DOMString read FTarget;
799
property Data: DOMString read FNodeValue write SetNodeValue;
802
// Attribute declaration - Attr descendant which carries rudimentary type info
803
// must be severely improved while developing Level 3
812
TDOMAttrDef = class(TDOMAttr)
814
FExternallyDeclared: Boolean;
815
FDefault: TAttrDefault;
817
FEnumeration: array of DOMString;
819
function AddEnumToken(Buf: DOMPChar; Len: Integer): Boolean;
820
function HasEnumToken(const aValue: DOMString): Boolean;
821
function CloneNode(deep: Boolean; ACloneOwner: TDOMDocument): TDOMNode; overload; override;
822
property Default: TAttrDefault read FDefault write FDefault;
823
property ExternallyDeclared: Boolean read FExternallyDeclared write FExternallyDeclared;
824
property Tag: Cardinal read FTag write FTag;
827
// TNodePool - custom memory management for TDOMNode's
828
// One pool manages objects of the same InstanceSize (may be of various classes)
833
// following: array of TDOMNode instances
836
TNodePool = class(TObject)
838
FCurrExtent: PExtent;
839
FCurrExtentSize: Integer;
840
FElementSize: Integer;
841
FCurrBlock: TDOMNode;
842
FFirstFree: TDOMNode;
843
procedure AddExtent(AElemCount: Integer);
845
constructor Create(AElementSize: Integer; AElementCount: Integer = 32);
846
destructor Destroy; override;
847
function AllocNode(AClass: TDOMNodeClass): TDOMNode;
848
procedure FreeNode(ANode: TDOMNode);
852
// URIs of predefined namespaces
854
stduri_xml: DOMString = 'http://www.w3.org/XML/1998/namespace';
855
stduri_xmlns: DOMString = 'http://www.w3.org/2000/xmlns/';
857
function StrToXMLValue(const s: string): string; // removes #0
858
function XMLValueToStr(const s: string): string; // reverse of StrToXMLValue (except for invalid #0)
859
function EncodeLesserAndGreaterThan(const s: string): string;
861
// =======================================================
862
// =======================================================
866
function StrToXMLValue(const s: string): string;
868
function Convert(Dst: PChar; out NewLen: PtrUInt): boolean;
882
if Src-PChar(s)=length(s) then
887
'&': begin h:='&'; l:=5; end;
888
'<': begin h:='<'#0; l:=4; end;
889
'>': begin h:='>'#0; l:=4; end;
890
'"': begin h:='"'#0; l:=6; end;
891
'''': begin h:='''#0; l:=6; end;
893
if Dst<>nil then begin
903
if Dst<>nil then begin
904
for i:=1 to l do begin
921
if Result='' then exit;
922
if not Convert(nil,NewLen) then exit;
923
SetLength(Result,NewLen);
924
if NewLen=0 then exit;
925
Convert(PChar(Result),NewLen);
928
function XMLValueToStr(const s: string): string;
929
// convert & " &apos < >
934
if Pos('&',s)<1 then exit(s);
935
SetLength(Result,length(s));
941
if Src-PChar(s)=length(s) then
950
if (Src[1]='m') and (Src[2]='p') then begin
952
if Src^=';' then inc(Src);
956
end else if (Src[1]='p') and (Src[2]='o') and (Src[3]='s') then begin
958
if Src^=';' then inc(Src);
964
if (Src[1]='u') and (Src[2]='o') and (Src[3]='t') then begin
966
if Src^=';' then inc(Src);
972
if (Src[1]='t') then begin
974
if Src^=';' then inc(Src);
980
if (Src[1]='t') then begin
982
if Src^=';' then inc(Src);
997
SetLength(Result,Dst-PChar(Result));
1000
function EncodeLesserAndGreaterThan(const s: string): string;
1002
function Convert(Dst: PChar; out NewLen: PtrUInt): boolean;
1016
if Src-PChar(s)=length(s) then
1021
'<': begin h:='<'#0; l:=4; end;
1022
'>': begin h:='>'#0; l:=4; end;
1024
if Dst<>nil then begin
1034
if Dst<>nil then begin
1035
for i:=1 to l do begin
1052
if Result='' then exit;
1053
if not Convert(nil,NewLen) then exit;
1054
SetLength(Result,NewLen);
1055
if NewLen=0 then exit;
1056
Convert(PChar(Result),NewLen);
1059
{ a namespace-enabled NamedNodeMap }
1061
TAttributeMap = class(TDOMNamedNodeMap)
1063
function FindNS(nsIndex: Integer; const aLocalName: DOMString;
1064
out SortedIndex: LongWord): Boolean;
1065
function InternalRemoveNS(const nsURI, aLocalName: DOMString): TDOMNode;
1067
function getNamedItemNS(const namespaceURI, localName: DOMString): TDOMNode; override;
1068
function setNamedItemNS(arg: TDOMNode): TDOMNode; override;
1069
function removeNamedItemNS(const namespaceURI,localName: DOMString): TDOMNode; override;
1072
{ TDOMNodeAllChildEnumerator }
1074
constructor TDOMNodeAllChildEnumerator.Create(Node: TDOMNode);
1077
FEnd:=Node.GetNextNodeSkipChildren;
1080
function TDOMNodeAllChildEnumerator.MoveNext: boolean;
1082
if FCurrent=nil then
1083
FCurrent:=FNode.GetNextNode
1085
FCurrent:=FCurrent.GetNextNode;
1086
Result:=FCurrent<>FEnd;
1089
function TDOMNodeAllChildEnumerator.GetEnumerator: TDOMNodeAllChildEnumerator;
1094
{ TDOMNodeEnumerator }
1096
constructor TDOMNodeEnumerator.Create(Node: TDOMNode);
1101
function TDOMNodeEnumerator.MoveNext: boolean;
1103
if FCurrent=nil then
1104
FCurrent:=FNode.FirstChild
1106
FCurrent:=FCurrent.NextSibling;
1107
Result:=FCurrent<>nil;
1110
// -------------------------------------------------------
1112
// -------------------------------------------------------
1114
constructor EDOMError.Create(ACode: Integer; const ASituation: String);
1117
inherited Create(Self.ClassName + ' in ' + ASituation);
1120
constructor EDOMIndexSize.Create(const ASituation: String); // 1
1122
inherited Create(INDEX_SIZE_ERR, ASituation);
1125
constructor EDOMHierarchyRequest.Create(const ASituation: String); // 3
1127
inherited Create(HIERARCHY_REQUEST_ERR, ASituation);
1130
constructor EDOMWrongDocument.Create(const ASituation: String); // 4
1132
inherited Create(WRONG_DOCUMENT_ERR, ASituation);
1135
constructor EDOMNotFound.Create(const ASituation: String); // 8
1137
inherited Create(NOT_FOUND_ERR, ASituation);
1140
constructor EDOMNotSupported.Create(const ASituation: String); // 9
1142
inherited Create(NOT_SUPPORTED_ERR, ASituation);
1145
constructor EDOMInUseAttribute.Create(const ASituation: String); // 10
1147
inherited Create(INUSE_ATTRIBUTE_ERR, ASituation);
1150
constructor EDOMInvalidState.Create(const ASituation: String); // 11
1152
inherited Create(INVALID_STATE_ERR, ASituation);
1155
constructor EDOMSyntax.Create(const ASituation: String); // 12
1157
inherited Create(SYNTAX_ERR, ASituation);
1160
constructor EDOMInvalidModification.Create(const ASituation: String); // 13
1162
inherited Create(INVALID_MODIFICATION_ERR, ASituation);
1165
constructor EDOMNamespace.Create(const ASituation: String); // 14
1167
inherited Create(NAMESPACE_ERR, ASituation);
1170
constructor EDOMInvalidAccess.Create(const ASituation: String); // 15
1172
inherited Create(INVALID_ACCESS_ERR, ASituation);
1176
// -------------------------------------------------------
1178
// -------------------------------------------------------
1180
constructor TDOMNode.Create(AOwner: TDOMDocument);
1182
FOwnerDocument := AOwner;
1186
destructor TDOMNode.Destroy;
1188
if Assigned(FParentNode) then
1189
FParentNode.DetachChild(Self);
1193
procedure TDOMNode.FreeInstance;
1195
if Assigned(FPool) then
1198
TNodePool(FPool).FreeNode(Self);
1201
inherited FreeInstance;
1204
function TDOMNode.GetNodeValue: DOMString;
1209
procedure TDOMNode.SetNodeValue(const AValue: DOMString);
1214
function TDOMNode.GetChildNodes: TDOMNodeList;
1216
Result := FOwnerDocument.GetChildNodeList(Self);
1219
function TDOMNode.GetChildCount: SizeInt;
1224
function TDOMNode.GetEnumerator: TDOMNodeEnumerator;
1226
Result:=TDOMNodeEnumerator.Create(Self);
1229
function TDOMNode.GetEnumeratorAllChildren: TDOMNodeAllChildEnumerator;
1231
Result:=TDOMNodeAllChildEnumerator.Create(Self);
1234
function TDOMNode.GetNextNode: TDOMNode;
1238
Result:=GetNextNodeSkipChildren;
1241
function TDOMNode.GetNextNodeSkipChildren: TDOMNode;
1247
Node:=Result.NextSibling;
1248
if Node<>nil then exit(Node);
1249
Result:=Result.ParentNode;
1254
function TDOMNode.GetPreviousNode: TDOMNode;
1258
Result:=PreviousSibling;
1261
Node:=Result.GetLastLeaf;
1266
function TDOMNode.GetLastLeaf: TDOMNode;
1271
if Result=nil then exit;
1273
Node:=Result.LastChild;
1274
if Node=nil then exit;
1279
function TDOMNode.GetLevel: SizeInt;
1285
while Node<>nil do begin
1287
Node:=Node.ParentNode;
1291
function TDOMNode.GetFirstChild: TDOMNode;
1296
function TDOMNode.GetLastChild: TDOMNode;
1301
function TDOMNode.GetAttributes: TDOMNamedNodeMap;
1306
function TDOMNode.InsertBefore(NewChild, RefChild: TDOMNode): TDOMNode;
1308
Changing; // merely to comply with core3/nodeinsertbefore14
1309
raise EDOMHierarchyRequest.Create('Node.InsertBefore');
1313
function TDOMNode.ReplaceChild(NewChild, OldChild: TDOMNode): TDOMNode;
1315
Changing; // merely to comply with core3/nodereplacechild21
1316
raise EDOMHierarchyRequest.Create('Node.ReplaceChild');
1320
function TDOMNode.DetachChild(OldChild: TDOMNode): TDOMNode;
1322
// OldChild isn't in our child list
1323
raise EDOMNotFound.Create('Node.RemoveChild');
1327
function TDOMNode.RemoveChild(OldChild: TDOMNode): TDOMNode;
1329
Result := DetachChild(OldChild);
1332
function TDOMNode.AppendChild(NewChild: TDOMNode): TDOMNode;
1334
Result := InsertBefore(NewChild, nil);
1337
function TDOMNode.HasChildNodes: Boolean;
1342
function TDOMNode.CloneNode(deep: Boolean): TDOMNode;
1344
Result := CloneNode(deep, FOwnerDocument);
1347
function TDOMNode.CloneNode(deep: Boolean; ACloneOwner: TDOMDocument): TDOMNode;
1349
// !! CreateFmt() does not set Code property !!
1350
raise EDOMNotSupported.Create(Format('Cloning/importing of %s is not supported', [ClassName]));
1354
function TDOMNode.FindNode(const ANodeName: DOMString): TDOMNode;
1356
// FIX: we have no children, hence cannot find anything
1360
function TDOMNode.GetRevision: Integer;
1362
Result := FOwnerDocument.FRevision;
1365
function TDOMNode.IsSupported(const Feature, Version: DOMString): Boolean;
1367
Result := FOwnerDocument.Impl.HasFeature(Feature, Version);
1370
function TDOMNode.HasAttributes: Boolean;
1375
procedure TDOMNode.Normalize;
1377
Child, tmp: TDOMNode;
1380
Child := FirstChild;
1383
while Assigned(Child) do
1385
if Child.NodeType = TEXT_NODE then
1387
tmp := Child.NextSibling;
1388
if TDOMText(Child).Data <> '' then
1390
if Assigned(Txt) then
1392
Txt.AppendData(TDOMText(Child).Data);
1393
// TODO: maybe should be smarter
1394
Exclude(Txt.FFlags, nfIgnorableWS);
1398
Txt := TDOMText(Child);
1399
Child := Child.NextSibling;
1408
Child.Normalize; // should be recursive!
1409
Child := Child.NextSibling;
1415
function TDOMNode.GetTextContent: DOMString;
1417
Result := NodeValue;
1420
procedure TDOMNode.SetTextContent(const AValue: DOMString);
1422
SetNodeValue(AValue);
1425
function TDOMNode.GetNamespaceURI: DOMString;
1430
function TDOMNode.GetLocalName: DOMString;
1435
function TDOMNode.GetPrefix: DOMString;
1440
procedure TDOMNode.SetPrefix(const Value: DOMString);
1442
// do nothing, override for Elements and Attributes
1445
function TDOMNode.GetOwnerDocument: TDOMDocument;
1447
Result := FOwnerDocument;
1450
procedure TDOMNode.SetReadOnly(Value: Boolean);
1453
attrs: TDOMNamedNodeMap;
1457
Include(FFlags, nfReadOnly)
1459
Exclude(FFlags, nfReadOnly);
1460
child := FirstChild;
1461
while Assigned(child) do
1463
child.SetReadOnly(Value);
1464
child := child.NextSibling;
1466
if HasAttributes then
1468
attrs := Attributes;
1469
for I := 0 to attrs.Length-1 do
1470
attrs[I].SetReadOnly(Value);
1474
procedure TDOMNode.Changing;
1476
if (nfReadOnly in FFlags) and not (nfDestroying in FOwnerDocument.FFlags) then
1477
raise EDOMError.Create(NO_MODIFICATION_ALLOWED_ERR, 'Node.CheckReadOnly');
1480
function CompareDOMStrings(const s1, s2: DOMPChar; l1, l2: integer): integer;
1485
while (i<l1) and (Result=0) do begin
1486
Result:=ord(s1[i])-ord(s2[i]);
1491
// generic version (slow)
1492
function TDOMNode.CompareName(const name: DOMString): Integer;
1494
SelfName: DOMString;
1496
SelfName := NodeName;
1497
Result := CompareDOMStrings(DOMPChar(name), DOMPChar(SelfName), Length(name), Length(SelfName));
1500
// This will return nil for Entity, Notation, DocType and DocFragment's
1501
function GetAncestorElement(n: TDOMNode): TDOMElement;
1507
result := TDOMDocument(n).documentElement;
1509
result := TDOMAttr(n).OwnerElement;
1511
parent := n.ParentNode;
1512
while Assigned(parent) and (parent.NodeType <> ELEMENT_NODE) do
1513
parent := parent.ParentNode;
1514
Result := TDOMElement(parent);
1518
// TODO: specs prescribe to return default namespace if APrefix=null,
1519
// but we aren't able to distinguish null from an empty string.
1520
// This breaks level3/nodelookupnamespaceuri08 which passes an empty string.
1521
function TDOMNode.LookupNamespaceURI(const APrefix: DOMString): DOMString;
1524
Map: TDOMNamedNodeMap;
1530
if nodeType = ELEMENT_NODE then
1532
if (nfLevel2 in FFlags) and (TDOMElement(Self).Prefix = APrefix) then
1534
result := Self.NamespaceURI;
1537
if HasAttributes then
1540
for I := 0 to Map.Length-1 do
1542
Attr := TDOMAttr(Map[I]);
1543
// should ignore level 1 atts here
1544
if ((Attr.Prefix = 'xmlns') and (Attr.localName = APrefix)) or
1545
((Attr.localName = 'xmlns') and (APrefix = '')) then
1547
result := Attr.NodeValue;
1553
result := GetAncestorElement(Self).LookupNamespaceURI(APrefix);
1556
function TDOMNode.LookupPrefix(const nsURI: DOMString): DOMString;
1559
if (nsURI = '') or (Self = nil) then
1561
if nodeType = ELEMENT_NODE then
1562
result := TDOMElement(Self).InternalLookupPrefix(nsURI, TDOMElement(Self))
1564
result := GetAncestorElement(Self).LookupPrefix(nsURI);
1567
function TDOMNode.IsDefaultNamespace(const nsURI: DOMString): Boolean;
1570
Map: TDOMNamedNodeMap;
1576
if nodeType = ELEMENT_NODE then
1578
if TDOMElement(Self).FNSI.PrefixLen = 0 then
1580
result := (nsURI = namespaceURI);
1583
else if HasAttributes then
1586
for I := 0 to Map.Length-1 do
1588
Attr := TDOMAttr(Map[I]);
1589
if Attr.LocalName = 'xmlns' then
1591
result := (Attr.Value = nsURI);
1597
result := GetAncestorElement(Self).IsDefaultNamespace(nsURI);
1600
function TDOMNode.GetBaseURI: DOMString;
1605
result := TDOMDocument(Self).FDocumentURI;
1606
PROCESSING_INSTRUCTION_NODE:
1607
if Assigned(ParentNode) then
1608
result := ParentNode.GetBaseURI
1610
result := OwnerDocument.DocumentURI;
1616
//------------------------------------------------------------------------------
1619
TNodeTypeEnum = ELEMENT_NODE..NOTATION_NODE;
1620
TNodeTypeSet = set of TNodeTypeEnum;
1623
stdChildren = [TEXT_NODE, ENTITY_REFERENCE_NODE, PROCESSING_INSTRUCTION_NODE,
1624
COMMENT_NODE, CDATA_SECTION_NODE, ELEMENT_NODE];
1626
ValidChildren: array [TNodeTypeEnum] of TNodeTypeSet = (
1627
stdChildren, { element }
1628
[TEXT_NODE, ENTITY_REFERENCE_NODE], { attribute }
1631
stdChildren, { ent ref }
1632
stdChildren, { entity }
1635
[ELEMENT_NODE, DOCUMENT_TYPE_NODE, PROCESSING_INSTRUCTION_NODE, COMMENT_NODE], { document }
1637
stdChildren, { fragment }
1641
function TDOMNode_WithChildren.GetFirstChild: TDOMNode;
1643
Result := FFirstChild;
1646
function TDOMNode_WithChildren.GetLastChild: TDOMNode;
1648
Result := FLastChild;
1651
destructor TDOMNode_WithChildren.Destroy;
1654
FChildNodes.Free; // its destructor will zero the field
1658
function TDOMNode_WithChildren.InsertBefore(NewChild, RefChild: TDOMNode):
1662
NewChildType: Integer;
1665
NewChildType := NewChild.NodeType;
1668
if NewChild.FOwnerDocument <> FOwnerDocument then
1670
if (NewChildType <> DOCUMENT_TYPE_NODE) or
1671
(NewChild.FOwnerDocument <> nil) then
1672
raise EDOMWrongDocument.Create('NodeWC.InsertBefore');
1675
if Assigned(RefChild) and (RefChild.ParentNode <> Self) then
1676
raise EDOMNotFound.Create('NodeWC.InsertBefore');
1678
// TODO: skip checking Fragments as well? (Fragment itself cannot be in the tree)
1679
if not (NewChildType in [TEXT_NODE, CDATA_SECTION_NODE, COMMENT_NODE, PROCESSING_INSTRUCTION_NODE]) and (NewChild.FirstChild <> nil) then
1682
while Assigned(Tmp) do
1684
if Tmp = NewChild then
1685
raise EDOMHierarchyRequest.Create('NodeWC.InsertBefore (cycle in tree)');
1686
Tmp := Tmp.ParentNode;
1689
if NewChild = RefChild then // inserting node before itself is a no-op
1692
Inc(FOwnerDocument.FRevision); // invalidate nodelists
1694
if NewChildType = DOCUMENT_FRAGMENT_NODE then
1696
Tmp := NewChild.FirstChild;
1697
if Assigned(Tmp) then
1699
while Assigned(Tmp) do
1701
if not (Tmp.NodeType in ValidChildren[NodeType]) then
1702
raise EDOMHierarchyRequest.Create('NodeWC.InsertBefore');
1703
Tmp := Tmp.NextSibling;
1706
while Assigned(TDOMDocumentFragment(NewChild).FFirstChild) do
1707
InsertBefore(TDOMDocumentFragment(NewChild).FFirstChild, RefChild);
1712
if not (NewChildType in ValidChildren[NodeType]) then
1713
raise EDOMHierarchyRequest.Create('NodeWC.InsertBefore');
1715
if Assigned(NewChild.FParentNode) then
1716
NewChild.FParentNode.DetachChild(NewChild);
1718
NewChild.FNextSibling := RefChild;
1719
if RefChild = nil then // append to the end
1721
if Assigned(FFirstChild) then
1723
FLastChild.FNextSibling := NewChild;
1724
NewChild.FPreviousSibling := FLastChild;
1726
FFirstChild := NewChild;
1727
FLastChild := NewChild;
1729
else // insert before RefChild
1731
if RefChild = FFirstChild then
1732
FFirstChild := NewChild
1735
RefChild.FPreviousSibling.FNextSibling := NewChild;
1736
NewChild.FPreviousSibling := RefChild.FPreviousSibling;
1738
RefChild.FPreviousSibling := NewChild;
1740
NewChild.FParentNode := Self;
1743
function TDOMNode_WithChildren.ReplaceChild(NewChild, OldChild: TDOMNode):
1746
InsertBefore(NewChild, OldChild);
1747
if Assigned(OldChild) then
1748
RemoveChild(OldChild);
1752
function TDOMNode_WithChildren.DetachChild(OldChild: TDOMNode): TDOMNode;
1756
if OldChild.ParentNode <> Self then
1757
raise EDOMNotFound.Create('NodeWC.RemoveChild');
1759
Inc(FOwnerDocument.FRevision); // invalidate nodelists
1761
if OldChild = FFirstChild then
1762
FFirstChild := FFirstChild.FNextSibling
1764
OldChild.FPreviousSibling.FNextSibling := OldChild.FNextSibling;
1766
if OldChild = FLastChild then
1767
FLastChild := FLastChild.FPreviousSibling
1769
OldChild.FNextSibling.FPreviousSibling := OldChild.FPreviousSibling;
1771
// Make sure removed child does not contain references to nowhere
1772
OldChild.FPreviousSibling := nil;
1773
OldChild.FNextSibling := nil;
1774
OldChild.FParentNode := nil;
1778
procedure TDOMNode_WithChildren.InternalAppend(NewChild: TDOMNode);
1780
if Assigned(FFirstChild) then
1782
FLastChild.FNextSibling := NewChild;
1783
NewChild.FPreviousSibling := FLastChild;
1785
FFirstChild := NewChild;
1786
FLastChild := NewChild;
1787
NewChild.FParentNode := Self;
1790
function TDOMNode_WithChildren.HasChildNodes: Boolean;
1792
Result := Assigned(FFirstChild);
1795
function TDOMNode_WithChildren.GetChildCount: SizeInt;
1799
if FFirstChild=nil then exit(0);
1800
if FChildNodes<>nil then
1801
Result:=FChildNodes.Count
1805
while Node<>nil do begin
1807
Node:=Node.NextSibling;
1813
function TDOMNode_WithChildren.FindNode(const ANodeName: DOMString): TDOMNode;
1815
Result := FFirstChild;
1816
while Assigned(Result) do
1818
if Result.CompareName(ANodeName)=0 then
1820
Result := Result.NextSibling;
1825
procedure TDOMNode_WithChildren.CloneChildren(ACopy: TDOMNode;
1826
ACloneOwner: TDOMDocument);
1831
while Assigned(node) do
1833
TDOMNode_WithChildren(ACopy).InternalAppend(node.CloneNode(True, ACloneOwner));
1834
node := node.NextSibling;
1838
procedure TDOMNode_WithChildren.FreeChildren;
1840
child, next: TDOMNode;
1842
child := FFirstChild;
1843
while Assigned(child) do
1845
next := child.NextSibling;
1846
child.FParentNode := nil;
1847
child.Destroy; // we know it's not nil, so save a call
1854
function TDOMNode_WithChildren.GetTextContent: DOMString;
1859
child := FFirstChild;
1860
// TODO: probably very slow, optimization needed
1861
while Assigned(child) do
1863
case child.NodeType of
1864
TEXT_NODE: if not (nfIgnorableWS in child.FFlags) then
1865
Result := Result + TDOMText(child).Data;
1866
COMMENT_NODE, PROCESSING_INSTRUCTION_NODE: ; // ignored
1868
Result := Result + child.TextContent;
1870
child := child.NextSibling;
1874
procedure TDOMNode_WithChildren.SetTextContent(const AValue: DOMString);
1877
while Assigned(FFirstChild) do
1878
DetachChild(FFirstChild);
1879
if AValue <> '' then
1880
AppendChild(FOwnerDocument.CreateTextNode(AValue));
1883
// -------------------------------------------------------
1885
// -------------------------------------------------------
1887
constructor TDOMNodeList.Create(ANode: TDOMNode);
1891
FRevision := ANode.GetRevision-1; // force BuildList at first access
1892
FList := TFPList.Create;
1895
destructor TDOMNodeList.Destroy;
1897
if (FNode is TDOMNode_WithChildren) and
1898
(TDOMNode_WithChildren(FNode).FChildNodes = Self) then
1899
TDOMNode_WithChildren(FNode).FChildNodes := nil
1901
FNode.FOwnerDocument.NodeListDestroyed(Self);
1906
function TDOMNodeList.NodeFilter(aNode: TDOMNode): TFilterResult;
1908
// accept all nodes but don't allow recursion
1909
Result := frNorecurseTrue;
1912
procedure TDOMNodeList.BuildList;
1914
current, next: TDOMNode;
1918
FRevision := FNode.GetRevision; // refresh
1920
current := FNode.FirstChild;
1922
while Assigned(current) do
1924
res := NodeFilter(current);
1925
if res in [frTrue, frNorecurseTrue] then
1929
if res in [frTrue, frFalse] then
1930
next := current.FirstChild;
1934
while current <> FNode do
1936
next := current.NextSibling;
1937
if Assigned(next) then
1939
current := current.ParentNode;
1946
function TDOMNodeList.GetCount: LongWord;
1948
if FRevision <> FNode.GetRevision then
1951
Result := FList.Count;
1954
function TDOMNodeList.GetItem(index: LongWord): TDOMNode;
1956
if FRevision <> FNode.GetRevision then
1959
if index < LongWord(FList.Count) then
1960
Result := TDOMNode(FList.List^[index])
1967
constructor TDOMElementList.Create(ANode: TDOMNode; const AFilter: DOMString);
1969
inherited Create(ANode);
1971
UseFilter := filter <> '*';
1974
constructor TDOMElementList.Create(ANode: TDOMNode; const nsURI, localName: DOMString);
1976
inherited Create(ANode);
1977
localNameFilter := localName;
1979
FMatchAnyNS := (nsURI = '*');
1980
if not FMatchAnyNS then
1981
FNSIndexFilter := ANode.FOwnerDocument.IndexOfNS(nsURI);
1982
UseFilter := (localName <> '*');
1985
function TDOMElementList.NodeFilter(aNode: TDOMNode): TFilterResult;
1990
if aNode.NodeType = ELEMENT_NODE then with TDOMElement(aNode) do
1994
if (FMatchAnyNS or (FNSI.NSIndex = Word(FNSIndexFilter))) then
1996
I := FNSI.PrefixLen;
1997
L := system.Length(FNSI.QName^.Key);
1998
if (not UseFilter or ((L-I = system.Length(localNameFilter)) and
1999
CompareMem(@FNSI.QName^.Key[I+1], DOMPChar(localNameFilter), system.Length(localNameFilter)*sizeof(DOMChar)))) then
2003
else if (not UseFilter or (TagName = Filter)) then
2009
// -------------------------------------------------------
2011
// -------------------------------------------------------
2013
constructor TDOMNamedNodeMap.Create(AOwner: TDOMNode; ANodeType: Integer);
2017
FNodeType := ANodeType;
2020
destructor TDOMNamedNodeMap.Destroy;
2025
if FPosList<>nil then begin
2026
for I := FPosList.Count-1 downto 0 do
2027
TDOMNode(FPosList[I]).Free;
2033
function TDOMNamedNodeMap.GetSortedItem(index: LongWord): TDOMNode;
2035
Result := TDOMNode(FSortedList.List^[index]);
2038
function TDOMNamedNodeMap.GetPosItem(index: LongWord): TDOMNode;
2040
Result := TDOMNode(FPosList.List^[index]);
2043
function TDOMNamedNodeMap.GetLength: LongWord;
2045
if FPosList<>nil then
2046
Result := FPosList.Count
2051
function TDOMNamedNodeMap.FindSorted(const name: DOMString; out Index: LongWord): Boolean;
2053
L, H, I, C: Integer;
2057
if FPosList<>nil then begin
2058
H := FSortedList.Count - 1;
2062
C := TDOMNode(FSortedList.List^[I]).CompareName(name);
2063
if C > 0 then L := I + 1 else
2077
function TDOMNamedNodeMap.GetNamedItem(const name: DOMString): TDOMNode;
2081
if FindSorted(name, i) then
2082
Result := TDOMNode(FSortedList.List^[i])
2087
// Note: this *may* raise NOT_SUPPORTED_ERR if the document is e.g. HTML.
2088
// This isn't checked now.
2089
function TDOMNamedNodeMap.GetNamedItemNS(const namespaceURI, localName: DOMString): TDOMNode;
2094
function TDOMNamedNodeMap.ValidateInsert(arg: TDOMNode): Integer;
2096
AttrOwner: TDOMNode;
2099
if nfReadOnly in FOwner.FFlags then
2100
Result := NO_MODIFICATION_ALLOWED_ERR
2101
else if arg.FOwnerDocument <> FOwner.FOwnerDocument then
2102
Result := WRONG_DOCUMENT_ERR
2103
else if arg.NodeType <> FNodeType then
2104
Result := HIERARCHY_REQUEST_ERR
2105
else if (FNodeType = ATTRIBUTE_NODE) then
2107
AttrOwner := TDOMAttr(arg).ownerElement;
2108
if Assigned(AttrOwner) and (AttrOwner <> FOwner) then
2109
Result := INUSE_ATTRIBUTE_ERR;
2113
function TDOMNamedNodeMap.SetNamedItem(arg: TDOMNode): TDOMNode;
2119
res := ValidateInsert(arg);
2121
raise EDOMError.Create(res, 'NamedNodeMap.SetNamedItem');
2123
if FNodeType = ATTRIBUTE_NODE then
2125
TDOMAttr(arg).FOwnerElement := TDOMElement(FOwner);
2126
Exists := FindSorted(TDOMAttr(arg).Name, i); // optimization
2129
Exists := FindSorted(arg.NodeName, i);
2133
Result := TDOMNode(FSortedList.List^[i]);
2134
if (Result <> arg) then
2136
if (FNodeType = ATTRIBUTE_NODE) then
2137
TDOMAttr(Result).FOwnerElement := nil;
2138
FSortedList.List^[i] := arg;
2139
i:=FPosList.IndexOf(Result);
2140
FPosList.List^[i] := arg;
2144
if FSortedList=nil then FSortedList:=TFPList.Create;
2145
FSortedList.Insert(i, arg);
2146
if FPosList=nil then FPosList:=TFPList.Create;
2151
function TDOMNamedNodeMap.SetNamedItemNS(arg: TDOMNode): TDOMNode;
2153
{ Since the map contains only namespaceless nodes (all having empty
2154
localName and namespaceURI properties), a namespaced arg won't match
2155
any of them. Therefore, add it using nodeName as key.
2156
Note: a namespaceless arg is another story, as it will match *any* node
2157
in the map. This can be considered as a flaw in specs. }
2158
Result := SetNamedItem(arg);
2161
function TDOMNamedNodeMap.DeleteSorted(index: LongWord): TDOMNode;
2163
Result := TDOMNode(FSortedList.List^[index]);
2164
FSortedList.Delete(index);
2165
FPosList.Remove(Result);
2166
if FNodeType = ATTRIBUTE_NODE then
2167
TDOMAttr(Result).FOwnerElement := nil;
2170
procedure TDOMNamedNodeMap.RestoreDefault(const name: DOMString);
2175
if FNodeType = ATTRIBUTE_NODE then
2177
if not Assigned(TDOMElement(FOwner).FNSI.QName) then // safeguard
2179
eldef := TDOMElement(TDOMElement(FOwner).FNSI.QName^.Data);
2180
if Assigned(eldef) then
2182
// TODO: can be avoided by linking attributes directly to their defs
2183
attrdef := eldef.GetAttributeNode(name);
2184
if Assigned(attrdef) and (TDOMAttrDef(attrdef).FDefault in [adDefault, adFixed]) then
2185
TDOMElement(FOwner).RestoreDefaultAttr(attrdef);
2190
function TDOMNamedNodeMap.InternalRemove(const name: DOMString): TDOMNode;
2195
if FindSorted(name, i) then
2197
Result := DeleteSorted(I);
2198
RestoreDefault(name);
2202
function TDOMNamedNodeMap.RemoveNamedItem(const name: DOMString): TDOMNode;
2204
if nfReadOnly in FOwner.FFlags then
2205
raise EDOMError.Create(NO_MODIFICATION_ALLOWED_ERR, 'NamedNodeMap.RemoveNamedItem');
2206
Result := InternalRemove(name);
2207
if Result = nil then
2208
raise EDOMNotFound.Create('NamedNodeMap.RemoveNamedItem');
2211
function TDOMNamedNodeMap.RemoveNamedItemNS(const namespaceURI, localName: DOMString): TDOMNode;
2213
// see comments to SetNamedItemNS. Related tests are written clever enough
2214
// in the sense they don't expect NO_MODIFICATION_ERR in first place.
2215
raise EDOMNotFound.Create('NamedNodeMap.RemoveNamedItemNS');
2221
// Since list is kept sorted by nodeName, we must use linear search here.
2222
// This routine is not called while parsing, so parsing speed is not lowered.
2223
function TAttributeMap.FindNS(nsIndex: Integer; const aLocalName: DOMString;
2224
out SortedIndex: LongWord): Boolean;
2229
if FSortedList<>nil then begin
2230
for I := 0 to FSortedList.Count-1 do
2232
with TDOMAttr(FSortedList.List^[I]) do
2234
if nsIndex = FNSI.NSIndex then
2236
P := DOMPChar(FNSI.QName^.Key);
2237
if FNSI.PrefixLen > 1 then
2238
Inc(P, FNSI.PrefixLen);
2239
if CompareDOMStrings(DOMPChar(aLocalName), P, System.Length(aLocalName), System.Length(FNSI.QName^.Key) - FNSI.PrefixLen) = 0 then
2249
SortedIndex := High(SortedIndex)-1;
2253
function TAttributeMap.InternalRemoveNS(const nsURI, aLocalName: DOMString): TDOMNode;
2259
nsIndex := FOwner.FOwnerDocument.IndexOfNS(nsURI);
2260
if (nsIndex >= 0) and FindNS(nsIndex, aLocalName, i) then
2262
Result := DeleteSorted(I);
2263
RestoreDefault(TDOMAttr(Result).FNSI.QName^.Key);
2267
function TAttributeMap.getNamedItemNS(const namespaceURI, localName: DOMString): TDOMNode;
2272
nsIndex := FOwner.FOwnerDocument.IndexOfNS(namespaceURI);
2273
if (nsIndex >= 0) and FindNS(nsIndex, localName, i) then
2274
Result := TDOMNode(FSortedList.List^[i])
2279
function TAttributeMap.setNamedItemNS(arg: TDOMNode): TDOMNode;
2285
res := ValidateInsert(arg);
2287
raise EDOMError.Create(res, 'NamedNodeMap.SetNamedItemNS');
2290
with TDOMAttr(arg) do
2292
// calling LocalName is no good... but it is done once
2293
if FindNS(FNSI.NSIndex, localName, i) then
2295
Result := TDOMNode(FSortedList.List^[i]);
2296
FSortedList.Delete(i);
2297
FPosList.Remove(Result);
2299
// Do a non-namespace search in order to keep the list sorted on nodeName
2300
Exists := FindSorted(FNSI.QName^.Key, i);
2301
if Exists and (Result = nil) then // case when arg has no namespace
2303
Result := TDOMNode(FSortedList.List^[i]);
2304
FSortedList.List^[i] := arg;
2305
i:=FPosList.IndexOf(Result);
2306
FPosList.List^[i] := arg;
2309
if FSortedList=nil then FSortedList:=TFPList.Create;
2310
FSortedList.Insert(i, arg);
2311
if FPosList=nil then FPosList:=TFPList.Create;
2315
if Assigned(Result) then
2316
TDOMAttr(Result).FOwnerElement := nil;
2317
TDOMAttr(arg).FOwnerElement := TDOMElement(FOwner);
2320
function TAttributeMap.removeNamedItemNS(const namespaceURI,
2321
localName: DOMString): TDOMNode;
2323
if nfReadOnly in FOwner.FFlags then
2324
raise EDOMError.Create(NO_MODIFICATION_ALLOWED_ERR, 'NamedNodeMap.RemoveNamedItemNS');
2325
Result := InternalRemoveNS(namespaceURI, localName);
2326
if Result = nil then
2327
raise EDOMNotFound.Create('NamedNodeMap.RemoveNamedItemNS');
2330
// -------------------------------------------------------
2332
// -------------------------------------------------------
2334
function TDOMCharacterData.GetLength: LongWord;
2336
Result := system.Length(FNodeValue);
2339
function TDOMCharacterData.GetNodeValue: DOMString;
2341
Result := FNodeValue;
2344
procedure TDOMCharacterData.SetNodeValue(const AValue: DOMString);
2347
FNodeValue := AValue;
2350
function TDOMCharacterData.SubstringData(offset, count: LongWord): DOMString;
2352
if offset > Length then
2353
raise EDOMIndexSize.Create('CharacterData.SubstringData');
2354
Result := Copy(FNodeValue, offset + 1, count);
2357
procedure TDOMCharacterData.AppendData(const arg: DOMString);
2360
FNodeValue := FNodeValue + arg;
2363
procedure TDOMCharacterData.InsertData(offset: LongWord; const arg: DOMString);
2366
if offset > Length then
2367
raise EDOMIndexSize.Create('CharacterData.InsertData');
2368
Insert(arg, FNodeValue, offset+1);
2371
procedure TDOMCharacterData.DeleteData(offset, count: LongWord);
2374
if offset > Length then
2375
raise EDOMIndexSize.Create('CharacterData.DeleteData');
2376
Delete(FNodeValue, offset+1, count);
2379
procedure TDOMCharacterData.ReplaceData(offset, count: LongWord; const arg: DOMString);
2381
DeleteData(offset, count);
2382
InsertData(offset, arg);
2386
// -------------------------------------------------------
2388
// -------------------------------------------------------
2390
function TDOMDocumentFragment.GetNodeType: Integer;
2392
Result := DOCUMENT_FRAGMENT_NODE;
2395
function TDOMDocumentFragment.GetNodeName: DOMString;
2397
Result := '#document-fragment';
2400
function TDOMDocumentFragment.CloneNode(deep: Boolean; aCloneOwner: TDOMDocument): TDOMNode;
2402
Result := aCloneOwner.CreateDocumentFragment;
2404
CloneChildren(Result, aCloneOwner);
2407
// -------------------------------------------------------
2408
// DOMImplementation
2409
// -------------------------------------------------------
2411
{ if nsIdx = -1, checks only the name. Otherwise additionally checks if the prefix is
2412
valid for standard namespace specified by nsIdx.
2413
Non-negative return value is Pos(':', QName), negative is DOM error code. }
2414
function CheckQName(const QName: DOMString; nsIdx: Integer; Xml11: Boolean): Integer;
2418
if not IsXmlName(QName, Xml11) then
2420
Result := -INVALID_CHARACTER_ERR;
2425
Result := Pos(DOMChar(':'), QName);
2428
for I := Result+1 to L-1 do // check for second colon (Use IndexWord?)
2429
if QName[I] = ':' then
2431
Result := -NAMESPACE_ERR;
2434
// Name validity has already been checked by IsXmlName() call above.
2435
// So just check that colon isn't first or last char, and that it is follwed by NameStartChar.
2436
if ((Result = 1) or (Result = L) or not IsXmlName(@QName[Result+1], 1, Xml11)) then
2438
Result := -NAMESPACE_ERR;
2442
if nsIdx < 0 then Exit;
2443
// QName contains prefix, but no namespace
2444
if ((nsIdx = 0) and (Result > 0)) or
2445
// Bad usage of 'http://www.w3.org/2000/xmlns/'
2446
((((L = 5) or (Result = 6)) and (Pos(DOMString('xmlns'), QName) = 1)) <> (nsIdx = 2)) or
2447
// Bad usage of 'http://www.w3.org/XML/1998/namespace'
2448
((Result = 4) and (Pos(DOMString('xml'), QName) = 1) and (nsIdx <> 1)) then
2449
Result := -NAMESPACE_ERR;
2452
function TDOMImplementation.HasFeature(const feature, version: DOMString):
2457
s := feature; // force Ansi, features do not contain non-ASCII chars
2458
Result := (SameText(s, 'XML') and ((version = '') or (version = '1.0') or (version = '2.0'))) or
2459
(SameText(s, 'Core') and ((version = '') or (version = '2.0')));
2463
function TDOMImplementation.CreateDocumentType(const QualifiedName, PublicID,
2464
SystemID: DOMString): TDOMDocumentType;
2468
res := CheckQName(QualifiedName, -1, False);
2470
raise EDOMError.Create(-res, 'Implementation.CreateDocumentType');
2471
Result := TDOMDocumentType.Create(nil);
2472
Result.FName := QualifiedName;
2474
// DOM does not restrict PublicID without SystemID (unlike XML spec)
2475
Result.FPublicID := PublicID;
2476
Result.FSystemID := SystemID;
2479
function TDOMImplementation.CreateDocument(const NamespaceURI,
2480
QualifiedName: DOMString; doctype: TDOMDocumentType): TDOMDocument;
2484
if Assigned(doctype) and Assigned(doctype.OwnerDocument) then
2485
raise EDOMWrongDocument.Create('Implementation.CreateDocument');
2486
Result := TXMLDocument.Create;
2487
Result.FImplementation := Self;
2489
if Assigned(doctype) then
2491
Doctype.FOwnerDocument := Result;
2492
Result.AppendChild(doctype);
2494
Root := Result.CreateElementNS(NamespaceURI, QualifiedName);
2495
Result.AppendChild(Root);
2503
// -------------------------------------------------------
2505
// -------------------------------------------------------
2507
constructor TDOMDocument.Create;
2509
inherited Create(nil);
2510
FOwnerDocument := Self;
2511
FMaxPoolSize := (TDOMAttr.InstanceSize + sizeof(Pointer)-1) and not (sizeof(Pointer)-1) + sizeof(Pointer);
2512
FPools := AllocMem(FMaxPoolSize);
2513
FNames := THashTable.Create(256, True);
2514
SetLength(FNamespaces, 3);
2515
// Namespace #0 should always be an empty string
2516
FNamespaces[1] := stduri_xml;
2517
FNamespaces[2] := stduri_xmlns;
2518
FEmptyNode := TDOMElement.Create(Self);
2519
FNodeLists := THashTable.Create(32, True);
2522
destructor TDOMDocument.Destroy;
2526
Include(FFlags, nfDestroying);
2527
FreeAndNil(FIDList); // set to nil before starting destroying children
2531
for i := 0 to (FMaxPoolSize div sizeof(TNodePool))-1 do
2534
FNames.Free; // free the nametable after inherited has destroyed the children
2535
// (because children reference the nametable)
2538
function TDOMDocument.Alloc(AClass: TDOMNodeClass): TDOMNode;
2543
size := (AClass.InstanceSize + sizeof(Pointer)-1) and not (sizeof(Pointer)-1);
2544
if size > FMaxPoolSize then
2546
Result := TDOMNode(AClass.NewInstance);
2550
pp := FPools^[size div sizeof(TNodePool)];
2553
pp := TNodePool.Create(size);
2554
FPools^[size div sizeof(TNodePool)] := pp;
2556
Result := pp.AllocNode(AClass);
2559
function TDOMDocument.AddID(Attr: TDOMAttr): Boolean;
2565
if FIDList = nil then
2566
FIDList := THashTable.Create(256, False);
2569
p := FIDList.FindOrAdd(DOMPChar(ID), Length(ID), Exists);
2570
Result := not Exists;
2572
p^.Data := Attr.OwnerElement;
2575
// This shouldn't be called if document has no IDs,
2576
// or when it is being destroyed
2577
// TODO: This could be much faster if removing ID happens
2578
// upon modification of corresponding attribute value.
2580
procedure TDOMDocument.RemoveID(Elem: TDOMElement);
2582
FIDList.RemoveData(Elem);
2585
function TDOMDocument.GetNodeType: Integer;
2587
Result := DOCUMENT_NODE;
2590
function TDOMDocument.GetNodeName: DOMString;
2592
Result := '#document';
2595
function TDOMDocument.GetTextContent: DOMString;
2600
procedure TDOMDocument.SetTextContent(const value: DOMString);
2602
// Document ignores setting TextContent
2605
function TDOMDocument.GetOwnerDocument: TDOMDocument;
2610
function TDOMDocument.InsertBefore(NewChild, RefChild: TDOMNode): TDOMNode;
2614
nType := NewChild.NodeType;
2615
if ((nType = ELEMENT_NODE) and Assigned(DocumentElement)) or
2616
((nType = DOCUMENT_TYPE_NODE) and Assigned(DocType)) then
2617
raise EDOMHierarchyRequest.Create('Document.InsertBefore');
2618
Result := inherited InsertBefore(NewChild, RefChild);
2621
function TDOMDocument.ReplaceChild(NewChild, OldChild: TDOMNode): TDOMNode;
2625
nType := NewChild.NodeType;
2626
if ((nType = ELEMENT_NODE) and (OldChild = DocumentElement)) or // root can be replaced by another element
2627
((nType = DOCUMENT_TYPE_NODE) and (OldChild = DocType)) then // and so can be DTD
2629
inherited InsertBefore(NewChild, OldChild);
2630
Result := RemoveChild(OldChild);
2633
Result := inherited ReplaceChild(NewChild, OldChild);
2636
function TDOMDocument.GetDocumentElement: TDOMElement;
2640
node := FFirstChild;
2641
while Assigned(node) and (node.NodeType <> ELEMENT_NODE) do
2642
node := node.NextSibling;
2643
Result := TDOMElement(node);
2646
function TDOMDocument.GetDocType: TDOMDocumentType;
2650
node := FFirstChild;
2651
while Assigned(node) and (node.NodeType <> DOCUMENT_TYPE_NODE) do
2652
node := node.NextSibling;
2653
Result := TDOMDocumentType(node);
2656
function TDOMDocument.CreateElement(const tagName: DOMString): TDOMElement;
2658
if not IsXmlName(tagName, FXML11) then
2659
raise EDOMError.Create(INVALID_CHARACTER_ERR, 'DOMDocument.CreateElement');
2660
TDOMNode(Result) := Alloc(TDOMElement);
2661
Result.Create(Self);
2662
Result.FNSI.QName := FNames.FindOrAdd(DOMPChar(tagName), Length(tagName));
2663
Result.AttachDefaultAttrs;
2666
function TDOMDocument.CreateElementBuf(Buf: DOMPChar; Length: Integer): TDOMElement;
2668
TDOMNode(Result) := Alloc(TDOMElement);
2669
Result.Create(Self);
2670
Result.FNSI.QName := FNames.FindOrAdd(Buf, Length);
2673
function TDOMDocument.CreateDocumentFragment: TDOMDocumentFragment;
2675
TDOMNode(Result) := Alloc(TDOMDocumentFragment);
2676
Result.Create(Self);
2679
function TDOMDocument.CreateTextNode(const data: DOMString): TDOMText;
2681
TDOMNode(Result) := Alloc(TDOMText);
2682
Result.Create(Self);
2683
Result.FNodeValue := data;
2686
function TDOMDocument.CreateTextNodeBuf(Buf: DOMPChar; Length: Integer; IgnWS: Boolean): TDOMText;
2688
TDOMNode(Result) := Alloc(TDOMText);
2689
Result.Create(Self);
2690
SetString(Result.FNodeValue, Buf, Length);
2692
Include(Result.FFlags, nfIgnorableWS);
2696
function TDOMDocument.CreateComment(const data: DOMString): TDOMComment;
2698
TDOMNode(Result) := Alloc(TDOMComment);
2699
Result.Create(Self);
2700
Result.FNodeValue := data;
2703
function TDOMDocument.CreateCommentBuf(Buf: DOMPChar; Length: Integer): TDOMComment;
2705
TDOMNode(Result) := Alloc(TDOMComment);
2706
Result.Create(Self);
2707
SetString(Result.FNodeValue, Buf, Length);
2710
function TDOMDocument.CreateCDATASection(const data: DOMString):
2713
raise EDOMNotSupported.Create('DOMDocument.CreateCDATASection');
2717
function TDOMDocument.CreateProcessingInstruction(const target,
2718
data: DOMString): TDOMProcessingInstruction;
2720
raise EDOMNotSupported.Create('DOMDocument.CreateProcessingInstruction');
2724
function TDOMDocument.CreateAttribute(const name: DOMString): TDOMAttr;
2726
if not IsXmlName(name, FXML11) then
2727
raise EDOMError.Create(INVALID_CHARACTER_ERR, 'DOMDocument.CreateAttribute');
2728
TDOMNode(Result) := Alloc(TDOMAttr);
2729
Result.Create(Self);
2730
Result.FNSI.QName := FNames.FindOrAdd(DOMPChar(name), Length(name));
2731
Include(Result.FFlags, nfSpecified);
2734
function TDOMDocument.CreateAttributeBuf(Buf: DOMPChar; Length: Integer): TDOMAttr;
2736
TDOMNode(Result) := Alloc(TDOMAttr);
2737
Result.Create(Self);
2738
Result.FNSI.QName := FNames.FindOrAdd(buf, Length);
2739
Include(Result.FFlags, nfSpecified);
2742
function TDOMDocument.CreateAttributeDef(Buf: DOMPChar; Length: Integer): TDOMAttrDef;
2744
// not using custom allocation here
2745
Result := TDOMAttrDef.Create(Self);
2746
Result.FNSI.QName := FNames.FindOrAdd(Buf, Length);
2749
function TDOMDocument.CreateEntityReference(const name: DOMString):
2750
TDOMEntityReference;
2752
raise EDOMNotSupported.Create('DOMDocument.CreateEntityReference');
2756
function TDOMDocument.GetChildNodeList(aNode: TDOMNode): TDOMNodeList;
2758
if not (aNode is TDOMNode_WithChildren) then
2759
aNode := FEmptyNode;
2760
Result := TDOMNode_WithChildren(aNode).FChildNodes;
2761
if Result = nil then
2763
Result := TDOMNodeList.Create(aNode);
2764
TDOMNode_WithChildren(aNode).FChildNodes := Result;
2768
function TDOMDocument.GetElementList(aNode: TDOMNode; const nsURI, aLocalName: DOMString;
2769
UseNS: Boolean): TDOMNodeList;
2775
L := (sizeof(Pointer) div sizeof(DOMChar)) + Length(aLocalName);
2777
Inc(L, Length(nsURI)+1);
2778
GetMem(Key, L*sizeof(DOMChar));
2780
// compose the key for hashing
2782
PPointer(P)^ := aNode;
2784
Move(DOMPChar(aLocalName)^, P^, Length(aLocalName)*sizeof(DOMChar));
2787
Inc(P, Length(aLocalName));
2788
P^ := #12; Inc(P); // separator -- diff ('foo','bar') from 'foobar'
2789
Move(DOMPChar(nsURI)^, P^, Length(nsURI)*sizeof(DOMChar));
2791
// try finding in the hashtable
2792
Item := FNodeLists.FindOrAdd(Key, L);
2793
Result := TDOMNodeList(Item^.Data);
2794
if Result = nil then
2797
Result := TDOMElementList.Create(aNode, nsURI, aLocalName)
2799
Result := TDOMElementList.Create(aNode, aLocalName);
2800
Item^.Data := Result;
2807
function TDOMDocument.GetElementsByTagName(const tagname: DOMString): TDOMNodeList;
2809
Result := GetElementList(Self, '', tagname, False);
2812
function TDOMDocument.GetElementsByTagNameNS(const nsURI, aLocalName: DOMString): TDOMNodeList;
2814
Result := GetElementList(Self, nsURI, aLocalName, True);
2817
{ This is linear hence slow. However:
2818
- if user code frees each nodelist ASAP, there are only few items in the hashtable
2819
- if user code does not free nodelists, this is not called at all.
2821
procedure TDOMDocument.NodeListDestroyed(aList: TDOMNodeList);
2823
if not (nfDestroying in FFlags) then
2824
FNodeLists.RemoveData(aList);
2827
function TDOMDocument.CreateAttributeNS(const nsURI,
2828
QualifiedName: DOMString): TDOMAttr;
2830
idx, PrefIdx: Integer;
2832
idx := IndexOfNS(nsURI, True);
2833
PrefIdx := CheckQName(QualifiedName, idx, FXml11);
2835
raise EDOMError.Create(-PrefIdx, 'Document.CreateAttributeNS');
2836
TDOMNode(Result) := Alloc(TDOMAttr);
2837
Result.Create(Self);
2838
Result.FNSI.QName := FNames.FindOrAdd(DOMPChar(QualifiedName), Length(QualifiedName));
2839
Result.FNSI.NSIndex := Word(idx);
2840
Result.FNSI.PrefixLen := Word(PrefIdx);
2841
Include(Result.FFlags, nfLevel2);
2842
Include(Result.FFlags, nfSpecified);
2845
function TDOMDocument.CreateElementNS(const nsURI,
2846
QualifiedName: DOMString): TDOMElement;
2848
idx, PrefIdx: Integer;
2850
idx := IndexOfNS(nsURI, True);
2851
PrefIdx := CheckQName(QualifiedName, idx, FXml11);
2853
raise EDOMError.Create(-PrefIdx, 'Document.CreateElementNS');
2854
TDOMNode(Result) := Alloc(TDOMElement);
2855
Result.Create(Self);
2856
Result.FNSI.QName := FNames.FindOrAdd(DOMPChar(QualifiedName), Length(QualifiedName));
2857
Result.FNSI.NSIndex := Word(idx);
2858
Result.FNSI.PrefixLen := Word(PrefIdx);
2859
Include(Result.FFlags, nfLevel2);
2860
Result.AttachDefaultAttrs;
2863
function TDOMDocument.GetElementById(const ElementID: DOMString): TDOMElement;
2866
if Assigned(FIDList) then
2867
Result := TDOMElement(FIDList.Get(DOMPChar(ElementID), Length(ElementID)));
2870
function TDOMDocument.ImportNode(ImportedNode: TDOMNode;
2871
Deep: Boolean): TDOMNode;
2873
Result := ImportedNode.CloneNode(Deep, Self);
2876
function TDOMDocument.IndexOfNS(const nsURI: DOMString; AddIfAbsent: Boolean): Integer;
2880
// TODO: elaborate implementation
2881
for I := 0 to Length(FNamespaces)-1 do
2882
if FNamespaces[I] = nsURI then
2889
Result := Length(FNamespaces);
2890
SetLength(FNamespaces, Result+1);
2891
FNamespaces[Result] := nsURI;
2898
function TXMLDocument.CreateCDATASection(const data: DOMString):
2901
TDOMNode(Result) := Alloc(TDOMCDATASection);
2902
Result.Create(Self);
2903
Result.FNodeValue := data;
2906
function TXMLDocument.CreateProcessingInstruction(const target,
2907
data: DOMString): TDOMProcessingInstruction;
2909
if not IsXmlName(target, FXML11) then
2910
raise EDOMError.Create(INVALID_CHARACTER_ERR, 'XMLDocument.CreateProcessingInstruction');
2911
TDOMNode(Result) := Alloc(TDOMProcessingInstruction);
2912
Result.Create(Self);
2913
Result.FTarget := target;
2914
Result.FNodeValue := data;
2917
function TXMLDocument.CreateEntityReference(const name: DOMString):
2918
TDOMEntityReference;
2920
dType: TDOMDocumentType;
2923
if not IsXmlName(name, FXML11) then
2924
raise EDOMError.Create(INVALID_CHARACTER_ERR, 'XMLDocument.CreateEntityReference');
2925
TDOMNode(Result) := Alloc(TDOMEntityReference);
2926
Result.Create(Self);
2927
Result.FName := name;
2929
if Assigned(dType) then
2931
TDOMNode(ent) := dType.Entities.GetNamedItem(name);
2932
if Assigned(ent) then
2933
ent.CloneChildren(Result, Self);
2935
Result.SetReadOnly(True);
2938
procedure TXMLDocument.SetXMLVersion(const aValue: DOMString);
2940
FXMLVersion := aValue;
2941
FXML11 := (aValue = '1.1');
2946
function TDOMNode_NS.GetNodeName: DOMString;
2948
// Because FNSI.QName is not set by the TDOMNode itself, but is set by
2949
// other classes/functions, it is necessary to check if FNSQ.QName is
2951
if assigned(FNSI.QName) then
2952
Result := FNSI.QName^.Key
2957
function TDOMNode_NS.GetLocalName: DOMString;
2959
if nfLevel2 in FFlags then
2960
Result := Copy(FNSI.QName^.Key, FNSI.PrefixLen+1, MaxInt)
2965
function TDOMNode_NS.GetNamespaceURI: DOMString;
2967
Result := FOwnerDocument.FNamespaces[FNSI.NSIndex];
2970
function TDOMNode_NS.GetPrefix: DOMString;
2972
if FNSI.PrefixLen < 2 then
2975
Result := Copy(FNSI.QName^.Key, 1, FNSI.PrefixLen-1);
2978
procedure TDOMNode_NS.SetPrefix(const Value: DOMString);
2983
if not IsXmlName(Value, FOwnerDocument.FXml11) then
2984
raise EDOMError.Create(INVALID_CHARACTER_ERR, 'Node.SetPrefix');
2986
if (Pos(DOMChar(':'), Value) > 0) or not (nfLevel2 in FFlags) or
2987
((Value = 'xml') and (FNSI.NSIndex <> 1)) or
2988
((ClassType = TDOMAttr) and // BAD!
2989
((Value = 'xmlns') and (FNSI.NSIndex <> 2)) or (FNSI.QName^.Key = 'xmlns')) then
2990
raise EDOMNamespace.Create('Node.SetPrefix');
2992
// TODO: rehash properly
2993
NewName := Value + ':' + Copy(FNSI.QName^.Key, FNSI.PrefixLen+1, MaxInt);
2994
FNSI.QName := FOwnerDocument.FNames.FindOrAdd(DOMPChar(NewName), Length(NewName));
2995
FNSI.PrefixLen := Length(Value)+1;
2998
function TDOMNode_NS.CompareName(const AName: DOMString): Integer;
3000
Result := CompareDOMStrings(DOMPChar(AName), DOMPChar(NodeName), Length(AName), Length(NodeName));
3003
procedure TDOMNode_NS.SetNSI(const nsUri: DOMString; ColonPos: Integer);
3005
FNSI.NSIndex := FOwnerDocument.IndexOfNS(nsURI, True);
3006
FNSI.PrefixLen := ColonPos;
3007
Include(FFlags, nfLevel2);
3010
// -------------------------------------------------------
3012
// -------------------------------------------------------
3014
function TDOMAttr.GetNodeType: Integer;
3016
Result := ATTRIBUTE_NODE;
3019
destructor TDOMAttr.Destroy;
3021
if Assigned(FOwnerElement) and not (nfDestroying in FOwnerElement.FFlags) then
3022
// TODO: This may raise NOT_FOUND_ERR in case something's really wrong
3023
FOwnerElement.RemoveAttributeNode(Self);
3027
function TDOMAttr.CloneNode(deep: Boolean; ACloneOwner: TDOMDocument): TDOMNode;
3029
// Cloned attribute is always specified and carries its children
3030
if nfLevel2 in FFlags then
3031
Result := ACloneOwner.CreateAttributeNS(namespaceURI, NodeName)
3033
Result := ACloneOwner.CreateAttribute(NodeName);
3034
TDOMAttr(Result).FDataType := FDataType;
3035
CloneChildren(Result, ACloneOwner);
3038
function TDOMAttr.GetNodeValue: DOMString;
3040
Result := GetTextContent;
3041
if FDataType <> dtCdata then
3042
NormalizeSpaces(Result);
3045
procedure TDOMAttr.SetNodeValue(const AValue: DOMString);
3047
SetTextContent(AValue);
3048
Include(FFlags, nfSpecified);
3051
function TDOMAttr.GetSpecified: Boolean;
3053
Result := nfSpecified in FFlags;
3056
function TDOMAttr.GetIsID: Boolean;
3058
Result := FDataType = dtID;
3061
// -------------------------------------------------------
3063
// -------------------------------------------------------
3065
function TDOMElement.GetNodeType: Integer;
3067
Result := ELEMENT_NODE;
3070
destructor TDOMElement.Destroy;
3072
Include(FFlags, nfDestroying);
3073
if Assigned(FOwnerDocument.FIDList) then
3074
FOwnerDocument.RemoveID(Self);
3075
FreeAndNil(FAttributes);
3079
function TDOMElement.CloneNode(deep: Boolean; ACloneOwner: TDOMDocument): TDOMNode;
3082
Attr, AttrClone: TDOMAttr;
3084
if ACloneOwner <> FOwnerDocument then
3086
// Importing has to go the hard way...
3087
if nfLevel2 in FFlags then
3088
Result := ACloneOwner.CreateElementNS(NamespaceURI, NodeName)
3090
Result := ACloneOwner.CreateElement(NodeName);
3091
if Assigned(FAttributes) then
3093
for i := 0 to FAttributes.Length - 1 do
3095
Attr := TDOMAttr(FAttributes[i]);
3096
// destroy defaulted attributes (if any), it is safe because caller had not seen them yet
3097
if Attr.Specified then
3098
TDOMElement(Result).SetAttributeNode(TDOMAttr(Attr.CloneNode(True, ACloneOwner))).Free;
3102
else // Cloning may cheat a little bit.
3104
Result := FOwnerDocument.Alloc(TDOMElement);
3105
TDOMElement(Result).Create(FOwnerDocument);
3106
TDOMElement(Result).FNSI := FNSI;
3107
if nfLevel2 in FFlags then
3108
Include(Result.FFlags, nfLevel2);
3109
if Assigned(FAttributes) then
3111
// clone all attributes, but preserve nfSpecified flag
3112
for i := 0 to FAttributes.Length - 1 do
3114
Attr := TDOMAttr(FAttributes[i]);
3115
AttrClone := TDOMAttr(Attr.CloneNode(True, ACloneOwner));
3116
if not Attr.Specified then
3117
Exclude(AttrClone.FFlags, nfSpecified);
3118
TDOMElement(Result).SetAttributeNode(AttrClone);
3123
CloneChildren(Result, ACloneOwner);
3126
function TDOMElement.IsEmpty: boolean;
3128
Result:=(FAttributes=nil) or (FAttributes.Length=0);
3131
procedure TDOMElement.AttachDefaultAttrs;
3134
attrdef: TDOMAttrDef;
3137
if not Assigned(FNSI.QName) then // safeguard
3139
eldef := TDOMElement(FNSI.QName^.Data);
3140
if Assigned(eldef) and Assigned(eldef.FAttributes) then
3142
for I := 0 to eldef.FAttributes.Length-1 do
3144
attrdef := TDOMAttrDef(eldef.FAttributes[I]);
3145
if attrdef.FDefault in [adDefault, adFixed] then
3146
RestoreDefaultAttr(attrdef);
3151
function TDOMElement.InternalLookupPrefix(const nsURI: DOMString; Original: TDOMElement): DOMString;
3159
if (nfLevel2 in FFlags) and (namespaceURI = nsURI) and (FNSI.PrefixLen > 0) then
3162
if Original.LookupNamespaceURI(result) = nsURI then
3165
if Assigned(FAttributes) then
3167
for I := 0 to FAttributes.Length-1 do
3169
Attr := TDOMAttr(FAttributes[I]);
3170
if (Attr.Prefix = 'xmlns') and (Attr.Value = nsURI) then
3172
result := Attr.LocalName;
3173
if Original.LookupNamespaceURI(result) = nsURI then
3178
result := GetAncestorElement(Self).InternalLookupPrefix(nsURI, Original);
3181
procedure TDOMElement.RestoreDefaultAttr(AttrDef: TDOMAttr);
3185
AttrName, nsuri: DOMString;
3187
Attr := TDOMAttr(AttrDef.CloneNode(True));
3188
AttrName := Attr.Name;
3189
ColonPos := Pos(DOMChar(':'), AttrName);
3190
if Pos(DOMString('xmlns'), AttrName) = 1 then
3192
if (Length(AttrName) = 5) or (ColonPos = 6) then
3193
Attr.SetNSI(stduri_xmlns, ColonPos);
3195
else if ColonPos > 0 then
3197
if (ColonPos = 4) and (Pos(DOMString('xml'), AttrName) = 1) then
3198
Attr.SetNSI(stduri_xml, 4)
3201
nsuri := LookupNamespaceURI(Copy(AttrName, 1, ColonPos-1));
3202
// TODO: what if prefix isn't defined?
3203
Attr.SetNSI(nsuri, ColonPos);
3206
// TODO: this is cheat, should look at config['namespaces'] instead.
3207
// revisit when it is implemented.
3208
if nfLevel2 in FFlags then
3209
Include(Attr.FFlags, nfLevel2);
3210
// There should be no matching attribute at this point, so non-namespace method is ok
3211
SetAttributeNode(Attr);
3214
procedure TDOMElement.Normalize;
3218
if Assigned(FAttributes) then
3219
for I := 0 to FAttributes.Length - 1 do
3220
FAttributes[I].Normalize;
3221
inherited Normalize;
3224
function TDOMElement.GetAttributes: TDOMNamedNodeMap;
3226
if FAttributes=nil then
3227
FAttributes := TAttributeMap.Create(Self, ATTRIBUTE_NODE);
3228
Result := FAttributes;
3231
function TDOMElement.GetAttribute(const name: DOMString): DOMString;
3235
SetLength(Result, 0);
3236
if Assigned(FAttributes) then
3238
Attr := FAttributes.GetNamedItem(name);
3239
if Assigned(Attr) then
3240
Result := Attr.NodeValue;
3244
function TDOMElement.GetAttributeNS(const nsURI, aLocalName: DOMString): DOMString;
3248
SetLength(Result, 0);
3249
if Assigned(FAttributes) then
3251
Attr := FAttributes.GetNamedItemNS(nsURI, aLocalName);
3252
if Assigned(Attr) then
3253
Result := Attr.NodeValue;
3257
procedure TDOMElement.SetAttribute(const name, value: DOMString);
3263
if Attributes.FindSorted(name, I) then
3264
Attr := Attributes.SortedItem[I] as TDOMAttr
3267
Attr := FOwnerDocument.CreateAttribute(name);
3268
Attr.FOwnerElement := Self;
3269
if FAttributes.FSortedList=nil then FAttributes.FSortedList:=TFPList.Create;
3270
FAttributes.FSortedList.Insert(I, Attr);
3271
if FAttributes.FPosList=nil then FAttributes.FPosList:=TFPList.Create;
3272
FAttributes.FPosList.Add(Attr);
3274
Attr.NodeValue := value;
3277
procedure TDOMElement.RemoveAttribute(const name: DOMString);
3280
// (note) NamedNodeMap.RemoveNamedItem can raise NOT_FOUND_ERR and we should not.
3281
if Assigned(FAttributes) then
3282
FAttributes.InternalRemove(name).Free;
3285
procedure TDOMElement.RemoveAttributeNS(const nsURI,
3286
aLocalName: DOMString);
3289
if Assigned(FAttributes) then
3290
TAttributeMap(FAttributes).InternalRemoveNS(nsURI, aLocalName).Free;
3293
procedure TDOMElement.SetAttributeNS(const nsURI, qualifiedName,
3298
idx, prefIdx: Integer;
3301
idx := FOwnerDocument.IndexOfNS(nsURI, True);
3302
prefIdx := CheckQName(qualifiedName, idx, FOwnerDocument.FXml11);
3304
raise EDOMError.Create(-prefIdx, 'Element.SetAttributeNS');
3306
if TAttributeMap(Attributes).FindNS(idx, Copy(qualifiedName, prefIdx+1, MaxInt), I) then
3308
Attr := TDOMAttr(FAttributes[I]);
3309
// need to reinsert because the nodeName may change
3310
FAttributes.FPosList.Remove(FAttributes.FSortedList.List^[i]);
3311
FAttributes.FSortedList.Delete(I);
3315
TDOMNode(Attr) := FOwnerDocument.Alloc(TDOMAttr);
3316
Attr.Create(FOwnerDocument);
3317
Attr.FOwnerElement := Self;
3318
Attr.FNSI.NSIndex := Word(idx);
3319
Include(Attr.FFlags, nfLevel2);
3321
// keep list sorted by DOM Level 1 name
3322
FAttributes.FindSorted(qualifiedName, I);
3323
if FAttributes.FSortedList=nil then FAttributes.FSortedList:=TFPList.Create;
3324
FAttributes.FSortedList.Insert(I, Attr);
3325
if FAttributes.FPosList=nil then FAttributes.FPosList:=TFPList.Create;
3326
FAttributes.FPosList.Add(Attr);
3327
// TODO: rehash properly, same issue as with Node.SetPrefix()
3328
Attr.FNSI.QName := FOwnerDocument.FNames.FindOrAdd(DOMPChar(qualifiedName), Length(qualifiedName));
3329
Attr.FNSI.PrefixLen := Word(prefIdx);
3330
attr.NodeValue := value;
3333
function TDOMElement.GetAttributeNode(const name: DOMString): TDOMAttr;
3335
if Assigned(FAttributes) then
3336
Result := FAttributes.GetNamedItem(name) as TDOMAttr
3341
function TDOMElement.GetAttributeNodeNS(const nsURI, aLocalName: DOMString): TDOMAttr;
3343
if Assigned(FAttributes) then
3344
Result := FAttributes.GetNamedItemNS(nsURI, aLocalName) as TDOMAttr
3349
function TDOMElement.SetAttributeNode(NewAttr: TDOMAttr): TDOMAttr;
3351
Result := Attributes.SetNamedItem(NewAttr) as TDOMAttr;
3354
function TDOMElement.SetAttributeNodeNS(NewAttr: TDOMAttr): TDOMAttr;
3356
Result := Attributes.SetNamedItemNS(NewAttr) as TDOMAttr;
3360
function TDOMElement.RemoveAttributeNode(OldAttr: TDOMAttr): TDOMAttr;
3364
if Assigned(FAttributes) and (FAttributes.FSortedList<>nil)
3365
and (FAttributes.FSortedList.Remove(OldAttr) > -1)
3367
FAttributes.FPosList.Remove(OldAttr);
3368
if Assigned(OldAttr.FNSI.QName) then // safeguard
3369
FAttributes.RestoreDefault(OldAttr.FNSI.QName^.Key);
3370
Result.FOwnerElement := nil;
3373
raise EDOMNotFound.Create('Element.RemoveAttributeNode');
3376
function TDOMElement.GetElementsByTagName(const name: DOMString): TDOMNodeList;
3378
Result := FOwnerDocument.GetElementList(Self, '', name, False);
3381
function TDOMElement.GetElementsByTagNameNS(const nsURI, aLocalName: DOMString): TDOMNodeList;
3383
Result := FOwnerDocument.GetElementList(Self, nsURI, aLocalName, True);
3386
function TDOMElement.hasAttribute(const name: DOMString): Boolean;
3388
Result := Assigned(FAttributes) and
3389
Assigned(FAttributes.GetNamedItem(name));
3392
function TDOMElement.hasAttributeNS(const nsURI, aLocalName: DOMString): Boolean;
3394
Result := Assigned(FAttributes) and
3395
Assigned(FAttributes.getNamedItemNS(nsURI, aLocalName));
3398
function TDOMElement.HasAttributes: Boolean;
3400
Result := Assigned(FAttributes) and (FAttributes.Length > 0);
3403
// -------------------------------------------------------
3405
// -------------------------------------------------------
3407
function TDOMText.GetNodeType: Integer;
3409
Result := TEXT_NODE;
3412
function TDOMText.GetNodeName: DOMString;
3417
procedure TDOMText.SetNodeValue(const aValue: DOMString);
3419
inherited SetNodeValue(aValue);
3420
// TODO: may analyze aValue, but this will slow things down...
3421
Exclude(FFlags, nfIgnorableWS);
3424
function TDOMText.CloneNode(deep: Boolean; ACloneOwner: TDOMDocument): TDOMNode;
3426
Result := ACloneOwner.CreateTextNode(FNodeValue);
3429
function TDOMText.SplitText(offset: LongWord): TDOMText;
3432
if offset > Length then
3433
raise EDOMIndexSize.Create('Text.SplitText');
3435
Result := TDOMText.Create(FOwnerDocument);
3436
Result.FNodeValue := Copy(FNodeValue, offset + 1, Length);
3437
Result.FFlags := FFlags * [nfIgnorableWS];
3438
FNodeValue := Copy(FNodeValue, 1, offset);
3439
if Assigned(FParentNode) then
3440
FParentNode.InsertBefore(Result, FNextSibling);
3443
function TDOMText.IsElementContentWhitespace: Boolean;
3445
Result := nfIgnorableWS in FFlags;
3448
// -------------------------------------------------------
3450
// -------------------------------------------------------
3452
function TDOMComment.GetNodeType: Integer;
3454
Result := COMMENT_NODE;
3457
function TDOMComment.GetNodeName: DOMString;
3459
Result := '#comment';
3462
function TDOMComment.CloneNode(deep: Boolean; ACloneOwner: TDOMDocument): TDOMNode;
3464
Result := ACloneOwner.CreateComment(FNodeValue);
3468
// -------------------------------------------------------
3470
// -------------------------------------------------------
3472
function TDOMCDATASection.GetNodeType: Integer;
3474
Result := CDATA_SECTION_NODE;
3477
function TDOMCDATASection.GetNodeName: DOMString;
3479
Result := '#cdata-section';
3482
function TDOMCDATASection.CloneNode(deep: Boolean; ACloneOwner: TDOMDocument): TDOMNode;
3484
Result := ACloneOwner.CreateCDATASection(FNodeValue);
3488
// -------------------------------------------------------
3490
// -------------------------------------------------------
3492
function TDOMDocumentType.GetNodeType: Integer;
3494
Result := DOCUMENT_TYPE_NODE;
3497
function TDOMDocumentType.GetNodeName: DOMString;
3502
destructor TDOMDocumentType.Destroy;
3509
function TDOMDocumentType.GetEntities: TDOMNamedNodeMap;
3511
if FEntities = nil then
3512
FEntities := TDOMNamedNodeMap.Create(Self, ENTITY_NODE);
3513
Result := FEntities;
3516
function TDOMDocumentType.GetNotations: TDOMNamedNodeMap;
3518
if FNotations = nil then
3519
FNotations := TDOMNamedNodeMap.Create(Self, NOTATION_NODE);
3520
Result := FNotations;
3523
// -------------------------------------------------------
3525
// -------------------------------------------------------
3527
function TDOMNotation.GetNodeType: Integer;
3529
Result := NOTATION_NODE;
3532
function TDOMNotation.GetNodeName: DOMString;
3537
function TDOMNotation.CloneNode(deep: Boolean; ACloneOwner: TDOMDocument): TDOMNode;
3539
Result := ACloneOwner.Alloc(TDOMNotation);
3540
TDOMNotation(Result).Create(ACloneOwner);
3541
TDOMNotation(Result).FName := FName;
3542
TDOMNotation(Result).FPublicID := PublicID;
3543
TDOMNotation(Result).FSystemID := SystemID;
3544
// notation cannot have children, ignore Deep
3548
// -------------------------------------------------------
3550
// -------------------------------------------------------
3552
function TDOMEntity.GetNodeType: Integer;
3554
Result := ENTITY_NODE;
3557
function TDOMEntity.GetNodeName: DOMString;
3562
function TDOMEntity.CloneNode(deep: Boolean; aCloneOwner: TDOMDocument): TDOMNode;
3564
Result := aCloneOwner.Alloc(TDOMEntity);
3565
TDOMEntity(Result).Create(aCloneOwner);
3566
TDOMEntity(Result).FName := FName;
3567
TDOMEntity(Result).FSystemID := FSystemID;
3568
TDOMEntity(Result).FPublicID := FPublicID;
3569
TDOMEntity(Result).FNotationName := FNotationName;
3571
CloneChildren(Result, aCloneOwner);
3572
Result.SetReadOnly(True);
3575
// -------------------------------------------------------
3577
// -------------------------------------------------------
3579
function TDOMEntityReference.GetNodeType: Integer;
3581
Result := ENTITY_REFERENCE_NODE;
3584
function TDOMEntityReference.GetNodeName: DOMString;
3589
function TDOMEntityReference.CloneNode(deep: Boolean; ACloneOwner: TDOMDocument): TDOMNode;
3591
Result := ACloneOwner.CreateEntityReference(FName);
3594
// -------------------------------------------------------
3595
// ProcessingInstruction
3596
// -------------------------------------------------------
3598
function TDOMProcessingInstruction.CloneNode(deep: Boolean;
3599
ACloneOwner: TDOMDocument): TDOMNode;
3601
Result := ACloneOwner.CreateProcessingInstruction(Target, Data);
3604
function TDOMProcessingInstruction.GetNodeType: Integer;
3606
Result := PROCESSING_INSTRUCTION_NODE;
3609
function TDOMProcessingInstruction.GetNodeName: DOMString;
3614
function TDOMProcessingInstruction.GetNodeValue: DOMString;
3616
Result := FNodeValue;
3619
procedure TDOMProcessingInstruction.SetNodeValue(const AValue: DOMString);
3622
FNodeValue := AValue;
3627
function TDOMAttrDef.CloneNode(deep: Boolean; ACloneOwner: TDOMDocument): TDOMNode;
3629
Result := inherited CloneNode(deep, ACloneOwner);
3630
Exclude(Result.FFlags, nfSpecified);
3633
function TDOMAttrDef.AddEnumToken(Buf: DOMPChar; Len: Integer): Boolean;
3637
// TODO: this implementaion is the slowest possible...
3639
L := Length(FEnumeration);
3640
for I := 0 to L-1 do
3642
if CompareDomStrings(Buf, DOMPChar(FEnumeration[I]), Len, Length(FEnumeration[I])) = 0 then
3645
SetLength(FEnumeration, L+1);
3646
SetString(FEnumeration[L], Buf, Len);
3650
function TDOMAttrDef.HasEnumToken(const aValue: DOMString): Boolean;
3655
if Length(FEnumeration) = 0 then
3657
for I := 0 to Length(FEnumeration)-1 do
3659
if FEnumeration[I] = aValue then
3667
constructor TNodePool.Create(AElementSize: Integer; AElementCount: Integer);
3669
FElementSize := AElementSize;
3670
AddExtent(AElementCount);
3673
destructor TNodePool.Destroy;
3676
ptr, ptr_end: PAnsiChar;
3680
ptr := PAnsiChar(FCurrBlock) + FElementSize;
3681
sz := FCurrExtentSize;
3682
while Assigned(ext) do
3684
// call destructors for everyone still there
3685
ptr_end := PAnsiChar(ext) + sizeof(TExtent) + (sz - 1) * FElementSize;
3686
while ptr <= ptr_end do
3688
if TDOMNode(ptr).FPool = Self then
3689
TObject(ptr).Destroy;
3690
Inc(ptr, FElementSize);
3692
// dispose the extent and pass to the next one
3697
ptr := PAnsiChar(ext) + sizeof(TExtent);
3702
procedure TNodePool.AddExtent(AElemCount: Integer);
3706
Assert((FCurrExtent = nil) or
3707
(PAnsiChar(FCurrBlock) < PAnsiChar(FCurrExtent) + sizeof(TExtent)));
3708
Assert(AElemCount > 0);
3710
GetMem(ext, sizeof(TExtent) + AElemCount * FElementSize);
3711
ext^.Next := FCurrExtent;
3712
// point to the beginning of the last block of extent
3713
FCurrBlock := TDOMNode(PAnsiChar(ext) + sizeof(TExtent) + (AElemCount - 1) * FElementSize);
3715
FCurrExtentSize := AElemCount;
3718
function TNodePool.AllocNode(AClass: TDOMNodeClass): TDOMNode;
3720
if Assigned(FFirstFree) then
3722
Result := FFirstFree; // remove from free list
3723
FFirstFree := TDOMNode(Result.FPool);
3727
if PAnsiChar(FCurrBlock) < PAnsiChar(FCurrExtent) + sizeof(TExtent) then
3728
AddExtent(FCurrExtentSize * 2);
3729
Result := FCurrBlock;
3730
Dec(PAnsiChar(FCurrBlock), FElementSize);
3732
AClass.InitInstance(Result);
3733
Result.FPool := Self; // mark as used
3736
procedure TNodePool.FreeNode(ANode: TDOMNode);
3738
ANode.FPool := FFirstFree;
3739
FFirstFree := ANode;