mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-10 03:45:46 +00:00
Added CData operations (append, insert, delete, replace, substring).
Added Element {[sg]et,remove}Attribute Check for illegal child in Node ops appendChild, insertBefore and replaceChild.
This commit is contained in:
parent
a85eb3e06f
commit
5998e7b720
@ -76,8 +76,7 @@ struct DOM_NodeOps {
|
||||
JSBool (*replaceChild)(JSContext *cx, DOM_Node *node, DOM_Node *child,
|
||||
DOM_Node *old);
|
||||
JSBool (*removeChild) (JSContext *cx, DOM_Node *node, DOM_Node *old);
|
||||
JSBool (*appendChild) (JSContext *cx, DOM_Node *node,
|
||||
DOM_Node *child);
|
||||
JSBool (*appendChild) (JSContext *cx, DOM_Node *node, DOM_Node *child);
|
||||
|
||||
/* free up Node-private data */
|
||||
void (*destroyNode) (JSContext *cx, DOM_Node *node);
|
||||
@ -116,7 +115,13 @@ DOM_ReflectNodeStub(JSContext *cx, DOM_Node *node);
|
||||
struct DOM_ElementOps {
|
||||
/*
|
||||
* if this succeeds, pre-existing JS reflections (DOM_Attributes)
|
||||
* will be updated as well.
|
||||
* will be updated as well. Removal (removeAttribute, etc.) of an
|
||||
* attribute is signalled by passing a value of NULL. The
|
||||
* setAttribute function is responsible for doing
|
||||
* DOM_SignalException(cx, DOM_INVALID_NAME_ERR) if it's an
|
||||
* invalid name. (The DOM_NO_MODIFICATION_ALLOWED_ERR case is
|
||||
* handled in the DOM code itself, and no call will be made to the
|
||||
* setAttribute handler in that case.)
|
||||
*/
|
||||
JSBool (*setAttribute)(JSContext *cx, DOM_Element *element,
|
||||
const char *name, const char *value);
|
||||
@ -227,10 +232,21 @@ struct DOM_Document {
|
||||
DOM_Node node;
|
||||
};
|
||||
|
||||
typedef enum DOM_CDataOperationCode {
|
||||
CDATA_APPEND,
|
||||
CDATA_INSERT,
|
||||
CDATA_DELETE,
|
||||
CDATA_REPLACE
|
||||
} DOM_CDataOperationCode;
|
||||
|
||||
typedef JSBool (*DOM_CDataOp)(JSContext *cx, DOM_CharacterData *cdata,
|
||||
DOM_CDataOperationCode op);
|
||||
|
||||
struct DOM_CharacterData {
|
||||
DOM_Node node;
|
||||
char *data;
|
||||
uintN len;
|
||||
DOM_CDataOp notify;
|
||||
};
|
||||
|
||||
struct DOM_Text {
|
||||
@ -238,7 +254,7 @@ struct DOM_Text {
|
||||
};
|
||||
|
||||
DOM_Text *
|
||||
DOM_NewText(const char *data, int64 len);
|
||||
DOM_NewText(const char *data, int64 len, DOM_CDataOp notify);
|
||||
|
||||
JSObject *
|
||||
DOM_NewTextObject(JSContext *cx, DOM_Text *text);
|
||||
|
@ -80,6 +80,95 @@ element_setter(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
element_getAttribute(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
|
||||
jsval *rval)
|
||||
{
|
||||
DOM_Element *element;
|
||||
JSString *name;
|
||||
char *value;
|
||||
JSBool cache;
|
||||
|
||||
if (!JS_ConvertArguments(cx, argc, argv, "S", &name))
|
||||
return JS_FALSE;
|
||||
|
||||
element = (DOM_Element *)JS_GetPrivate(cx, obj);
|
||||
if (!element)
|
||||
return JS_TRUE;
|
||||
|
||||
value = element->ops->getAttribute(cx, element, JS_GetStringBytes(name),
|
||||
&cache);
|
||||
if (value) {
|
||||
*rval = STRING_TO_JSVAL(JS_InternString(cx, value));
|
||||
if (!JSVAL_TO_STRING(*rval))
|
||||
return JS_FALSE;
|
||||
} else {
|
||||
*rval = STRING_TO_JSVAL(JS_GetEmptyStringValue(cx));
|
||||
}
|
||||
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
element_setAttribute(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
|
||||
jsval *rval)
|
||||
{
|
||||
DOM_Element *element;
|
||||
JSString *name, *value;
|
||||
|
||||
if (!JS_ConvertArguments(cx, argc, argv, "SS", &name, &value))
|
||||
return JS_FALSE;
|
||||
|
||||
element = (DOM_Element *)JS_GetPrivate(cx, obj);
|
||||
if (!element)
|
||||
return JS_TRUE;
|
||||
|
||||
return element->ops->setAttribute(cx, element, JS_GetStringBytes(name),
|
||||
JS_GetStringBytes(value));
|
||||
|
||||
}
|
||||
|
||||
static JSBool
|
||||
element_removeAttribute(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
|
||||
jsval *rval)
|
||||
{
|
||||
DOM_Element *element;
|
||||
JSString *name;
|
||||
|
||||
if (!JS_ConvertArguments(cx, argc, argv, "S", &name))
|
||||
return JS_FALSE;
|
||||
|
||||
element = (DOM_Element *)JS_GetPrivate(cx, obj);
|
||||
if (!element)
|
||||
return JS_TRUE;
|
||||
|
||||
/* NULL setAttribute call indicates removal */
|
||||
return element->ops->setAttribute(cx, element, JS_GetStringBytes(name),
|
||||
NULL);
|
||||
|
||||
}
|
||||
|
||||
static JSBool
|
||||
element_getAttributeNode(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
|
||||
jsval *rval)
|
||||
{
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
element_setAttributeNode(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
|
||||
jsval *rval)
|
||||
{
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
element_removeAttributeNode(JSContext *cx, JSObject *obj, uintN argc,
|
||||
jsval *argv, jsval *rval)
|
||||
{
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
element_finalize(JSContext *cx, JSObject *obj)
|
||||
{
|
||||
@ -117,8 +206,7 @@ DOM_ObjectForElement(JSContext *cx, DOM_Element *element)
|
||||
}
|
||||
|
||||
static JSBool
|
||||
Element(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
|
||||
jsval *vp)
|
||||
Element(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *vp)
|
||||
{
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
@ -185,6 +185,61 @@ if (refNode->parent != node) { \
|
||||
} \
|
||||
PR_END_MACRO
|
||||
|
||||
static JSBool
|
||||
IsLegalChild(DOM_Node *node, DOM_Node *child)
|
||||
{
|
||||
switch(node->type) {
|
||||
case NODE_TYPE_ATTRIBUTE:
|
||||
if (child->type == NODE_TYPE_TEXT ||
|
||||
child->type == NODE_TYPE_ENTITY_REF)
|
||||
return JS_TRUE;
|
||||
return JS_FALSE;
|
||||
if (child->type == NODE_TYPE_ELEMENT ||
|
||||
child->type == NODE_TYPE_COMMENT ||
|
||||
child->type == NODE_TYPE_TEXT ||
|
||||
child->type == NODE_TYPE_PI ||
|
||||
child->type == NODE_TYPE_CDATA ||
|
||||
child->type == NODE_TYPE_ENTITY_REF)
|
||||
return JS_TRUE;
|
||||
return JS_FALSE;
|
||||
case NODE_TYPE_ELEMENT:
|
||||
case NODE_TYPE_DOCFRAGMENT:
|
||||
case NODE_TYPE_ENTITY_REF:
|
||||
if (child->type == NODE_TYPE_ELEMENT ||
|
||||
child->type == NODE_TYPE_PI ||
|
||||
child->type == NODE_TYPE_COMMENT ||
|
||||
child->type == NODE_TYPE_TEXT ||
|
||||
child->type == NODE_TYPE_CDATA ||
|
||||
child->type == NODE_TYPE_ENTITY_REF)
|
||||
return JS_TRUE;
|
||||
return JS_FALSE;
|
||||
case NODE_TYPE_DOCUMENT:
|
||||
if(child->type == NODE_TYPE_PI ||
|
||||
child->type == NODE_TYPE_COMMENT ||
|
||||
child->type == NODE_TYPE_DOCTYPE)
|
||||
return JS_TRUE;
|
||||
if (child->type == NODE_TYPE_ELEMENT)
|
||||
/* XXX check to make sure it's the only one */
|
||||
return JS_TRUE;
|
||||
return JS_FALSE;
|
||||
case NODE_TYPE_DOCTYPE:
|
||||
if (child->type == NODE_TYPE_NOTATION ||
|
||||
child->type == NODE_TYPE_ENTITY)
|
||||
return JS_TRUE;
|
||||
return JS_FALSE;
|
||||
default: /* PI, COMMENT, TEXT, CDATA, ENTITY, NOTATION */
|
||||
return JS_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
#define CHECK_LEGAL_CHILD(node, child) \
|
||||
PR_BEGIN_MACRO \
|
||||
if (!IsLegalChild(node, child)) { \
|
||||
DOM_SignalException(cx, DOM_HIERARCHY_REQUEST_ERR); \
|
||||
return JS_FALSE; \
|
||||
} \
|
||||
PR_END_MACRO
|
||||
|
||||
static JSBool
|
||||
node_insertBefore(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
|
||||
jsval *vp)
|
||||
@ -206,6 +261,7 @@ node_insertBefore(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
CHECK_LEGAL_CHILD(node, newNode);
|
||||
FAIL_UNLESS_CHILD(node, refNode);
|
||||
REMOVE_FROM_TREE(newNode);
|
||||
|
||||
@ -240,6 +296,7 @@ node_replaceChild(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
|
||||
!newNode || !oldNode)
|
||||
return JS_TRUE;
|
||||
|
||||
CHECK_LEGAL_CHILD(node, newNode);
|
||||
FAIL_UNLESS_CHILD(node, oldNode);
|
||||
REMOVE_FROM_TREE(newNode);
|
||||
|
||||
@ -290,6 +347,7 @@ node_appendChild(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
|
||||
if (!node || !newNode)
|
||||
return JS_TRUE;
|
||||
|
||||
CHECK_LEGAL_CHILD(node, newNode);
|
||||
REMOVE_FROM_TREE(newNode);
|
||||
|
||||
newNode->parent = node;
|
||||
|
@ -73,6 +73,183 @@ cdata_setter(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
cdata_substringData(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
|
||||
jsval *vp)
|
||||
{
|
||||
JSString *substr;
|
||||
uint32 offset, count;
|
||||
DOM_CharacterData *cdata;
|
||||
|
||||
if (!JS_ConvertArguments(cx, argc, argv, "uu", &offset, &count))
|
||||
return JS_FALSE;
|
||||
|
||||
cdata = (DOM_CharacterData *)JS_GetPrivate(cx, obj);
|
||||
if (!cdata) {
|
||||
*vp = STRING_TO_JSVAL(JS_GetEmptyStringValue(cx));
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
if (offset > cdata->len || offset < 0 || count < 0) {
|
||||
DOM_SignalException(cx, DOM_INDEX_SIZE_ERR);
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
if (offset + count > cdata->len)
|
||||
count = cdata->len - offset;
|
||||
|
||||
substr = JS_NewStringCopyN(cx, cdata->data + offset, count);
|
||||
if (!substr)
|
||||
return JS_FALSE;
|
||||
|
||||
*vp = STRING_TO_JSVAL(substr);
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
cdata_appendData(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
|
||||
jsval *vp)
|
||||
{
|
||||
JSString *newData;
|
||||
uint32 newlen;
|
||||
DOM_CharacterData *cdata;
|
||||
|
||||
if (!JS_ConvertArguments(cx, argc, argv, "S", &newData))
|
||||
return JS_FALSE;
|
||||
|
||||
cdata = (DOM_CharacterData *)JS_GetPrivate(cx, obj);
|
||||
if (!cdata)
|
||||
return JS_TRUE;
|
||||
|
||||
newlen = JS_GetStringLength(newData);
|
||||
|
||||
cdata->data = XP_REALLOC(cdata->data, cdata->len + newlen);
|
||||
if (!cdata->data)
|
||||
return JS_FALSE;
|
||||
|
||||
XP_MEMCPY(cdata->data + cdata->len, JS_GetStringBytes(newData),
|
||||
newlen);
|
||||
cdata->len += newlen;
|
||||
|
||||
return cdata->notify(cx, cdata, CDATA_APPEND);
|
||||
}
|
||||
|
||||
static JSBool
|
||||
cdata_insertData(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
|
||||
jsval *vp)
|
||||
{
|
||||
uint32 offset, newlen;
|
||||
JSString *newData;
|
||||
char *data2;
|
||||
DOM_CharacterData *cdata;
|
||||
|
||||
if (!JS_ConvertArguments(cx, argc, argv, "uS", &offset, &newData))
|
||||
return JS_FALSE;
|
||||
|
||||
cdata = (DOM_CharacterData *)JS_GetPrivate(cx, obj);
|
||||
if (!cdata)
|
||||
return JS_TRUE;
|
||||
|
||||
if (offset < 0 || offset > cdata->len) {
|
||||
DOM_SignalException(cx, DOM_INDEX_SIZE_ERR);
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
newlen = JS_GetStringLength(newData);
|
||||
data2 = XP_ALLOC(cdata->len + newlen);
|
||||
if (!data2)
|
||||
return JS_FALSE;
|
||||
|
||||
XP_MEMCPY(data2, cdata->data, offset);
|
||||
XP_MEMCPY(data2 + offset, JS_GetStringBytes(newData), newlen);
|
||||
XP_MEMCPY(data2 + offset + newlen, cdata->data + offset,
|
||||
cdata->len - offset);
|
||||
|
||||
XP_FREE(cdata->data);
|
||||
cdata->data = data2;
|
||||
cdata->len += newlen;
|
||||
|
||||
return cdata->notify(cx, cdata, CDATA_INSERT);
|
||||
}
|
||||
|
||||
static JSBool
|
||||
cdata_deleteData(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
|
||||
jsval *vp)
|
||||
{
|
||||
uint32 offset, count;
|
||||
DOM_CharacterData *cdata;
|
||||
char *data2;
|
||||
|
||||
if (!JS_ConvertArguments(cx, argc, argv, "uu", &offset, &count))
|
||||
return JS_FALSE;
|
||||
|
||||
cdata = (DOM_CharacterData *)JS_GetPrivate(cx, obj);
|
||||
if (!cdata)
|
||||
return JS_TRUE;
|
||||
|
||||
if (offset < 0 || offset > cdata->len || count < 0) {
|
||||
DOM_SignalException(cx, DOM_INDEX_SIZE_ERR);
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
if (offset + count > cdata->len)
|
||||
count = cdata->len - offset;
|
||||
|
||||
data2 = XP_ALLOC(cdata->len - count);
|
||||
if (!data2)
|
||||
return JS_FALSE;
|
||||
XP_MEMCPY(data2, cdata->data, offset);
|
||||
XP_MEMCPY(data2 + offset, cdata->data + offset + count,
|
||||
cdata->len - offset - count);
|
||||
XP_FREE(cdata->data);
|
||||
cdata->data = data2;
|
||||
cdata->len -= count;
|
||||
|
||||
return cdata->notify(cx, cdata, CDATA_DELETE);
|
||||
}
|
||||
|
||||
static JSBool
|
||||
cdata_replaceData(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
|
||||
jsval *vp)
|
||||
{
|
||||
uint32 offset, count, newlen;
|
||||
JSString *newData;
|
||||
DOM_CharacterData *cdata;
|
||||
|
||||
char *data2;
|
||||
|
||||
if (!JS_ConvertArguments(cx, argc, argv, "uuS", &offset, &count,
|
||||
&newData))
|
||||
return JS_FALSE;
|
||||
|
||||
cdata = (DOM_CharacterData *)JS_GetPrivate(cx, obj);
|
||||
if (!cdata)
|
||||
return JS_TRUE;
|
||||
|
||||
if (offset < 0 || offset > cdata->len || count < 0) {
|
||||
DOM_SignalException(cx, DOM_INDEX_SIZE_ERR);
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
if (offset + count > cdata->len)
|
||||
count = cdata->len - offset;
|
||||
|
||||
newlen = JS_GetStringLength(newData);
|
||||
|
||||
data2 = XP_ALLOC(cdata->len - count + newlen);
|
||||
if (!data2)
|
||||
return JS_FALSE;
|
||||
XP_MEMCPY(data2, cdata->data, offset);
|
||||
XP_MEMCPY(data2 + offset, JS_GetStringBytes(newData), newlen);
|
||||
XP_MEMCPY(data2 + offset + newlen, cdata->data + offset + count,
|
||||
cdata->len - offset - count);
|
||||
XP_FREE(cdata->data);
|
||||
cdata->data = data2;
|
||||
cdata->len += newlen - count;
|
||||
|
||||
return cdata->notify(cx, cdata, CDATA_REPLACE);
|
||||
}
|
||||
|
||||
static JSClass DOM_CDataClass = {
|
||||
"CharacterData", JSCLASS_HAS_PRIVATE,
|
||||
JS_PropertyStub, JS_PropertyStub, cdata_getter, cdata_setter,
|
||||
@ -88,6 +265,11 @@ static JSPropertySpec cdata_props[] = {
|
||||
};
|
||||
|
||||
static JSFunctionSpec cdata_methods[] = {
|
||||
{"substringData", cdata_substringData, 2},
|
||||
{"appendData", cdata_appendData, 1},
|
||||
{"insertData", cdata_insertData, 2},
|
||||
{"deleteData", cdata_deleteData, 2},
|
||||
{"replaceData", cdata_replaceData, 3},
|
||||
{0}
|
||||
};
|
||||
|
||||
@ -142,7 +324,7 @@ DOM_NewTextObject(JSContext *cx, DOM_Text *text)
|
||||
}
|
||||
|
||||
DOM_Text *
|
||||
DOM_NewText(const char *data, int64 length)
|
||||
DOM_NewText(const char *data, int64 length, DOM_CDataOp notify)
|
||||
{
|
||||
XP_ASSERT((0 && "DOM_NewText NYI"));
|
||||
return NULL;
|
||||
|
Loading…
Reference in New Issue
Block a user