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