mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-08-18 17:09:16 +02:00
+ Part of DOM level 2 namespace support (handling namespaced attributes)
* Cloning/importing attributes and elements preserves their namespaces * Importing an element does not import non-specified attributes. + Separate, much faster, implementation for element cloning. git-svn-id: trunk@13226 -
This commit is contained in:
parent
398b5806b1
commit
2cd0a1ad27
@ -344,11 +344,10 @@ type
|
||||
function SetNamedItem(arg: TDOMNode): TDOMNode;
|
||||
function RemoveNamedItem(const name: DOMString): TDOMNode;
|
||||
// Introduced in DOM Level 2:
|
||||
function getNamedItemNS(const namespaceURI, localName: DOMString): TDOMNode;
|
||||
function setNamedItemNS(arg: TDOMNode): TDOMNode;
|
||||
function removeNamedItemNS(const namespaceURI,localName: DOMString): TDOMNode;
|
||||
function getNamedItemNS(const namespaceURI, localName: DOMString): TDOMNode; virtual;
|
||||
function setNamedItemNS(arg: TDOMNode): TDOMNode; virtual;
|
||||
function removeNamedItemNS(const namespaceURI,localName: DOMString): TDOMNode; virtual;
|
||||
|
||||
// FIX: made readonly. Reason: Anyone was allowed to insert any node without any checking.
|
||||
property Item[index: LongWord]: TDOMNode read GetItem; default;
|
||||
property Length: LongWord read GetLength;
|
||||
end;
|
||||
@ -788,6 +787,19 @@ const
|
||||
|
||||
implementation
|
||||
|
||||
{ a namespace-enabled NamedNodeMap }
|
||||
type
|
||||
TAttributeMap = class(TDOMNamedNodeMap)
|
||||
private
|
||||
function FindNS(nsIndex: Integer; const aLocalName: DOMString;
|
||||
out Index: LongWord): Boolean;
|
||||
function InternalRemoveNS(const nsURI, aLocalName: DOMString): TDOMNode;
|
||||
public
|
||||
function getNamedItemNS(const namespaceURI, localName: DOMString): TDOMNode; override;
|
||||
function setNamedItemNS(arg: TDOMNode): TDOMNode; override;
|
||||
function removeNamedItemNS(const namespaceURI,localName: DOMString): TDOMNode; override;
|
||||
end;
|
||||
|
||||
// -------------------------------------------------------
|
||||
// DOM Exception
|
||||
// -------------------------------------------------------
|
||||
@ -1551,10 +1563,10 @@ begin
|
||||
Result := nil;
|
||||
end;
|
||||
|
||||
// Note: this *may* raise NOT_SUPPORTED_ERR if the document is e.g. HTML.
|
||||
// This isn't checked now.
|
||||
function TDOMNamedNodeMap.GetNamedItemNS(const namespaceURI, localName: DOMString): TDOMNode;
|
||||
begin
|
||||
// TODO: implement TDOMNamedNodeMap.GetNamedItemNS
|
||||
raise EDOMNotSupported.Create('TDOMNamedNodeMap.GetNamedItemNS');
|
||||
Result := nil;
|
||||
end;
|
||||
|
||||
@ -1608,15 +1620,13 @@ begin
|
||||
end;
|
||||
|
||||
function TDOMNamedNodeMap.SetNamedItemNS(arg: TDOMNode): TDOMNode;
|
||||
var
|
||||
res: Integer;
|
||||
begin
|
||||
// TODO: implement TDOMNamedNodeMap.SetNamedItemNS
|
||||
res := ValidateInsert(arg);
|
||||
if res <> 0 then
|
||||
raise EDOMError.Create(res, 'NamedNodeMap.SetNamedItemNS');
|
||||
|
||||
Result := nil;
|
||||
{ Since the map contains only namespaceless nodes (all having empty
|
||||
localName and namespaceURI properties), a namespaced arg won't match
|
||||
any of them. Therefore, add it using nodeName as key.
|
||||
Note: a namespaceless arg is another story, as it will match *any* node
|
||||
in the map. This can be considered as a flaw in specs. }
|
||||
Result := SetNamedItem(arg);
|
||||
end;
|
||||
|
||||
function TDOMNamedNodeMap.Delete(index: LongWord): TDOMNode;
|
||||
@ -1670,12 +1680,112 @@ end;
|
||||
|
||||
function TDOMNamedNodeMap.RemoveNamedItemNS(const namespaceURI, localName: DOMString): TDOMNode;
|
||||
begin
|
||||
if nfReadOnly in FOwner.FFlags then
|
||||
raise EDOMError.Create(NO_MODIFICATION_ALLOWED_ERR, 'NamedNodeMap.RemoveNamedItemNS');
|
||||
// TODO: Implement TDOMNamedNodeMap.RemoveNamedItemNS
|
||||
// see comments to SetNamedItemNS. Related tests are written clever enough
|
||||
// in the sense they don't expect NO_MODIFICATION_ERR in first place.
|
||||
raise EDOMNotFound.Create('NamedNodeMap.RemoveNamedItemNS');
|
||||
Result := nil;
|
||||
end;
|
||||
|
||||
{ TAttributeMap }
|
||||
|
||||
// Since list is kept sorted by nodeName, we must use linear search here.
|
||||
// This routine is not called while parsing, so parsing speed is not lowered.
|
||||
function TAttributeMap.FindNS(nsIndex: Integer; const aLocalName: DOMString;
|
||||
out Index: LongWord): Boolean;
|
||||
var
|
||||
I: Integer;
|
||||
P: DOMPChar;
|
||||
begin
|
||||
for I := 0 to FList.Count-1 do
|
||||
begin
|
||||
with TDOMAttr(FList.List^[I]) do
|
||||
begin
|
||||
if nsIndex = FNSI.NSIndex then
|
||||
begin
|
||||
P := DOMPChar(FNSI.QName^.Key);
|
||||
if FNSI.PrefixLen > 1 then
|
||||
Inc(P, FNSI.PrefixLen);
|
||||
if CompareDOMStrings(DOMPChar(aLocalName), P, System.Length(aLocalName), System.Length(FNSI.QName^.Key) - FNSI.PrefixLen) = 0 then
|
||||
begin
|
||||
Index := I;
|
||||
Result := True;
|
||||
Exit;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
Result := False;
|
||||
end;
|
||||
|
||||
function TAttributeMap.InternalRemoveNS(const nsURI, aLocalName: DOMString): TDOMNode;
|
||||
var
|
||||
i: Cardinal;
|
||||
nsIndex: Integer;
|
||||
begin
|
||||
Result := nil;
|
||||
nsIndex := FOwner.FOwnerDocument.IndexOfNS(nsURI);
|
||||
if (nsIndex >= 0) and FindNS(nsIndex, aLocalName, i) then
|
||||
begin
|
||||
Result := Delete(I);
|
||||
RestoreDefault(TDOMAttr(Result).FNSI.QName^.Key);
|
||||
end;
|
||||
end;
|
||||
|
||||
function TAttributeMap.getNamedItemNS(const namespaceURI, localName: DOMString): TDOMNode;
|
||||
var
|
||||
nsIndex: Integer;
|
||||
i: LongWord;
|
||||
begin
|
||||
nsIndex := FOwner.FOwnerDocument.IndexOfNS(namespaceURI);
|
||||
if (nsIndex >= 0) and FindNS(nsIndex, localName, i) then
|
||||
Result := TDOMNode(FList.List^[i])
|
||||
else
|
||||
Result := nil;
|
||||
end;
|
||||
|
||||
function TAttributeMap.setNamedItemNS(arg: TDOMNode): TDOMNode;
|
||||
var
|
||||
i: LongWord;
|
||||
res: Integer;
|
||||
Exists: Boolean;
|
||||
begin
|
||||
res := ValidateInsert(arg);
|
||||
if res <> 0 then
|
||||
raise EDOMError.Create(res, 'NamedNodeMap.SetNamedItemNS');
|
||||
|
||||
Result := nil;
|
||||
with TDOMAttr(arg) do
|
||||
begin
|
||||
// calling LocalName is no good... but it is done once
|
||||
if FindNS(FNSI.NSIndex, localName, i) then
|
||||
begin
|
||||
Result := TDOMNode(FList.List^[i]);
|
||||
FList.Delete(i);
|
||||
end;
|
||||
// Do a non-namespace search in order to keep the list sorted on nodeName
|
||||
Exists := Find(FNSI.QName^.Key, i);
|
||||
if Exists and (Result = nil) then // case when arg has no namespace
|
||||
begin
|
||||
Result := TDOMNode(FList.List^[i]);
|
||||
FList.List^[i] := arg;
|
||||
end
|
||||
else
|
||||
FList.Insert(i, arg);
|
||||
end;
|
||||
if Assigned(Result) then
|
||||
TDOMAttr(Result).FOwnerElement := nil;
|
||||
TDOMAttr(arg).FOwnerElement := TDOMElement(FOwner);
|
||||
end;
|
||||
|
||||
function TAttributeMap.removeNamedItemNS(const namespaceURI,
|
||||
localName: DOMString): TDOMNode;
|
||||
begin
|
||||
if nfReadOnly in FOwner.FFlags then
|
||||
raise EDOMError.Create(NO_MODIFICATION_ALLOWED_ERR, 'NamedNodeMap.RemoveNamedItemNS');
|
||||
Result := InternalRemoveNS(namespaceURI, localName);
|
||||
if Result = nil then
|
||||
raise EDOMNotFound.Create('NamedNodeMap.RemoveNamedItemNS');
|
||||
end;
|
||||
|
||||
// -------------------------------------------------------
|
||||
// CharacterData
|
||||
@ -2353,6 +2463,9 @@ end;
|
||||
function TDOMAttr.CloneNode(deep: Boolean; ACloneOwner: TDOMDocument): TDOMNode;
|
||||
begin
|
||||
// Cloned attribute is always specified and carries its children
|
||||
if nfLevel2 in FFlags then
|
||||
Result := ACloneOwner.CreateAttributeNS(namespaceURI, NodeName)
|
||||
else
|
||||
Result := ACloneOwner.CreateAttribute(NodeName);
|
||||
TDOMAttr(Result).FDataType := FDataType;
|
||||
CloneChildren(Result, ACloneOwner);
|
||||
@ -2390,7 +2503,6 @@ begin
|
||||
Include(FFlags, nfDestroying);
|
||||
if Assigned(FOwnerDocument.FIDList) then
|
||||
FOwnerDocument.RemoveID(Self);
|
||||
// FIX: Attribute nodes are now freed by TDOMNamedNodeMap.Destroy
|
||||
FreeAndNil(FAttributes);
|
||||
inherited Destroy;
|
||||
end;
|
||||
@ -2398,12 +2510,45 @@ end;
|
||||
function TDOMElement.CloneNode(deep: Boolean; ACloneOwner: TDOMDocument): TDOMNode;
|
||||
var
|
||||
i: Integer;
|
||||
Attr, AttrClone: TDOMAttr;
|
||||
begin
|
||||
if ACloneOwner <> FOwnerDocument then
|
||||
begin
|
||||
// Importing has to go the hard way...
|
||||
if nfLevel2 in FFlags then
|
||||
Result := ACloneOwner.CreateElementNS(NamespaceURI, NodeName)
|
||||
else
|
||||
Result := ACloneOwner.CreateElement(NodeName);
|
||||
if Assigned(FAttributes) then
|
||||
begin
|
||||
for i := 0 to FAttributes.Length - 1 do
|
||||
TDOMElement(Result).SetAttributeNode(TDOMAttr(FAttributes[i].CloneNode(True, ACloneOwner)));
|
||||
begin
|
||||
Attr := TDOMAttr(FAttributes[i]);
|
||||
// destroy defaulted attributes (if any), it is safe because caller had not seen them yet
|
||||
if Attr.Specified then
|
||||
TDOMElement(Result).SetAttributeNode(TDOMAttr(Attr.CloneNode(True, ACloneOwner))).Free;
|
||||
end;
|
||||
end;
|
||||
end
|
||||
else // Cloning may cheat a little bit.
|
||||
begin
|
||||
Result := FOwnerDocument.Alloc(TDOMElement);
|
||||
TDOMElement(Result).Create(FOwnerDocument);
|
||||
TDOMElement(Result).FNSI := FNSI;
|
||||
if nfLevel2 in FFlags then
|
||||
Include(Result.FFlags, nfLevel2);
|
||||
if Assigned(FAttributes) then
|
||||
begin
|
||||
// clone all attributes, but preserve nfSpecified flag
|
||||
for i := 0 to FAttributes.Length - 1 do
|
||||
begin
|
||||
Attr := TDOMAttr(FAttributes[i]);
|
||||
AttrClone := TDOMAttr(Attr.CloneNode(True, ACloneOwner));
|
||||
if not Attr.Specified then
|
||||
Exclude(AttrClone.FFlags, nfSpecified);
|
||||
TDOMElement(Result).SetAttributeNode(AttrClone);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
if deep then
|
||||
CloneChildren(Result, ACloneOwner);
|
||||
@ -2450,7 +2595,7 @@ end;
|
||||
function TDOMElement.GetAttributes: TDOMNamedNodeMap;
|
||||
begin
|
||||
if FAttributes=nil then
|
||||
FAttributes := TDOMNamedNodeMap.Create(Self, ATTRIBUTE_NODE);
|
||||
FAttributes := TAttributeMap.Create(Self, ATTRIBUTE_NODE);
|
||||
Result := FAttributes;
|
||||
end;
|
||||
|
||||
@ -2509,13 +2654,14 @@ procedure TDOMElement.RemoveAttributeNS(const nsURI,
|
||||
aLocalName: DOMString);
|
||||
begin
|
||||
Changing;
|
||||
// TODO: Implement TDOMElement.RemoveAttributeNS
|
||||
raise EDOMNotSupported.Create('TDOMElement.RemoveAttributeNS');
|
||||
if Assigned(FAttributes) then
|
||||
TAttributeMap(FAttributes).InternalRemoveNS(nsURI, aLocalName).Free;
|
||||
end;
|
||||
|
||||
procedure TDOMElement.SetAttributeNS(const nsURI, qualifiedName,
|
||||
value: DOMString);
|
||||
var
|
||||
I: Cardinal;
|
||||
Attr: TDOMAttr;
|
||||
idx, prefIdx: Integer;
|
||||
begin
|
||||
@ -2525,13 +2671,26 @@ begin
|
||||
if prefIdx < 0 then
|
||||
raise EDOMError.Create(-prefIdx, 'Element.SetAttributeNS');
|
||||
|
||||
Attr := Attributes.GetNamedItemNS(nsURI, qualifiedName) as TDOMAttr;
|
||||
if attr = nil then
|
||||
if TAttributeMap(Attributes).FindNS(idx, Copy(qualifiedName, prefIdx+1, MaxInt), I) then
|
||||
begin
|
||||
attr := FOwnerDocument.CreateAttributeNS(nsURI, qualifiedName);
|
||||
// TODO 5: keep sorted!
|
||||
FAttributes.FList.Add(attr);
|
||||
Attr := TDOMAttr(FAttributes[I]);
|
||||
// need to reinsert because the nodeName may change
|
||||
FAttributes.FList.Delete(I);
|
||||
end
|
||||
else
|
||||
begin
|
||||
TDOMNode(Attr) := FOwnerDocument.Alloc(TDOMAttr);
|
||||
Attr.Create(FOwnerDocument);
|
||||
Attr.FOwnerElement := Self;
|
||||
Attr.FNSI.NSIndex := Word(idx);
|
||||
Include(Attr.FFlags, nfLevel2);
|
||||
end;
|
||||
// keep list sorted by DOM Level 1 name
|
||||
FAttributes.Find(qualifiedName, I);
|
||||
FAttributes.FList.Insert(I, Attr);
|
||||
// TODO: rehash properly, same issue as with Node.SetPrefix()
|
||||
Attr.FNSI.QName := FOwnerDocument.FNames.FindOrAdd(DOMPChar(qualifiedName), Length(qualifiedName));
|
||||
Attr.FNSI.PrefixLen := Word(prefIdx);
|
||||
attr.NodeValue := value;
|
||||
end;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user