michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsArrayEnumerator.h" michael@0: #include "nsID.h" michael@0: #include "nsCOMArray.h" michael@0: #include "nsUnicharInputStream.h" michael@0: #include "nsPrintfCString.h" michael@0: #include "nsAutoPtr.h" michael@0: michael@0: #define PL_ARENA_CONST_ALIGN_MASK 3 michael@0: #include "nsPersistentProperties.h" michael@0: #include "nsIProperties.h" michael@0: michael@0: struct PropertyTableEntry : public PLDHashEntryHdr michael@0: { michael@0: // both of these are arena-allocated michael@0: const char *mKey; michael@0: const char16_t *mValue; michael@0: }; michael@0: michael@0: static char16_t* michael@0: ArenaStrdup(const nsAFlatString& aString, PLArenaPool* aArena) michael@0: { michael@0: void *mem; michael@0: // add one to include the null terminator michael@0: int32_t len = (aString.Length()+1) * sizeof(char16_t); michael@0: PL_ARENA_ALLOCATE(mem, aArena, len); michael@0: NS_ASSERTION(mem, "Couldn't allocate space!\n"); michael@0: if (mem) { michael@0: memcpy(mem, aString.get(), len); michael@0: } michael@0: return static_cast(mem); michael@0: } michael@0: michael@0: static char* michael@0: ArenaStrdup(const nsAFlatCString& aString, PLArenaPool* aArena) michael@0: { michael@0: void *mem; michael@0: // add one to include the null terminator michael@0: int32_t len = (aString.Length()+1) * sizeof(char); michael@0: PL_ARENA_ALLOCATE(mem, aArena, len); michael@0: NS_ASSERTION(mem, "Couldn't allocate space!\n"); michael@0: if (mem) michael@0: memcpy(mem, aString.get(), len); michael@0: return static_cast(mem); michael@0: } michael@0: michael@0: static const struct PLDHashTableOps property_HashTableOps = { michael@0: PL_DHashAllocTable, michael@0: PL_DHashFreeTable, michael@0: PL_DHashStringKey, michael@0: PL_DHashMatchStringKey, michael@0: PL_DHashMoveEntryStub, michael@0: PL_DHashClearEntryStub, michael@0: PL_DHashFinalizeStub, michael@0: nullptr, michael@0: }; michael@0: michael@0: // michael@0: // parser stuff michael@0: // michael@0: enum EParserState { michael@0: eParserState_AwaitingKey, michael@0: eParserState_Key, michael@0: eParserState_AwaitingValue, michael@0: eParserState_Value, michael@0: eParserState_Comment michael@0: }; michael@0: michael@0: enum EParserSpecial { michael@0: eParserSpecial_None, // not parsing a special character michael@0: eParserSpecial_Escaped, // awaiting a special character michael@0: eParserSpecial_Unicode // parsing a \Uxxx value michael@0: }; michael@0: michael@0: class nsPropertiesParser michael@0: { michael@0: public: michael@0: nsPropertiesParser(nsIPersistentProperties* aProps) : michael@0: mHaveMultiLine(false), mState(eParserState_AwaitingKey), michael@0: mProps(aProps) {} michael@0: michael@0: void FinishValueState(nsAString& aOldValue) { michael@0: static const char trimThese[] = " \t"; michael@0: mKey.Trim(trimThese, false, true); michael@0: michael@0: // This is really ugly hack but it should be fast michael@0: char16_t backup_char; michael@0: uint32_t minLength = mMinLength; michael@0: if (minLength) michael@0: { michael@0: backup_char = mValue[minLength-1]; michael@0: mValue.SetCharAt('x', minLength-1); michael@0: } michael@0: mValue.Trim(trimThese, false, true); michael@0: if (minLength) michael@0: mValue.SetCharAt(backup_char, minLength-1); michael@0: michael@0: mProps->SetStringProperty(NS_ConvertUTF16toUTF8(mKey), mValue, aOldValue); michael@0: mSpecialState = eParserSpecial_None; michael@0: WaitForKey(); michael@0: } michael@0: michael@0: EParserState GetState() { return mState; } michael@0: michael@0: static NS_METHOD SegmentWriter(nsIUnicharInputStream* aStream, michael@0: void* aClosure, michael@0: const char16_t *aFromSegment, michael@0: uint32_t aToOffset, michael@0: uint32_t aCount, michael@0: uint32_t *aWriteCount); michael@0: michael@0: nsresult ParseBuffer(const char16_t* aBuffer, uint32_t aBufferLength); michael@0: michael@0: private: michael@0: bool ParseValueCharacter( michael@0: char16_t c, // character that is just being parsed michael@0: const char16_t* cur, // pointer to character c in the buffer michael@0: const char16_t* &tokenStart, // string copying is done in blocks as big as michael@0: // possible, tokenStart points to the beginning michael@0: // of this block michael@0: nsAString& oldValue); // when duplicate property is found, new value michael@0: // is stored into hashtable and the old one is michael@0: // placed in this variable michael@0: michael@0: void WaitForKey() { michael@0: mState = eParserState_AwaitingKey; michael@0: } michael@0: michael@0: void EnterKeyState() { michael@0: mKey.Truncate(); michael@0: mState = eParserState_Key; michael@0: } michael@0: michael@0: void WaitForValue() { michael@0: mState = eParserState_AwaitingValue; michael@0: } michael@0: michael@0: void EnterValueState() { michael@0: mValue.Truncate(); michael@0: mMinLength = 0; michael@0: mState = eParserState_Value; michael@0: mSpecialState = eParserSpecial_None; michael@0: } michael@0: michael@0: void EnterCommentState() { michael@0: mState = eParserState_Comment; michael@0: } michael@0: michael@0: nsAutoString mKey; michael@0: nsAutoString mValue; michael@0: michael@0: uint32_t mUnicodeValuesRead; // should be 4! michael@0: char16_t mUnicodeValue; // currently parsed unicode value michael@0: bool mHaveMultiLine; // is TRUE when last processed characters form michael@0: // any of following sequences: michael@0: // - "\\\r" michael@0: // - "\\\n" michael@0: // - "\\\r\n" michael@0: // - any sequence above followed by any michael@0: // combination of ' ' and '\t' michael@0: bool mMultiLineCanSkipN; // TRUE if "\\\r" was detected michael@0: uint32_t mMinLength; // limit right trimming at the end to not trim michael@0: // escaped whitespaces michael@0: EParserState mState; michael@0: // if we see a '\' then we enter this special state michael@0: EParserSpecial mSpecialState; michael@0: nsIPersistentProperties* mProps; michael@0: }; michael@0: michael@0: inline bool IsWhiteSpace(char16_t aChar) michael@0: { michael@0: return (aChar == ' ') || (aChar == '\t') || michael@0: (aChar == '\r') || (aChar == '\n'); michael@0: } michael@0: michael@0: inline bool IsEOL(char16_t aChar) michael@0: { michael@0: return (aChar == '\r') || (aChar == '\n'); michael@0: } michael@0: michael@0: michael@0: bool nsPropertiesParser::ParseValueCharacter( michael@0: char16_t c, const char16_t* cur, const char16_t* &tokenStart, michael@0: nsAString& oldValue) michael@0: { michael@0: switch (mSpecialState) { michael@0: michael@0: // the normal state - look for special characters michael@0: case eParserSpecial_None: michael@0: switch (c) { michael@0: case '\\': michael@0: if (mHaveMultiLine) michael@0: // there is nothing to append to mValue yet michael@0: mHaveMultiLine = false; michael@0: else michael@0: mValue += Substring(tokenStart, cur); michael@0: michael@0: mSpecialState = eParserSpecial_Escaped; michael@0: break; michael@0: michael@0: case '\n': michael@0: // if we detected multiline and got only "\\\r" ignore next "\n" if any michael@0: if (mHaveMultiLine && mMultiLineCanSkipN) { michael@0: // but don't allow another '\n' to be skipped michael@0: mMultiLineCanSkipN = false; michael@0: // Now there is nothing to append to the mValue since we are skipping michael@0: // whitespaces at the beginning of the new line of the multiline michael@0: // property. Set tokenStart properly to ensure that nothing is appended michael@0: // if we find regular line-end or the end of the buffer. michael@0: tokenStart = cur+1; michael@0: break; michael@0: } michael@0: // no break michael@0: michael@0: case '\r': michael@0: // we're done! We have a key and value michael@0: mValue += Substring(tokenStart, cur); michael@0: FinishValueState(oldValue); michael@0: mHaveMultiLine = false; michael@0: break; michael@0: michael@0: default: michael@0: // there is nothing to do with normal characters, michael@0: // but handle multilines correctly michael@0: if (mHaveMultiLine) { michael@0: if (c == ' ' || c == '\t') { michael@0: // don't allow another '\n' to be skipped michael@0: mMultiLineCanSkipN = false; michael@0: // Now there is nothing to append to the mValue since we are skipping michael@0: // whitespaces at the beginning of the new line of the multiline michael@0: // property. Set tokenStart properly to ensure that nothing is appended michael@0: // if we find regular line-end or the end of the buffer. michael@0: tokenStart = cur+1; michael@0: break; michael@0: } michael@0: mHaveMultiLine = false; michael@0: tokenStart = cur; michael@0: } michael@0: break; // from switch on (c) michael@0: } michael@0: break; // from switch on (mSpecialState) michael@0: michael@0: // saw a \ character, so parse the character after that michael@0: case eParserSpecial_Escaped: michael@0: // probably want to start parsing at the next token michael@0: // other characters, like 'u' might override this michael@0: tokenStart = cur+1; michael@0: mSpecialState = eParserSpecial_None; michael@0: michael@0: switch (c) { michael@0: michael@0: // the easy characters - \t, \n, and so forth michael@0: case 't': michael@0: mValue += char16_t('\t'); michael@0: mMinLength = mValue.Length(); michael@0: break; michael@0: case 'n': michael@0: mValue += char16_t('\n'); michael@0: mMinLength = mValue.Length(); michael@0: break; michael@0: case 'r': michael@0: mValue += char16_t('\r'); michael@0: mMinLength = mValue.Length(); michael@0: break; michael@0: case '\\': michael@0: mValue += char16_t('\\'); michael@0: break; michael@0: michael@0: // switch to unicode mode! michael@0: case 'u': michael@0: case 'U': michael@0: mSpecialState = eParserSpecial_Unicode; michael@0: mUnicodeValuesRead = 0; michael@0: mUnicodeValue = 0; michael@0: break; michael@0: michael@0: // a \ immediately followed by a newline means we're going multiline michael@0: case '\r': michael@0: case '\n': michael@0: mHaveMultiLine = true; michael@0: mMultiLineCanSkipN = (c == '\r'); michael@0: mSpecialState = eParserSpecial_None; michael@0: break; michael@0: michael@0: default: michael@0: // don't recognize the character, so just append it michael@0: mValue += c; michael@0: break; michael@0: } michael@0: break; michael@0: michael@0: // we're in the middle of parsing a 4-character unicode value michael@0: // like \u5f39 michael@0: case eParserSpecial_Unicode: michael@0: michael@0: if(('0' <= c) && (c <= '9')) michael@0: mUnicodeValue = michael@0: (mUnicodeValue << 4) | (c - '0'); michael@0: else if(('a' <= c) && (c <= 'f')) michael@0: mUnicodeValue = michael@0: (mUnicodeValue << 4) | (c - 'a' + 0x0a); michael@0: else if(('A' <= c) && (c <= 'F')) michael@0: mUnicodeValue = michael@0: (mUnicodeValue << 4) | (c - 'A' + 0x0a); michael@0: else { michael@0: // non-hex character. Append what we have, and move on. michael@0: mValue += mUnicodeValue; michael@0: mMinLength = mValue.Length(); michael@0: mSpecialState = eParserSpecial_None; michael@0: michael@0: // leave tokenStart at this unknown character, so it gets appended michael@0: tokenStart = cur; michael@0: michael@0: // ensure parsing this non-hex character again michael@0: return false; michael@0: } michael@0: michael@0: if (++mUnicodeValuesRead >= 4) { michael@0: tokenStart = cur+1; michael@0: mSpecialState = eParserSpecial_None; michael@0: mValue += mUnicodeValue; michael@0: mMinLength = mValue.Length(); michael@0: } michael@0: michael@0: break; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: NS_METHOD nsPropertiesParser::SegmentWriter(nsIUnicharInputStream* aStream, michael@0: void* aClosure, michael@0: const char16_t *aFromSegment, michael@0: uint32_t aToOffset, michael@0: uint32_t aCount, michael@0: uint32_t *aWriteCount) michael@0: { michael@0: nsPropertiesParser *parser = michael@0: static_cast(aClosure); michael@0: michael@0: parser->ParseBuffer(aFromSegment, aCount); michael@0: michael@0: *aWriteCount = aCount; michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult nsPropertiesParser::ParseBuffer(const char16_t* aBuffer, michael@0: uint32_t aBufferLength) michael@0: { michael@0: const char16_t* cur = aBuffer; michael@0: const char16_t* end = aBuffer + aBufferLength; michael@0: michael@0: // points to the start/end of the current key or value michael@0: const char16_t* tokenStart = nullptr; michael@0: michael@0: // if we're in the middle of parsing a key or value, make sure michael@0: // the current token points to the beginning of the current buffer michael@0: if (mState == eParserState_Key || michael@0: mState == eParserState_Value) { michael@0: tokenStart = aBuffer; michael@0: } michael@0: michael@0: nsAutoString oldValue; michael@0: michael@0: while (cur != end) { michael@0: michael@0: char16_t c = *cur; michael@0: michael@0: switch (mState) { michael@0: case eParserState_AwaitingKey: michael@0: if (c == '#' || c == '!') michael@0: EnterCommentState(); michael@0: michael@0: else if (!IsWhiteSpace(c)) { michael@0: // not a comment, not whitespace, we must have found a key! michael@0: EnterKeyState(); michael@0: tokenStart = cur; michael@0: } michael@0: break; michael@0: michael@0: case eParserState_Key: michael@0: if (c == '=' || c == ':') { michael@0: mKey += Substring(tokenStart, cur); michael@0: WaitForValue(); michael@0: } michael@0: break; michael@0: michael@0: case eParserState_AwaitingValue: michael@0: if (IsEOL(c)) { michael@0: // no value at all! mimic the normal value-ending michael@0: EnterValueState(); michael@0: FinishValueState(oldValue); michael@0: } michael@0: michael@0: // ignore white space leading up to the value michael@0: else if (!IsWhiteSpace(c)) { michael@0: tokenStart = cur; michael@0: EnterValueState(); michael@0: michael@0: // make sure to handle this first character michael@0: if (ParseValueCharacter(c, cur, tokenStart, oldValue)) michael@0: cur++; michael@0: // If the character isn't consumed, don't do cur++ and parse michael@0: // the character again. This can happen f.e. for char 'X' in sequence michael@0: // "\u00X". This character can be control character and must be michael@0: // processed again. michael@0: continue; michael@0: } michael@0: break; michael@0: michael@0: case eParserState_Value: michael@0: if (ParseValueCharacter(c, cur, tokenStart, oldValue)) michael@0: cur++; michael@0: // See few lines above for reason of doing this michael@0: continue; michael@0: michael@0: case eParserState_Comment: michael@0: // stay in this state till we hit EOL michael@0: if (c == '\r' || c== '\n') michael@0: WaitForKey(); michael@0: break; michael@0: } michael@0: michael@0: // finally, advance to the next character michael@0: cur++; michael@0: } michael@0: michael@0: // if we're still parsing the value and are in eParserSpecial_None, then michael@0: // append whatever we have.. michael@0: if (mState == eParserState_Value && tokenStart && michael@0: mSpecialState == eParserSpecial_None) { michael@0: mValue += Substring(tokenStart, cur); michael@0: } michael@0: // if we're still parsing the key, then append whatever we have.. michael@0: else if (mState == eParserState_Key && tokenStart) { michael@0: mKey += Substring(tokenStart, cur); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsPersistentProperties::nsPersistentProperties() michael@0: : mIn(nullptr) michael@0: { michael@0: mSubclass = static_cast(this); michael@0: michael@0: PL_DHashTableInit(&mTable, &property_HashTableOps, nullptr, michael@0: sizeof(PropertyTableEntry), 20); michael@0: michael@0: PL_INIT_ARENA_POOL(&mArena, "PersistentPropertyArena", 2048); michael@0: } michael@0: michael@0: nsPersistentProperties::~nsPersistentProperties() michael@0: { michael@0: PL_FinishArenaPool(&mArena); michael@0: if (mTable.ops) michael@0: PL_DHashTableFinish(&mTable); michael@0: } michael@0: michael@0: nsresult michael@0: nsPersistentProperties::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult) michael@0: { michael@0: if (aOuter) michael@0: return NS_ERROR_NO_AGGREGATION; michael@0: nsRefPtr props = new nsPersistentProperties(); michael@0: return props->QueryInterface(aIID, aResult); michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS(nsPersistentProperties, nsIPersistentProperties, nsIProperties) michael@0: michael@0: NS_IMETHODIMP michael@0: nsPersistentProperties::Load(nsIInputStream *aIn) michael@0: { michael@0: nsresult rv = nsSimpleUnicharStreamFactory::GetInstance()-> michael@0: CreateInstanceFromUTF8Stream(aIn, getter_AddRefs(mIn)); michael@0: michael@0: if (rv != NS_OK) { michael@0: NS_WARNING("Error creating UnicharInputStream"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: nsPropertiesParser parser(mSubclass); michael@0: michael@0: uint32_t nProcessed; michael@0: // If this 4096 is changed to some other value, make sure to adjust michael@0: // the bug121341.properties test file accordingly. michael@0: while (NS_SUCCEEDED(rv = mIn->ReadSegments(nsPropertiesParser::SegmentWriter, &parser, 4096, &nProcessed)) && michael@0: nProcessed != 0); michael@0: mIn = nullptr; michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: // We may have an unprocessed value at this point michael@0: // if the last line did not have a proper line ending. michael@0: if (parser.GetState() == eParserState_Value) { michael@0: nsAutoString oldValue; michael@0: parser.FinishValueState(oldValue); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsPersistentProperties::SetStringProperty(const nsACString& aKey, michael@0: const nsAString& aNewValue, michael@0: nsAString& aOldValue) michael@0: { michael@0: const nsAFlatCString& flatKey = PromiseFlatCString(aKey); michael@0: PropertyTableEntry *entry = michael@0: static_cast michael@0: (PL_DHashTableOperate(&mTable, flatKey.get(), PL_DHASH_ADD)); michael@0: michael@0: if (entry->mKey) { michael@0: aOldValue = entry->mValue; michael@0: NS_WARNING(nsPrintfCString("the property %s already exists\n", michael@0: flatKey.get()).get()); michael@0: } michael@0: else { michael@0: aOldValue.Truncate(); michael@0: } michael@0: michael@0: entry->mKey = ArenaStrdup(flatKey, &mArena); michael@0: entry->mValue = ArenaStrdup(PromiseFlatString(aNewValue), &mArena); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsPersistentProperties::Save(nsIOutputStream* aOut, const nsACString& aHeader) michael@0: { michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsPersistentProperties::Subclass(nsIPersistentProperties* aSubclass) michael@0: { michael@0: if (aSubclass) { michael@0: mSubclass = aSubclass; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsPersistentProperties::GetStringProperty(const nsACString& aKey, michael@0: nsAString& aValue) michael@0: { michael@0: const nsAFlatCString& flatKey = PromiseFlatCString(aKey); michael@0: michael@0: PropertyTableEntry *entry = michael@0: static_cast michael@0: (PL_DHashTableOperate(&mTable, flatKey.get(), PL_DHASH_LOOKUP)); michael@0: michael@0: if (PL_DHASH_ENTRY_IS_FREE(entry)) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: aValue = entry->mValue; michael@0: return NS_OK; michael@0: } michael@0: michael@0: static PLDHashOperator michael@0: AddElemToArray(PLDHashTable* table, PLDHashEntryHdr *hdr, michael@0: uint32_t i, void *arg) michael@0: { michael@0: nsCOMArray* props = michael@0: static_cast*>(arg); michael@0: PropertyTableEntry* entry = michael@0: static_cast(hdr); michael@0: michael@0: nsPropertyElement *element = michael@0: new nsPropertyElement(nsDependentCString(entry->mKey), michael@0: nsDependentString(entry->mValue)); michael@0: michael@0: props->AppendObject(element); michael@0: michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: nsPersistentProperties::Enumerate(nsISimpleEnumerator** aResult) michael@0: { michael@0: nsCOMArray props; michael@0: michael@0: // We know the necessary size; we can avoid growing it while adding elements michael@0: props.SetCapacity(mTable.entryCount); michael@0: michael@0: // Step through hash entries populating a transient array michael@0: uint32_t n = michael@0: PL_DHashTableEnumerate(&mTable, AddElemToArray, (void *)&props); michael@0: if (n < mTable.entryCount) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: return NS_NewArrayEnumerator(aResult, props); michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // XXX Some day we'll unify the nsIPersistentProperties interface with michael@0: // nsIProperties, but until now... michael@0: michael@0: NS_IMETHODIMP michael@0: nsPersistentProperties::Get(const char* prop, const nsIID & uuid, void* *result) michael@0: { michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsPersistentProperties::Set(const char* prop, nsISupports* value) michael@0: { michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: NS_IMETHODIMP michael@0: nsPersistentProperties::Undefine(const char* prop) michael@0: { michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsPersistentProperties::Has(const char* prop, bool *result) michael@0: { michael@0: PropertyTableEntry *entry = michael@0: static_cast michael@0: (PL_DHashTableOperate(&mTable, prop, PL_DHASH_LOOKUP)); michael@0: michael@0: *result = (entry && PL_DHASH_ENTRY_IS_BUSY(entry)); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsPersistentProperties::GetKeys(uint32_t *count, char ***keys) michael@0: { michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // PropertyElement michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: NS_METHOD michael@0: nsPropertyElement::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult) michael@0: { michael@0: if (aOuter) michael@0: return NS_ERROR_NO_AGGREGATION; michael@0: nsRefPtr propElem = new nsPropertyElement(); michael@0: return propElem->QueryInterface(aIID, aResult); michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS(nsPropertyElement, nsIPropertyElement) michael@0: michael@0: NS_IMETHODIMP michael@0: nsPropertyElement::GetKey(nsACString& aReturnKey) michael@0: { michael@0: aReturnKey = mKey; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsPropertyElement::GetValue(nsAString& aReturnValue) michael@0: { michael@0: aReturnValue = mValue; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsPropertyElement::SetKey(const nsACString& aKey) michael@0: { michael@0: mKey = aKey; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsPropertyElement::SetValue(const nsAString& aValue) michael@0: { michael@0: mValue = aValue; michael@0: return NS_OK; michael@0: } michael@0: michael@0: ////////////////////////////////////////////////////////////////////////////////