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 "mozilla/dom/URLSearchParams.h"
8 
9 // XXX encoding_rs.h is not self-contained, this order is required
10 #include "mozilla/Encoding.h"
11 #include "encoding_rs.h"
12 
13 #include <new>
14 #include <type_traits>
15 #include <utility>
16 #include "js/StructuredClone.h"
17 #include "mozilla/ArrayIterator.h"
18 #include "mozilla/Attributes.h"
19 #include "mozilla/ErrorResult.h"
20 #include "mozilla/MacroForEach.h"
21 #include "mozilla/NotNull.h"
22 #include "mozilla/dom/BindingDeclarations.h"
23 #include "mozilla/dom/Record.h"
24 #include "mozilla/dom/StructuredCloneHolder.h"
25 #include "mozilla/dom/URLSearchParamsBinding.h"
26 #include "mozilla/fallible.h"
27 #include "nsDOMString.h"
28 #include "nsError.h"
29 #include "nsIGlobalObject.h"
30 #include "nsLiteralString.h"
31 #include "nsPrintfCString.h"
32 #include "nsString.h"
33 #include "nsStringFlags.h"
34 #include "nsStringIterator.h"
35 #include "nsStringStream.h"
36 #include "nsURLHelper.h"
37 
38 namespace mozilla {
39 namespace dom {
40 
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(URLSearchParams,mParent,mObserver)41 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(URLSearchParams, mParent, mObserver)
42 NS_IMPL_CYCLE_COLLECTING_ADDREF(URLSearchParams)
43 NS_IMPL_CYCLE_COLLECTING_RELEASE(URLSearchParams)
44 
45 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(URLSearchParams)
46   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
47   NS_INTERFACE_MAP_ENTRY(nsISupports)
48 NS_INTERFACE_MAP_END
49 
50 URLSearchParams::URLSearchParams(nsISupports* aParent,
51                                  URLSearchParamsObserver* aObserver)
52     : mParams(new URLParams()), mParent(aParent), mObserver(aObserver) {}
53 
~URLSearchParams()54 URLSearchParams::~URLSearchParams() { DeleteAll(); }
55 
WrapObject(JSContext * aCx,JS::Handle<JSObject * > aGivenProto)56 JSObject* URLSearchParams::WrapObject(JSContext* aCx,
57                                       JS::Handle<JSObject*> aGivenProto) {
58   return URLSearchParams_Binding::Wrap(aCx, this, aGivenProto);
59 }
60 
61 /* static */
Constructor(const GlobalObject & aGlobal,const USVStringSequenceSequenceOrUSVStringUSVStringRecordOrUSVString & aInit,ErrorResult & aRv)62 already_AddRefed<URLSearchParams> URLSearchParams::Constructor(
63     const GlobalObject& aGlobal,
64     const USVStringSequenceSequenceOrUSVStringUSVStringRecordOrUSVString& aInit,
65     ErrorResult& aRv) {
66   RefPtr<URLSearchParams> sp =
67       new URLSearchParams(aGlobal.GetAsSupports(), nullptr);
68 
69   if (aInit.IsUSVString()) {
70     NS_ConvertUTF16toUTF8 input(aInit.GetAsUSVString());
71     if (StringBeginsWith(input, "?"_ns)) {
72       sp->ParseInput(Substring(input, 1, input.Length() - 1));
73     } else {
74       sp->ParseInput(input);
75     }
76   } else if (aInit.IsUSVStringSequenceSequence()) {
77     const Sequence<Sequence<nsString>>& list =
78         aInit.GetAsUSVStringSequenceSequence();
79     for (uint32_t i = 0; i < list.Length(); ++i) {
80       const Sequence<nsString>& item = list[i];
81       if (item.Length() != 2) {
82         nsPrintfCString err("Expected 2 items in pair but got %zu",
83                             item.Length());
84         aRv.ThrowTypeError(err);
85         return nullptr;
86       }
87       sp->Append(item[0], item[1]);
88     }
89   } else if (aInit.IsUSVStringUSVStringRecord()) {
90     const Record<nsString, nsString>& record =
91         aInit.GetAsUSVStringUSVStringRecord();
92     for (auto& entry : record.Entries()) {
93       sp->Append(entry.mKey, entry.mValue);
94     }
95   } else {
96     MOZ_CRASH("This should not happen.");
97   }
98 
99   return sp.forget();
100 }
101 
ParseInput(const nsACString & aInput)102 void URLSearchParams::ParseInput(const nsACString& aInput) {
103   mParams->ParseInput(aInput);
104 }
105 
Get(const nsAString & aName,nsString & aRetval)106 void URLSearchParams::Get(const nsAString& aName, nsString& aRetval) {
107   return mParams->Get(aName, aRetval);
108 }
109 
GetAll(const nsAString & aName,nsTArray<nsString> & aRetval)110 void URLSearchParams::GetAll(const nsAString& aName,
111                              nsTArray<nsString>& aRetval) {
112   return mParams->GetAll(aName, aRetval);
113 }
114 
Set(const nsAString & aName,const nsAString & aValue)115 void URLSearchParams::Set(const nsAString& aName, const nsAString& aValue) {
116   mParams->Set(aName, aValue);
117   NotifyObserver();
118 }
119 
Append(const nsAString & aName,const nsAString & aValue)120 void URLSearchParams::Append(const nsAString& aName, const nsAString& aValue) {
121   mParams->Append(aName, aValue);
122   NotifyObserver();
123 }
124 
Has(const nsAString & aName)125 bool URLSearchParams::Has(const nsAString& aName) {
126   return mParams->Has(aName);
127 }
128 
Delete(const nsAString & aName)129 void URLSearchParams::Delete(const nsAString& aName) {
130   mParams->Delete(aName);
131   NotifyObserver();
132 }
133 
DeleteAll()134 void URLSearchParams::DeleteAll() { mParams->DeleteAll(); }
135 
Serialize(nsAString & aValue) const136 void URLSearchParams::Serialize(nsAString& aValue) const {
137   mParams->Serialize(aValue);
138 }
139 
NotifyObserver()140 void URLSearchParams::NotifyObserver() {
141   if (mObserver) {
142     mObserver->URLSearchParamsUpdated(this);
143   }
144 }
145 
GetIterableLength() const146 uint32_t URLSearchParams::GetIterableLength() const {
147   return mParams->Length();
148 }
149 
GetKeyAtIndex(uint32_t aIndex) const150 const nsAString& URLSearchParams::GetKeyAtIndex(uint32_t aIndex) const {
151   return mParams->GetKeyAtIndex(aIndex);
152 }
153 
GetValueAtIndex(uint32_t aIndex) const154 const nsAString& URLSearchParams::GetValueAtIndex(uint32_t aIndex) const {
155   return mParams->GetValueAtIndex(aIndex);
156 }
157 
Sort(ErrorResult & aRv)158 void URLSearchParams::Sort(ErrorResult& aRv) {
159   mParams->Sort();
160   NotifyObserver();
161 }
162 
WriteStructuredClone(JSStructuredCloneWriter * aWriter) const163 bool URLSearchParams::WriteStructuredClone(
164     JSStructuredCloneWriter* aWriter) const {
165   const uint32_t& nParams = mParams->Length();
166   if (!JS_WriteUint32Pair(aWriter, nParams, 0)) {
167     return false;
168   }
169   for (uint32_t i = 0; i < nParams; ++i) {
170     if (!StructuredCloneHolder::WriteString(aWriter,
171                                             mParams->GetKeyAtIndex(i)) ||
172         !StructuredCloneHolder::WriteString(aWriter,
173                                             mParams->GetValueAtIndex(i))) {
174       return false;
175     }
176   }
177   return true;
178 }
179 
ReadStructuredClone(JSStructuredCloneReader * aReader)180 bool URLSearchParams::ReadStructuredClone(JSStructuredCloneReader* aReader) {
181   MOZ_ASSERT(aReader);
182 
183   DeleteAll();
184 
185   uint32_t nParams, zero;
186   nsAutoString key, value;
187   if (!JS_ReadUint32Pair(aReader, &nParams, &zero)) {
188     return false;
189   }
190   MOZ_ASSERT(zero == 0);
191   for (uint32_t i = 0; i < nParams; ++i) {
192     if (!StructuredCloneHolder::ReadString(aReader, key) ||
193         !StructuredCloneHolder::ReadString(aReader, value)) {
194       return false;
195     }
196     Append(key, value);
197   }
198   return true;
199 }
200 
WriteStructuredClone(JSContext * aCx,JSStructuredCloneWriter * aWriter) const201 bool URLSearchParams::WriteStructuredClone(
202     JSContext* aCx, JSStructuredCloneWriter* aWriter) const {
203   return WriteStructuredClone(aWriter);
204 }
205 
206 // static
ReadStructuredClone(JSContext * aCx,nsIGlobalObject * aGlobal,JSStructuredCloneReader * aReader)207 already_AddRefed<URLSearchParams> URLSearchParams::ReadStructuredClone(
208     JSContext* aCx, nsIGlobalObject* aGlobal,
209     JSStructuredCloneReader* aReader) {
210   RefPtr<URLSearchParams> params = new URLSearchParams(aGlobal);
211   if (!params->ReadStructuredClone(aReader)) {
212     return nullptr;
213   }
214   return params.forget();
215 }
216 
217 // contentTypeWithCharset can be set to the contentType or
218 // contentType+charset based on what the spec says.
219 // See: https://fetch.spec.whatwg.org/#concept-bodyinit-extract
GetSendInfo(nsIInputStream ** aBody,uint64_t * aContentLength,nsACString & aContentTypeWithCharset,nsACString & aCharset) const220 nsresult URLSearchParams::GetSendInfo(nsIInputStream** aBody,
221                                       uint64_t* aContentLength,
222                                       nsACString& aContentTypeWithCharset,
223                                       nsACString& aCharset) const {
224   aContentTypeWithCharset.AssignLiteral(
225       "application/x-www-form-urlencoded;charset=UTF-8");
226   aCharset.AssignLiteral("UTF-8");
227 
228   nsAutoString serialized;
229   Serialize(serialized);
230   NS_ConvertUTF16toUTF8 converted(serialized);
231   *aContentLength = converted.Length();
232   return NS_NewCStringInputStream(aBody, std::move(converted));
233 }
234 
235 }  // namespace dom
236 }  // namespace mozilla
237