mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-09-13 10:29:17 +02:00
Final strike for #13605:
src/dom.pp: * GetElementsByTagName[NS] results now get cached in a hashtable. Repeated calls to GetElementsByTagName with same arguments return the same instance of NodeList. All NodeLists created during document lifetime are destroyed with the document. src/xmlutils.pp: * THashTable.Lookup(), changed SetString to SetLength+Move because SetString truncates on #0 + added THashTable.RemoveData() method tests/api.xml: - No longer need to 'garbage collect' the NodeLists. git-svn-id: trunk@13180 -
This commit is contained in:
parent
17bbe72d08
commit
035fe43b72
@ -413,6 +413,7 @@ type
|
|||||||
FNamespaces: TNamespaces;
|
FNamespaces: TNamespaces;
|
||||||
FNames: THashTable;
|
FNames: THashTable;
|
||||||
FEmptyNode: TDOMElement;
|
FEmptyNode: TDOMElement;
|
||||||
|
FNodeLists: THashTable;
|
||||||
function GetDocumentElement: TDOMElement;
|
function GetDocumentElement: TDOMElement;
|
||||||
function GetDocType: TDOMDocumentType;
|
function GetDocType: TDOMDocumentType;
|
||||||
function GetNodeType: Integer; override;
|
function GetNodeType: Integer; override;
|
||||||
@ -422,8 +423,8 @@ type
|
|||||||
procedure SetTextContent(const value: DOMString); override;
|
procedure SetTextContent(const value: DOMString); override;
|
||||||
procedure RemoveID(Elem: TDOMElement);
|
procedure RemoveID(Elem: TDOMElement);
|
||||||
function GetChildNodeList(aNode: TDOMNode): TDOMNodeList;
|
function GetChildNodeList(aNode: TDOMNode): TDOMNodeList;
|
||||||
function GetElementList(aNode: TDOMNode; const tagName: DOMString): TDOMNodeList;
|
function GetElementList(aNode: TDOMNode; const nsURI, aLocalName: DOMString; UseNS: Boolean): TDOMNodeList;
|
||||||
function GetElementList(aNode: TDOMNode; const nsURI, aLocalName: DOMString): TDOMNodeList;
|
procedure NodeListDestroyed(aList: TDOMNodeList);
|
||||||
public
|
public
|
||||||
function IndexOfNS(const nsURI: DOMString; AddIfAbsent: Boolean = False): Integer;
|
function IndexOfNS(const nsURI: DOMString; AddIfAbsent: Boolean = False): Integer;
|
||||||
property DocType: TDOMDocumentType read GetDocType;
|
property DocType: TDOMDocumentType read GetDocType;
|
||||||
@ -1342,7 +1343,9 @@ destructor TDOMNodeList.Destroy;
|
|||||||
begin
|
begin
|
||||||
if (FNode is TDOMNode_WithChildren) and
|
if (FNode is TDOMNode_WithChildren) and
|
||||||
(TDOMNode_WithChildren(FNode).FChildNodes = Self) then
|
(TDOMNode_WithChildren(FNode).FChildNodes = Self) then
|
||||||
TDOMNode_WithChildren(FNode).FChildNodes := nil;
|
TDOMNode_WithChildren(FNode).FChildNodes := nil
|
||||||
|
else
|
||||||
|
FNode.FOwnerDocument.NodeListDestroyed(Self);
|
||||||
FList.Free;
|
FList.Free;
|
||||||
inherited Destroy;
|
inherited Destroy;
|
||||||
end;
|
end;
|
||||||
@ -1780,12 +1783,14 @@ begin
|
|||||||
FNamespaces[1] := stduri_xml;
|
FNamespaces[1] := stduri_xml;
|
||||||
FNamespaces[2] := stduri_xmlns;
|
FNamespaces[2] := stduri_xmlns;
|
||||||
FEmptyNode := TDOMElement.Create(Self);
|
FEmptyNode := TDOMElement.Create(Self);
|
||||||
|
FNodeLists := THashTable.Create(32, True);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
destructor TDOMDocument.Destroy;
|
destructor TDOMDocument.Destroy;
|
||||||
begin
|
begin
|
||||||
Include(FFlags, nfDestroying);
|
Include(FFlags, nfDestroying);
|
||||||
FreeAndNil(FIDList); // set to nil before starting destroying children
|
FreeAndNil(FIDList); // set to nil before starting destroying children
|
||||||
|
FNodeLists.Free;
|
||||||
FEmptyNode.Free;
|
FEmptyNode.Free;
|
||||||
inherited Destroy;
|
inherited Destroy;
|
||||||
FNames.Free; // free the nametable after inherited has destroyed the children
|
FNames.Free; // free the nametable after inherited has destroyed the children
|
||||||
@ -1992,24 +1997,63 @@ begin
|
|||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TDOMDocument.GetElementList(aNode: TDOMNode; const tagName: DOMString): TDOMNodeList;
|
function TDOMDocument.GetElementList(aNode: TDOMNode; const nsURI, aLocalName: DOMString;
|
||||||
|
UseNS: Boolean): TDOMNodeList;
|
||||||
|
var
|
||||||
|
L: Integer;
|
||||||
|
Key, P: DOMPChar;
|
||||||
|
Item: PHashItem;
|
||||||
begin
|
begin
|
||||||
Result := TDOMElementList.Create(aNode, tagname);
|
L := (sizeof(Pointer) div sizeof(WideChar)) + Length(aLocalName);
|
||||||
end;
|
if UseNS then
|
||||||
|
Inc(L, Length(nsURI)+1);
|
||||||
function TDOMDocument.GetElementList(aNode: TDOMNode; const nsURI, aLocalName: DOMString): TDOMNodeList;
|
GetMem(Key, L*sizeof(WideChar));
|
||||||
begin
|
try
|
||||||
Result := TDOMElementList.Create(aNode, nsURI, aLocalName);
|
// compose the key for hashing
|
||||||
|
P := Key;
|
||||||
|
PPointer(P)^ := aNode;
|
||||||
|
Inc(PPointer(P));
|
||||||
|
Move(DOMPChar(aLocalName)^, P^, Length(aLocalName)*sizeof(WideChar));
|
||||||
|
if UseNS then
|
||||||
|
begin
|
||||||
|
Inc(P, Length(aLocalName));
|
||||||
|
P^ := #12; Inc(P); // separator -- diff ('foo','bar') from 'foobar'
|
||||||
|
Move(DOMPChar(nsURI)^, P^, Length(nsURI)*sizeof(WideChar));
|
||||||
|
end;
|
||||||
|
// try finding in the hashtable
|
||||||
|
Item := FNodeLists.FindOrAdd(Key, L);
|
||||||
|
Result := TDOMNodeList(Item^.Data);
|
||||||
|
if Result = nil then
|
||||||
|
begin
|
||||||
|
if UseNS then
|
||||||
|
Result := TDOMElementList.Create(aNode, nsURI, aLocalName)
|
||||||
|
else
|
||||||
|
Result := TDOMElementList.Create(aNode, aLocalName);
|
||||||
|
Item^.Data := Result;
|
||||||
|
end;
|
||||||
|
finally
|
||||||
|
FreeMem(Key);
|
||||||
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TDOMDocument.GetElementsByTagName(const tagname: DOMString): TDOMNodeList;
|
function TDOMDocument.GetElementsByTagName(const tagname: DOMString): TDOMNodeList;
|
||||||
begin
|
begin
|
||||||
Result := GetElementList(Self, tagname);
|
Result := GetElementList(Self, '', tagname, False);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TDOMDocument.GetElementsByTagNameNS(const nsURI, aLocalName: DOMString): TDOMNodeList;
|
function TDOMDocument.GetElementsByTagNameNS(const nsURI, aLocalName: DOMString): TDOMNodeList;
|
||||||
begin
|
begin
|
||||||
Result := GetElementList(Self, nsURI, aLocalName);
|
Result := GetElementList(Self, nsURI, aLocalName, True);
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ This is linear hence slow. However:
|
||||||
|
- if user code frees each nodelist ASAP, there are only few items in the hashtable
|
||||||
|
- if user code does not free nodelists, this is not called at all.
|
||||||
|
}
|
||||||
|
procedure TDOMDocument.NodeListDestroyed(aList: TDOMNodeList);
|
||||||
|
begin
|
||||||
|
if not (nfDestroying in FFlags) then
|
||||||
|
FNodeLists.RemoveData(aList);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TDOMDocument.CreateAttributeNS(const nsURI,
|
function TDOMDocument.CreateAttributeNS(const nsURI,
|
||||||
@ -2382,12 +2426,12 @@ end;
|
|||||||
|
|
||||||
function TDOMElement.GetElementsByTagName(const name: DOMString): TDOMNodeList;
|
function TDOMElement.GetElementsByTagName(const name: DOMString): TDOMNodeList;
|
||||||
begin
|
begin
|
||||||
Result := FOwnerDocument.GetElementList(Self, name);
|
Result := FOwnerDocument.GetElementList(Self, '', name, False);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TDOMElement.GetElementsByTagNameNS(const nsURI, aLocalName: DOMString): TDOMNodeList;
|
function TDOMElement.GetElementsByTagNameNS(const nsURI, aLocalName: DOMString): TDOMNodeList;
|
||||||
begin
|
begin
|
||||||
Result := FOwnerDocument.GetElementList(Self, nsURI, aLocalName);
|
Result := FOwnerDocument.GetElementList(Self, nsURI, aLocalName, True);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TDOMElement.hasAttribute(const name: DOMString): Boolean;
|
function TDOMElement.hasAttribute(const name: DOMString): Boolean;
|
||||||
|
@ -66,6 +66,7 @@ type
|
|||||||
function FindOrAdd(Key: PWideChar; KeyLen: Integer): PHashItem; overload;
|
function FindOrAdd(Key: PWideChar; KeyLen: Integer): PHashItem; overload;
|
||||||
function Get(Key: PWideChar; KeyLen: Integer): TObject;
|
function Get(Key: PWideChar; KeyLen: Integer): TObject;
|
||||||
function Remove(Entry: PHashItem): Boolean;
|
function Remove(Entry: PHashItem): Boolean;
|
||||||
|
function RemoveData(aData: TObject): Boolean;
|
||||||
procedure ForEach(proc: THashForEach; arg: Pointer);
|
procedure ForEach(proc: THashForEach; arg: Pointer);
|
||||||
property Count: LongWord read FCount;
|
property Count: LongWord read FCount;
|
||||||
end;
|
end;
|
||||||
@ -423,7 +424,10 @@ begin
|
|||||||
else
|
else
|
||||||
begin
|
begin
|
||||||
New(Result);
|
New(Result);
|
||||||
SetString(Result^.Key, Key, KeyLength);
|
// SetString for WideStrings trims on zero chars
|
||||||
|
// need to investigate and report
|
||||||
|
SetLength(Result^.Key, KeyLength);
|
||||||
|
Move(Key^, Pointer(Result^.Key)^, KeyLength*sizeof(WideChar));
|
||||||
Result^.HashValue := h;
|
Result^.HashValue := h;
|
||||||
Result^.Data := nil;
|
Result^.Data := nil;
|
||||||
Result^.Next := nil;
|
Result^.Next := nil;
|
||||||
@ -478,6 +482,33 @@ begin
|
|||||||
Result := False;
|
Result := False;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
// this does not free the aData object
|
||||||
|
function THashTable.RemoveData(aData: TObject): Boolean;
|
||||||
|
var
|
||||||
|
i: Integer;
|
||||||
|
chain: PPHashItem;
|
||||||
|
e: PHashItem;
|
||||||
|
begin
|
||||||
|
for i := 0 to FBucketCount-1 do
|
||||||
|
begin
|
||||||
|
chain := @FBucket[i];
|
||||||
|
while Assigned(chain^) do
|
||||||
|
begin
|
||||||
|
if chain^^.Data = aData then
|
||||||
|
begin
|
||||||
|
e := chain^;
|
||||||
|
chain^ := e^.Next;
|
||||||
|
Dispose(e);
|
||||||
|
Dec(FCount);
|
||||||
|
Result := True;
|
||||||
|
Exit;
|
||||||
|
end;
|
||||||
|
chain := @chain^^.Next;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
Result := False;
|
||||||
|
end;
|
||||||
|
|
||||||
procedure THashTable.ForEach(proc: THashForEach; arg: Pointer);
|
procedure THashTable.ForEach(proc: THashForEach; arg: Pointer);
|
||||||
var
|
var
|
||||||
i: Integer;
|
i: Integer;
|
||||||
|
@ -153,7 +153,7 @@
|
|||||||
<item id="cloneNode" result="Node">
|
<item id="cloneNode" result="Node">
|
||||||
<arg>deep</arg>
|
<arg>deep</arg>
|
||||||
</item>
|
</item>
|
||||||
<item id="getElementsByTagName" gc="yes">
|
<item id="getElementsByTagName">
|
||||||
<arg>tagname</arg>
|
<arg>tagname</arg>
|
||||||
</item>
|
</item>
|
||||||
<item id="childNodes"/>
|
<item id="childNodes"/>
|
||||||
@ -240,7 +240,7 @@
|
|||||||
<arg>namespaceURI</arg>
|
<arg>namespaceURI</arg>
|
||||||
<arg>localName</arg>
|
<arg>localName</arg>
|
||||||
</item>
|
</item>
|
||||||
<item id="getElementsByTagNameNS" gc="yes">
|
<item id="getElementsByTagNameNS">
|
||||||
<arg>namespaceURI</arg>
|
<arg>namespaceURI</arg>
|
||||||
<arg>localName</arg>
|
<arg>localName</arg>
|
||||||
</item>
|
</item>
|
||||||
|
Loading…
Reference in New Issue
Block a user