1/* -*- Mode: C++; 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 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6#include "nsISupports.idl" 7interface nsIURI; 8interface nsIObjectInputStream; 9interface nsIURIMutator; 10 11%{C++ 12#include "nsString.h" 13#include "nsCOMPtr.h" 14#include <functional> 15 16#undef SetPort // XXX Windows! 17 18namespace mozilla { 19class Encoding; 20} 21 22namespace mozilla { 23namespace ipc { 24class URIParams; 25} // namespace ipc 26} // namespace mozilla 27 28template <class T> 29class BaseURIMutator 30{ 31// This is the base class that can be extended by implementors of nsIURIMutator 32// in order to avoid code duplication 33// Class type T should be the type of the class that implements nsIURI 34protected: 35 MOZ_MUST_USE nsresult InitFromURI(T* aURI) 36 { 37 nsCOMPtr<nsIURI> clone; 38 nsresult rv = aURI->Clone(getter_AddRefs(clone)); 39 if (NS_FAILED(rv)) { 40 return rv; 41 } 42 mURI = static_cast<T*>(clone.get()); 43 return NS_OK; 44 } 45 46 MOZ_MUST_USE nsresult InitFromInputStream(nsIObjectInputStream* aStream) 47 { 48 RefPtr<T> uri = new T(); 49 nsresult rv = uri->Read(aStream); 50 if (NS_FAILED(rv)) { 51 return rv; 52 } 53 mURI = uri.forget(); 54 return NS_OK; 55 } 56 57 MOZ_MUST_USE nsresult InitFromIPCParams(const mozilla::ipc::URIParams& aParams) 58 { 59 RefPtr<T> uri = new T(); 60 bool ret = uri->Deserialize(aParams); 61 if (!ret) { 62 return NS_ERROR_FAILURE; 63 } 64 mURI = uri.forget(); 65 return NS_OK; 66 } 67 68 MOZ_MUST_USE nsresult InitFromSpec(const nsACString& aSpec) 69 { 70 nsresult rv = NS_OK; 71 RefPtr<T> uri; 72 if (mURI) { 73 // This only works because all other Init methods create a new object 74 mURI.swap(uri); 75 } else { 76 uri = new T(); 77 } 78 79 rv = uri->SetSpecInternal(aSpec); 80 if (NS_FAILED(rv)) { 81 return rv; 82 } 83 mURI = uri.forget(); 84 return NS_OK; 85 } 86 87 RefPtr<T> mURI; 88}; 89 90// Since most implementations of nsIURIMutator would extend BaseURIMutator, 91// some methods would have the same implementation. We provide a useful macro 92// to avoid code duplication. 93#define NS_DEFINE_NSIMUTATOR_COMMON \ 94 MOZ_MUST_USE NS_IMETHOD \ 95 Deserialize(const mozilla::ipc::URIParams& aParams) override \ 96 { \ 97 return InitFromIPCParams(aParams); \ 98 } \ 99 \ 100 MOZ_MUST_USE NS_IMETHOD \ 101 Read(nsIObjectInputStream* aStream) override \ 102 { \ 103 return InitFromInputStream(aStream); \ 104 } \ 105 \ 106 MOZ_MUST_USE NS_IMETHOD \ 107 Finalize(nsIURI** aURI) override \ 108 { \ 109 mURI.forget(aURI); return NS_OK; \ 110 } \ 111 \ 112 MOZ_MUST_USE NS_IMETHOD \ 113 SetSpec(const nsACString& aSpec, nsIURIMutator** aMutator) override \ 114 { \ 115 if (aMutator) NS_ADDREF(*aMutator = this); \ 116 return InitFromSpec(aSpec); \ 117 } \ 118%} 119 120[ptr] native Encoding(const mozilla::Encoding); 121[ref] native const_URIParams_ref(const mozilla::ipc::URIParams); 122 123[scriptable, builtinclass, uuid(1fc53257-898b-4c5e-b69c-05bc84b4cd8f)] 124interface nsIURISetSpec : nsISupports 125{ 126 /** 127 * This setter is different from all other setters because it may be used to 128 * initialize the object. We define it separately allowing mutator implementors 129 * to define it separately, while the rest of the setters may be simply 130 * forwarded to the mutable URI. 131 */ 132 [must_use] nsIURIMutator setSpec(in AUTF8String aSpec); 133}; 134 135/** 136 * These methods allow the mutator to change various parts of the URI. 137 * They return the same nsIURIMutator so that we may chain setter operations: 138 * Example: 139 * let newURI = uri.mutate() 140 * .setSpec("http://example.com") 141 * .setQuery("hello") 142 * .finalize(); 143 */ 144[scriptable, builtinclass, uuid(5403a6ec-99d7-405e-8b45-9f805bbdfcef)] 145interface nsIURISetters : nsIURISetSpec 146{ 147 /** 148 * Setting the scheme outside of a protocol handler implementation is highly 149 * discouraged since that will generally lead to incorrect results. 150 */ 151 [must_use] nsIURIMutator setScheme(in AUTF8String aScheme); 152 [must_use] nsIURIMutator setUserPass(in AUTF8String aUserPass); 153 [must_use] nsIURIMutator setUsername(in AUTF8String aUsername); 154 [must_use] nsIURIMutator setPassword(in AUTF8String aPassword); 155 156 /** 157 * If you setHostPort to a value that only has a host part, the port 158 * will not be reset. To reset the port set it to -1 beforehand. 159 * If setting the host succeeds, this method will return NS_OK, even if 160 * setting the port fails (error in parsing the port, or value out of range) 161 */ 162 [must_use] nsIURIMutator setHostPort(in AUTF8String aHostPort); 163 [must_use] nsIURIMutator setHost(in AUTF8String aHost); 164 [must_use] nsIURIMutator setPort(in long aPort); 165 [must_use] nsIURIMutator setPathQueryRef(in AUTF8String aPathQueryRef); 166 [must_use] nsIURIMutator setRef(in AUTF8String aRef); 167 [must_use] nsIURIMutator setFilePath(in AUTF8String aFilePath); 168 [must_use] nsIURIMutator setQuery(in AUTF8String aQuery); 169 [must_use, noscript] nsIURIMutator setQueryWithEncoding(in AUTF8String query, in Encoding encoding); 170}; 171 172%{C++ 173 174// Using this macro instead of NS_FORWARD_SAFE_NSIURISETTERS makes chaining 175// setter operations possible. 176#define NS_FORWARD_SAFE_NSIURISETTERS_RET(_to) \ 177 MOZ_MUST_USE NS_IMETHOD \ 178 SetScheme(const nsACString& aScheme, nsIURIMutator** aMutator) override \ 179 { \ 180 if (aMutator) NS_ADDREF(*aMutator = this); \ 181 return !_to ? NS_ERROR_NULL_POINTER : _to->SetScheme(aScheme); \ 182 } \ 183 MOZ_MUST_USE NS_IMETHOD \ 184 SetUserPass(const nsACString& aUserPass, nsIURIMutator** aMutator) override \ 185 { \ 186 if (aMutator) NS_ADDREF(*aMutator = this); \ 187 return !_to ? NS_ERROR_NULL_POINTER : _to->SetUserPass(aUserPass); \ 188 } \ 189 MOZ_MUST_USE NS_IMETHOD \ 190 SetUsername(const nsACString& aUsername, nsIURIMutator** aMutator) override \ 191 { \ 192 if (aMutator) NS_ADDREF(*aMutator = this); \ 193 return !_to ? NS_ERROR_NULL_POINTER : _to->SetUsername(aUsername); \ 194 } \ 195 MOZ_MUST_USE NS_IMETHOD \ 196 SetPassword(const nsACString& aPassword, nsIURIMutator** aMutator) override \ 197 { \ 198 if (aMutator) NS_ADDREF(*aMutator = this); \ 199 return !_to ? NS_ERROR_NULL_POINTER : _to->SetPassword(aPassword); \ 200 } \ 201 MOZ_MUST_USE NS_IMETHOD \ 202 SetHostPort(const nsACString& aHostPort, nsIURIMutator** aMutator) override \ 203 { \ 204 if (aMutator) NS_ADDREF(*aMutator = this); \ 205 return !_to ? NS_ERROR_NULL_POINTER : _to->SetHostPort(aHostPort); \ 206 } \ 207 MOZ_MUST_USE NS_IMETHOD \ 208 SetHost(const nsACString& aHost, nsIURIMutator** aMutator) override \ 209 { \ 210 if (aMutator) NS_ADDREF(*aMutator = this); \ 211 return !_to ? NS_ERROR_NULL_POINTER : _to->SetHost(aHost); \ 212 } \ 213 MOZ_MUST_USE NS_IMETHOD \ 214 SetPort(int32_t aPort, nsIURIMutator** aMutator) override \ 215 { \ 216 if (aMutator) NS_ADDREF(*aMutator = this); \ 217 return !_to ? NS_ERROR_NULL_POINTER : _to->SetPort(aPort); \ 218 } \ 219 MOZ_MUST_USE NS_IMETHOD \ 220 SetPathQueryRef(const nsACString& aPathQueryRef, nsIURIMutator** aMutator) override \ 221 { \ 222 if (aMutator) NS_ADDREF(*aMutator = this); \ 223 return !_to ? NS_ERROR_NULL_POINTER : _to->SetPathQueryRef(aPathQueryRef); \ 224 } \ 225 MOZ_MUST_USE NS_IMETHOD \ 226 SetRef(const nsACString& aRef, nsIURIMutator** aMutator) override \ 227 { \ 228 if (aMutator) NS_ADDREF(*aMutator = this); \ 229 return !_to ? NS_ERROR_NULL_POINTER : _to->SetRef(aRef); \ 230 } \ 231 MOZ_MUST_USE NS_IMETHOD \ 232 SetFilePath(const nsACString& aFilePath, nsIURIMutator** aMutator) override \ 233 { \ 234 if (aMutator) NS_ADDREF(*aMutator = this); \ 235 return !_to ? NS_ERROR_NULL_POINTER : _to->SetFilePath(aFilePath); \ 236 } \ 237 MOZ_MUST_USE NS_IMETHOD \ 238 SetQuery(const nsACString& aQuery, nsIURIMutator** aMutator) override \ 239 { \ 240 if (aMutator) NS_ADDREF(*aMutator = this); \ 241 return !_to ? NS_ERROR_NULL_POINTER : _to->SetQuery(aQuery); \ 242 } \ 243 MOZ_MUST_USE NS_IMETHOD \ 244 SetQueryWithEncoding(const nsACString& query, const mozilla::Encoding *encoding, nsIURIMutator** aMutator) override \ 245 { \ 246 if (aMutator) NS_ADDREF(*aMutator = this); \ 247 return !_to ? NS_ERROR_NULL_POINTER : _to->SetQueryWithEncoding(query, encoding); \ 248 } \ 249 250%} 251 252[scriptable, builtinclass, uuid(4d1f3103-1c44-4dcd-b717-5d22a697a7d9)] 253interface nsIURIMutator : nsIURISetters 254{ 255 /** 256 * Initializes the URI by reading from the input stream. 257 * The input stream must contain the serialization of the same object type. 258 * See nsISerializable. 259 */ 260 [must_use] 261 void read(in nsIObjectInputStream aInputStream); 262 263 /** 264 * Initalizes the URI by reading IPC URIParams. 265 * See nsIIPCSerializableURI. 266 */ 267 [noscript, notxpcom, must_use] 268 nsresult deserialize(in const_URIParams_ref aParams); 269 270 /** 271 * Finishes changing or constructing the URI and returns an immutable URI. 272 */ 273 [must_use] 274 nsIURI finalize(); 275}; 276 277%{C++ 278 279// This templated struct is used to extract the class type of the method 280// passed to NS_MutatorMethod. 281template <typename Method> 282struct nsMethodTypeTraits; 283 284template <class C, typename R, typename... As> 285struct nsMethodTypeTraits<R(C::*)(As...)> 286{ 287 typedef C class_type; 288}; 289 290#ifdef NS_HAVE_STDCALL 291template <class C, typename R, typename... As> 292struct nsMethodTypeTraits<R(__stdcall C::*)(As...)> 293{ 294 typedef C class_type; 295}; 296#endif 297 298// This helper returns a std::function that will be applied on the 299// nsIURIMutator. The type of `Interface` will be deduced from the method type. 300// aMethod will be called on the target object if it successfully QIs to 301// `Interface`, and the arguments will be passed to the call. 302template <typename Method, typename... Args> 303const std::function<nsresult(nsIURIMutator*)> 304NS_MutatorMethod(Method aMethod, Args ...aArgs) 305{ 306 // Capture arguments by value, otherwise we crash. 307 return [=](nsIURIMutator* aMutator) { 308 typedef typename nsMethodTypeTraits<Method>::class_type Interface; 309 nsresult rv; 310 nsCOMPtr<Interface> target = do_QueryInterface(aMutator, &rv); 311 NS_ENSURE_SUCCESS(rv, rv); 312 rv = (target->*aMethod)(aArgs...); 313 NS_ENSURE_SUCCESS(rv, rv); 314 return NS_OK; 315 }; 316} 317 318// This class provides a useful helper that allows chaining of setter operations 319class MOZ_STACK_CLASS NS_MutateURI 320{ 321public: 322 explicit NS_MutateURI(nsIURI* aURI); 323 explicit NS_MutateURI(const char * aContractID); 324 325 explicit NS_MutateURI(nsIURIMutator* m) 326 { 327 mStatus = m ? NS_OK : NS_ERROR_NULL_POINTER; 328 mMutator = m; 329 NS_ENSURE_SUCCESS_VOID(mStatus); 330 } 331 332 NS_MutateURI& SetSpec(const nsACString& aSpec) 333 { 334 if (NS_FAILED(mStatus)) { 335 return *this; 336 } 337 mStatus = mMutator->SetSpec(aSpec, nullptr); 338 NS_ENSURE_SUCCESS(mStatus, *this); 339 return *this; 340 } 341 NS_MutateURI& SetScheme(const nsACString& aScheme) 342 { 343 if (NS_FAILED(mStatus)) { 344 return *this; 345 } 346 mStatus = mMutator->SetScheme(aScheme, nullptr); 347 NS_ENSURE_SUCCESS(mStatus, *this); 348 return *this; 349 } 350 NS_MutateURI& SetUserPass(const nsACString& aUserPass) 351 { 352 if (NS_FAILED(mStatus)) { 353 return *this; 354 } 355 mStatus = mMutator->SetUserPass(aUserPass, nullptr); 356 NS_ENSURE_SUCCESS(mStatus, *this); 357 return *this; 358 } 359 NS_MutateURI& SetUsername(const nsACString& aUsername) 360 { 361 if (NS_FAILED(mStatus)) { 362 return *this; 363 } 364 mStatus = mMutator->SetUsername(aUsername, nullptr); 365 NS_ENSURE_SUCCESS(mStatus, *this); 366 return *this; 367 } 368 NS_MutateURI& SetPassword(const nsACString& aPassword) 369 { 370 if (NS_FAILED(mStatus)) { 371 return *this; 372 } 373 mStatus = mMutator->SetPassword(aPassword, nullptr); 374 NS_ENSURE_SUCCESS(mStatus, *this); 375 return *this; 376 } 377 NS_MutateURI& SetHostPort(const nsACString& aHostPort) 378 { 379 if (NS_FAILED(mStatus)) { 380 return *this; 381 } 382 mStatus = mMutator->SetHostPort(aHostPort, nullptr); 383 NS_ENSURE_SUCCESS(mStatus, *this); 384 return *this; 385 } 386 NS_MutateURI& SetHost(const nsACString& aHost) 387 { 388 if (NS_FAILED(mStatus)) { 389 return *this; 390 } 391 mStatus = mMutator->SetHost(aHost, nullptr); 392 NS_ENSURE_SUCCESS(mStatus, *this); 393 return *this; 394 } 395 NS_MutateURI& SetPort(int32_t aPort) 396 { 397 if (NS_FAILED(mStatus)) { 398 return *this; 399 } 400 mStatus = mMutator->SetPort(aPort, nullptr); 401 NS_ENSURE_SUCCESS(mStatus, *this); 402 return *this; 403 } 404 NS_MutateURI& SetPathQueryRef(const nsACString& aPathQueryRef) 405 { 406 if (NS_FAILED(mStatus)) { 407 return *this; 408 } 409 mStatus = mMutator->SetPathQueryRef(aPathQueryRef, nullptr); 410 NS_ENSURE_SUCCESS(mStatus, *this); 411 return *this; 412 } 413 NS_MutateURI& SetRef(const nsACString& aRef) 414 { 415 if (NS_FAILED(mStatus)) { 416 return *this; 417 } 418 mStatus = mMutator->SetRef(aRef, nullptr); 419 NS_ENSURE_SUCCESS(mStatus, *this); 420 return *this; 421 } 422 NS_MutateURI& SetFilePath(const nsACString& aFilePath) 423 { 424 if (NS_FAILED(mStatus)) { 425 return *this; 426 } 427 mStatus = mMutator->SetFilePath(aFilePath, nullptr); 428 NS_ENSURE_SUCCESS(mStatus, *this); 429 return *this; 430 } 431 NS_MutateURI& SetQuery(const nsACString& aQuery) 432 { 433 if (NS_FAILED(mStatus)) { 434 return *this; 435 } 436 mStatus = mMutator->SetQuery(aQuery, nullptr); 437 NS_ENSURE_SUCCESS(mStatus, *this); 438 return *this; 439 } 440 NS_MutateURI& SetQueryWithEncoding(const nsACString& query, const mozilla::Encoding *encoding) 441 { 442 if (NS_FAILED(mStatus)) { 443 return *this; 444 } 445 mStatus = mMutator->SetQueryWithEncoding(query, encoding, nullptr); 446 NS_ENSURE_SUCCESS(mStatus, *this); 447 return *this; 448 } 449 450 /** 451 * This method allows consumers to call the methods declared in other 452 * interfaces implemented by the mutator object. 453 * 454 * Example: 455 * nsCOMPtr<nsIURI> uri; 456 * nsresult rv = NS_MutateURI(new URIClass::Mutator()) 457 * .SetSpec(aSpec) 458 * .Apply(NS_MutatorMethod(&SomeInterface::Method, arg1, arg2)) 459 * .Finalize(uri); 460 * 461 * If mMutator does not implement SomeInterface, do_QueryInterface will fail 462 * and the method will not be called. 463 * If aMethod does not exist, or if there is a mismatch between argument 464 * types, or the number of arguments, then there will be a compile error. 465 */ 466 NS_MutateURI& Apply(const std::function<nsresult(nsIURIMutator*)>& aFunction) 467 { 468 if (NS_FAILED(mStatus)) { 469 return *this; 470 } 471 mStatus = aFunction(mMutator); 472 NS_ENSURE_SUCCESS(mStatus, *this); 473 return *this; 474 } 475 476 template <class C> 477 MOZ_MUST_USE nsresult Finalize(nsCOMPtr<C>& aURI) 478 { 479 NS_ENSURE_SUCCESS(mStatus, mStatus); 480 481 nsCOMPtr<nsIURI> uri; 482 mStatus = mMutator->Finalize(getter_AddRefs(uri)); 483 NS_ENSURE_SUCCESS(mStatus, mStatus); 484 485 aURI = do_QueryInterface(uri, &mStatus); 486 NS_ENSURE_SUCCESS(mStatus, mStatus); 487 488 mStatus = NS_ERROR_NOT_AVAILABLE; // Second call to Finalize should fail. 489 return NS_OK; 490 } 491 492 // Overload for nsIURI to avoid query interface. 493 MOZ_MUST_USE nsresult Finalize(nsCOMPtr<nsIURI>& aURI) 494 { 495 NS_ENSURE_SUCCESS(mStatus, mStatus); 496 mStatus = mMutator->Finalize(getter_AddRefs(aURI)); 497 NS_ENSURE_SUCCESS(mStatus, mStatus); 498 499 mStatus = NS_ERROR_NOT_AVAILABLE; // Second call to Finalize should fail. 500 return NS_OK; 501 } 502 503 template <class C> 504 MOZ_MUST_USE nsresult Finalize(C** aURI) 505 { 506 NS_ENSURE_SUCCESS(mStatus, mStatus); 507 508 nsCOMPtr<nsIURI> uri; 509 mStatus = mMutator->Finalize(getter_AddRefs(uri)); 510 NS_ENSURE_SUCCESS(mStatus, mStatus); 511 512 nsCOMPtr<C> result = do_QueryInterface(uri, &mStatus); 513 NS_ENSURE_SUCCESS(mStatus, mStatus); 514 515 result.forget(aURI); 516 mStatus = NS_ERROR_NOT_AVAILABLE; // Second call to Finalize should fail. 517 return NS_OK; 518 } 519 520 MOZ_MUST_USE nsresult Finalize(nsIURI** aURI) 521 { 522 NS_ENSURE_SUCCESS(mStatus, mStatus); 523 mStatus = mMutator->Finalize(aURI); 524 NS_ENSURE_SUCCESS(mStatus, mStatus); 525 526 mStatus = NS_ERROR_NOT_AVAILABLE; // Second call to Finalize should fail. 527 return NS_OK; 528 } 529 530 nsresult GetStatus() { return mStatus; } 531private: 532 nsresult mStatus; 533 nsCOMPtr<nsIURIMutator> mMutator; 534}; 535 536%} 537