diff --git a/content/xul/content/src/nsXULAttributeValue.cpp b/content/xul/content/src/nsXULAttributeValue.cpp index 5d4d50de259..e87cd178d81 100644 --- a/content/xul/content/src/nsXULAttributeValue.cpp +++ b/content/xul/content/src/nsXULAttributeValue.cpp @@ -80,7 +80,8 @@ nsresult nsXULAttributeValue::GetValue( nsAWritableString& aResult ) } -nsresult nsXULAttributeValue::SetValue(const nsAReadableString& aValue, PRBool forceAtom) +nsresult nsXULAttributeValue::SetValue(const nsAReadableString& aValue, + PRBool forceAtom) { nsCOMPtr newAtom; @@ -89,7 +90,10 @@ nsresult nsXULAttributeValue::SetValue(const nsAReadableString& aValue, PRBool f // atom table: the style system frequently asks for it, and if the // table is "unprimed" we see quite a bit of thrashing as the 'id' // value is repeatedly added and then removed from the atom table. - if ((aValue.Length() <= kMaxAtomValueLength) || forceAtom) + + PRUint32 len = aValue.Length(); + + if (len && ((len <= kMaxAtomValueLength) || forceAtom)) { newAtom = getter_AddRefs( NS_NewAtom(aValue) ); } @@ -104,10 +108,14 @@ nsresult nsXULAttributeValue::SetValue(const nsAReadableString& aValue, PRBool f mValue = (void*)(PRWord(newAtom.get()) | kAtomType); } else { - PRUnichar* str = ToNewUnicode(aValue); - if (! str) - return NS_ERROR_OUT_OF_MEMORY; - + PRUnichar* str = nsnull; + + if (len) { + str = ToNewUnicode(aValue); + if (! str) + return NS_ERROR_OUT_OF_MEMORY; + } + mValue = str; } diff --git a/dom/public/idl/base/domstubs.idl b/dom/public/idl/base/domstubs.idl index ec9534c8b9b..18220abafe2 100644 --- a/dom/public/idl/base/domstubs.idl +++ b/dom/public/idl/base/domstubs.idl @@ -45,15 +45,13 @@ inline PRBool DOMStringIsNull(const nsAReadableString& aString) { - // Null string code goes here. - - return PR_FALSE; + return aString.IsVoid(); } inline void SetDOMStringToNull(nsAWritableString& aString) { aString.Truncate(); - // Set aString to null here. + aString.SetIsVoid(PR_TRUE); } %} diff --git a/js/src/xpconnect/src/xpcconvert.cpp b/js/src/xpconnect/src/xpcconvert.cpp index 4d789dc296d..4aa822c3ed7 100644 --- a/js/src/xpconnect/src/xpcconvert.cpp +++ b/js/src/xpconnect/src/xpcconvert.cpp @@ -290,11 +290,18 @@ XPCConvert::NativeData2JS(XPCCallContext& ccx, jsval* d, const void* s, if(!p) break; - JSString *str = XPCStringConvert::ReadableToJSString(cx, *p); - if(!str) - return JS_FALSE; + if(!p->IsVoid()) { + JSString *str = + XPCStringConvert::ReadableToJSString(cx, *p); + if(!str) + return JS_FALSE; + + *d = STRING_TO_JSVAL(str); + } + + // *d is defaulted to JSVAL_NULL so no need to set it + // again if p is a "void" string - *d = STRING_TO_JSVAL(str); break; } @@ -535,7 +542,6 @@ XPCConvert::JSData2Native(XPCCallContext& ccx, void* d, jsval s, case nsXPTType::T_DOMSTRING: { static const NS_NAMED_LITERAL_STRING(sEmptyString, ""); - static const NS_NAMED_LITERAL_STRING(sNullString, "null"); static const NS_NAMED_LITERAL_STRING(sVoidString, "undefined"); const PRUnichar* chars; @@ -548,14 +554,7 @@ XPCConvert::JSData2Native(XPCCallContext& ccx, void* d, jsval s, chars = sVoidString.get(); length = sVoidString.Length(); } - else if(JSVAL_IS_NULL(s)) - { - // XXX We don't yet have a way to represent a null nsAXXXString - // XXX Do we *want* to use "null"? - chars = sNullString.get(); - length = sNullString.Length(); - } - else + else if(!JSVAL_IS_NULL(s)) { str = JS_ValueToString(cx, s); if(!str) @@ -577,8 +576,6 @@ XPCConvert::JSData2Native(XPCCallContext& ccx, void* d, jsval s, } } - NS_ASSERTION(chars, "I must be really confused"); - if(useAllocator) { if(str) @@ -591,13 +588,22 @@ XPCConvert::JSData2Native(XPCCallContext& ccx, void* d, jsval s, // Ask for the shared buffer handle, which will root the // string. if(isNewString && ! wrapper->GetSharedBufferHandle()) - return JS_FALSE; + return JS_FALSE; + + *((nsAReadableString**)d) = wrapper; + } + else if(JSVAL_IS_NULL(s)) + { + XPCReadableJSStringWrapper *wrapper = + new XPCReadableJSStringWrapper(); + if(!wrapper) + return JS_FALSE; *((nsAReadableString**)d) = wrapper; } else { - nsAReadableString *rs = new nsString(chars, length); + nsAReadableString *rs = new nsAutoString(chars, length); if(!rs) return JS_FALSE; *((nsAReadableString**)d) = rs; @@ -606,7 +612,11 @@ XPCConvert::JSData2Native(XPCCallContext& ccx, void* d, jsval s, else { nsAWritableString* ws = *((nsAWritableString**)d); - ws->Assign(chars); + + if(JSVAL_IS_NULL(s)) + ws->SetIsVoid(PR_TRUE); + else + ws->Assign(chars); } return JS_TRUE; } diff --git a/js/src/xpconnect/src/xpcprivate.h b/js/src/xpconnect/src/xpcprivate.h index fee677f6a01..d54bbdaa3a3 100644 --- a/js/src/xpconnect/src/xpcprivate.h +++ b/js/src/xpconnect/src/xpcprivate.h @@ -2212,11 +2212,16 @@ class XPCReadableJSStringWrapper : public nsDependentString public: XPCReadableJSStringWrapper(JSString *str) : nsDependentString(NS_REINTERPRET_CAST(PRUnichar *, - JS_GetStringChars(str)), + JS_GetStringChars(str)), JS_GetStringLength(str)), mStr(str), mBufferHandle(0), mHandleIsShared(JS_FALSE) { } + XPCReadableJSStringWrapper() : + nsDependentString(nsnull, (PRUint32)0), mStr(nsnull), + mBufferHandle(nsnull), mHandleIsShared(JS_FALSE) + { } + ~XPCReadableJSStringWrapper(); // buffer-handle accessors @@ -2230,6 +2235,11 @@ public: return BufferHandle(JS_TRUE); } + PRBool IsVoid() const + { + return mStr == nsnull; + } + protected: struct WrapperBufferHandle : public nsSharedBufferHandleWithAllocator @@ -2267,6 +2277,41 @@ protected: JSBool mHandleIsShared; }; +// "voidable" nsAString implementation +class XPCVoidableString : public nsAutoString +{ +public: + XPCVoidableString() : + nsAutoString(), mIsVoid(PR_FALSE) + { } + + char_type* GetWritableFragment(nsWritableFragment& aFragment, + nsFragmentRequest aRequest, + PRUint32 aOffset) + { + mIsVoid = PR_FALSE; + + return nsAutoString::GetWritableFragment(aFragment, aRequest, aOffset); + } + + PRBool IsVoid() const + { + return mIsVoid; + } + + void SetIsVoid(PRBool aVoid) + { + if(aVoid && !mIsVoid) { + Truncate(); + } + + mIsVoid = aVoid; + } + +protected: + PRBool mIsVoid; +}; + // readable string conversions, static methods only class XPCStringConvert { diff --git a/js/src/xpconnect/src/xpcstring.cpp b/js/src/xpconnect/src/xpcstring.cpp index e2d14250456..f23e7ed7f5a 100644 --- a/js/src/xpconnect/src/xpcstring.cpp +++ b/js/src/xpconnect/src/xpcstring.cpp @@ -77,6 +77,11 @@ XPCReadableJSStringWrapper::~XPCReadableJSStringWrapper() const nsSharedBufferHandle* XPCReadableJSStringWrapper::BufferHandle(JSBool shared) const { + if (!mStr) { + // This is a "void" string, no buffer handle available + return nsnull; + } + XPCReadableJSStringWrapper * mutable_this = NS_CONST_CAST(XPCReadableJSStringWrapper *, this); diff --git a/js/src/xpconnect/src/xpcwrappednative.cpp b/js/src/xpconnect/src/xpcwrappednative.cpp index 8fa1ade330b..38045ecd934 100644 --- a/js/src/xpconnect/src/xpcwrappednative.cpp +++ b/js/src/xpconnect/src/xpcwrappednative.cpp @@ -1750,7 +1750,7 @@ XPCWrappedNative::CallMethod(XPCCallContext& ccx, // Is an 'out' DOMString. Make a new nsAWritableString // now and then continue in order to skip the call to // JSData2Native - if(!(dp->val.p = new nsString())) + if(!(dp->val.p = new XPCVoidableString())) { JS_ReportOutOfMemory(ccx); goto done; diff --git a/layout/xul/base/src/outliner/src/nsOutlinerBodyFrame.cpp b/layout/xul/base/src/outliner/src/nsOutlinerBodyFrame.cpp index a231e67075c..f8dc20c9f44 100644 --- a/layout/xul/base/src/outliner/src/nsOutlinerBodyFrame.cpp +++ b/layout/xul/base/src/outliner/src/nsOutlinerBodyFrame.cpp @@ -172,8 +172,10 @@ nsOutlinerColumn::nsOutlinerColumn(nsIContent* aColElement, nsIFrame* aFrame) // Fetch the ID. mColElement->GetAttr(kNameSpaceID_None, nsHTMLAtoms::id, mID); - // Cache the ID as an atom. - mIDAtom = getter_AddRefs(NS_NewAtom(mID)); + // If we have an ID, cache the ID as an atom. + if (!mID.IsEmpty()) { + mIDAtom = getter_AddRefs(NS_NewAtom(mID)); + } nsCOMPtr styleContext; aFrame->GetStyleContext(getter_AddRefs(styleContext)); diff --git a/layout/xul/base/src/tree/src/nsTreeBodyFrame.cpp b/layout/xul/base/src/tree/src/nsTreeBodyFrame.cpp index a231e67075c..f8dc20c9f44 100644 --- a/layout/xul/base/src/tree/src/nsTreeBodyFrame.cpp +++ b/layout/xul/base/src/tree/src/nsTreeBodyFrame.cpp @@ -172,8 +172,10 @@ nsOutlinerColumn::nsOutlinerColumn(nsIContent* aColElement, nsIFrame* aFrame) // Fetch the ID. mColElement->GetAttr(kNameSpaceID_None, nsHTMLAtoms::id, mID); - // Cache the ID as an atom. - mIDAtom = getter_AddRefs(NS_NewAtom(mID)); + // If we have an ID, cache the ID as an atom. + if (!mID.IsEmpty()) { + mIDAtom = getter_AddRefs(NS_NewAtom(mID)); + } nsCOMPtr styleContext; aFrame->GetStyleContext(getter_AddRefs(styleContext)); diff --git a/xpcom/ds/nsAtomTable.cpp b/xpcom/ds/nsAtomTable.cpp index 7dc646e0c01..1d7cde6caf2 100644 --- a/xpcom/ds/nsAtomTable.cpp +++ b/xpcom/ds/nsAtomTable.cpp @@ -68,7 +68,7 @@ AtomTableGetKey(PLDHashTable *table, PLDHashEntryHdr *entry) PR_STATIC_CALLBACK(PLDHashNumber) AtomTableHashKey(PLDHashTable *table, const void *key) { - return nsCRT::HashCode(NS_STATIC_CAST(const PRUnichar*,key)); + return nsCRT::HashCode(NS_STATIC_CAST(const PRUnichar*, key)); } PR_STATIC_CALLBACK(PRBool) @@ -273,6 +273,12 @@ static AtomTableEntry* GetAtomHashEntry(const nsAString& aString) NS_COM nsIAtom* NS_NewAtom( const nsAString& aString ) { + if (aString.IsEmpty()) { + NS_ERROR("Atom requested for empty string!"); + + return nsnull; + } + AtomTableEntry *he = GetAtomHashEntry(aString); AtomImpl* atom = he->mAtom;