URL.cpp (9827B)
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 "URL.h" 8 9 #include "MainThreadUtils.h" 10 #include "URLMainThread.h" 11 #include "URLWorker.h" 12 #include "mozilla/RefPtr.h" 13 #include "mozilla/dom/BindingUtils.h" 14 #include "mozilla/dom/Document.h" 15 #include "nsASCIIMask.h" 16 #include "nsContentUtils.h" 17 #include "nsIURIMutator.h" 18 #include "nsNetUtil.h" 19 20 namespace mozilla::dom { 21 22 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(URL, mParent, mSearchParams) 23 24 NS_IMPL_CYCLE_COLLECTING_ADDREF(URL) 25 NS_IMPL_CYCLE_COLLECTING_RELEASE(URL) 26 27 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(URL) 28 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY 29 NS_INTERFACE_MAP_ENTRY(nsISupports) 30 NS_INTERFACE_MAP_END 31 32 JSObject* URL::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) { 33 return URL_Binding::Wrap(aCx, this, aGivenProto); 34 } 35 36 /* static */ 37 already_AddRefed<URL> URL::Constructor(const GlobalObject& aGlobal, 38 const nsACString& aURL, 39 const Optional<nsACString>& aBase, 40 ErrorResult& aRv) { 41 if (aBase.WasPassed()) { 42 return Constructor(aGlobal.GetAsSupports(), aURL, aBase.Value(), aRv); 43 } 44 45 return Constructor(aGlobal.GetAsSupports(), aURL, nullptr, aRv); 46 } 47 48 /* static */ 49 already_AddRefed<URL> URL::Constructor(nsISupports* aParent, 50 const nsACString& aURL, 51 const nsACString& aBase, 52 ErrorResult& aRv) { 53 nsCOMPtr<nsIURI> baseUri; 54 nsresult rv = NS_NewURI(getter_AddRefs(baseUri), aBase); 55 if (NS_WARN_IF(NS_FAILED(rv))) { 56 aRv.ThrowTypeError<MSG_INVALID_URL>(aBase); 57 return nullptr; 58 } 59 60 return Constructor(aParent, aURL, baseUri, aRv); 61 } 62 63 /* static */ 64 already_AddRefed<URL> URL::Constructor(nsISupports* aParent, 65 const nsACString& aURL, nsIURI* aBase, 66 ErrorResult& aRv) { 67 nsCOMPtr<nsIURI> uri; 68 nsresult rv = NS_NewURI(getter_AddRefs(uri), aURL, nullptr, aBase); 69 if (NS_FAILED(rv)) { 70 // No need to warn in this case. It's common to use the URL constructor 71 // to determine if a URL is valid and an exception will be propagated. 72 aRv.ThrowTypeError<MSG_INVALID_URL>(aURL); 73 return nullptr; 74 } 75 76 return MakeAndAddRef<URL>(aParent, std::move(uri)); 77 } 78 79 already_AddRefed<URL> URL::FromURI(GlobalObject& aGlobal, nsIURI* aURI) { 80 return MakeAndAddRef<URL>(aGlobal.GetAsSupports(), aURI); 81 } 82 83 void URL::CreateObjectURL(const GlobalObject& aGlobal, 84 const BlobOrMediaSource& aObj, nsACString& aResult, 85 ErrorResult& aRv) { 86 if (NS_IsMainThread()) { 87 URLMainThread::CreateObjectURL(aGlobal, aObj, aResult, aRv); 88 } else { 89 URLWorker::CreateObjectURL(aGlobal, aObj, aResult, aRv); 90 } 91 } 92 93 void URL::RevokeObjectURL(const GlobalObject& aGlobal, const nsACString& aURL, 94 ErrorResult& aRv) { 95 if (aURL.Contains('#')) { 96 // Don't revoke URLs that contain fragments. 97 return; 98 } 99 100 if (NS_IsMainThread()) { 101 URLMainThread::RevokeObjectURL(aGlobal, aURL, aRv); 102 } else { 103 URLWorker::RevokeObjectURL(aGlobal, aURL, aRv); 104 } 105 } 106 107 bool URL::IsBoundToBlob(const GlobalObject& aGlobal, const nsACString& aURL, 108 ErrorResult& aRv) { 109 if (NS_IsMainThread()) { 110 return URLMainThread::IsBoundToBlob(aGlobal, aURL, aRv); 111 } 112 return URLWorker::IsBoundToBlob(aGlobal, aURL, aRv); 113 } 114 115 already_AddRefed<nsIURI> URL::ParseURI(const nsACString& aURL, 116 const Optional<nsACString>& aBase) { 117 nsCOMPtr<nsIURI> baseUri; 118 nsCOMPtr<nsIURI> uri; 119 120 if (aBase.WasPassed()) { 121 nsresult rv = NS_NewURI(getter_AddRefs(baseUri), aBase.Value()); 122 if (NS_FAILED(rv)) { 123 return nullptr; 124 } 125 } 126 127 nsresult rv = NS_NewURI(getter_AddRefs(uri), aURL, nullptr, baseUri); 128 if (NS_FAILED(rv)) { 129 return nullptr; 130 } 131 132 return uri.forget(); 133 }; 134 135 already_AddRefed<URL> URL::Parse(const GlobalObject& aGlobal, 136 const nsACString& aURL, 137 const Optional<nsACString>& aBase) { 138 nsCOMPtr<nsIURI> uri = ParseURI(aURL, aBase); 139 if (!uri) { 140 return nullptr; 141 } 142 return MakeAndAddRef<URL>(aGlobal.GetAsSupports(), std::move(uri)); 143 } 144 145 bool URL::CanParse(const GlobalObject& aGlobal, const nsACString& aURL, 146 const Optional<nsACString>& aBase) { 147 nsCOMPtr<nsIURI> uri = ParseURI(aURL, aBase); 148 return !!uri; 149 } 150 151 URLSearchParams* URL::SearchParams() { 152 CreateSearchParamsIfNeeded(); 153 return mSearchParams; 154 } 155 156 void URL::CreateSearchParamsIfNeeded() { 157 if (!mSearchParams) { 158 mSearchParams = new URLSearchParams(mParent, this); 159 UpdateURLSearchParams(); 160 } 161 } 162 163 void URL::SetSearch(const nsACString& aSearch) { 164 SetSearchInternal(aSearch); 165 UpdateURLSearchParams(); 166 } 167 168 void URL::URLSearchParamsUpdated(URLSearchParams* aSearchParams) { 169 MOZ_ASSERT(mSearchParams); 170 MOZ_ASSERT(mSearchParams == aSearchParams); 171 172 nsAutoCString search; 173 mSearchParams->Serialize(search); 174 SetSearchInternal(search); 175 } 176 177 #define URL_GETTER(value, func) \ 178 MOZ_ASSERT(mURI); \ 179 mURI->func(value); 180 181 void URL::GetHref(nsACString& aHref) const { URL_GETTER(aHref, GetSpec); } 182 183 void URL::SetHref(const nsACString& aHref, ErrorResult& aRv) { 184 nsCOMPtr<nsIURI> uri; 185 nsresult rv = NS_NewURI(getter_AddRefs(uri), aHref); 186 if (NS_FAILED(rv)) { 187 aRv.ThrowTypeError<MSG_INVALID_URL>(aHref); 188 return; 189 } 190 191 mURI = std::move(uri); 192 UpdateURLSearchParams(); 193 } 194 195 void URL::GetOrigin(nsACString& aOrigin) const { 196 nsresult rv = 197 nsContentUtils::GetWebExposedOriginSerialization(URI(), aOrigin); 198 if (NS_WARN_IF(NS_FAILED(rv))) { 199 aOrigin.Truncate(); 200 } 201 } 202 203 void URL::GetProtocol(nsACString& aProtocol) const { 204 URL_GETTER(aProtocol, GetScheme); 205 aProtocol.Append(char16_t(':')); 206 } 207 208 void URL::SetProtocol(const nsACString& aProtocol) { 209 nsCOMPtr<nsIURI> uri(URI()); 210 if (!uri) { 211 return; 212 } 213 uri = net::TryChangeProtocol(uri, aProtocol); 214 if (!uri) { 215 return; 216 } 217 mURI = std::move(uri); 218 } 219 220 void URL::GetUsername(nsACString& aUsername) const { 221 URL_GETTER(aUsername, GetUsername); 222 } 223 224 void URL::SetUsername(const nsACString& aUsername) { 225 MOZ_ASSERT(mURI); 226 (void)NS_MutateURI(mURI).SetUsername(aUsername).Finalize(mURI); 227 } 228 229 void URL::GetPassword(nsACString& aPassword) const { 230 URL_GETTER(aPassword, GetPassword); 231 } 232 233 void URL::SetPassword(const nsACString& aPassword) { 234 MOZ_ASSERT(mURI); 235 236 (void)NS_MutateURI(mURI).SetPassword(aPassword).Finalize(mURI); 237 } 238 239 void URL::GetHost(nsACString& aHost) const { URL_GETTER(aHost, GetHostPort); } 240 241 void URL::SetHost(const nsACString& aHost) { 242 MOZ_ASSERT(mURI); 243 (void)NS_MutateURI(mURI).SetHostPort(aHost).Finalize(mURI); 244 } 245 246 void URL::GetHostname(nsACString& aHostname) const { 247 MOZ_ASSERT(mURI); 248 aHostname.Truncate(); 249 nsContentUtils::GetHostOrIPv6WithBrackets(mURI, aHostname); 250 } 251 252 void URL::SetHostname(const nsACString& aHostname) { 253 MOZ_ASSERT(mURI); 254 255 // nsStandardURL returns NS_ERROR_UNEXPECTED for an empty hostname 256 // The return code is silently ignored 257 (void)NS_MutateURI(mURI).SetHost(aHostname).Finalize(mURI); 258 } 259 260 void URL::GetPort(nsACString& aPort) const { 261 MOZ_ASSERT(mURI); 262 aPort.Truncate(); 263 264 int32_t port; 265 nsresult rv = mURI->GetPort(&port); 266 if (NS_SUCCEEDED(rv) && port != -1) { 267 aPort.AppendInt(port, 10); 268 } 269 } 270 271 void URL::SetPort(const nsACString& aPort) { 272 nsresult rv; 273 nsAutoCString portStr(aPort); 274 int32_t port = -1; 275 276 // nsIURI uses -1 as default value. 277 portStr.StripTaggedASCII(ASCIIMask::MaskCRLFTab()); 278 if (!portStr.IsEmpty()) { 279 // To be valid, the port must start with an ASCII digit. 280 // (nsACString::ToInteger ignores leading junk, so check before calling.) 281 if (!IsAsciiDigit(portStr[0])) { 282 return; 283 } 284 port = portStr.ToInteger(&rv); 285 if (NS_FAILED(rv)) { 286 return; 287 } 288 } 289 290 (void)NS_MutateURI(mURI).SetPort(port).Finalize(mURI); 291 } 292 293 void URL::GetPathname(nsACString& aPathname) const { 294 MOZ_ASSERT(mURI); 295 // Do not throw! Not having a valid URI or URL should result in an empty 296 // string. 297 mURI->GetFilePath(aPathname); 298 } 299 300 void URL::SetPathname(const nsACString& aPathname) { 301 MOZ_ASSERT(mURI); 302 303 // Do not throw! 304 (void)NS_MutateURI(mURI).SetFilePath(aPathname).Finalize(mURI); 305 } 306 307 void URL::GetSearch(nsACString& aSearch) const { 308 MOZ_ASSERT(mURI); 309 310 aSearch.Truncate(); 311 312 // Do not throw! Not having a valid URI or URL should result in an empty 313 // string. 314 315 nsresult rv; 316 rv = mURI->GetQuery(aSearch); 317 if (NS_SUCCEEDED(rv) && !aSearch.IsEmpty()) { 318 aSearch.Insert('?', 0); 319 } 320 } 321 322 void URL::GetHash(nsACString& aHash) const { 323 MOZ_ASSERT(mURI); 324 aHash.Truncate(); 325 nsresult rv = mURI->GetRef(aHash); 326 if (NS_SUCCEEDED(rv) && !aHash.IsEmpty()) { 327 aHash.Insert('#', 0); 328 } 329 } 330 331 void URL::SetHash(const nsACString& aHash) { 332 MOZ_ASSERT(mURI); 333 334 (void)NS_MutateURI(mURI).SetRef(aHash).Finalize(mURI); 335 } 336 337 void URL::SetSearchInternal(const nsACString& aSearch) { 338 MOZ_ASSERT(mURI); 339 340 // Ignore failures to be compatible with NS4. 341 (void)NS_MutateURI(mURI).SetQuery(aSearch).Finalize(mURI); 342 } 343 344 void URL::UpdateURLSearchParams() { 345 if (!mSearchParams) { 346 return; 347 } 348 349 nsAutoCString search; 350 nsresult rv = URI()->GetQuery(search); 351 if (NS_WARN_IF(NS_FAILED(rv))) { 352 search.Truncate(); 353 } 354 355 mSearchParams->ParseInput(search); 356 } 357 358 nsIURI* URL::URI() const { 359 MOZ_ASSERT(mURI); 360 return mURI; 361 } 362 363 } // namespace mozilla::dom