Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "URLSearchParams.h"
7 #include "mozilla/dom/URLSearchParamsBinding.h"
9 namespace mozilla {
10 namespace dom {
12 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_1(URLSearchParams, mObservers)
13 NS_IMPL_CYCLE_COLLECTING_ADDREF(URLSearchParams)
14 NS_IMPL_CYCLE_COLLECTING_RELEASE(URLSearchParams)
16 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(URLSearchParams)
17 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
18 NS_INTERFACE_MAP_ENTRY(nsISupports)
19 NS_INTERFACE_MAP_END
21 URLSearchParams::URLSearchParams()
22 {
23 SetIsDOMBinding();
24 }
26 URLSearchParams::~URLSearchParams()
27 {
28 DeleteAll();
29 }
31 JSObject*
32 URLSearchParams::WrapObject(JSContext* aCx)
33 {
34 return URLSearchParamsBinding::Wrap(aCx, this);
35 }
37 /* static */ already_AddRefed<URLSearchParams>
38 URLSearchParams::Constructor(const GlobalObject& aGlobal,
39 const nsAString& aInit,
40 ErrorResult& aRv)
41 {
42 nsRefPtr<URLSearchParams> sp = new URLSearchParams();
43 sp->ParseInput(NS_ConvertUTF16toUTF8(aInit), nullptr);
44 return sp.forget();
45 }
47 /* static */ already_AddRefed<URLSearchParams>
48 URLSearchParams::Constructor(const GlobalObject& aGlobal,
49 URLSearchParams& aInit,
50 ErrorResult& aRv)
51 {
52 nsRefPtr<URLSearchParams> sp = new URLSearchParams();
53 aInit.mSearchParams.EnumerateRead(CopyEnumerator, sp);
54 return sp.forget();
55 }
57 void
58 URLSearchParams::ParseInput(const nsACString& aInput,
59 URLSearchParamsObserver* aObserver)
60 {
61 // Remove all the existing data before parsing a new input.
62 DeleteAll();
64 nsACString::const_iterator start, end;
65 aInput.BeginReading(start);
66 aInput.EndReading(end);
67 nsACString::const_iterator iter(start);
69 while (start != end) {
70 nsAutoCString string;
72 if (FindCharInReadable('&', iter, end)) {
73 string.Assign(Substring(start, iter));
74 start = ++iter;
75 } else {
76 string.Assign(Substring(start, end));
77 start = end;
78 }
80 if (string.IsEmpty()) {
81 continue;
82 }
84 nsACString::const_iterator eqStart, eqEnd;
85 string.BeginReading(eqStart);
86 string.EndReading(eqEnd);
87 nsACString::const_iterator eqIter(eqStart);
89 nsAutoCString name;
90 nsAutoCString value;
92 if (FindCharInReadable('=', eqIter, eqEnd)) {
93 name.Assign(Substring(eqStart, eqIter));
95 ++eqIter;
96 value.Assign(Substring(eqIter, eqEnd));
97 } else {
98 name.Assign(string);
99 }
101 nsAutoCString decodedName;
102 DecodeString(name, decodedName);
104 nsAutoCString decodedValue;
105 DecodeString(value, decodedValue);
107 AppendInternal(NS_ConvertUTF8toUTF16(decodedName),
108 NS_ConvertUTF8toUTF16(decodedValue));
109 }
111 NotifyObservers(aObserver);
112 }
114 void
115 URLSearchParams::DecodeString(const nsACString& aInput, nsACString& aOutput)
116 {
117 nsACString::const_iterator start, end;
118 aInput.BeginReading(start);
119 aInput.EndReading(end);
121 while (start != end) {
122 // replace '+' with U+0020
123 if (*start == '+') {
124 aOutput.Append(' ');
125 ++start;
126 continue;
127 }
129 // Percent decode algorithm
130 if (*start == '%') {
131 nsACString::const_iterator first(start);
132 ++first;
134 nsACString::const_iterator second(first);
135 ++second;
137 #define ASCII_HEX_DIGIT( x ) \
138 ((x >= 0x41 && x <= 0x46) || \
139 (x >= 0x61 && x <= 0x66) || \
140 (x >= 0x30 && x <= 0x39))
142 #define HEX_DIGIT( x ) \
143 (*x >= 0x30 && *x <= 0x39 \
144 ? *x - 0x30 \
145 : (*x >= 0x41 && *x <= 0x46 \
146 ? *x - 0x37 \
147 : *x - 0x57))
149 if (first != end && second != end &&
150 ASCII_HEX_DIGIT(*first) && ASCII_HEX_DIGIT(*second)) {
151 aOutput.Append(HEX_DIGIT(first) * 16 + HEX_DIGIT(second));
152 start = ++second;
153 continue;
155 } else {
156 aOutput.Append('%');
157 ++start;
158 continue;
159 }
160 }
162 aOutput.Append(*start);
163 ++start;
164 }
165 }
167 /* static */ PLDHashOperator
168 URLSearchParams::CopyEnumerator(const nsAString& aName,
169 nsTArray<nsString>* aArray,
170 void *userData)
171 {
172 URLSearchParams* aSearchParams = static_cast<URLSearchParams*>(userData);
174 nsTArray<nsString>* newArray = new nsTArray<nsString>();
175 newArray->AppendElements(*aArray);
177 aSearchParams->mSearchParams.Put(aName, newArray);
178 return PL_DHASH_NEXT;
179 }
181 void
182 URLSearchParams::AddObserver(URLSearchParamsObserver* aObserver)
183 {
184 MOZ_ASSERT(aObserver);
185 MOZ_ASSERT(!mObservers.Contains(aObserver));
186 mObservers.AppendElement(aObserver);
187 }
189 void
190 URLSearchParams::RemoveObserver(URLSearchParamsObserver* aObserver)
191 {
192 MOZ_ASSERT(aObserver);
193 mObservers.RemoveElement(aObserver);
194 }
196 void
197 URLSearchParams::Get(const nsAString& aName, nsString& aRetval)
198 {
199 nsTArray<nsString>* array;
200 if (!mSearchParams.Get(aName, &array)) {
201 aRetval.Truncate();
202 return;
203 }
205 aRetval.Assign(array->ElementAt(0));
206 }
208 void
209 URLSearchParams::GetAll(const nsAString& aName, nsTArray<nsString>& aRetval)
210 {
211 nsTArray<nsString>* array;
212 if (!mSearchParams.Get(aName, &array)) {
213 return;
214 }
216 aRetval.AppendElements(*array);
217 }
219 void
220 URLSearchParams::Set(const nsAString& aName, const nsAString& aValue)
221 {
222 nsTArray<nsString>* array;
223 if (!mSearchParams.Get(aName, &array)) {
224 array = new nsTArray<nsString>();
225 array->AppendElement(aValue);
226 mSearchParams.Put(aName, array);
227 } else {
228 array->ElementAt(0) = aValue;
229 }
231 NotifyObservers(nullptr);
232 }
234 void
235 URLSearchParams::Append(const nsAString& aName, const nsAString& aValue)
236 {
237 AppendInternal(aName, aValue);
238 NotifyObservers(nullptr);
239 }
241 void
242 URLSearchParams::AppendInternal(const nsAString& aName, const nsAString& aValue)
243 {
244 nsTArray<nsString>* array;
245 if (!mSearchParams.Get(aName, &array)) {
246 array = new nsTArray<nsString>();
247 mSearchParams.Put(aName, array);
248 }
250 array->AppendElement(aValue);
251 }
253 bool
254 URLSearchParams::Has(const nsAString& aName)
255 {
256 return mSearchParams.Get(aName, nullptr);
257 }
259 void
260 URLSearchParams::Delete(const nsAString& aName)
261 {
262 nsTArray<nsString>* array;
263 if (!mSearchParams.Get(aName, &array)) {
264 return;
265 }
267 mSearchParams.Remove(aName);
269 NotifyObservers(nullptr);
270 }
272 void
273 URLSearchParams::DeleteAll()
274 {
275 mSearchParams.Clear();
276 }
278 class MOZ_STACK_CLASS SerializeData
279 {
280 public:
281 SerializeData()
282 : mFirst(true)
283 {}
285 nsAutoString mValue;
286 bool mFirst;
288 void Serialize(const nsCString& aInput)
289 {
290 const unsigned char* p = (const unsigned char*) aInput.get();
292 while (p && *p) {
293 // ' ' to '+'
294 if (*p == 0x20) {
295 mValue.Append(0x2B);
296 // Percent Encode algorithm
297 } else if (*p == 0x2A || *p == 0x2D || *p == 0x2E ||
298 (*p >= 0x30 && *p <= 0x39) ||
299 (*p >= 0x41 && *p <= 0x5A) || *p == 0x5F ||
300 (*p >= 0x61 && *p <= 0x7A)) {
301 mValue.Append(*p);
302 } else {
303 mValue.AppendPrintf("%%%X", *p);
304 }
306 ++p;
307 }
308 }
309 };
311 void
312 URLSearchParams::Serialize(nsAString& aValue) const
313 {
314 SerializeData data;
315 mSearchParams.EnumerateRead(SerializeEnumerator, &data);
316 aValue.Assign(data.mValue);
317 }
319 /* static */ PLDHashOperator
320 URLSearchParams::SerializeEnumerator(const nsAString& aName,
321 nsTArray<nsString>* aArray,
322 void *userData)
323 {
324 SerializeData* data = static_cast<SerializeData*>(userData);
326 for (uint32_t i = 0, len = aArray->Length(); i < len; ++i) {
327 if (data->mFirst) {
328 data->mFirst = false;
329 } else {
330 data->mValue.Append(NS_LITERAL_STRING("&"));
331 }
333 data->Serialize(NS_ConvertUTF16toUTF8(aName));
334 data->mValue.Append(NS_LITERAL_STRING("="));
335 data->Serialize(NS_ConvertUTF16toUTF8(aArray->ElementAt(i)));
336 }
338 return PL_DHASH_NEXT;
339 }
341 void
342 URLSearchParams::NotifyObservers(URLSearchParamsObserver* aExceptObserver)
343 {
344 for (uint32_t i = 0; i < mObservers.Length(); ++i) {
345 if (mObservers[i] != aExceptObserver) {
346 mObservers[i]->URLSearchParamsUpdated();
347 }
348 }
349 }
351 } // namespace dom
352 } // namespace mozilla