URLPattern.cpp (11979B)
1 /* -*- Mode: IDL; 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 file, 4 * You can obtain one at http://mozilla.org/MPL/2.0/. 5 */ 6 #include "URLPattern.h" 7 8 #include "mozilla/ErrorResult.h" 9 #include "mozilla/net/MozURL.h" 10 11 namespace mozilla::dom { 12 13 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(URLPattern, mParent) 14 15 NS_IMPL_CYCLE_COLLECTING_ADDREF(URLPattern) 16 NS_IMPL_CYCLE_COLLECTING_RELEASE(URLPattern) 17 18 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(URLPattern) 19 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY 20 NS_INTERFACE_MAP_ENTRY(nsISupports) 21 NS_INTERFACE_MAP_END 22 23 JSObject* URLPattern::WrapObject(JSContext* aCx, 24 JS::Handle<JSObject*> aGivenProto) { 25 return URLPattern_Binding::Wrap(aCx, this, aGivenProto); 26 } 27 28 void GlueToBindingInit(const UrlpInit& aGInit, URLPatternInit& aBInit) { 29 if (aGInit.protocol.valid) { 30 aBInit.mProtocol.Construct(aGInit.protocol.string); 31 } 32 if (aGInit.username.valid) { 33 aBInit.mUsername.Construct(aGInit.username.string); 34 } 35 if (aGInit.password.valid) { 36 aBInit.mPassword.Construct(aGInit.password.string); 37 } 38 if (aGInit.hostname.valid) { 39 aBInit.mHostname.Construct(aGInit.hostname.string); 40 } 41 if (aGInit.port.valid) { 42 aBInit.mPort.Construct(aGInit.port.string); 43 } 44 if (aGInit.pathname.valid) { 45 aBInit.mPathname.Construct(aGInit.pathname.string); 46 } 47 if (aGInit.search.valid) { 48 aBInit.mSearch.Construct(aGInit.search.string); 49 } 50 if (aGInit.hash.valid) { 51 aBInit.mHash.Construct(aGInit.hash.string); 52 } 53 if (aGInit.base_url.valid) { 54 aBInit.mBaseURL.Construct(aGInit.base_url.string); 55 } 56 } 57 58 void BindingToGlueInit(const URLPatternInit& aBInit, UrlpInit& aGInit) { 59 if (aBInit.mProtocol.WasPassed()) { 60 aGInit.protocol = net::CreateMaybeString(aBInit.mProtocol.Value(), true); 61 } 62 if (aBInit.mUsername.WasPassed()) { 63 aGInit.username = net::CreateMaybeString(aBInit.mUsername.Value(), true); 64 } 65 if (aBInit.mPassword.WasPassed()) { 66 aGInit.password = net::CreateMaybeString(aBInit.mPassword.Value(), true); 67 } 68 if (aBInit.mHostname.WasPassed()) { 69 aGInit.hostname = net::CreateMaybeString(aBInit.mHostname.Value(), true); 70 } 71 if (aBInit.mPort.WasPassed()) { 72 aGInit.port = net::CreateMaybeString(aBInit.mPort.Value(), true); 73 } 74 if (aBInit.mPathname.WasPassed()) { 75 aGInit.pathname = net::CreateMaybeString(aBInit.mPathname.Value(), true); 76 } 77 if (aBInit.mSearch.WasPassed()) { 78 aGInit.search = net::CreateMaybeString(aBInit.mSearch.Value(), true); 79 } 80 if (aBInit.mHash.WasPassed()) { 81 aGInit.hash = net::CreateMaybeString(aBInit.mHash.Value(), true); 82 } 83 if (aBInit.mBaseURL.WasPassed()) { 84 aGInit.base_url = net::CreateMaybeString(aBInit.mBaseURL.Value(), true); 85 } 86 } 87 88 // static 89 already_AddRefed<URLPattern> URLPattern::Constructor( 90 const GlobalObject& aGlobal, const UTF8StringOrURLPatternInit& aInput, 91 const URLPatternOptions& aOptions, ErrorResult& rv) { 92 MOZ_LOG(gUrlPatternLog, LogLevel::Debug, 93 ("UrlPattern::Constructor() (without base)")); 94 UrlpPattern pattern{}; 95 UrlpOptions options{}; 96 options.ignore_case = aOptions.mIgnoreCase; 97 if (!aInput.IsURLPatternInit()) { 98 bool res = urlp_parse_pattern_from_string(&aInput.GetAsUTF8String(), 99 nullptr, options, &pattern); 100 if (!res) { 101 rv.ThrowTypeError("Failed to create URLPattern (from string)"); 102 return nullptr; 103 } 104 } else { 105 UrlpInit init{}; 106 URLPatternInit b_init; 107 b_init = aInput.GetAsURLPatternInit(); 108 BindingToGlueInit(b_init, init); 109 if (init.base_url.valid && init.base_url.string.Equals("")) { 110 rv.ThrowTypeError("Should not provide empty base url with init"); 111 return nullptr; 112 } 113 bool res = urlp_parse_pattern_from_init(&init, options, &pattern); 114 if (!res) { 115 rv.ThrowTypeError("Failed to create URLPattern (from init)"); 116 return nullptr; 117 } 118 } 119 120 return MakeAndAddRef<URLPattern>(aGlobal.GetAsSupports(), pattern, 121 aOptions.mIgnoreCase); 122 } 123 124 // static 125 already_AddRefed<URLPattern> URLPattern::Constructor( 126 const GlobalObject& aGlobal, const UTF8StringOrURLPatternInit& aInput, 127 const nsACString& aBase, const URLPatternOptions& aOptions, 128 ErrorResult& rv) { 129 MOZ_LOG(gUrlPatternLog, LogLevel::Debug, 130 ("UrlPattern::Constructor() (w base)")); 131 UrlpPattern pattern{}; 132 UrlpOptions options{}; 133 options.ignore_case = aOptions.mIgnoreCase; 134 if (!aInput.IsURLPatternInit()) { 135 bool res = urlp_parse_pattern_from_string(&aInput.GetAsUTF8String(), &aBase, 136 options, &pattern); 137 if (!res) { 138 rv.ThrowTypeError( 139 "Failed to create URLPattern with base url (from string)"); 140 return nullptr; 141 } 142 } else { 143 if (!aBase.IsEmpty()) { 144 rv.ThrowTypeError("Should not provide base url with init"); 145 return nullptr; 146 } 147 UrlpInit init{}; 148 URLPatternInit b_init; 149 b_init = aInput.GetAsURLPatternInit(); 150 BindingToGlueInit(b_init, init); 151 bool res = urlp_parse_pattern_from_init(&init, options, &pattern); 152 if (!res) { 153 rv.ThrowTypeError( 154 "Failed to create URLPattern with base url (from init)"); 155 return nullptr; 156 } 157 } 158 return MakeAndAddRef<URLPattern>(aGlobal.GetAsSupports(), pattern, 159 aOptions.mIgnoreCase); 160 } 161 162 URLPattern::~URLPattern() { urlp_pattern_free(mPattern); } 163 164 void ConvertGroupsToRecord( 165 const nsTHashMap<nsCStringHashKey, MaybeString>& aGroups, 166 Optional<Record<nsCString, OwningUTF8StringOrUndefined>>& aRes) { 167 Record<nsCString, OwningUTF8StringOrUndefined> record; 168 for (auto iter = aGroups.ConstIter(); !iter.Done(); iter.Next()) { 169 OwningUTF8StringOrUndefined value; 170 value.SetUndefined(); 171 MaybeString s = iter.Data(); 172 if (s.valid) { 173 value.SetAsUTF8String().Assign(s.string); 174 } 175 auto* entry = record.Entries().AppendElement().get(); 176 entry->mKey.Assign(iter.Key()); 177 entry->mValue = std::move(value); 178 } 179 aRes.Construct(std::move(record)); 180 } 181 182 void GlueToBindingComponent(const net::UrlpComponentResult& aGlueCompRes, 183 URLPatternComponentResult& aBindingCompRes) { 184 aBindingCompRes.mInput.Construct(aGlueCompRes.mInput); 185 ConvertGroupsToRecord(aGlueCompRes.mGroups, aBindingCompRes.mGroups); 186 } 187 188 void ConvertInputsToSequence( 189 const CopyableTArray<UrlpInput>& aInputs, 190 Optional<Sequence<OwningUTF8StringOrURLPatternInit>>& aRes, 191 ErrorResult& rv) { 192 Sequence<OwningUTF8StringOrURLPatternInit> sequence; 193 for (const auto& input : aInputs) { 194 OwningUTF8StringOrURLPatternInit variant; 195 if (input.string_or_init_type == UrlpStringOrInitType::String) { 196 variant.SetAsUTF8String().Assign(input.str); 197 } else { 198 GlueToBindingInit(input.init, variant.SetAsURLPatternInit()); 199 } 200 201 if (!sequence.AppendElement(std::move(variant), fallible)) { 202 aRes.Reset(); 203 rv.ThrowOperationError("Failed to append inputs list to sequence"); 204 return; 205 } 206 } 207 aRes.Construct(std::move(sequence)); 208 } 209 210 void GlueToBindingResult(const net::UrlpResult& aGlueRes, 211 URLPatternResult& aBindingRes, ErrorResult& rv) { 212 if (aGlueRes.mProtocol.isSome()) { 213 URLPatternComponentResult tmp; 214 GlueToBindingComponent(aGlueRes.mProtocol.value(), tmp); 215 aBindingRes.mProtocol.Construct(std::move(tmp)); 216 } 217 if (aGlueRes.mUsername.isSome()) { 218 URLPatternComponentResult tmp; 219 GlueToBindingComponent(aGlueRes.mUsername.value(), tmp); 220 aBindingRes.mUsername.Construct(std::move(tmp)); 221 } 222 if (aGlueRes.mPassword.isSome()) { 223 URLPatternComponentResult tmp; 224 GlueToBindingComponent(aGlueRes.mPassword.value(), tmp); 225 aBindingRes.mPassword.Construct(std::move(tmp)); 226 } 227 if (aGlueRes.mHostname.isSome()) { 228 URLPatternComponentResult tmp; 229 GlueToBindingComponent(aGlueRes.mHostname.value(), tmp); 230 aBindingRes.mHostname.Construct(std::move(tmp)); 231 } 232 if (aGlueRes.mPort.isSome()) { 233 URLPatternComponentResult tmp; 234 GlueToBindingComponent(aGlueRes.mPort.value(), tmp); 235 aBindingRes.mPort.Construct(std::move(tmp)); 236 } 237 if (aGlueRes.mPathname.isSome()) { 238 URLPatternComponentResult tmp; 239 GlueToBindingComponent(aGlueRes.mPathname.value(), tmp); 240 aBindingRes.mPathname.Construct(std::move(tmp)); 241 } 242 if (aGlueRes.mSearch.isSome()) { 243 URLPatternComponentResult tmp; 244 GlueToBindingComponent(aGlueRes.mSearch.value(), tmp); 245 aBindingRes.mSearch.Construct(std::move(tmp)); 246 } 247 if (aGlueRes.mHash.isSome()) { 248 URLPatternComponentResult tmp; 249 GlueToBindingComponent(aGlueRes.mHash.value(), tmp); 250 aBindingRes.mHash.Construct(std::move(tmp)); 251 } 252 ConvertInputsToSequence(aGlueRes.mInputs, aBindingRes.mInputs, rv); 253 } 254 255 bool URLPattern::Test(const UTF8StringOrURLPatternInit& aInput, 256 const Optional<nsACString>& aBaseUrl, ErrorResult& rv) { 257 MOZ_LOG(gUrlPatternLog, LogLevel::Debug, ("UrlPattern::Test()")); 258 UrlpInput input; 259 Maybe<nsAutoCString> execBaseUrl; 260 if (aInput.IsURLPatternInit()) { 261 UrlpInit initGlue{}; 262 BindingToGlueInit(aInput.GetAsURLPatternInit(), initGlue); 263 input = net::CreateUrlpInput(initGlue); 264 if (aBaseUrl.WasPassed()) { 265 rv.ThrowTypeError( 266 "Do not pass baseUrl separately with init, use init's baseURL " 267 "property"); 268 return false; 269 } 270 } else { 271 input = net::CreateUrlpInput(aInput.GetAsUTF8String()); 272 if (aBaseUrl.WasPassed()) { 273 execBaseUrl.emplace(aBaseUrl.Value()); 274 } 275 } 276 return net::UrlpPatternTest(mPattern, input, execBaseUrl, mIgnoreCase); 277 } 278 279 void URLPattern::Exec(const UTF8StringOrURLPatternInit& aInput, 280 const Optional<nsACString>& aBaseUrl, 281 Nullable<URLPatternResult>& aResult, ErrorResult& rv) { 282 MOZ_LOG(gUrlPatternLog, LogLevel::Debug, ("UrlPattern::Exec()")); 283 UrlpInput input; 284 Maybe<nsAutoCString> execBaseUrl; 285 if (aInput.IsURLPatternInit()) { 286 UrlpInit initGlue{}; 287 BindingToGlueInit(aInput.GetAsURLPatternInit(), initGlue); 288 input = net::CreateUrlpInput(initGlue); 289 if (aBaseUrl.WasPassed()) { 290 rv.ThrowTypeError( 291 "Do not pass baseUrl separately with init, use init's baseURL " 292 "property"); 293 return; 294 } 295 } else { 296 input = net::CreateUrlpInput(aInput.GetAsUTF8String()); 297 if (aBaseUrl.WasPassed()) { 298 execBaseUrl.emplace(aBaseUrl.Value()); 299 } 300 } 301 302 Maybe<net::UrlpResult> patternResult = 303 net::UrlpPatternExec(mPattern, input, execBaseUrl, mIgnoreCase); 304 if (patternResult.isSome()) { 305 URLPatternResult res; 306 GlueToBindingResult(patternResult.value(), res, rv); 307 if (rv.Failed()) { 308 aResult.SetNull(); 309 return; 310 } 311 aResult.SetValue(std::move(res)); 312 return; 313 } 314 aResult.SetNull(); 315 } 316 317 void URLPattern::GetProtocol(nsACString& aProtocol) const { 318 aProtocol.Assign(net::UrlpGetProtocol(mPattern)); 319 } 320 321 void URLPattern::GetUsername(nsACString& aUsername) const { 322 aUsername.Assign(net::UrlpGetUsername(mPattern)); 323 } 324 325 void URLPattern::GetPassword(nsACString& aPassword) const { 326 aPassword.Assign(net::UrlpGetPassword(mPattern)); 327 } 328 329 void URLPattern::GetHostname(nsACString& aHostname) const { 330 aHostname.Assign(net::UrlpGetHostname(mPattern)); 331 } 332 333 void URLPattern::GetPort(nsACString& aPort) const { 334 aPort.Assign(net::UrlpGetPort(mPattern)); 335 } 336 337 void URLPattern::GetPathname(nsACString& aPathname) const { 338 aPathname.Assign(net::UrlpGetPathname(mPattern)); 339 } 340 341 void URLPattern::GetSearch(nsACString& aSearch) const { 342 aSearch.Assign(net::UrlpGetSearch(mPattern)); 343 } 344 345 void URLPattern::GetHash(nsACString& aHash) const { 346 aHash.Assign(net::UrlpGetHash(mPattern)); 347 } 348 349 bool URLPattern::HasRegExpGroups() const { 350 return urlp_get_has_regexp_groups(mPattern); 351 } 352 353 } // namespace mozilla::dom