procedure ozdom_setAttributeValue(const aDoc: IDOMDocument; const aElement: IDOMElement; const aAttr: IDOMAttr; aLxAttr: xmlAttrPtr; const aValue: DOMString); procedure _RaiseAlreadyExists; begin raise EDOMException.create(NOT_SUPPORTED_ERR, 'Cannot change the nodeValue of a default attribute if it already exists as a specific attribute.', nil, 'ozdom_setAttributeValue'); end; procedure _SetValueDefault; // The messy DTD-default-attribute stuff. var lxNs: xmlNsPtr; lxAttrDecl: xmlAttributePtr; lxElem: xmlNodePtr; begin // Clone the attribute declaration into a normal attribute and add it to its owner element. // Problematic scenario: // The default attribute is hold by two wrappers. // The first one changes the nodeValue -> a normal attribute is created. // The second one changes the nodeValue -> again, a normal attribute is created. // This will result is two attributes with the same name being created. // We'll solve this by looking up if an equal "normal" attribute already exists; // a bit messy though. lxElem := (aElement as IDOMLxNodeAccess).getLxNodePtr(); lxAttrDecl := xmlAttributePtr(aLxAttr); // Do not allow changes to fixed attributes. if (lxAttrDecl.def = XML_ATTRIBUTE_FIXED) then raise EDOMException.Create(NO_MODIFICATION_ALLOWED_ERR, 'Cannot change the value of a FIXED attribute.', nil, 'ozdom_setAttributeValue'); // Process only, if the default attribute is not already existent as a namespace declaration // attribute (nsDef entry). IOW, do not allow changes to namespace declaration attributes. if // @attrDecl.prefix is "xmlns" if it declares a prefixed namespace. ((lxAttrDecl.prefix <> nil) and (xmlStrEqual(lxAttrDecl.prefix, xmlCharPtr('xmlns')) = 1)) or // @attrDecl.name is "xmlns" if it declares a default namespace. (xmlStrEqual(lxAttrDecl.name, xmlCharPtr('xmlns')) = 1) then begin raise EDOMException.Create(NO_MODIFICATION_ALLOWED_ERR, 'Changing of default (from a DTD) namespace declaration attributes is not supported (yet).', nil, 'ozdom_setAttributeValue'); // TODO -oKB: Support changing of default ns-decl attributes. end; // Process only, if the default attribute was not already converted to a "normal" attribute. // This is needed for the scenario where the default attribute is hold by multiple wrappers. if (ozdom_getNormalAttribute(lxElem, lxAttrDecl.prefix, lxAttrDecl.name) <> nil) then // EXCEPTION. _RaiseAlreadyExists; // Copy the namespace. // Because an attribute declaration carries only the prefix with it, we have to lookup its // namespace. if (lxAttrDecl.prefix <> nil) then begin // TODO 1 -oKB: We need a *strict* ns-lookup-by-prefix function in Libxml2. if (ozdom_searchNsByPrefixStrict(lxElem.doc, lxElem, lxAttrDecl.prefix, lxNs) <> 1) then raise EDOMException.Create(NAMESPACE_ERR, Format('No namespace declaration in scope for the prefix "%s".', [UTF8Decode(lxAttrDecl.prefix)]), nil, 'ozdom_setAttributeValue'); end else lxNs := nil; // Clone the default attr; i.e. create a "specified" attribute. // Don't clone the value, since we are going to change it afterwards anyway. aLxAttr := ozdom_cloneAttrDecl(lxAttrDecl, aDoc, false, lxNs); if (aLxAttr = nil) then raise EDOMException.Create(NAMESPACE_ERR, 'Failed to clone a default attribute.', nil, 'ozdom_setAttributeValue'); // The attr-wrapper will be re-initialized with a clone of the attribute declaration. if (aAttr <> nil) then (aAttr as IInternalDOMNode).initInternalStructure(xmlNodePtr(aLxAttr)); // Append it to the element's attributes. ozdom_appendAttributeRaw(lxElem, aLxAttr); // Set the value. if (aValue <> '') then xmlAddChild(xmlNodePtr(aLxAttr), xmlNewDocText(lxElem.doc, xmlCharPtr(UTF8Encode(aValue)))); end; procedure _RemoveContent; var cur, tmp: xmlNodePtr; begin // Don't free the nodes, since they might be referenced by wrappers. // xmlFreeNodeList(aLxAttr.children); // TODO -oKB: This is not very efficient, but how to do it otherwise? cur := aLxAttr.children; while (cur <> nil) do begin tmp := cur; cur := cur.next; tmp.next := nil; tmp.prev := nil; tmp.parent := nil; (aDoc as IInternalDOMDocument).addOrphanNode(xmlNodePtr(tmp)); end; aLxAttr.children := nil; aLxAttr.last := nil; end; begin if (aLxAttr.type_ = XML_ATTRIBUTE_DECL) then begin _SetValueDefault(); end else begin if (aLxAttr.children <> nil) then begin _RemoveContent(); end; // Set the value. if (aValue <> '') then xmlAddChild(xmlNodePtr(aLxAttr), xmlNewDocText(aLxAttr.doc, xmlCharPtr(UTF8Encode(aValue)))); end; end; procedure TDOMElement.internalSetAttributeNS(const aNamespaceURI, aPrefix, aLocalName, aValue: DOMString); var lxPrefix, lxNamespaceURI, lxLocalName: xmlCharPtr; lxAttr: xmlAttrPtr; begin lxLocalName := xmlCharPtr(UTF8Encode(aLocalName)); if (aPrefix = '') then lxPrefix := nil else lxPrefix := xmlCharPtr(UTF8Encode(aPrefix)); if (aNamespaceURI = '') then lxNamespaceURI := nil else lxNamespaceURI := xmlCharPtr(UTF8Encode(aNamespaceURI)); // xmlHasNSProp() will also return default/fixed attribute declarations of a DTD. lxAttr := xmlHasNsProp(fXmlNode, lxLocalName, lxNamespaceURI); // Do not modify fixed attributes. if (lxAttr <> nil) then check_fixedAttr(lxAttr); // Create a new attribute-node if needed. if (lxAttr = nil) then begin lxAttr := xmlNewDocProp(fXmlNode.doc, lxLocalName, nil); if (lxAttr = nil) then raise EDOMException.Create(0, 'Internal Libxml2 error: xmlNewDocProp() failed.', self, 'internalSetAttributeNS'); // Aquire the namespace structure. if (lxNamespaceURI <> nil) then begin lxAttr.ns := (fOwnerDocument as IInternalDOMDocument).acquireNs(lxNamespaceURI, lxPrefix); if (lxAttr.ns = nil) then raise EDOMException.Create(0, 'Failed to acquire a namespace structure.', self, 'internalSetAttributeNS'); end; // Append it to the element's attributes. ozdom_appendAttributeRaw(fXmlNode, lxAttr); end; // Set the attribute's value. ozdom_setAttributeValue(fOwnerDocument, self, nil, lxAttr, aValue); (fOwnerDocument as IInternalDOMDocument).switchOnChanged; end; procedure TDOMElement.internalSetNSDeclarationAttribute(const aPrefix, aNamespaceURI: DOMString); var lxNs: xmlNsPtr; begin lxNs := ozdom_getDeclaredNamespaceByPrefix(fXmlNode, aPrefix); if (lxNs = nil) then begin // Create a new namespace declaration attribute. // This puts the ns into the nsDef list. lxNs := ozdom_CreateNsDeclAttr(fXmlNode, aNamespaceURI, aPrefix); (fOwnerDocument as IInternalDOMDocument).switchOnChanged; end else begin // TODO -oKB: We do not yet support changing of the value of a namespace declaration attribute. // Remove it and add a new one as a workaround. if (strComp(lxNs.href, xmlCharPtr(UTF8Encode(aNamespaceURI))) <> 0) then raise EDOMException.Create(NO_MODIFICATION_ALLOWED_ERR, 'Cannot change the value of a ' + 'namespace declaration attribute.', self, 'setAttribute'); end; end;