xpcom/ds/nsPersistentProperties.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/xpcom/ds/nsPersistentProperties.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,683 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +#include "nsArrayEnumerator.h"
    1.10 +#include "nsID.h"
    1.11 +#include "nsCOMArray.h"
    1.12 +#include "nsUnicharInputStream.h"
    1.13 +#include "nsPrintfCString.h"
    1.14 +#include "nsAutoPtr.h"
    1.15 +
    1.16 +#define PL_ARENA_CONST_ALIGN_MASK 3
    1.17 +#include "nsPersistentProperties.h"
    1.18 +#include "nsIProperties.h"
    1.19 +
    1.20 +struct PropertyTableEntry : public PLDHashEntryHdr
    1.21 +{
    1.22 +  // both of these are arena-allocated
    1.23 +  const char *mKey;
    1.24 +  const char16_t *mValue;
    1.25 +};
    1.26 +
    1.27 +static char16_t*
    1.28 +ArenaStrdup(const nsAFlatString& aString, PLArenaPool* aArena)
    1.29 +{
    1.30 +  void *mem;
    1.31 +  // add one to include the null terminator
    1.32 +  int32_t len = (aString.Length()+1) * sizeof(char16_t);
    1.33 +  PL_ARENA_ALLOCATE(mem, aArena, len);
    1.34 +  NS_ASSERTION(mem, "Couldn't allocate space!\n");
    1.35 +  if (mem) {
    1.36 +    memcpy(mem, aString.get(), len);
    1.37 +  }
    1.38 +  return static_cast<char16_t*>(mem);
    1.39 +}
    1.40 +
    1.41 +static char*
    1.42 +ArenaStrdup(const nsAFlatCString& aString, PLArenaPool* aArena)
    1.43 +{
    1.44 +  void *mem;
    1.45 +  // add one to include the null terminator
    1.46 +  int32_t len = (aString.Length()+1) * sizeof(char);
    1.47 +  PL_ARENA_ALLOCATE(mem, aArena, len);
    1.48 +  NS_ASSERTION(mem, "Couldn't allocate space!\n");
    1.49 +  if (mem)
    1.50 +    memcpy(mem, aString.get(), len);
    1.51 +  return static_cast<char*>(mem);
    1.52 +}
    1.53 +
    1.54 +static const struct PLDHashTableOps property_HashTableOps = {
    1.55 +  PL_DHashAllocTable,
    1.56 +  PL_DHashFreeTable,
    1.57 +  PL_DHashStringKey,
    1.58 +  PL_DHashMatchStringKey,
    1.59 +  PL_DHashMoveEntryStub,
    1.60 +  PL_DHashClearEntryStub,
    1.61 +  PL_DHashFinalizeStub,
    1.62 +  nullptr,
    1.63 +};
    1.64 +
    1.65 +//
    1.66 +// parser stuff
    1.67 +//
    1.68 +enum EParserState {
    1.69 +  eParserState_AwaitingKey,
    1.70 +  eParserState_Key,
    1.71 +  eParserState_AwaitingValue,
    1.72 +  eParserState_Value,
    1.73 +  eParserState_Comment
    1.74 +};
    1.75 +
    1.76 +enum EParserSpecial {
    1.77 +  eParserSpecial_None,          // not parsing a special character
    1.78 +  eParserSpecial_Escaped,       // awaiting a special character
    1.79 +  eParserSpecial_Unicode        // parsing a \Uxxx value
    1.80 +};
    1.81 +
    1.82 +class nsPropertiesParser
    1.83 +{
    1.84 +public:
    1.85 +  nsPropertiesParser(nsIPersistentProperties* aProps) :
    1.86 +    mHaveMultiLine(false), mState(eParserState_AwaitingKey),
    1.87 +    mProps(aProps) {}
    1.88 +
    1.89 +  void FinishValueState(nsAString& aOldValue) {
    1.90 +    static const char trimThese[] = " \t";
    1.91 +    mKey.Trim(trimThese, false, true);
    1.92 +
    1.93 +    // This is really ugly hack but it should be fast
    1.94 +    char16_t backup_char;
    1.95 +    uint32_t minLength = mMinLength;
    1.96 +    if (minLength)
    1.97 +    {
    1.98 +      backup_char = mValue[minLength-1];
    1.99 +      mValue.SetCharAt('x', minLength-1);
   1.100 +    }
   1.101 +    mValue.Trim(trimThese, false, true);
   1.102 +    if (minLength)
   1.103 +      mValue.SetCharAt(backup_char, minLength-1);
   1.104 +
   1.105 +    mProps->SetStringProperty(NS_ConvertUTF16toUTF8(mKey), mValue, aOldValue);
   1.106 +    mSpecialState = eParserSpecial_None;
   1.107 +    WaitForKey();
   1.108 +  }
   1.109 +
   1.110 +  EParserState GetState() { return mState; }
   1.111 +
   1.112 +  static NS_METHOD SegmentWriter(nsIUnicharInputStream* aStream,
   1.113 +                                 void* aClosure,
   1.114 +                                 const char16_t *aFromSegment,
   1.115 +                                 uint32_t aToOffset,
   1.116 +                                 uint32_t aCount,
   1.117 +                                 uint32_t *aWriteCount);
   1.118 +
   1.119 +  nsresult ParseBuffer(const char16_t* aBuffer, uint32_t aBufferLength);
   1.120 +
   1.121 +private:
   1.122 +  bool ParseValueCharacter(
   1.123 +    char16_t c,                  // character that is just being parsed
   1.124 +    const char16_t* cur,         // pointer to character c in the buffer
   1.125 +    const char16_t* &tokenStart, // string copying is done in blocks as big as
   1.126 +                                  // possible, tokenStart points to the beginning
   1.127 +                                  // of this block
   1.128 +    nsAString& oldValue);         // when duplicate property is found, new value
   1.129 +                                  // is stored into hashtable and the old one is
   1.130 +                                  // placed in this variable
   1.131 +
   1.132 +  void WaitForKey() {
   1.133 +    mState = eParserState_AwaitingKey;
   1.134 +  }
   1.135 +
   1.136 +  void EnterKeyState() {
   1.137 +    mKey.Truncate();
   1.138 +    mState = eParserState_Key;
   1.139 +  }
   1.140 +
   1.141 +  void WaitForValue() {
   1.142 +    mState = eParserState_AwaitingValue;
   1.143 +  }
   1.144 +
   1.145 +  void EnterValueState() {
   1.146 +    mValue.Truncate();
   1.147 +    mMinLength = 0;
   1.148 +    mState = eParserState_Value;
   1.149 +    mSpecialState = eParserSpecial_None;
   1.150 +  }
   1.151 +
   1.152 +  void EnterCommentState() {
   1.153 +    mState = eParserState_Comment;
   1.154 +  }
   1.155 +
   1.156 +  nsAutoString mKey;
   1.157 +  nsAutoString mValue;
   1.158 +
   1.159 +  uint32_t  mUnicodeValuesRead; // should be 4!
   1.160 +  char16_t mUnicodeValue;      // currently parsed unicode value
   1.161 +  bool      mHaveMultiLine;     // is TRUE when last processed characters form
   1.162 +                                // any of following sequences:
   1.163 +                                //  - "\\\r"
   1.164 +                                //  - "\\\n"
   1.165 +                                //  - "\\\r\n"
   1.166 +                                //  - any sequence above followed by any
   1.167 +                                //    combination of ' ' and '\t'
   1.168 +  bool      mMultiLineCanSkipN; // TRUE if "\\\r" was detected
   1.169 +  uint32_t  mMinLength;         // limit right trimming at the end to not trim
   1.170 +                                // escaped whitespaces
   1.171 +  EParserState mState;
   1.172 +  // if we see a '\' then we enter this special state
   1.173 +  EParserSpecial mSpecialState;
   1.174 +  nsIPersistentProperties* mProps;
   1.175 +};
   1.176 +
   1.177 +inline bool IsWhiteSpace(char16_t aChar)
   1.178 +{
   1.179 +  return (aChar == ' ') || (aChar == '\t') ||
   1.180 +         (aChar == '\r') || (aChar == '\n');
   1.181 +}
   1.182 +
   1.183 +inline bool IsEOL(char16_t aChar)
   1.184 +{
   1.185 +  return (aChar == '\r') || (aChar == '\n');
   1.186 +}
   1.187 +
   1.188 +
   1.189 +bool nsPropertiesParser::ParseValueCharacter(
   1.190 +    char16_t c, const char16_t* cur, const char16_t* &tokenStart,
   1.191 +    nsAString& oldValue)
   1.192 +{
   1.193 +  switch (mSpecialState) {
   1.194 +
   1.195 +    // the normal state - look for special characters
   1.196 +  case eParserSpecial_None:
   1.197 +    switch (c) {
   1.198 +    case '\\':
   1.199 +      if (mHaveMultiLine)
   1.200 +        // there is nothing to append to mValue yet
   1.201 +        mHaveMultiLine = false;
   1.202 +      else
   1.203 +        mValue += Substring(tokenStart, cur);
   1.204 +
   1.205 +      mSpecialState = eParserSpecial_Escaped;
   1.206 +      break;
   1.207 +
   1.208 +    case '\n':
   1.209 +      // if we detected multiline and got only "\\\r" ignore next "\n" if any
   1.210 +      if (mHaveMultiLine && mMultiLineCanSkipN) {
   1.211 +        // but don't allow another '\n' to be skipped
   1.212 +        mMultiLineCanSkipN = false;
   1.213 +        // Now there is nothing to append to the mValue since we are skipping
   1.214 +        // whitespaces at the beginning of the new line of the multiline
   1.215 +        // property. Set tokenStart properly to ensure that nothing is appended
   1.216 +        // if we find regular line-end or the end of the buffer.
   1.217 +        tokenStart = cur+1;
   1.218 +        break;
   1.219 +      }
   1.220 +      // no break
   1.221 +
   1.222 +    case '\r':
   1.223 +      // we're done! We have a key and value
   1.224 +      mValue += Substring(tokenStart, cur);
   1.225 +      FinishValueState(oldValue);
   1.226 +      mHaveMultiLine = false;
   1.227 +      break;
   1.228 +
   1.229 +    default:
   1.230 +      // there is nothing to do with normal characters,
   1.231 +      // but handle multilines correctly
   1.232 +      if (mHaveMultiLine) {
   1.233 +        if (c == ' ' || c == '\t') {
   1.234 +          // don't allow another '\n' to be skipped
   1.235 +          mMultiLineCanSkipN = false;
   1.236 +          // Now there is nothing to append to the mValue since we are skipping
   1.237 +          // whitespaces at the beginning of the new line of the multiline
   1.238 +          // property. Set tokenStart properly to ensure that nothing is appended
   1.239 +          // if we find regular line-end or the end of the buffer.
   1.240 +          tokenStart = cur+1;
   1.241 +          break;
   1.242 +        }
   1.243 +        mHaveMultiLine = false;
   1.244 +        tokenStart = cur;
   1.245 +      }
   1.246 +      break; // from switch on (c)
   1.247 +    }
   1.248 +    break; // from switch on (mSpecialState)
   1.249 +
   1.250 +    // saw a \ character, so parse the character after that
   1.251 +  case eParserSpecial_Escaped:
   1.252 +    // probably want to start parsing at the next token
   1.253 +    // other characters, like 'u' might override this
   1.254 +    tokenStart = cur+1;
   1.255 +    mSpecialState = eParserSpecial_None;
   1.256 +
   1.257 +    switch (c) {
   1.258 +
   1.259 +      // the easy characters - \t, \n, and so forth
   1.260 +    case 't':
   1.261 +      mValue += char16_t('\t');
   1.262 +      mMinLength = mValue.Length();
   1.263 +      break;
   1.264 +    case 'n':
   1.265 +      mValue += char16_t('\n');
   1.266 +      mMinLength = mValue.Length();
   1.267 +      break;
   1.268 +    case 'r':
   1.269 +      mValue += char16_t('\r');
   1.270 +      mMinLength = mValue.Length();
   1.271 +      break;
   1.272 +    case '\\':
   1.273 +      mValue += char16_t('\\');
   1.274 +      break;
   1.275 +
   1.276 +      // switch to unicode mode!
   1.277 +    case 'u':
   1.278 +    case 'U':
   1.279 +      mSpecialState = eParserSpecial_Unicode;
   1.280 +      mUnicodeValuesRead = 0;
   1.281 +      mUnicodeValue = 0;
   1.282 +      break;
   1.283 +
   1.284 +      // a \ immediately followed by a newline means we're going multiline
   1.285 +    case '\r':
   1.286 +    case '\n':
   1.287 +      mHaveMultiLine = true;
   1.288 +      mMultiLineCanSkipN = (c == '\r');
   1.289 +      mSpecialState = eParserSpecial_None;
   1.290 +      break;
   1.291 +
   1.292 +    default:
   1.293 +      // don't recognize the character, so just append it
   1.294 +      mValue += c;
   1.295 +      break;
   1.296 +    }
   1.297 +    break;
   1.298 +
   1.299 +    // we're in the middle of parsing a 4-character unicode value
   1.300 +    // like \u5f39
   1.301 +  case eParserSpecial_Unicode:
   1.302 +
   1.303 +    if(('0' <= c) && (c <= '9'))
   1.304 +      mUnicodeValue =
   1.305 +        (mUnicodeValue << 4) | (c - '0');
   1.306 +    else if(('a' <= c) && (c <= 'f'))
   1.307 +      mUnicodeValue =
   1.308 +        (mUnicodeValue << 4) | (c - 'a' + 0x0a);
   1.309 +    else if(('A' <= c) && (c <= 'F'))
   1.310 +      mUnicodeValue =
   1.311 +        (mUnicodeValue << 4) | (c - 'A' + 0x0a);
   1.312 +    else {
   1.313 +      // non-hex character. Append what we have, and move on.
   1.314 +      mValue += mUnicodeValue;
   1.315 +      mMinLength = mValue.Length();
   1.316 +      mSpecialState = eParserSpecial_None;
   1.317 +
   1.318 +      // leave tokenStart at this unknown character, so it gets appended
   1.319 +      tokenStart = cur;
   1.320 +
   1.321 +      // ensure parsing this non-hex character again
   1.322 +      return false;
   1.323 +    }
   1.324 +
   1.325 +    if (++mUnicodeValuesRead >= 4) {
   1.326 +      tokenStart = cur+1;
   1.327 +      mSpecialState = eParserSpecial_None;
   1.328 +      mValue += mUnicodeValue;
   1.329 +      mMinLength = mValue.Length();
   1.330 +    }
   1.331 +
   1.332 +    break;
   1.333 +  }
   1.334 +
   1.335 +  return true;
   1.336 +}
   1.337 +
   1.338 +NS_METHOD nsPropertiesParser::SegmentWriter(nsIUnicharInputStream* aStream,
   1.339 +                                            void* aClosure,
   1.340 +                                            const char16_t *aFromSegment,
   1.341 +                                            uint32_t aToOffset,
   1.342 +                                            uint32_t aCount,
   1.343 +                                            uint32_t *aWriteCount)
   1.344 +{
   1.345 +  nsPropertiesParser *parser =
   1.346 +    static_cast<nsPropertiesParser *>(aClosure);
   1.347 +
   1.348 +  parser->ParseBuffer(aFromSegment, aCount);
   1.349 +
   1.350 +  *aWriteCount = aCount;
   1.351 +  return NS_OK;
   1.352 +}
   1.353 +
   1.354 +nsresult nsPropertiesParser::ParseBuffer(const char16_t* aBuffer,
   1.355 +                                         uint32_t aBufferLength)
   1.356 +{
   1.357 +  const char16_t* cur = aBuffer;
   1.358 +  const char16_t* end = aBuffer + aBufferLength;
   1.359 +
   1.360 +  // points to the start/end of the current key or value
   1.361 +  const char16_t* tokenStart = nullptr;
   1.362 +
   1.363 +  // if we're in the middle of parsing a key or value, make sure
   1.364 +  // the current token points to the beginning of the current buffer
   1.365 +  if (mState == eParserState_Key ||
   1.366 +      mState == eParserState_Value) {
   1.367 +    tokenStart = aBuffer;
   1.368 +  }
   1.369 +
   1.370 +  nsAutoString oldValue;
   1.371 +
   1.372 +  while (cur != end) {
   1.373 +
   1.374 +    char16_t c = *cur;
   1.375 +
   1.376 +    switch (mState) {
   1.377 +    case eParserState_AwaitingKey:
   1.378 +      if (c == '#' || c == '!')
   1.379 +        EnterCommentState();
   1.380 +
   1.381 +      else if (!IsWhiteSpace(c)) {
   1.382 +        // not a comment, not whitespace, we must have found a key!
   1.383 +        EnterKeyState();
   1.384 +        tokenStart = cur;
   1.385 +      }
   1.386 +      break;
   1.387 +
   1.388 +    case eParserState_Key:
   1.389 +      if (c == '=' || c == ':') {
   1.390 +        mKey += Substring(tokenStart, cur);
   1.391 +        WaitForValue();
   1.392 +      }
   1.393 +      break;
   1.394 +
   1.395 +    case eParserState_AwaitingValue:
   1.396 +      if (IsEOL(c)) {
   1.397 +        // no value at all! mimic the normal value-ending
   1.398 +        EnterValueState();
   1.399 +        FinishValueState(oldValue);
   1.400 +      }
   1.401 +
   1.402 +      // ignore white space leading up to the value
   1.403 +      else if (!IsWhiteSpace(c)) {
   1.404 +        tokenStart = cur;
   1.405 +        EnterValueState();
   1.406 +
   1.407 +        // make sure to handle this first character
   1.408 +        if (ParseValueCharacter(c, cur, tokenStart, oldValue))
   1.409 +          cur++;
   1.410 +        // If the character isn't consumed, don't do cur++ and parse
   1.411 +        // the character again. This can happen f.e. for char 'X' in sequence
   1.412 +        // "\u00X". This character can be control character and must be
   1.413 +        // processed again.
   1.414 +        continue;
   1.415 +      }
   1.416 +      break;
   1.417 +
   1.418 +    case eParserState_Value:
   1.419 +      if (ParseValueCharacter(c, cur, tokenStart, oldValue))
   1.420 +        cur++;
   1.421 +      // See few lines above for reason of doing this
   1.422 +      continue;
   1.423 +
   1.424 +    case eParserState_Comment:
   1.425 +      // stay in this state till we hit EOL
   1.426 +      if (c == '\r' || c== '\n')
   1.427 +        WaitForKey();
   1.428 +      break;
   1.429 +    }
   1.430 +
   1.431 +    // finally, advance to the next character
   1.432 +    cur++;
   1.433 +  }
   1.434 +
   1.435 +  // if we're still parsing the value and are in eParserSpecial_None, then
   1.436 +  // append whatever we have..
   1.437 +  if (mState == eParserState_Value && tokenStart &&
   1.438 +      mSpecialState == eParserSpecial_None) {
   1.439 +    mValue += Substring(tokenStart, cur);
   1.440 +  }
   1.441 +  // if we're still parsing the key, then append whatever we have..
   1.442 +  else if (mState == eParserState_Key && tokenStart) {
   1.443 +    mKey += Substring(tokenStart, cur);
   1.444 +  }
   1.445 +
   1.446 +  return NS_OK;
   1.447 +}
   1.448 +
   1.449 +nsPersistentProperties::nsPersistentProperties()
   1.450 +: mIn(nullptr)
   1.451 +{
   1.452 +  mSubclass = static_cast<nsIPersistentProperties*>(this);
   1.453 +
   1.454 +  PL_DHashTableInit(&mTable, &property_HashTableOps, nullptr,
   1.455 +                    sizeof(PropertyTableEntry), 20);
   1.456 +
   1.457 +  PL_INIT_ARENA_POOL(&mArena, "PersistentPropertyArena", 2048);
   1.458 +}
   1.459 +
   1.460 +nsPersistentProperties::~nsPersistentProperties()
   1.461 +{
   1.462 +  PL_FinishArenaPool(&mArena);
   1.463 +  if (mTable.ops)
   1.464 +    PL_DHashTableFinish(&mTable);
   1.465 +}
   1.466 +
   1.467 +nsresult
   1.468 +nsPersistentProperties::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult)
   1.469 +{
   1.470 +  if (aOuter)
   1.471 +    return NS_ERROR_NO_AGGREGATION;
   1.472 +  nsRefPtr<nsPersistentProperties> props = new nsPersistentProperties();
   1.473 +  return props->QueryInterface(aIID, aResult);
   1.474 +}
   1.475 +
   1.476 +NS_IMPL_ISUPPORTS(nsPersistentProperties, nsIPersistentProperties, nsIProperties)
   1.477 +
   1.478 +NS_IMETHODIMP
   1.479 +nsPersistentProperties::Load(nsIInputStream *aIn)
   1.480 +{
   1.481 +  nsresult rv = nsSimpleUnicharStreamFactory::GetInstance()->
   1.482 +    CreateInstanceFromUTF8Stream(aIn, getter_AddRefs(mIn));
   1.483 +
   1.484 +  if (rv != NS_OK) {
   1.485 +    NS_WARNING("Error creating UnicharInputStream");
   1.486 +    return NS_ERROR_FAILURE;
   1.487 +  }
   1.488 +
   1.489 +  nsPropertiesParser parser(mSubclass);
   1.490 +
   1.491 +  uint32_t nProcessed;
   1.492 +  // If this 4096 is changed to some other value, make sure to adjust
   1.493 +  // the bug121341.properties test file accordingly.
   1.494 +  while (NS_SUCCEEDED(rv = mIn->ReadSegments(nsPropertiesParser::SegmentWriter, &parser, 4096, &nProcessed)) &&
   1.495 +         nProcessed != 0);
   1.496 +  mIn = nullptr;
   1.497 +  if (NS_FAILED(rv))
   1.498 +    return rv;
   1.499 +
   1.500 +  // We may have an unprocessed value at this point
   1.501 +  // if the last line did not have a proper line ending.
   1.502 +  if (parser.GetState() == eParserState_Value) {
   1.503 +    nsAutoString oldValue;
   1.504 +    parser.FinishValueState(oldValue);
   1.505 +  }
   1.506 +
   1.507 +  return NS_OK;
   1.508 +}
   1.509 +
   1.510 +NS_IMETHODIMP
   1.511 +nsPersistentProperties::SetStringProperty(const nsACString& aKey,
   1.512 +                                          const nsAString& aNewValue,
   1.513 +                                          nsAString& aOldValue)
   1.514 +{
   1.515 +  const nsAFlatCString&  flatKey = PromiseFlatCString(aKey);
   1.516 +  PropertyTableEntry *entry =
   1.517 +    static_cast<PropertyTableEntry*>
   1.518 +               (PL_DHashTableOperate(&mTable, flatKey.get(), PL_DHASH_ADD));
   1.519 +
   1.520 +  if (entry->mKey) {
   1.521 +    aOldValue = entry->mValue;
   1.522 +    NS_WARNING(nsPrintfCString("the property %s already exists\n",
   1.523 +                               flatKey.get()).get());
   1.524 +  }
   1.525 +  else {
   1.526 +    aOldValue.Truncate();
   1.527 +  }
   1.528 +
   1.529 +  entry->mKey = ArenaStrdup(flatKey, &mArena);
   1.530 +  entry->mValue = ArenaStrdup(PromiseFlatString(aNewValue), &mArena);
   1.531 +
   1.532 +  return NS_OK;
   1.533 +}
   1.534 +
   1.535 +NS_IMETHODIMP
   1.536 +nsPersistentProperties::Save(nsIOutputStream* aOut, const nsACString& aHeader)
   1.537 +{
   1.538 +  return NS_ERROR_NOT_IMPLEMENTED;
   1.539 +}
   1.540 +
   1.541 +NS_IMETHODIMP
   1.542 +nsPersistentProperties::Subclass(nsIPersistentProperties* aSubclass)
   1.543 +{
   1.544 +  if (aSubclass) {
   1.545 +    mSubclass = aSubclass;
   1.546 +  }
   1.547 +
   1.548 +  return NS_OK;
   1.549 +}
   1.550 +
   1.551 +NS_IMETHODIMP
   1.552 +nsPersistentProperties::GetStringProperty(const nsACString& aKey,
   1.553 +                                          nsAString& aValue)
   1.554 +{
   1.555 +  const nsAFlatCString&  flatKey = PromiseFlatCString(aKey);
   1.556 +
   1.557 +  PropertyTableEntry *entry =
   1.558 +    static_cast<PropertyTableEntry*>
   1.559 +               (PL_DHashTableOperate(&mTable, flatKey.get(), PL_DHASH_LOOKUP));
   1.560 +
   1.561 +  if (PL_DHASH_ENTRY_IS_FREE(entry))
   1.562 +    return NS_ERROR_FAILURE;
   1.563 +
   1.564 +  aValue = entry->mValue;
   1.565 +  return NS_OK;
   1.566 +}
   1.567 +
   1.568 +static PLDHashOperator
   1.569 +AddElemToArray(PLDHashTable* table, PLDHashEntryHdr *hdr,
   1.570 +               uint32_t i, void *arg)
   1.571 +{
   1.572 +  nsCOMArray<nsIPropertyElement>* props =
   1.573 +    static_cast<nsCOMArray<nsIPropertyElement>*>(arg);
   1.574 +  PropertyTableEntry* entry =
   1.575 +    static_cast<PropertyTableEntry*>(hdr);
   1.576 +
   1.577 +  nsPropertyElement *element =
   1.578 +    new nsPropertyElement(nsDependentCString(entry->mKey),
   1.579 +                          nsDependentString(entry->mValue));
   1.580 +
   1.581 +  props->AppendObject(element);
   1.582 +
   1.583 +  return PL_DHASH_NEXT;
   1.584 +}
   1.585 +
   1.586 +
   1.587 +NS_IMETHODIMP
   1.588 +nsPersistentProperties::Enumerate(nsISimpleEnumerator** aResult)
   1.589 +{
   1.590 +  nsCOMArray<nsIPropertyElement> props;
   1.591 +
   1.592 +  // We know the necessary size; we can avoid growing it while adding elements
   1.593 +  props.SetCapacity(mTable.entryCount);
   1.594 +
   1.595 +  // Step through hash entries populating a transient array
   1.596 +  uint32_t n =
   1.597 +    PL_DHashTableEnumerate(&mTable, AddElemToArray, (void *)&props);
   1.598 +  if (n < mTable.entryCount)
   1.599 +    return NS_ERROR_OUT_OF_MEMORY;
   1.600 +
   1.601 +  return NS_NewArrayEnumerator(aResult, props);
   1.602 +}
   1.603 +
   1.604 +////////////////////////////////////////////////////////////////////////////////
   1.605 +// XXX Some day we'll unify the nsIPersistentProperties interface with
   1.606 +// nsIProperties, but until now...
   1.607 +
   1.608 +NS_IMETHODIMP
   1.609 +nsPersistentProperties::Get(const char* prop, const nsIID & uuid, void* *result)
   1.610 +{
   1.611 +  return NS_ERROR_NOT_IMPLEMENTED;
   1.612 +}
   1.613 +
   1.614 +NS_IMETHODIMP
   1.615 +nsPersistentProperties::Set(const char* prop, nsISupports* value)
   1.616 +{
   1.617 +  return NS_ERROR_NOT_IMPLEMENTED;
   1.618 +}
   1.619 +NS_IMETHODIMP
   1.620 +nsPersistentProperties::Undefine(const char* prop)
   1.621 +{
   1.622 +  return NS_ERROR_NOT_IMPLEMENTED;
   1.623 +}
   1.624 +
   1.625 +NS_IMETHODIMP
   1.626 +nsPersistentProperties::Has(const char* prop, bool *result)
   1.627 +{
   1.628 +  PropertyTableEntry *entry =
   1.629 +    static_cast<PropertyTableEntry*>
   1.630 +               (PL_DHashTableOperate(&mTable, prop, PL_DHASH_LOOKUP));
   1.631 +
   1.632 +  *result = (entry && PL_DHASH_ENTRY_IS_BUSY(entry));
   1.633 +
   1.634 +  return NS_OK;
   1.635 +}
   1.636 +
   1.637 +NS_IMETHODIMP
   1.638 +nsPersistentProperties::GetKeys(uint32_t *count, char ***keys)
   1.639 +{
   1.640 +    return NS_ERROR_NOT_IMPLEMENTED;
   1.641 +}
   1.642 +
   1.643 +////////////////////////////////////////////////////////////////////////////////
   1.644 +// PropertyElement
   1.645 +////////////////////////////////////////////////////////////////////////////////
   1.646 +
   1.647 +NS_METHOD
   1.648 +nsPropertyElement::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult)
   1.649 +{
   1.650 +  if (aOuter)
   1.651 +    return NS_ERROR_NO_AGGREGATION;
   1.652 +  nsRefPtr<nsPropertyElement> propElem = new nsPropertyElement();
   1.653 +  return propElem->QueryInterface(aIID, aResult);
   1.654 +}
   1.655 +
   1.656 +NS_IMPL_ISUPPORTS(nsPropertyElement, nsIPropertyElement)
   1.657 +
   1.658 +NS_IMETHODIMP
   1.659 +nsPropertyElement::GetKey(nsACString& aReturnKey)
   1.660 +{
   1.661 +  aReturnKey = mKey;
   1.662 +  return NS_OK;
   1.663 +}
   1.664 +
   1.665 +NS_IMETHODIMP
   1.666 +nsPropertyElement::GetValue(nsAString& aReturnValue)
   1.667 +{
   1.668 +  aReturnValue = mValue;
   1.669 +  return NS_OK;
   1.670 +}
   1.671 +
   1.672 +NS_IMETHODIMP
   1.673 +nsPropertyElement::SetKey(const nsACString& aKey)
   1.674 +{
   1.675 +  mKey = aKey;
   1.676 +  return NS_OK;
   1.677 +}
   1.678 +
   1.679 +NS_IMETHODIMP
   1.680 +nsPropertyElement::SetValue(const nsAString& aValue)
   1.681 +{
   1.682 +  mValue = aValue;
   1.683 +  return NS_OK;
   1.684 +}
   1.685 +
   1.686 +////////////////////////////////////////////////////////////////////////////////

mercurial