/src/mozilla-central/xpcom/ds/nsPersistentProperties.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ |
3 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
4 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
5 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | | |
7 | | #include "nsArrayEnumerator.h" |
8 | | #include "nsID.h" |
9 | | #include "nsCOMArray.h" |
10 | | #include "nsUnicharInputStream.h" |
11 | | #include "nsPrintfCString.h" |
12 | | #include "nsAutoPtr.h" |
13 | | |
14 | | #include "nsPersistentProperties.h" |
15 | | #include "nsIProperties.h" |
16 | | |
17 | | #include "mozilla/ArenaAllocatorExtensions.h" |
18 | | |
19 | | using mozilla::ArenaStrdup; |
20 | | |
21 | | struct PropertyTableEntry : public PLDHashEntryHdr |
22 | | { |
23 | | // both of these are arena-allocated |
24 | | const char* mKey; |
25 | | const char16_t* mValue; |
26 | | }; |
27 | | |
28 | | static const struct PLDHashTableOps property_HashTableOps = { |
29 | | PLDHashTable::HashStringKey, |
30 | | PLDHashTable::MatchStringKey, |
31 | | PLDHashTable::MoveEntryStub, |
32 | | PLDHashTable::ClearEntryStub, |
33 | | nullptr, |
34 | | }; |
35 | | |
36 | | // |
37 | | // parser stuff |
38 | | // |
39 | | enum EParserState |
40 | | { |
41 | | eParserState_AwaitingKey, |
42 | | eParserState_Key, |
43 | | eParserState_AwaitingValue, |
44 | | eParserState_Value, |
45 | | eParserState_Comment |
46 | | }; |
47 | | |
48 | | enum EParserSpecial |
49 | | { |
50 | | eParserSpecial_None, // not parsing a special character |
51 | | eParserSpecial_Escaped, // awaiting a special character |
52 | | eParserSpecial_Unicode // parsing a \Uxxx value |
53 | | }; |
54 | | |
55 | | class MOZ_STACK_CLASS nsPropertiesParser |
56 | | { |
57 | | public: |
58 | | explicit nsPropertiesParser(nsIPersistentProperties* aProps) |
59 | | : mUnicodeValuesRead(0) |
60 | | , mUnicodeValue(u'\0') |
61 | | , mHaveMultiLine(false) |
62 | | , mMultiLineCanSkipN(false) |
63 | | , mMinLength(0) |
64 | | , mState(eParserState_AwaitingKey) |
65 | | , mSpecialState(eParserSpecial_None) |
66 | | , mProps(aProps) |
67 | 0 | { |
68 | 0 | } |
69 | | |
70 | | void FinishValueState(nsAString& aOldValue) |
71 | 0 | { |
72 | 0 | static const char trimThese[] = " \t"; |
73 | 0 | mKey.Trim(trimThese, false, true); |
74 | 0 |
|
75 | 0 | // This is really ugly hack but it should be fast |
76 | 0 | char16_t backup_char; |
77 | 0 | uint32_t minLength = mMinLength; |
78 | 0 | if (minLength) { |
79 | 0 | backup_char = mValue[minLength - 1]; |
80 | 0 | mValue.SetCharAt('x', minLength - 1); |
81 | 0 | } |
82 | 0 | mValue.Trim(trimThese, false, true); |
83 | 0 | if (minLength) { |
84 | 0 | mValue.SetCharAt(backup_char, minLength - 1); |
85 | 0 | } |
86 | 0 |
|
87 | 0 | mProps->SetStringProperty(NS_ConvertUTF16toUTF8(mKey), mValue, aOldValue); |
88 | 0 | mSpecialState = eParserSpecial_None; |
89 | 0 | WaitForKey(); |
90 | 0 | } |
91 | | |
92 | 0 | EParserState GetState() { return mState; } |
93 | | |
94 | | static nsresult SegmentWriter(nsIUnicharInputStream* aStream, |
95 | | void* aClosure, |
96 | | const char16_t* aFromSegment, |
97 | | uint32_t aToOffset, |
98 | | uint32_t aCount, |
99 | | uint32_t* aWriteCount); |
100 | | |
101 | | nsresult ParseBuffer(const char16_t* aBuffer, uint32_t aBufferLength); |
102 | | |
103 | | private: |
104 | | bool ParseValueCharacter( |
105 | | char16_t aChar, // character that is just being parsed |
106 | | const char16_t* aCur, // pointer to character aChar in the buffer |
107 | | const char16_t*& aTokenStart, // string copying is done in blocks as big as |
108 | | // possible, aTokenStart points to the beginning |
109 | | // of this block |
110 | | nsAString& aOldValue); // when duplicate property is found, new value |
111 | | // is stored into hashtable and the old one is |
112 | | // placed in this variable |
113 | | |
114 | | void WaitForKey() |
115 | 0 | { |
116 | 0 | mState = eParserState_AwaitingKey; |
117 | 0 | } |
118 | | |
119 | | void EnterKeyState() |
120 | 0 | { |
121 | 0 | mKey.Truncate(); |
122 | 0 | mState = eParserState_Key; |
123 | 0 | } |
124 | | |
125 | | void WaitForValue() |
126 | 0 | { |
127 | 0 | mState = eParserState_AwaitingValue; |
128 | 0 | } |
129 | | |
130 | | void EnterValueState() |
131 | 0 | { |
132 | 0 | mValue.Truncate(); |
133 | 0 | mMinLength = 0; |
134 | 0 | mState = eParserState_Value; |
135 | 0 | mSpecialState = eParserSpecial_None; |
136 | 0 | } |
137 | | |
138 | | void EnterCommentState() |
139 | 0 | { |
140 | 0 | mState = eParserState_Comment; |
141 | 0 | } |
142 | | |
143 | | nsAutoString mKey; |
144 | | nsAutoString mValue; |
145 | | |
146 | | uint32_t mUnicodeValuesRead; // should be 4! |
147 | | char16_t mUnicodeValue; // currently parsed unicode value |
148 | | bool mHaveMultiLine; // is TRUE when last processed characters form |
149 | | // any of following sequences: |
150 | | // - "\\\r" |
151 | | // - "\\\n" |
152 | | // - "\\\r\n" |
153 | | // - any sequence above followed by any |
154 | | // combination of ' ' and '\t' |
155 | | bool mMultiLineCanSkipN; // TRUE if "\\\r" was detected |
156 | | uint32_t mMinLength; // limit right trimming at the end to not trim |
157 | | // escaped whitespaces |
158 | | EParserState mState; |
159 | | // if we see a '\' then we enter this special state |
160 | | EParserSpecial mSpecialState; |
161 | | nsCOMPtr<nsIPersistentProperties> mProps; |
162 | | }; |
163 | | |
164 | | inline bool |
165 | | IsWhiteSpace(char16_t aChar) |
166 | 0 | { |
167 | 0 | return (aChar == ' ') || (aChar == '\t') || |
168 | 0 | (aChar == '\r') || (aChar == '\n'); |
169 | 0 | } |
170 | | |
171 | | inline bool |
172 | | IsEOL(char16_t aChar) |
173 | 0 | { |
174 | 0 | return (aChar == '\r') || (aChar == '\n'); |
175 | 0 | } |
176 | | |
177 | | |
178 | | bool |
179 | | nsPropertiesParser::ParseValueCharacter(char16_t aChar, const char16_t* aCur, |
180 | | const char16_t*& aTokenStart, |
181 | | nsAString& aOldValue) |
182 | 0 | { |
183 | 0 | switch (mSpecialState) { |
184 | 0 | // the normal state - look for special characters |
185 | 0 | case eParserSpecial_None: |
186 | 0 | switch (aChar) { |
187 | 0 | case '\\': |
188 | 0 | if (mHaveMultiLine) { |
189 | 0 | // there is nothing to append to mValue yet |
190 | 0 | mHaveMultiLine = false; |
191 | 0 | } else { |
192 | 0 | mValue += Substring(aTokenStart, aCur); |
193 | 0 | } |
194 | 0 |
|
195 | 0 | mSpecialState = eParserSpecial_Escaped; |
196 | 0 | break; |
197 | 0 |
|
198 | 0 | case '\n': |
199 | 0 | // if we detected multiline and got only "\\\r" ignore next "\n" if any |
200 | 0 | if (mHaveMultiLine && mMultiLineCanSkipN) { |
201 | 0 | // but don't allow another '\n' to be skipped |
202 | 0 | mMultiLineCanSkipN = false; |
203 | 0 | // Now there is nothing to append to the mValue since we are skipping |
204 | 0 | // whitespaces at the beginning of the new line of the multiline |
205 | 0 | // property. Set aTokenStart properly to ensure that nothing is appended |
206 | 0 | // if we find regular line-end or the end of the buffer. |
207 | 0 | aTokenStart = aCur + 1; |
208 | 0 | break; |
209 | 0 | } |
210 | 0 | MOZ_FALLTHROUGH; |
211 | 0 |
|
212 | 0 | case '\r': |
213 | 0 | // we're done! We have a key and value |
214 | 0 | mValue += Substring(aTokenStart, aCur); |
215 | 0 | FinishValueState(aOldValue); |
216 | 0 | mHaveMultiLine = false; |
217 | 0 | break; |
218 | 0 |
|
219 | 0 | default: |
220 | 0 | // there is nothing to do with normal characters, |
221 | 0 | // but handle multilines correctly |
222 | 0 | if (mHaveMultiLine) { |
223 | 0 | if (aChar == ' ' || aChar == '\t') { |
224 | 0 | // don't allow another '\n' to be skipped |
225 | 0 | mMultiLineCanSkipN = false; |
226 | 0 | // Now there is nothing to append to the mValue since we are skipping |
227 | 0 | // whitespaces at the beginning of the new line of the multiline |
228 | 0 | // property. Set aTokenStart properly to ensure that nothing is appended |
229 | 0 | // if we find regular line-end or the end of the buffer. |
230 | 0 | aTokenStart = aCur + 1; |
231 | 0 | break; |
232 | 0 | } |
233 | 0 | mHaveMultiLine = false; |
234 | 0 | aTokenStart = aCur; |
235 | 0 | } |
236 | 0 | break; // from switch on (aChar) |
237 | 0 | } |
238 | 0 | break; // from switch on (mSpecialState) |
239 | 0 |
|
240 | 0 | // saw a \ character, so parse the character after that |
241 | 0 | case eParserSpecial_Escaped: |
242 | 0 | // probably want to start parsing at the next token |
243 | 0 | // other characters, like 'u' might override this |
244 | 0 | aTokenStart = aCur + 1; |
245 | 0 | mSpecialState = eParserSpecial_None; |
246 | 0 |
|
247 | 0 | switch (aChar) { |
248 | 0 | // the easy characters - \t, \n, and so forth |
249 | 0 | case 't': |
250 | 0 | mValue += char16_t('\t'); |
251 | 0 | mMinLength = mValue.Length(); |
252 | 0 | break; |
253 | 0 | case 'n': |
254 | 0 | mValue += char16_t('\n'); |
255 | 0 | mMinLength = mValue.Length(); |
256 | 0 | break; |
257 | 0 | case 'r': |
258 | 0 | mValue += char16_t('\r'); |
259 | 0 | mMinLength = mValue.Length(); |
260 | 0 | break; |
261 | 0 | case '\\': |
262 | 0 | mValue += char16_t('\\'); |
263 | 0 | break; |
264 | 0 |
|
265 | 0 | // switch to unicode mode! |
266 | 0 | case 'u': |
267 | 0 | case 'U': |
268 | 0 | mSpecialState = eParserSpecial_Unicode; |
269 | 0 | mUnicodeValuesRead = 0; |
270 | 0 | mUnicodeValue = 0; |
271 | 0 | break; |
272 | 0 |
|
273 | 0 | // a \ immediately followed by a newline means we're going multiline |
274 | 0 | case '\r': |
275 | 0 | case '\n': |
276 | 0 | mHaveMultiLine = true; |
277 | 0 | mMultiLineCanSkipN = (aChar == '\r'); |
278 | 0 | mSpecialState = eParserSpecial_None; |
279 | 0 | break; |
280 | 0 |
|
281 | 0 | default: |
282 | 0 | // don't recognize the character, so just append it |
283 | 0 | mValue += aChar; |
284 | 0 | break; |
285 | 0 | } |
286 | 0 | break; |
287 | 0 |
|
288 | 0 | // we're in the middle of parsing a 4-character unicode value |
289 | 0 | // like \u5f39 |
290 | 0 | case eParserSpecial_Unicode: |
291 | 0 | if ('0' <= aChar && aChar <= '9') { |
292 | 0 | mUnicodeValue = |
293 | 0 | (mUnicodeValue << 4) | (aChar - '0'); |
294 | 0 | } else if ('a' <= aChar && aChar <= 'f') { |
295 | 0 | mUnicodeValue = |
296 | 0 | (mUnicodeValue << 4) | (aChar - 'a' + 0x0a); |
297 | 0 | } else if ('A' <= aChar && aChar <= 'F') { |
298 | 0 | mUnicodeValue = |
299 | 0 | (mUnicodeValue << 4) | (aChar - 'A' + 0x0a); |
300 | 0 | } else { |
301 | 0 | // non-hex character. Append what we have, and move on. |
302 | 0 | mValue += mUnicodeValue; |
303 | 0 | mMinLength = mValue.Length(); |
304 | 0 | mSpecialState = eParserSpecial_None; |
305 | 0 |
|
306 | 0 | // leave aTokenStart at this unknown character, so it gets appended |
307 | 0 | aTokenStart = aCur; |
308 | 0 |
|
309 | 0 | // ensure parsing this non-hex character again |
310 | 0 | return false; |
311 | 0 | } |
312 | 0 | |
313 | 0 | if (++mUnicodeValuesRead >= 4) { |
314 | 0 | aTokenStart = aCur + 1; |
315 | 0 | mSpecialState = eParserSpecial_None; |
316 | 0 | mValue += mUnicodeValue; |
317 | 0 | mMinLength = mValue.Length(); |
318 | 0 | } |
319 | 0 |
|
320 | 0 | break; |
321 | 0 | } |
322 | 0 |
|
323 | 0 | return true; |
324 | 0 | } |
325 | | |
326 | | nsresult |
327 | | nsPropertiesParser::SegmentWriter(nsIUnicharInputStream* aStream, |
328 | | void* aClosure, |
329 | | const char16_t* aFromSegment, |
330 | | uint32_t aToOffset, |
331 | | uint32_t aCount, |
332 | | uint32_t* aWriteCount) |
333 | 0 | { |
334 | 0 | nsPropertiesParser* parser = static_cast<nsPropertiesParser*>(aClosure); |
335 | 0 | parser->ParseBuffer(aFromSegment, aCount); |
336 | 0 |
|
337 | 0 | *aWriteCount = aCount; |
338 | 0 | return NS_OK; |
339 | 0 | } |
340 | | |
341 | | nsresult |
342 | | nsPropertiesParser::ParseBuffer(const char16_t* aBuffer, |
343 | | uint32_t aBufferLength) |
344 | 0 | { |
345 | 0 | const char16_t* cur = aBuffer; |
346 | 0 | const char16_t* end = aBuffer + aBufferLength; |
347 | 0 |
|
348 | 0 | // points to the start/end of the current key or value |
349 | 0 | const char16_t* tokenStart = nullptr; |
350 | 0 |
|
351 | 0 | // if we're in the middle of parsing a key or value, make sure |
352 | 0 | // the current token points to the beginning of the current buffer |
353 | 0 | if (mState == eParserState_Key || |
354 | 0 | mState == eParserState_Value) { |
355 | 0 | tokenStart = aBuffer; |
356 | 0 | } |
357 | 0 |
|
358 | 0 | nsAutoString oldValue; |
359 | 0 |
|
360 | 0 | while (cur != end) { |
361 | 0 |
|
362 | 0 | char16_t c = *cur; |
363 | 0 |
|
364 | 0 | switch (mState) { |
365 | 0 | case eParserState_AwaitingKey: |
366 | 0 | if (c == '#' || c == '!') { |
367 | 0 | EnterCommentState(); |
368 | 0 | } |
369 | 0 | |
370 | 0 | else if (!IsWhiteSpace(c)) { |
371 | 0 | // not a comment, not whitespace, we must have found a key! |
372 | 0 | EnterKeyState(); |
373 | 0 | tokenStart = cur; |
374 | 0 | } |
375 | 0 | break; |
376 | 0 |
|
377 | 0 | case eParserState_Key: |
378 | 0 | if (c == '=' || c == ':') { |
379 | 0 | mKey += Substring(tokenStart, cur); |
380 | 0 | WaitForValue(); |
381 | 0 | } |
382 | 0 | break; |
383 | 0 |
|
384 | 0 | case eParserState_AwaitingValue: |
385 | 0 | if (IsEOL(c)) { |
386 | 0 | // no value at all! mimic the normal value-ending |
387 | 0 | EnterValueState(); |
388 | 0 | FinishValueState(oldValue); |
389 | 0 | } |
390 | 0 | |
391 | 0 | // ignore white space leading up to the value |
392 | 0 | else if (!IsWhiteSpace(c)) { |
393 | 0 | tokenStart = cur; |
394 | 0 | EnterValueState(); |
395 | 0 |
|
396 | 0 | // make sure to handle this first character |
397 | 0 | if (ParseValueCharacter(c, cur, tokenStart, oldValue)) { |
398 | 0 | cur++; |
399 | 0 | } |
400 | 0 | // If the character isn't consumed, don't do cur++ and parse |
401 | 0 | // the character again. This can happen f.e. for char 'X' in sequence |
402 | 0 | // "\u00X". This character can be control character and must be |
403 | 0 | // processed again. |
404 | 0 | continue; |
405 | 0 | } |
406 | 0 | break; |
407 | 0 |
|
408 | 0 | case eParserState_Value: |
409 | 0 | if (ParseValueCharacter(c, cur, tokenStart, oldValue)) { |
410 | 0 | cur++; |
411 | 0 | } |
412 | 0 | // See few lines above for reason of doing this |
413 | 0 | continue; |
414 | 0 |
|
415 | 0 | case eParserState_Comment: |
416 | 0 | // stay in this state till we hit EOL |
417 | 0 | if (c == '\r' || c == '\n') { |
418 | 0 | WaitForKey(); |
419 | 0 | } |
420 | 0 | break; |
421 | 0 | } |
422 | 0 |
|
423 | 0 | // finally, advance to the next character |
424 | 0 | cur++; |
425 | 0 | } |
426 | 0 |
|
427 | 0 | // if we're still parsing the value and are in eParserSpecial_None, then |
428 | 0 | // append whatever we have.. |
429 | 0 | if (mState == eParserState_Value && tokenStart && |
430 | 0 | mSpecialState == eParserSpecial_None) { |
431 | 0 | mValue += Substring(tokenStart, cur); |
432 | 0 | } |
433 | 0 | // if we're still parsing the key, then append whatever we have.. |
434 | 0 | else if (mState == eParserState_Key && tokenStart) { |
435 | 0 | mKey += Substring(tokenStart, cur); |
436 | 0 | } |
437 | 0 |
|
438 | 0 | return NS_OK; |
439 | 0 | } |
440 | | |
441 | | nsPersistentProperties::nsPersistentProperties() |
442 | | : mIn(nullptr) |
443 | | , mTable(&property_HashTableOps, sizeof(PropertyTableEntry), 16) |
444 | | , mArena() |
445 | 0 | { |
446 | 0 | } |
447 | | |
448 | | nsPersistentProperties::~nsPersistentProperties() |
449 | 0 | { |
450 | 0 | } |
451 | | |
452 | | size_t |
453 | | nsPersistentProperties::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const |
454 | 0 | { |
455 | 0 | // The memory used by mTable is accounted for in mArena. |
456 | 0 | size_t n = 0; |
457 | 0 | n += mArena.SizeOfExcludingThis(aMallocSizeOf); |
458 | 0 | n += mTable.ShallowSizeOfExcludingThis(aMallocSizeOf); |
459 | 0 | return aMallocSizeOf(this) + n; |
460 | 0 | } |
461 | | |
462 | | NS_IMPL_ISUPPORTS(nsPersistentProperties, nsIPersistentProperties, nsIProperties) |
463 | | |
464 | | NS_IMETHODIMP |
465 | | nsPersistentProperties::Load(nsIInputStream* aIn) |
466 | 0 | { |
467 | 0 | nsresult rv = NS_NewUnicharInputStream(aIn, getter_AddRefs(mIn)); |
468 | 0 |
|
469 | 0 | if (rv != NS_OK) { |
470 | 0 | NS_WARNING("Error creating UnicharInputStream"); |
471 | 0 | return NS_ERROR_FAILURE; |
472 | 0 | } |
473 | 0 |
|
474 | 0 | nsPropertiesParser parser(this); |
475 | 0 |
|
476 | 0 | uint32_t nProcessed; |
477 | 0 | // If this 4096 is changed to some other value, make sure to adjust |
478 | 0 | // the bug121341.properties test file accordingly. |
479 | 0 | while (NS_SUCCEEDED(rv = mIn->ReadSegments(nsPropertiesParser::SegmentWriter, |
480 | 0 | &parser, 4096, &nProcessed)) && |
481 | 0 | nProcessed != 0); |
482 | 0 | mIn = nullptr; |
483 | 0 | if (NS_FAILED(rv)) { |
484 | 0 | return rv; |
485 | 0 | } |
486 | 0 | |
487 | 0 | // We may have an unprocessed value at this point |
488 | 0 | // if the last line did not have a proper line ending. |
489 | 0 | if (parser.GetState() == eParserState_Value) { |
490 | 0 | nsAutoString oldValue; |
491 | 0 | parser.FinishValueState(oldValue); |
492 | 0 | } |
493 | 0 |
|
494 | 0 | return NS_OK; |
495 | 0 | } |
496 | | |
497 | | NS_IMETHODIMP |
498 | | nsPersistentProperties::SetStringProperty(const nsACString& aKey, |
499 | | const nsAString& aNewValue, |
500 | | nsAString& aOldValue) |
501 | 0 | { |
502 | 0 | const nsCString& flatKey = PromiseFlatCString(aKey); |
503 | 0 | auto entry = static_cast<PropertyTableEntry*> |
504 | 0 | (mTable.Add(flatKey.get())); |
505 | 0 |
|
506 | 0 | if (entry->mKey) { |
507 | 0 | aOldValue = entry->mValue; |
508 | 0 | NS_WARNING(nsPrintfCString("the property %s already exists", |
509 | 0 | flatKey.get()).get()); |
510 | 0 | } else { |
511 | 0 | aOldValue.Truncate(); |
512 | 0 | } |
513 | 0 |
|
514 | 0 | entry->mKey = ArenaStrdup(flatKey, mArena); |
515 | 0 | entry->mValue = ArenaStrdup(aNewValue, mArena); |
516 | 0 |
|
517 | 0 | return NS_OK; |
518 | 0 | } |
519 | | |
520 | | NS_IMETHODIMP |
521 | | nsPersistentProperties::Save(nsIOutputStream* aOut, const nsACString& aHeader) |
522 | 0 | { |
523 | 0 | return NS_ERROR_NOT_IMPLEMENTED; |
524 | 0 | } |
525 | | |
526 | | NS_IMETHODIMP |
527 | | nsPersistentProperties::GetStringProperty(const nsACString& aKey, |
528 | | nsAString& aValue) |
529 | 0 | { |
530 | 0 | const nsCString& flatKey = PromiseFlatCString(aKey); |
531 | 0 |
|
532 | 0 | auto entry = static_cast<PropertyTableEntry*>(mTable.Search(flatKey.get())); |
533 | 0 | if (!entry) { |
534 | 0 | return NS_ERROR_FAILURE; |
535 | 0 | } |
536 | 0 | |
537 | 0 | aValue = entry->mValue; |
538 | 0 | return NS_OK; |
539 | 0 | } |
540 | | |
541 | | NS_IMETHODIMP |
542 | | nsPersistentProperties::Enumerate(nsISimpleEnumerator** aResult) |
543 | 0 | { |
544 | 0 | nsCOMArray<nsIPropertyElement> props; |
545 | 0 |
|
546 | 0 | // We know the necessary size; we can avoid growing it while adding elements |
547 | 0 | props.SetCapacity(mTable.EntryCount()); |
548 | 0 |
|
549 | 0 | // Step through hash entries populating a transient array |
550 | 0 | for (auto iter = mTable.Iter(); !iter.Done(); iter.Next()) { |
551 | 0 | auto entry = static_cast<PropertyTableEntry*>(iter.Get()); |
552 | 0 |
|
553 | 0 | RefPtr<nsPropertyElement> element = |
554 | 0 | new nsPropertyElement(nsDependentCString(entry->mKey), |
555 | 0 | nsDependentString(entry->mValue)); |
556 | 0 |
|
557 | 0 | if (!props.AppendObject(element)) { |
558 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
559 | 0 | } |
560 | 0 | } |
561 | 0 |
|
562 | 0 | return NS_NewArrayEnumerator(aResult, props, NS_GET_IID(nsIPropertyElement)); |
563 | 0 | } |
564 | | |
565 | | //////////////////////////////////////////////////////////////////////////////// |
566 | | // XXX Some day we'll unify the nsIPersistentProperties interface with |
567 | | // nsIProperties, but until now... |
568 | | |
569 | | NS_IMETHODIMP |
570 | | nsPersistentProperties::Get(const char* aProp, const nsIID& aUUID, |
571 | | void** aResult) |
572 | 0 | { |
573 | 0 | return NS_ERROR_NOT_IMPLEMENTED; |
574 | 0 | } |
575 | | |
576 | | NS_IMETHODIMP |
577 | | nsPersistentProperties::Set(const char* aProp, nsISupports* value) |
578 | 0 | { |
579 | 0 | return NS_ERROR_NOT_IMPLEMENTED; |
580 | 0 | } |
581 | | NS_IMETHODIMP |
582 | | nsPersistentProperties::Undefine(const char* aProp) |
583 | 0 | { |
584 | 0 | return NS_ERROR_NOT_IMPLEMENTED; |
585 | 0 | } |
586 | | |
587 | | NS_IMETHODIMP |
588 | | nsPersistentProperties::Has(const char* aProp, bool* aResult) |
589 | 0 | { |
590 | 0 | *aResult = !!mTable.Search(aProp); |
591 | 0 | return NS_OK; |
592 | 0 | } |
593 | | |
594 | | NS_IMETHODIMP |
595 | | nsPersistentProperties::GetKeys(uint32_t* aCount, char*** aKeys) |
596 | 0 | { |
597 | 0 | return NS_ERROR_NOT_IMPLEMENTED; |
598 | 0 | } |
599 | | |
600 | | //////////////////////////////////////////////////////////////////////////////// |
601 | | // PropertyElement |
602 | | //////////////////////////////////////////////////////////////////////////////// |
603 | | |
604 | | nsresult |
605 | | nsPropertyElement::Create(nsISupports* aOuter, REFNSIID aIID, void** aResult) |
606 | 0 | { |
607 | 0 | if (aOuter) { |
608 | 0 | return NS_ERROR_NO_AGGREGATION; |
609 | 0 | } |
610 | 0 | RefPtr<nsPropertyElement> propElem = new nsPropertyElement(); |
611 | 0 | return propElem->QueryInterface(aIID, aResult); |
612 | 0 | } |
613 | | |
614 | | NS_IMPL_ISUPPORTS(nsPropertyElement, nsIPropertyElement) |
615 | | |
616 | | NS_IMETHODIMP |
617 | | nsPropertyElement::GetKey(nsACString& aReturnKey) |
618 | 0 | { |
619 | 0 | aReturnKey = mKey; |
620 | 0 | return NS_OK; |
621 | 0 | } |
622 | | |
623 | | NS_IMETHODIMP |
624 | | nsPropertyElement::GetValue(nsAString& aReturnValue) |
625 | 0 | { |
626 | 0 | aReturnValue = mValue; |
627 | 0 | return NS_OK; |
628 | 0 | } |
629 | | |
630 | | NS_IMETHODIMP |
631 | | nsPropertyElement::SetKey(const nsACString& aKey) |
632 | 0 | { |
633 | 0 | mKey = aKey; |
634 | 0 | return NS_OK; |
635 | 0 | } |
636 | | |
637 | | NS_IMETHODIMP |
638 | | nsPropertyElement::SetValue(const nsAString& aValue) |
639 | 0 | { |
640 | 0 | mValue = aValue; |
641 | 0 | return NS_OK; |
642 | 0 | } |
643 | | |
644 | | //////////////////////////////////////////////////////////////////////////////// |