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 "nsStringEnumerator.h"
8 #include "nsSimpleEnumerator.h"
9 #include "nsSupportsPrimitives.h"
10 #include "mozilla/Attributes.h"
11 #include "mozilla/ResultExtensions.h"
12 #include "mozilla/dom/IteratorResultBinding.h"
13 #include "mozilla/dom/RootedDictionary.h"
14 #include "mozilla/dom/ToJSValue.h"
15 #include "nsTArray.h"
16 
17 using namespace mozilla;
18 using namespace mozilla::dom;
19 
20 namespace {
21 
22 class JSStringEnumerator final : public nsIJSEnumerator {
23   NS_DECL_ISUPPORTS
24   NS_DECL_NSIJSENUMERATOR
25 
JSStringEnumerator(nsIStringEnumerator * aEnumerator)26   explicit JSStringEnumerator(nsIStringEnumerator* aEnumerator)
27       : mEnumerator(aEnumerator) {
28     MOZ_ASSERT(mEnumerator);
29   }
30 
31  private:
32   ~JSStringEnumerator() = default;
33 
34   nsCOMPtr<nsIStringEnumerator> mEnumerator;
35 };
36 
37 }  // anonymous namespace
38 
Iterator(nsIJSEnumerator ** aResult)39 nsresult JSStringEnumerator::Iterator(nsIJSEnumerator** aResult) {
40   RefPtr<JSStringEnumerator> result(this);
41   result.forget(aResult);
42   return NS_OK;
43 }
44 
Next(JSContext * aCx,JS::MutableHandleValue aResult)45 nsresult JSStringEnumerator::Next(JSContext* aCx,
46                                   JS::MutableHandleValue aResult) {
47   RootedDictionary<IteratorResult> result(aCx);
48 
49   nsAutoString elem;
50   if (NS_FAILED(mEnumerator->GetNext(elem))) {
51     result.mDone = true;
52   } else {
53     result.mDone = false;
54 
55     if (!ToJSValue(
56             aCx, elem,
57             JS::MutableHandleValue::fromMarkedLocation(&result.mValue))) {
58       return NS_ERROR_OUT_OF_MEMORY;
59     }
60   }
61 
62   if (!ToJSValue(aCx, result, aResult)) {
63     return NS_ERROR_OUT_OF_MEMORY;
64   }
65   return NS_OK;
66 }
67 
NS_IMPL_ISUPPORTS(JSStringEnumerator,nsIJSEnumerator)68 NS_IMPL_ISUPPORTS(JSStringEnumerator, nsIJSEnumerator)
69 
70 //
71 // nsStringEnumeratorBase
72 //
73 
74 nsresult nsStringEnumeratorBase::GetNext(nsAString& aResult) {
75   nsAutoCString str;
76   MOZ_TRY(GetNext(str));
77 
78   CopyUTF8toUTF16(str, aResult);
79   return NS_OK;
80 }
81 
82 NS_IMETHODIMP
StringIterator(nsIJSEnumerator ** aRetVal)83 nsStringEnumeratorBase::StringIterator(nsIJSEnumerator** aRetVal) {
84   auto result = MakeRefPtr<JSStringEnumerator>(this);
85   result.forget(aRetVal);
86   return NS_OK;
87 }
88 
89 //
90 // nsStringEnumerator
91 //
92 
93 class nsStringEnumerator final : public nsSimpleEnumerator,
94                                  public nsIStringEnumerator,
95                                  public nsIUTF8StringEnumerator {
96  public:
nsStringEnumerator(const nsTArray<nsString> * aArray,bool aOwnsArray)97   nsStringEnumerator(const nsTArray<nsString>* aArray, bool aOwnsArray)
98       : mArray(aArray), mIndex(0), mOwnsArray(aOwnsArray), mIsUnicode(true) {}
99 
nsStringEnumerator(const nsTArray<nsCString> * aArray,bool aOwnsArray)100   nsStringEnumerator(const nsTArray<nsCString>* aArray, bool aOwnsArray)
101       : mCArray(aArray), mIndex(0), mOwnsArray(aOwnsArray), mIsUnicode(false) {}
102 
nsStringEnumerator(const nsTArray<nsString> * aArray,nsISupports * aOwner)103   nsStringEnumerator(const nsTArray<nsString>* aArray, nsISupports* aOwner)
104       : mArray(aArray),
105         mIndex(0),
106         mOwner(aOwner),
107         mOwnsArray(false),
108         mIsUnicode(true) {}
109 
nsStringEnumerator(const nsTArray<nsCString> * aArray,nsISupports * aOwner)110   nsStringEnumerator(const nsTArray<nsCString>* aArray, nsISupports* aOwner)
111       : mCArray(aArray),
112         mIndex(0),
113         mOwner(aOwner),
114         mOwnsArray(false),
115         mIsUnicode(false) {}
116 
117   NS_DECL_ISUPPORTS_INHERITED
118   NS_DECL_NSIUTF8STRINGENUMERATOR
119   NS_DECL_NSISTRINGENUMERATORBASE
120 
121   // have to declare nsIStringEnumerator manually, because of
122   // overlapping method names
123   NS_IMETHOD GetNext(nsAString& aResult) override;
124   NS_DECL_NSISIMPLEENUMERATOR
125 
DefaultInterface()126   const nsID& DefaultInterface() override {
127     if (mIsUnicode) {
128       return NS_GET_IID(nsISupportsString);
129     }
130     return NS_GET_IID(nsISupportsCString);
131   }
132 
133  private:
~nsStringEnumerator()134   ~nsStringEnumerator() {
135     if (mOwnsArray) {
136       // const-casting is safe here, because the NS_New*
137       // constructors make sure mOwnsArray is consistent with
138       // the constness of the objects
139       if (mIsUnicode) {
140         delete const_cast<nsTArray<nsString>*>(mArray);
141       } else {
142         delete const_cast<nsTArray<nsCString>*>(mCArray);
143       }
144     }
145   }
146 
147   union {
148     const nsTArray<nsString>* mArray;
149     const nsTArray<nsCString>* mCArray;
150   };
151 
Count()152   inline uint32_t Count() {
153     return mIsUnicode ? mArray->Length() : mCArray->Length();
154   }
155 
156   uint32_t mIndex;
157 
158   // the owner allows us to hold a strong reference to the object
159   // that owns the array. Having a non-null value in mOwner implies
160   // that mOwnsArray is false, because we rely on the real owner
161   // to release the array
162   nsCOMPtr<nsISupports> mOwner;
163   bool mOwnsArray;
164   bool mIsUnicode;
165 };
166 
NS_IMPL_ISUPPORTS_INHERITED(nsStringEnumerator,nsSimpleEnumerator,nsIStringEnumerator,nsIUTF8StringEnumerator)167 NS_IMPL_ISUPPORTS_INHERITED(nsStringEnumerator, nsSimpleEnumerator,
168                             nsIStringEnumerator, nsIUTF8StringEnumerator)
169 
170 NS_IMETHODIMP
171 nsStringEnumerator::HasMore(bool* aResult) {
172   *aResult = mIndex < Count();
173   return NS_OK;
174 }
175 
176 NS_IMETHODIMP
HasMoreElements(bool * aResult)177 nsStringEnumerator::HasMoreElements(bool* aResult) { return HasMore(aResult); }
178 
179 NS_IMETHODIMP
GetNext(nsISupports ** aResult)180 nsStringEnumerator::GetNext(nsISupports** aResult) {
181   if (mIndex >= mArray->Length()) {
182     return NS_ERROR_FAILURE;
183   }
184 
185   if (mIsUnicode) {
186     nsSupportsString* stringImpl = new nsSupportsString();
187 
188     stringImpl->SetData(mArray->ElementAt(mIndex++));
189     *aResult = stringImpl;
190   } else {
191     nsSupportsCString* cstringImpl = new nsSupportsCString();
192 
193     cstringImpl->SetData(mCArray->ElementAt(mIndex++));
194     *aResult = cstringImpl;
195   }
196   NS_ADDREF(*aResult);
197   return NS_OK;
198 }
199 
200 NS_IMETHODIMP
GetNext(nsAString & aResult)201 nsStringEnumerator::GetNext(nsAString& aResult) {
202   if (NS_WARN_IF(mIndex >= Count())) {
203     return NS_ERROR_UNEXPECTED;
204   }
205 
206   if (mIsUnicode) {
207     aResult = mArray->ElementAt(mIndex++);
208   } else {
209     CopyUTF8toUTF16(mCArray->ElementAt(mIndex++), aResult);
210   }
211 
212   return NS_OK;
213 }
214 
215 NS_IMETHODIMP
GetNext(nsACString & aResult)216 nsStringEnumerator::GetNext(nsACString& aResult) {
217   if (NS_WARN_IF(mIndex >= Count())) {
218     return NS_ERROR_UNEXPECTED;
219   }
220 
221   if (mIsUnicode) {
222     CopyUTF16toUTF8(mArray->ElementAt(mIndex++), aResult);
223   } else {
224     aResult = mCArray->ElementAt(mIndex++);
225   }
226 
227   return NS_OK;
228 }
229 
230 NS_IMETHODIMP
StringIterator(nsIJSEnumerator ** aRetVal)231 nsStringEnumerator::StringIterator(nsIJSEnumerator** aRetVal) {
232   auto result = MakeRefPtr<JSStringEnumerator>(this);
233   result.forget(aRetVal);
234   return NS_OK;
235 }
236 
237 template <class T>
StringEnumeratorTail(T ** aResult)238 static inline nsresult StringEnumeratorTail(T** aResult) {
239   if (!*aResult) {
240     return NS_ERROR_OUT_OF_MEMORY;
241   }
242   NS_ADDREF(*aResult);
243   return NS_OK;
244 }
245 
246 //
247 // constructors
248 //
249 
NS_NewStringEnumerator(nsIStringEnumerator ** aResult,const nsTArray<nsString> * aArray,nsISupports * aOwner)250 nsresult NS_NewStringEnumerator(nsIStringEnumerator** aResult,
251                                 const nsTArray<nsString>* aArray,
252                                 nsISupports* aOwner) {
253   if (NS_WARN_IF(!aResult) || NS_WARN_IF(!aArray)) {
254     return NS_ERROR_INVALID_ARG;
255   }
256 
257   *aResult = new nsStringEnumerator(aArray, aOwner);
258   return StringEnumeratorTail(aResult);
259 }
260 
NS_NewUTF8StringEnumerator(nsIUTF8StringEnumerator ** aResult,const nsTArray<nsCString> * aArray,nsISupports * aOwner)261 nsresult NS_NewUTF8StringEnumerator(nsIUTF8StringEnumerator** aResult,
262                                     const nsTArray<nsCString>* aArray,
263                                     nsISupports* aOwner) {
264   if (NS_WARN_IF(!aResult) || NS_WARN_IF(!aArray)) {
265     return NS_ERROR_INVALID_ARG;
266   }
267 
268   *aResult = new nsStringEnumerator(aArray, aOwner);
269   return StringEnumeratorTail(aResult);
270 }
271 
NS_NewAdoptingStringEnumerator(nsIStringEnumerator ** aResult,nsTArray<nsString> * aArray)272 nsresult NS_NewAdoptingStringEnumerator(nsIStringEnumerator** aResult,
273                                         nsTArray<nsString>* aArray) {
274   if (NS_WARN_IF(!aResult) || NS_WARN_IF(!aArray)) {
275     return NS_ERROR_INVALID_ARG;
276   }
277 
278   *aResult = new nsStringEnumerator(aArray, true);
279   return StringEnumeratorTail(aResult);
280 }
281 
NS_NewAdoptingUTF8StringEnumerator(nsIUTF8StringEnumerator ** aResult,nsTArray<nsCString> * aArray)282 nsresult NS_NewAdoptingUTF8StringEnumerator(nsIUTF8StringEnumerator** aResult,
283                                             nsTArray<nsCString>* aArray) {
284   if (NS_WARN_IF(!aResult) || NS_WARN_IF(!aArray)) {
285     return NS_ERROR_INVALID_ARG;
286   }
287 
288   *aResult = new nsStringEnumerator(aArray, true);
289   return StringEnumeratorTail(aResult);
290 }
291 
292 // const ones internally just forward to the non-const equivalents
NS_NewStringEnumerator(nsIStringEnumerator ** aResult,const nsTArray<nsString> * aArray)293 nsresult NS_NewStringEnumerator(nsIStringEnumerator** aResult,
294                                 const nsTArray<nsString>* aArray) {
295   if (NS_WARN_IF(!aResult) || NS_WARN_IF(!aArray)) {
296     return NS_ERROR_INVALID_ARG;
297   }
298 
299   *aResult = new nsStringEnumerator(aArray, false);
300   return StringEnumeratorTail(aResult);
301 }
302 
NS_NewUTF8StringEnumerator(nsIUTF8StringEnumerator ** aResult,const nsTArray<nsCString> * aArray)303 nsresult NS_NewUTF8StringEnumerator(nsIUTF8StringEnumerator** aResult,
304                                     const nsTArray<nsCString>* aArray) {
305   if (NS_WARN_IF(!aResult) || NS_WARN_IF(!aArray)) {
306     return NS_ERROR_INVALID_ARG;
307   }
308 
309   *aResult = new nsStringEnumerator(aArray, false);
310   return StringEnumeratorTail(aResult);
311 }
312