|
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/. */ |
|
5 |
|
6 #include "URLSearchParams.h" |
|
7 #include "mozilla/dom/URLSearchParamsBinding.h" |
|
8 |
|
9 namespace mozilla { |
|
10 namespace dom { |
|
11 |
|
12 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_1(URLSearchParams, mObservers) |
|
13 NS_IMPL_CYCLE_COLLECTING_ADDREF(URLSearchParams) |
|
14 NS_IMPL_CYCLE_COLLECTING_RELEASE(URLSearchParams) |
|
15 |
|
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 |
|
20 |
|
21 URLSearchParams::URLSearchParams() |
|
22 { |
|
23 SetIsDOMBinding(); |
|
24 } |
|
25 |
|
26 URLSearchParams::~URLSearchParams() |
|
27 { |
|
28 DeleteAll(); |
|
29 } |
|
30 |
|
31 JSObject* |
|
32 URLSearchParams::WrapObject(JSContext* aCx) |
|
33 { |
|
34 return URLSearchParamsBinding::Wrap(aCx, this); |
|
35 } |
|
36 |
|
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 } |
|
46 |
|
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 } |
|
56 |
|
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(); |
|
63 |
|
64 nsACString::const_iterator start, end; |
|
65 aInput.BeginReading(start); |
|
66 aInput.EndReading(end); |
|
67 nsACString::const_iterator iter(start); |
|
68 |
|
69 while (start != end) { |
|
70 nsAutoCString string; |
|
71 |
|
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 } |
|
79 |
|
80 if (string.IsEmpty()) { |
|
81 continue; |
|
82 } |
|
83 |
|
84 nsACString::const_iterator eqStart, eqEnd; |
|
85 string.BeginReading(eqStart); |
|
86 string.EndReading(eqEnd); |
|
87 nsACString::const_iterator eqIter(eqStart); |
|
88 |
|
89 nsAutoCString name; |
|
90 nsAutoCString value; |
|
91 |
|
92 if (FindCharInReadable('=', eqIter, eqEnd)) { |
|
93 name.Assign(Substring(eqStart, eqIter)); |
|
94 |
|
95 ++eqIter; |
|
96 value.Assign(Substring(eqIter, eqEnd)); |
|
97 } else { |
|
98 name.Assign(string); |
|
99 } |
|
100 |
|
101 nsAutoCString decodedName; |
|
102 DecodeString(name, decodedName); |
|
103 |
|
104 nsAutoCString decodedValue; |
|
105 DecodeString(value, decodedValue); |
|
106 |
|
107 AppendInternal(NS_ConvertUTF8toUTF16(decodedName), |
|
108 NS_ConvertUTF8toUTF16(decodedValue)); |
|
109 } |
|
110 |
|
111 NotifyObservers(aObserver); |
|
112 } |
|
113 |
|
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); |
|
120 |
|
121 while (start != end) { |
|
122 // replace '+' with U+0020 |
|
123 if (*start == '+') { |
|
124 aOutput.Append(' '); |
|
125 ++start; |
|
126 continue; |
|
127 } |
|
128 |
|
129 // Percent decode algorithm |
|
130 if (*start == '%') { |
|
131 nsACString::const_iterator first(start); |
|
132 ++first; |
|
133 |
|
134 nsACString::const_iterator second(first); |
|
135 ++second; |
|
136 |
|
137 #define ASCII_HEX_DIGIT( x ) \ |
|
138 ((x >= 0x41 && x <= 0x46) || \ |
|
139 (x >= 0x61 && x <= 0x66) || \ |
|
140 (x >= 0x30 && x <= 0x39)) |
|
141 |
|
142 #define HEX_DIGIT( x ) \ |
|
143 (*x >= 0x30 && *x <= 0x39 \ |
|
144 ? *x - 0x30 \ |
|
145 : (*x >= 0x41 && *x <= 0x46 \ |
|
146 ? *x - 0x37 \ |
|
147 : *x - 0x57)) |
|
148 |
|
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; |
|
154 |
|
155 } else { |
|
156 aOutput.Append('%'); |
|
157 ++start; |
|
158 continue; |
|
159 } |
|
160 } |
|
161 |
|
162 aOutput.Append(*start); |
|
163 ++start; |
|
164 } |
|
165 } |
|
166 |
|
167 /* static */ PLDHashOperator |
|
168 URLSearchParams::CopyEnumerator(const nsAString& aName, |
|
169 nsTArray<nsString>* aArray, |
|
170 void *userData) |
|
171 { |
|
172 URLSearchParams* aSearchParams = static_cast<URLSearchParams*>(userData); |
|
173 |
|
174 nsTArray<nsString>* newArray = new nsTArray<nsString>(); |
|
175 newArray->AppendElements(*aArray); |
|
176 |
|
177 aSearchParams->mSearchParams.Put(aName, newArray); |
|
178 return PL_DHASH_NEXT; |
|
179 } |
|
180 |
|
181 void |
|
182 URLSearchParams::AddObserver(URLSearchParamsObserver* aObserver) |
|
183 { |
|
184 MOZ_ASSERT(aObserver); |
|
185 MOZ_ASSERT(!mObservers.Contains(aObserver)); |
|
186 mObservers.AppendElement(aObserver); |
|
187 } |
|
188 |
|
189 void |
|
190 URLSearchParams::RemoveObserver(URLSearchParamsObserver* aObserver) |
|
191 { |
|
192 MOZ_ASSERT(aObserver); |
|
193 mObservers.RemoveElement(aObserver); |
|
194 } |
|
195 |
|
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 } |
|
204 |
|
205 aRetval.Assign(array->ElementAt(0)); |
|
206 } |
|
207 |
|
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 } |
|
215 |
|
216 aRetval.AppendElements(*array); |
|
217 } |
|
218 |
|
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 } |
|
230 |
|
231 NotifyObservers(nullptr); |
|
232 } |
|
233 |
|
234 void |
|
235 URLSearchParams::Append(const nsAString& aName, const nsAString& aValue) |
|
236 { |
|
237 AppendInternal(aName, aValue); |
|
238 NotifyObservers(nullptr); |
|
239 } |
|
240 |
|
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 } |
|
249 |
|
250 array->AppendElement(aValue); |
|
251 } |
|
252 |
|
253 bool |
|
254 URLSearchParams::Has(const nsAString& aName) |
|
255 { |
|
256 return mSearchParams.Get(aName, nullptr); |
|
257 } |
|
258 |
|
259 void |
|
260 URLSearchParams::Delete(const nsAString& aName) |
|
261 { |
|
262 nsTArray<nsString>* array; |
|
263 if (!mSearchParams.Get(aName, &array)) { |
|
264 return; |
|
265 } |
|
266 |
|
267 mSearchParams.Remove(aName); |
|
268 |
|
269 NotifyObservers(nullptr); |
|
270 } |
|
271 |
|
272 void |
|
273 URLSearchParams::DeleteAll() |
|
274 { |
|
275 mSearchParams.Clear(); |
|
276 } |
|
277 |
|
278 class MOZ_STACK_CLASS SerializeData |
|
279 { |
|
280 public: |
|
281 SerializeData() |
|
282 : mFirst(true) |
|
283 {} |
|
284 |
|
285 nsAutoString mValue; |
|
286 bool mFirst; |
|
287 |
|
288 void Serialize(const nsCString& aInput) |
|
289 { |
|
290 const unsigned char* p = (const unsigned char*) aInput.get(); |
|
291 |
|
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 } |
|
305 |
|
306 ++p; |
|
307 } |
|
308 } |
|
309 }; |
|
310 |
|
311 void |
|
312 URLSearchParams::Serialize(nsAString& aValue) const |
|
313 { |
|
314 SerializeData data; |
|
315 mSearchParams.EnumerateRead(SerializeEnumerator, &data); |
|
316 aValue.Assign(data.mValue); |
|
317 } |
|
318 |
|
319 /* static */ PLDHashOperator |
|
320 URLSearchParams::SerializeEnumerator(const nsAString& aName, |
|
321 nsTArray<nsString>* aArray, |
|
322 void *userData) |
|
323 { |
|
324 SerializeData* data = static_cast<SerializeData*>(userData); |
|
325 |
|
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 } |
|
332 |
|
333 data->Serialize(NS_ConvertUTF16toUTF8(aName)); |
|
334 data->mValue.Append(NS_LITERAL_STRING("=")); |
|
335 data->Serialize(NS_ConvertUTF16toUTF8(aArray->ElementAt(i))); |
|
336 } |
|
337 |
|
338 return PL_DHASH_NEXT; |
|
339 } |
|
340 |
|
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 } |
|
350 |
|
351 } // namespace dom |
|
352 } // namespace mozilla |