URLSearchParams.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592
  1. /* -*- Mode: C++; tab-width: 8; 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. #include "URLSearchParams.h"
  6. #include "mozilla/dom/URLSearchParamsBinding.h"
  7. #include "mozilla/dom/EncodingUtils.h"
  8. #include "nsDOMString.h"
  9. #include "nsIInputStream.h"
  10. #include "nsStringStream.h"
  11. namespace mozilla {
  12. namespace dom {
  13. bool
  14. URLParams::Has(const nsAString& aName)
  15. {
  16. for (uint32_t i = 0, len = mParams.Length(); i < len; ++i) {
  17. if (mParams[i].mKey.Equals(aName)) {
  18. return true;
  19. }
  20. }
  21. return false;
  22. }
  23. void
  24. URLParams::Get(const nsAString& aName, nsString& aRetval)
  25. {
  26. SetDOMStringToNull(aRetval);
  27. for (uint32_t i = 0, len = mParams.Length(); i < len; ++i) {
  28. if (mParams[i].mKey.Equals(aName)) {
  29. aRetval.Assign(mParams[i].mValue);
  30. break;
  31. }
  32. }
  33. }
  34. void
  35. URLParams::GetAll(const nsAString& aName, nsTArray<nsString>& aRetval)
  36. {
  37. aRetval.Clear();
  38. for (uint32_t i = 0, len = mParams.Length(); i < len; ++i) {
  39. if (mParams[i].mKey.Equals(aName)) {
  40. aRetval.AppendElement(mParams[i].mValue);
  41. }
  42. }
  43. }
  44. void
  45. URLParams::Append(const nsAString& aName, const nsAString& aValue)
  46. {
  47. Param* param = mParams.AppendElement();
  48. param->mKey = aName;
  49. param->mValue = aValue;
  50. }
  51. void
  52. URLParams::Set(const nsAString& aName, const nsAString& aValue)
  53. {
  54. Param* param = nullptr;
  55. for (uint32_t i = 0, len = mParams.Length(); i < len;) {
  56. if (!mParams[i].mKey.Equals(aName)) {
  57. ++i;
  58. continue;
  59. }
  60. if (!param) {
  61. param = &mParams[i];
  62. ++i;
  63. continue;
  64. }
  65. // Remove duplicates.
  66. mParams.RemoveElementAt(i);
  67. --len;
  68. }
  69. if (!param) {
  70. param = mParams.AppendElement();
  71. param->mKey = aName;
  72. }
  73. param->mValue = aValue;
  74. }
  75. bool
  76. URLParams::Delete(const nsAString& aName)
  77. {
  78. bool found = false;
  79. for (uint32_t i = 0; i < mParams.Length();) {
  80. if (mParams[i].mKey.Equals(aName)) {
  81. mParams.RemoveElementAt(i);
  82. found = true;
  83. } else {
  84. ++i;
  85. }
  86. }
  87. return found;
  88. }
  89. void
  90. URLParams::ConvertString(const nsACString& aInput, nsAString& aOutput)
  91. {
  92. aOutput.Truncate();
  93. if (!mDecoder) {
  94. mDecoder = EncodingUtils::DecoderForEncoding("UTF-8");
  95. if (!mDecoder) {
  96. MOZ_ASSERT(mDecoder, "Failed to create a decoder.");
  97. return;
  98. }
  99. }
  100. int32_t inputLength = aInput.Length();
  101. int32_t outputLength = 0;
  102. nsresult rv = mDecoder->GetMaxLength(aInput.BeginReading(), inputLength,
  103. &outputLength);
  104. if (NS_WARN_IF(NS_FAILED(rv))) {
  105. return;
  106. }
  107. if (!aOutput.SetLength(outputLength, fallible)) {
  108. return;
  109. }
  110. int32_t newOutputLength = outputLength;
  111. rv = mDecoder->Convert(aInput.BeginReading(), &inputLength,
  112. aOutput.BeginWriting(), &newOutputLength);
  113. if (NS_FAILED(rv)) {
  114. aOutput.Truncate();
  115. return;
  116. }
  117. if (newOutputLength < outputLength) {
  118. aOutput.Truncate(newOutputLength);
  119. }
  120. }
  121. void
  122. URLParams::DecodeString(const nsACString& aInput, nsAString& aOutput)
  123. {
  124. nsACString::const_iterator start, end;
  125. aInput.BeginReading(start);
  126. aInput.EndReading(end);
  127. nsCString unescaped;
  128. while (start != end) {
  129. // replace '+' with U+0020
  130. if (*start == '+') {
  131. unescaped.Append(' ');
  132. ++start;
  133. continue;
  134. }
  135. // Percent decode algorithm
  136. if (*start == '%') {
  137. nsACString::const_iterator first(start);
  138. ++first;
  139. nsACString::const_iterator second(first);
  140. ++second;
  141. #define ASCII_HEX_DIGIT( x ) \
  142. ((x >= 0x41 && x <= 0x46) || \
  143. (x >= 0x61 && x <= 0x66) || \
  144. (x >= 0x30 && x <= 0x39))
  145. #define HEX_DIGIT( x ) \
  146. (*x >= 0x30 && *x <= 0x39 \
  147. ? *x - 0x30 \
  148. : (*x >= 0x41 && *x <= 0x46 \
  149. ? *x - 0x37 \
  150. : *x - 0x57))
  151. if (first != end && second != end &&
  152. ASCII_HEX_DIGIT(*first) && ASCII_HEX_DIGIT(*second)) {
  153. unescaped.Append(HEX_DIGIT(first) * 16 + HEX_DIGIT(second));
  154. start = ++second;
  155. continue;
  156. } else {
  157. unescaped.Append('%');
  158. ++start;
  159. continue;
  160. }
  161. }
  162. unescaped.Append(*start);
  163. ++start;
  164. }
  165. ConvertString(unescaped, aOutput);
  166. }
  167. void
  168. URLParams::ParseInput(const nsACString& aInput)
  169. {
  170. // Remove all the existing data before parsing a new input.
  171. DeleteAll();
  172. nsACString::const_iterator start, end;
  173. aInput.BeginReading(start);
  174. aInput.EndReading(end);
  175. nsACString::const_iterator iter(start);
  176. while (start != end) {
  177. nsAutoCString string;
  178. if (FindCharInReadable('&', iter, end)) {
  179. string.Assign(Substring(start, iter));
  180. start = ++iter;
  181. } else {
  182. string.Assign(Substring(start, end));
  183. start = end;
  184. }
  185. if (string.IsEmpty()) {
  186. continue;
  187. }
  188. nsACString::const_iterator eqStart, eqEnd;
  189. string.BeginReading(eqStart);
  190. string.EndReading(eqEnd);
  191. nsACString::const_iterator eqIter(eqStart);
  192. nsAutoCString name;
  193. nsAutoCString value;
  194. if (FindCharInReadable('=', eqIter, eqEnd)) {
  195. name.Assign(Substring(eqStart, eqIter));
  196. ++eqIter;
  197. value.Assign(Substring(eqIter, eqEnd));
  198. } else {
  199. name.Assign(string);
  200. }
  201. nsAutoString decodedName;
  202. DecodeString(name, decodedName);
  203. nsAutoString decodedValue;
  204. DecodeString(value, decodedValue);
  205. Append(decodedName, decodedValue);
  206. }
  207. }
  208. namespace {
  209. void SerializeString(const nsCString& aInput, nsAString& aValue)
  210. {
  211. const unsigned char* p = (const unsigned char*) aInput.get();
  212. const unsigned char* end = p + aInput.Length();
  213. while (p != end) {
  214. // ' ' to '+'
  215. if (*p == 0x20) {
  216. aValue.Append(0x2B);
  217. // Percent Encode algorithm
  218. } else if (*p == 0x2A || *p == 0x2D || *p == 0x2E ||
  219. (*p >= 0x30 && *p <= 0x39) ||
  220. (*p >= 0x41 && *p <= 0x5A) || *p == 0x5F ||
  221. (*p >= 0x61 && *p <= 0x7A)) {
  222. aValue.Append(*p);
  223. } else {
  224. aValue.AppendPrintf("%%%.2X", *p);
  225. }
  226. ++p;
  227. }
  228. }
  229. } // namespace
  230. void
  231. URLParams::Serialize(nsAString& aValue) const
  232. {
  233. aValue.Truncate();
  234. bool first = true;
  235. for (uint32_t i = 0, len = mParams.Length(); i < len; ++i) {
  236. if (first) {
  237. first = false;
  238. } else {
  239. aValue.Append('&');
  240. }
  241. SerializeString(NS_ConvertUTF16toUTF8(mParams[i].mKey), aValue);
  242. aValue.Append('=');
  243. SerializeString(NS_ConvertUTF16toUTF8(mParams[i].mValue), aValue);
  244. }
  245. }
  246. NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(URLSearchParams, mParent, mObserver)
  247. NS_IMPL_CYCLE_COLLECTING_ADDREF(URLSearchParams)
  248. NS_IMPL_CYCLE_COLLECTING_RELEASE(URLSearchParams)
  249. NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(URLSearchParams)
  250. NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
  251. NS_INTERFACE_MAP_ENTRY(nsIXHRSendable)
  252. NS_INTERFACE_MAP_ENTRY(nsISupports)
  253. NS_INTERFACE_MAP_END
  254. URLSearchParams::URLSearchParams(nsISupports* aParent,
  255. URLSearchParamsObserver* aObserver)
  256. : mParams(new URLParams())
  257. , mParent(aParent)
  258. , mObserver(aObserver)
  259. {
  260. }
  261. URLSearchParams::~URLSearchParams()
  262. {
  263. DeleteAll();
  264. }
  265. JSObject*
  266. URLSearchParams::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
  267. {
  268. return URLSearchParamsBinding::Wrap(aCx, this, aGivenProto);
  269. }
  270. /* static */ already_AddRefed<URLSearchParams>
  271. URLSearchParams::Constructor(const GlobalObject& aGlobal,
  272. const USVStringSequenceSequenceOrUSVStringUSVStringRecordOrUSVString& aInit,
  273. ErrorResult& aRv)
  274. {
  275. RefPtr<URLSearchParams> sp =
  276. new URLSearchParams(aGlobal.GetAsSupports(), nullptr);
  277. if (aInit.IsUSVString()) {
  278. NS_ConvertUTF16toUTF8 input(aInit.GetAsUSVString());
  279. if (StringBeginsWith(input, NS_LITERAL_CSTRING("?"))) {
  280. sp->ParseInput(Substring(input, 1, input.Length() - 1));
  281. } else {
  282. sp->ParseInput(input);
  283. }
  284. } else if (aInit.IsUSVStringSequenceSequence()) {
  285. const Sequence<Sequence<nsString>>& list =
  286. aInit.GetAsUSVStringSequenceSequence();
  287. for (uint32_t i = 0; i < list.Length(); ++i) {
  288. const Sequence<nsString>& item = list[i];
  289. if (item.Length() != 2) {
  290. aRv.Throw(NS_ERROR_DOM_TYPE_ERR);
  291. return nullptr;
  292. }
  293. sp->Append(item[0], item[1]);
  294. }
  295. } else if (aInit.IsUSVStringUSVStringRecord()) {
  296. const Record<nsString, nsString>& record =
  297. aInit.GetAsUSVStringUSVStringRecord();
  298. for (auto& entry : record.Entries()) {
  299. sp->Append(entry.mKey, entry.mValue);
  300. }
  301. } else {
  302. MOZ_CRASH("URLSearchParams: Invalid string");
  303. }
  304. return sp.forget();
  305. }
  306. void
  307. URLSearchParams::ParseInput(const nsACString& aInput)
  308. {
  309. mParams->ParseInput(aInput);
  310. }
  311. void
  312. URLSearchParams::Get(const nsAString& aName, nsString& aRetval)
  313. {
  314. return mParams->Get(aName, aRetval);
  315. }
  316. void
  317. URLSearchParams::GetAll(const nsAString& aName, nsTArray<nsString>& aRetval)
  318. {
  319. return mParams->GetAll(aName, aRetval);
  320. }
  321. void
  322. URLSearchParams::Set(const nsAString& aName, const nsAString& aValue)
  323. {
  324. mParams->Set(aName, aValue);
  325. NotifyObserver();
  326. }
  327. void
  328. URLSearchParams::Append(const nsAString& aName, const nsAString& aValue)
  329. {
  330. mParams->Append(aName, aValue);
  331. NotifyObserver();
  332. }
  333. bool
  334. URLSearchParams::Has(const nsAString& aName)
  335. {
  336. return mParams->Has(aName);
  337. }
  338. void
  339. URLSearchParams::Delete(const nsAString& aName)
  340. {
  341. if (mParams->Delete(aName)) {
  342. NotifyObserver();
  343. }
  344. }
  345. void
  346. URLSearchParams::DeleteAll()
  347. {
  348. mParams->DeleteAll();
  349. }
  350. void
  351. URLSearchParams::Serialize(nsAString& aValue) const
  352. {
  353. mParams->Serialize(aValue);
  354. }
  355. void
  356. URLSearchParams::NotifyObserver()
  357. {
  358. if (mObserver) {
  359. mObserver->URLSearchParamsUpdated(this);
  360. }
  361. }
  362. uint32_t
  363. URLSearchParams::GetIterableLength() const
  364. {
  365. return mParams->Length();
  366. }
  367. const nsAString&
  368. URLSearchParams::GetKeyAtIndex(uint32_t aIndex) const
  369. {
  370. return mParams->GetKeyAtIndex(aIndex);
  371. }
  372. const nsAString&
  373. URLSearchParams::GetValueAtIndex(uint32_t aIndex) const
  374. {
  375. return mParams->GetValueAtIndex(aIndex);
  376. }
  377. void
  378. URLSearchParams::Sort(ErrorResult& aRv)
  379. {
  380. aRv = mParams->Sort();
  381. if (!aRv.Failed()) {
  382. NotifyObserver();
  383. }
  384. }
  385. // Helper functions for structured cloning
  386. inline bool
  387. ReadString(JSStructuredCloneReader* aReader, nsString& aString)
  388. {
  389. MOZ_ASSERT(aReader);
  390. bool read;
  391. uint32_t nameLength, zero;
  392. read = JS_ReadUint32Pair(aReader, &nameLength, &zero);
  393. if (!read) {
  394. return false;
  395. }
  396. MOZ_ASSERT(zero == 0);
  397. aString.SetLength(nameLength);
  398. size_t charSize = sizeof(nsString::char_type);
  399. read = JS_ReadBytes(aReader, (void*) aString.BeginWriting(),
  400. nameLength * charSize);
  401. if (!read) {
  402. return false;
  403. }
  404. return true;
  405. }
  406. nsresult
  407. URLParams::Sort()
  408. {
  409. // Unfortunately we cannot use nsTArray<>.Sort() because it doesn't keep the
  410. // correct order of the values for equal keys.
  411. // Let's sort the keys, without duplicates.
  412. FallibleTArray<nsString> keys;
  413. for (const Param& param : mParams) {
  414. if (!keys.Contains(param.mKey) &&
  415. !keys.InsertElementSorted(param.mKey, fallible)) {
  416. return NS_ERROR_OUT_OF_MEMORY;
  417. }
  418. }
  419. FallibleTArray<Param> params;
  420. // Here we recreate the array starting from the sorted keys.
  421. for (uint32_t keyId = 0, keysLength = keys.Length(); keyId < keysLength;
  422. ++keyId) {
  423. const nsString& key = keys[keyId];
  424. for (const Param& param : mParams) {
  425. if (param.mKey.Equals(key) &&
  426. !params.AppendElement(param, fallible)) {
  427. return NS_ERROR_OUT_OF_MEMORY;
  428. }
  429. }
  430. }
  431. mParams.SwapElements(params);
  432. return NS_OK;
  433. }
  434. inline bool
  435. WriteString(JSStructuredCloneWriter* aWriter, const nsString& aString)
  436. {
  437. MOZ_ASSERT(aWriter);
  438. size_t charSize = sizeof(nsString::char_type);
  439. return JS_WriteUint32Pair(aWriter, aString.Length(), 0) &&
  440. JS_WriteBytes(aWriter, aString.get(), aString.Length() * charSize);
  441. }
  442. bool
  443. URLParams::WriteStructuredClone(JSStructuredCloneWriter* aWriter) const
  444. {
  445. const uint32_t& nParams = mParams.Length();
  446. if (!JS_WriteUint32Pair(aWriter, nParams, 0)) {
  447. return false;
  448. }
  449. for (uint32_t i = 0; i < nParams; ++i) {
  450. if (!WriteString(aWriter, mParams[i].mKey) ||
  451. !WriteString(aWriter, mParams[i].mValue)) {
  452. return false;
  453. }
  454. }
  455. return true;
  456. }
  457. bool
  458. URLParams::ReadStructuredClone(JSStructuredCloneReader* aReader)
  459. {
  460. MOZ_ASSERT(aReader);
  461. DeleteAll();
  462. uint32_t nParams, zero;
  463. nsAutoString key, value;
  464. if (!JS_ReadUint32Pair(aReader, &nParams, &zero)) {
  465. return false;
  466. }
  467. MOZ_ASSERT(zero == 0);
  468. for (uint32_t i = 0; i < nParams; ++i) {
  469. if (!ReadString(aReader, key) || !ReadString(aReader, value)) {
  470. return false;
  471. }
  472. Append(key, value);
  473. }
  474. return true;
  475. }
  476. bool
  477. URLSearchParams::WriteStructuredClone(JSStructuredCloneWriter* aWriter) const
  478. {
  479. return mParams->WriteStructuredClone(aWriter);
  480. }
  481. bool
  482. URLSearchParams::ReadStructuredClone(JSStructuredCloneReader* aReader)
  483. {
  484. return mParams->ReadStructuredClone(aReader);
  485. }
  486. NS_IMETHODIMP
  487. URLSearchParams::GetSendInfo(nsIInputStream** aBody, uint64_t* aContentLength,
  488. nsACString& aContentType, nsACString& aCharset)
  489. {
  490. aContentType.AssignLiteral("application/x-www-form-urlencoded");
  491. aCharset.AssignLiteral("UTF-8");
  492. nsAutoString serialized;
  493. Serialize(serialized);
  494. NS_ConvertUTF16toUTF8 converted(serialized);
  495. *aContentLength = converted.Length();
  496. return NS_NewCStringInputStream(aBody, converted);
  497. }
  498. } // namespace dom
  499. } // namespace mozilla