Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
michael@0 | 2 | /* vim:set ts=4 sw=4 sts=4 et cindent: */ |
michael@0 | 3 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 5 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 6 | |
michael@0 | 7 | #include "IPCMessageUtils.h" |
michael@0 | 8 | |
michael@0 | 9 | #include "nsStandardURL.h" |
michael@0 | 10 | #include "nsCRT.h" |
michael@0 | 11 | #include "nsEscape.h" |
michael@0 | 12 | #include "nsIFile.h" |
michael@0 | 13 | #include "nsIObjectInputStream.h" |
michael@0 | 14 | #include "nsIObjectOutputStream.h" |
michael@0 | 15 | #include "nsICharsetConverterManager.h" |
michael@0 | 16 | #include "nsIPrefService.h" |
michael@0 | 17 | #include "nsIPrefBranch.h" |
michael@0 | 18 | #include "nsIIDNService.h" |
michael@0 | 19 | #include "prlog.h" |
michael@0 | 20 | #include "nsAutoPtr.h" |
michael@0 | 21 | #include "nsIProgrammingLanguage.h" |
michael@0 | 22 | #include "nsIURLParser.h" |
michael@0 | 23 | #include "nsNetCID.h" |
michael@0 | 24 | #include "mozilla/MemoryReporting.h" |
michael@0 | 25 | #include "mozilla/ipc/URIUtils.h" |
michael@0 | 26 | #include <algorithm> |
michael@0 | 27 | |
michael@0 | 28 | using namespace mozilla::ipc; |
michael@0 | 29 | |
michael@0 | 30 | static NS_DEFINE_CID(kThisImplCID, NS_THIS_STANDARDURL_IMPL_CID); |
michael@0 | 31 | static NS_DEFINE_CID(kStandardURLCID, NS_STANDARDURL_CID); |
michael@0 | 32 | |
michael@0 | 33 | nsIIDNService *nsStandardURL::gIDN = nullptr; |
michael@0 | 34 | nsICharsetConverterManager *nsStandardURL::gCharsetMgr = nullptr; |
michael@0 | 35 | bool nsStandardURL::gInitialized = false; |
michael@0 | 36 | bool nsStandardURL::gEscapeUTF8 = true; |
michael@0 | 37 | bool nsStandardURL::gAlwaysEncodeInUTF8 = true; |
michael@0 | 38 | char nsStandardURL::gHostLimitDigits[] = { '/', '\\', '?', '#', 0 }; |
michael@0 | 39 | |
michael@0 | 40 | #if defined(PR_LOGGING) |
michael@0 | 41 | // |
michael@0 | 42 | // setenv NSPR_LOG_MODULES nsStandardURL:5 |
michael@0 | 43 | // |
michael@0 | 44 | static PRLogModuleInfo *gStandardURLLog; |
michael@0 | 45 | #endif |
michael@0 | 46 | |
michael@0 | 47 | // The Chromium code defines its own LOG macro which we don't want |
michael@0 | 48 | #undef LOG |
michael@0 | 49 | #define LOG(args) PR_LOG(gStandardURLLog, PR_LOG_DEBUG, args) |
michael@0 | 50 | #undef LOG_ENABLED |
michael@0 | 51 | #define LOG_ENABLED() PR_LOG_TEST(gStandardURLLog, PR_LOG_DEBUG) |
michael@0 | 52 | |
michael@0 | 53 | //---------------------------------------------------------------------------- |
michael@0 | 54 | |
michael@0 | 55 | #define ENSURE_MUTABLE() \ |
michael@0 | 56 | PR_BEGIN_MACRO \ |
michael@0 | 57 | if (!mMutable) { \ |
michael@0 | 58 | NS_WARNING("attempt to modify an immutable nsStandardURL"); \ |
michael@0 | 59 | return NS_ERROR_ABORT; \ |
michael@0 | 60 | } \ |
michael@0 | 61 | PR_END_MACRO |
michael@0 | 62 | |
michael@0 | 63 | //---------------------------------------------------------------------------- |
michael@0 | 64 | |
michael@0 | 65 | static nsresult |
michael@0 | 66 | EncodeString(nsIUnicodeEncoder *encoder, const nsAFlatString &str, nsACString &result) |
michael@0 | 67 | { |
michael@0 | 68 | nsresult rv; |
michael@0 | 69 | int32_t len = str.Length(); |
michael@0 | 70 | int32_t maxlen; |
michael@0 | 71 | |
michael@0 | 72 | rv = encoder->GetMaxLength(str.get(), len, &maxlen); |
michael@0 | 73 | if (NS_FAILED(rv)) |
michael@0 | 74 | return rv; |
michael@0 | 75 | |
michael@0 | 76 | char buf[256], *p = buf; |
michael@0 | 77 | if (uint32_t(maxlen) > sizeof(buf) - 1) { |
michael@0 | 78 | p = (char *) malloc(maxlen + 1); |
michael@0 | 79 | if (!p) |
michael@0 | 80 | return NS_ERROR_OUT_OF_MEMORY; |
michael@0 | 81 | } |
michael@0 | 82 | |
michael@0 | 83 | rv = encoder->Convert(str.get(), &len, p, &maxlen); |
michael@0 | 84 | if (NS_FAILED(rv)) |
michael@0 | 85 | goto end; |
michael@0 | 86 | if (rv == NS_ERROR_UENC_NOMAPPING) { |
michael@0 | 87 | NS_WARNING("unicode conversion failed"); |
michael@0 | 88 | rv = NS_ERROR_UNEXPECTED; |
michael@0 | 89 | goto end; |
michael@0 | 90 | } |
michael@0 | 91 | p[maxlen] = 0; |
michael@0 | 92 | result.Assign(p); |
michael@0 | 93 | |
michael@0 | 94 | len = sizeof(buf) - 1; |
michael@0 | 95 | rv = encoder->Finish(buf, &len); |
michael@0 | 96 | if (NS_FAILED(rv)) |
michael@0 | 97 | goto end; |
michael@0 | 98 | buf[len] = 0; |
michael@0 | 99 | result.Append(buf); |
michael@0 | 100 | |
michael@0 | 101 | end: |
michael@0 | 102 | encoder->Reset(); |
michael@0 | 103 | |
michael@0 | 104 | if (p != buf) |
michael@0 | 105 | free(p); |
michael@0 | 106 | return rv; |
michael@0 | 107 | } |
michael@0 | 108 | |
michael@0 | 109 | //---------------------------------------------------------------------------- |
michael@0 | 110 | // nsStandardURL::nsPrefObserver |
michael@0 | 111 | //---------------------------------------------------------------------------- |
michael@0 | 112 | |
michael@0 | 113 | #define NS_NET_PREF_ESCAPEUTF8 "network.standard-url.escape-utf8" |
michael@0 | 114 | #define NS_NET_PREF_ALWAYSENCODEINUTF8 "network.standard-url.encode-utf8" |
michael@0 | 115 | |
michael@0 | 116 | NS_IMPL_ISUPPORTS(nsStandardURL::nsPrefObserver, nsIObserver) |
michael@0 | 117 | |
michael@0 | 118 | NS_IMETHODIMP nsStandardURL:: |
michael@0 | 119 | nsPrefObserver::Observe(nsISupports *subject, |
michael@0 | 120 | const char *topic, |
michael@0 | 121 | const char16_t *data) |
michael@0 | 122 | { |
michael@0 | 123 | if (!strcmp(topic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) { |
michael@0 | 124 | nsCOMPtr<nsIPrefBranch> prefBranch( do_QueryInterface(subject) ); |
michael@0 | 125 | if (prefBranch) { |
michael@0 | 126 | PrefsChanged(prefBranch, NS_ConvertUTF16toUTF8(data).get()); |
michael@0 | 127 | } |
michael@0 | 128 | } |
michael@0 | 129 | return NS_OK; |
michael@0 | 130 | } |
michael@0 | 131 | |
michael@0 | 132 | //---------------------------------------------------------------------------- |
michael@0 | 133 | // nsStandardURL::nsSegmentEncoder |
michael@0 | 134 | //---------------------------------------------------------------------------- |
michael@0 | 135 | |
michael@0 | 136 | nsStandardURL:: |
michael@0 | 137 | nsSegmentEncoder::nsSegmentEncoder(const char *charset) |
michael@0 | 138 | : mCharset(charset) |
michael@0 | 139 | { |
michael@0 | 140 | } |
michael@0 | 141 | |
michael@0 | 142 | int32_t nsStandardURL:: |
michael@0 | 143 | nsSegmentEncoder::EncodeSegmentCount(const char *str, |
michael@0 | 144 | const URLSegment &seg, |
michael@0 | 145 | int16_t mask, |
michael@0 | 146 | nsAFlatCString &result, |
michael@0 | 147 | bool &appended, |
michael@0 | 148 | uint32_t extraLen) |
michael@0 | 149 | { |
michael@0 | 150 | // extraLen is characters outside the segment that will be |
michael@0 | 151 | // added when the segment is not empty (like the @ following |
michael@0 | 152 | // a username). |
michael@0 | 153 | appended = false; |
michael@0 | 154 | if (!str) |
michael@0 | 155 | return 0; |
michael@0 | 156 | int32_t len = 0; |
michael@0 | 157 | if (seg.mLen > 0) { |
michael@0 | 158 | uint32_t pos = seg.mPos; |
michael@0 | 159 | len = seg.mLen; |
michael@0 | 160 | |
michael@0 | 161 | // first honor the origin charset if appropriate. as an optimization, |
michael@0 | 162 | // only do this if the segment is non-ASCII. Further, if mCharset is |
michael@0 | 163 | // null or the empty string then the origin charset is UTF-8 and there |
michael@0 | 164 | // is nothing to do. |
michael@0 | 165 | nsAutoCString encBuf; |
michael@0 | 166 | if (mCharset && *mCharset && !nsCRT::IsAscii(str + pos, len)) { |
michael@0 | 167 | // we have to encode this segment |
michael@0 | 168 | if (mEncoder || InitUnicodeEncoder()) { |
michael@0 | 169 | NS_ConvertUTF8toUTF16 ucsBuf(Substring(str + pos, str + pos + len)); |
michael@0 | 170 | if (NS_SUCCEEDED(EncodeString(mEncoder, ucsBuf, encBuf))) { |
michael@0 | 171 | str = encBuf.get(); |
michael@0 | 172 | pos = 0; |
michael@0 | 173 | len = encBuf.Length(); |
michael@0 | 174 | } |
michael@0 | 175 | // else some failure occurred... assume UTF-8 is ok. |
michael@0 | 176 | } |
michael@0 | 177 | } |
michael@0 | 178 | |
michael@0 | 179 | // escape per RFC2396 unless UTF-8 and allowed by preferences |
michael@0 | 180 | int16_t escapeFlags = (gEscapeUTF8 || mEncoder) ? 0 : esc_OnlyASCII; |
michael@0 | 181 | |
michael@0 | 182 | uint32_t initLen = result.Length(); |
michael@0 | 183 | |
michael@0 | 184 | // now perform any required escaping |
michael@0 | 185 | if (NS_EscapeURL(str + pos, len, mask | escapeFlags, result)) { |
michael@0 | 186 | len = result.Length() - initLen; |
michael@0 | 187 | appended = true; |
michael@0 | 188 | } |
michael@0 | 189 | else if (str == encBuf.get()) { |
michael@0 | 190 | result += encBuf; // append only!! |
michael@0 | 191 | len = encBuf.Length(); |
michael@0 | 192 | appended = true; |
michael@0 | 193 | } |
michael@0 | 194 | len += extraLen; |
michael@0 | 195 | } |
michael@0 | 196 | return len; |
michael@0 | 197 | } |
michael@0 | 198 | |
michael@0 | 199 | const nsACString &nsStandardURL:: |
michael@0 | 200 | nsSegmentEncoder::EncodeSegment(const nsASingleFragmentCString &str, |
michael@0 | 201 | int16_t mask, |
michael@0 | 202 | nsAFlatCString &result) |
michael@0 | 203 | { |
michael@0 | 204 | const char *text; |
michael@0 | 205 | bool encoded; |
michael@0 | 206 | EncodeSegmentCount(str.BeginReading(text), URLSegment(0, str.Length()), mask, result, encoded); |
michael@0 | 207 | if (encoded) |
michael@0 | 208 | return result; |
michael@0 | 209 | return str; |
michael@0 | 210 | } |
michael@0 | 211 | |
michael@0 | 212 | bool nsStandardURL:: |
michael@0 | 213 | nsSegmentEncoder::InitUnicodeEncoder() |
michael@0 | 214 | { |
michael@0 | 215 | NS_ASSERTION(!mEncoder, "Don't call this if we have an encoder already!"); |
michael@0 | 216 | nsresult rv; |
michael@0 | 217 | if (!gCharsetMgr) { |
michael@0 | 218 | rv = CallGetService("@mozilla.org/charset-converter-manager;1", |
michael@0 | 219 | &gCharsetMgr); |
michael@0 | 220 | if (NS_FAILED(rv)) { |
michael@0 | 221 | NS_ERROR("failed to get charset-converter-manager"); |
michael@0 | 222 | return false; |
michael@0 | 223 | } |
michael@0 | 224 | } |
michael@0 | 225 | |
michael@0 | 226 | rv = gCharsetMgr->GetUnicodeEncoder(mCharset, getter_AddRefs(mEncoder)); |
michael@0 | 227 | if (NS_FAILED(rv)) { |
michael@0 | 228 | NS_ERROR("failed to get unicode encoder"); |
michael@0 | 229 | mEncoder = 0; // just in case |
michael@0 | 230 | return false; |
michael@0 | 231 | } |
michael@0 | 232 | |
michael@0 | 233 | return true; |
michael@0 | 234 | } |
michael@0 | 235 | |
michael@0 | 236 | #define GET_SEGMENT_ENCODER_INTERNAL(name, useUTF8) \ |
michael@0 | 237 | nsSegmentEncoder name(useUTF8 ? nullptr : mOriginCharset.get()) |
michael@0 | 238 | |
michael@0 | 239 | #define GET_SEGMENT_ENCODER(name) \ |
michael@0 | 240 | GET_SEGMENT_ENCODER_INTERNAL(name, gAlwaysEncodeInUTF8) |
michael@0 | 241 | |
michael@0 | 242 | #define GET_QUERY_ENCODER(name) \ |
michael@0 | 243 | GET_SEGMENT_ENCODER_INTERNAL(name, false) |
michael@0 | 244 | |
michael@0 | 245 | //---------------------------------------------------------------------------- |
michael@0 | 246 | // nsStandardURL <public> |
michael@0 | 247 | //---------------------------------------------------------------------------- |
michael@0 | 248 | |
michael@0 | 249 | #ifdef DEBUG_DUMP_URLS_AT_SHUTDOWN |
michael@0 | 250 | static PRCList gAllURLs; |
michael@0 | 251 | #endif |
michael@0 | 252 | |
michael@0 | 253 | nsStandardURL::nsStandardURL(bool aSupportsFileURL) |
michael@0 | 254 | : mDefaultPort(-1) |
michael@0 | 255 | , mPort(-1) |
michael@0 | 256 | , mHostA(nullptr) |
michael@0 | 257 | , mHostEncoding(eEncoding_ASCII) |
michael@0 | 258 | , mSpecEncoding(eEncoding_Unknown) |
michael@0 | 259 | , mURLType(URLTYPE_STANDARD) |
michael@0 | 260 | , mMutable(true) |
michael@0 | 261 | , mSupportsFileURL(aSupportsFileURL) |
michael@0 | 262 | { |
michael@0 | 263 | #if defined(PR_LOGGING) |
michael@0 | 264 | if (!gStandardURLLog) |
michael@0 | 265 | gStandardURLLog = PR_NewLogModule("nsStandardURL"); |
michael@0 | 266 | #endif |
michael@0 | 267 | |
michael@0 | 268 | LOG(("Creating nsStandardURL @%p\n", this)); |
michael@0 | 269 | |
michael@0 | 270 | if (!gInitialized) { |
michael@0 | 271 | gInitialized = true; |
michael@0 | 272 | InitGlobalObjects(); |
michael@0 | 273 | } |
michael@0 | 274 | |
michael@0 | 275 | // default parser in case nsIStandardURL::Init is never called |
michael@0 | 276 | mParser = net_GetStdURLParser(); |
michael@0 | 277 | |
michael@0 | 278 | #ifdef DEBUG_DUMP_URLS_AT_SHUTDOWN |
michael@0 | 279 | PR_APPEND_LINK(&mDebugCList, &gAllURLs); |
michael@0 | 280 | #endif |
michael@0 | 281 | } |
michael@0 | 282 | |
michael@0 | 283 | nsStandardURL::~nsStandardURL() |
michael@0 | 284 | { |
michael@0 | 285 | LOG(("Destroying nsStandardURL @%p\n", this)); |
michael@0 | 286 | |
michael@0 | 287 | if (mHostA) { |
michael@0 | 288 | free(mHostA); |
michael@0 | 289 | } |
michael@0 | 290 | #ifdef DEBUG_DUMP_URLS_AT_SHUTDOWN |
michael@0 | 291 | PR_REMOVE_LINK(&mDebugCList); |
michael@0 | 292 | #endif |
michael@0 | 293 | } |
michael@0 | 294 | |
michael@0 | 295 | #ifdef DEBUG_DUMP_URLS_AT_SHUTDOWN |
michael@0 | 296 | struct DumpLeakedURLs { |
michael@0 | 297 | DumpLeakedURLs() {} |
michael@0 | 298 | ~DumpLeakedURLs(); |
michael@0 | 299 | }; |
michael@0 | 300 | |
michael@0 | 301 | DumpLeakedURLs::~DumpLeakedURLs() |
michael@0 | 302 | { |
michael@0 | 303 | if (!PR_CLIST_IS_EMPTY(&gAllURLs)) { |
michael@0 | 304 | printf("Leaked URLs:\n"); |
michael@0 | 305 | for (PRCList *l = PR_LIST_HEAD(&gAllURLs); l != &gAllURLs; l = PR_NEXT_LINK(l)) { |
michael@0 | 306 | nsStandardURL *url = reinterpret_cast<nsStandardURL*>(reinterpret_cast<char*>(l) - offsetof(nsStandardURL, mDebugCList)); |
michael@0 | 307 | url->PrintSpec(); |
michael@0 | 308 | } |
michael@0 | 309 | } |
michael@0 | 310 | } |
michael@0 | 311 | #endif |
michael@0 | 312 | |
michael@0 | 313 | void |
michael@0 | 314 | nsStandardURL::InitGlobalObjects() |
michael@0 | 315 | { |
michael@0 | 316 | nsCOMPtr<nsIPrefBranch> prefBranch( do_GetService(NS_PREFSERVICE_CONTRACTID) ); |
michael@0 | 317 | if (prefBranch) { |
michael@0 | 318 | nsCOMPtr<nsIObserver> obs( new nsPrefObserver() ); |
michael@0 | 319 | prefBranch->AddObserver(NS_NET_PREF_ESCAPEUTF8, obs.get(), false); |
michael@0 | 320 | prefBranch->AddObserver(NS_NET_PREF_ALWAYSENCODEINUTF8, obs.get(), false); |
michael@0 | 321 | |
michael@0 | 322 | PrefsChanged(prefBranch, nullptr); |
michael@0 | 323 | } |
michael@0 | 324 | |
michael@0 | 325 | #ifdef DEBUG_DUMP_URLS_AT_SHUTDOWN |
michael@0 | 326 | PR_INIT_CLIST(&gAllURLs); |
michael@0 | 327 | #endif |
michael@0 | 328 | } |
michael@0 | 329 | |
michael@0 | 330 | void |
michael@0 | 331 | nsStandardURL::ShutdownGlobalObjects() |
michael@0 | 332 | { |
michael@0 | 333 | NS_IF_RELEASE(gIDN); |
michael@0 | 334 | NS_IF_RELEASE(gCharsetMgr); |
michael@0 | 335 | |
michael@0 | 336 | #ifdef DEBUG_DUMP_URLS_AT_SHUTDOWN |
michael@0 | 337 | if (gInitialized) { |
michael@0 | 338 | // This instanciates a dummy class, and will trigger the class |
michael@0 | 339 | // destructor when libxul is unloaded. This is equivalent to atexit(), |
michael@0 | 340 | // but gracefully handles dlclose(). |
michael@0 | 341 | static DumpLeakedURLs d; |
michael@0 | 342 | } |
michael@0 | 343 | #endif |
michael@0 | 344 | } |
michael@0 | 345 | |
michael@0 | 346 | //---------------------------------------------------------------------------- |
michael@0 | 347 | // nsStandardURL <private> |
michael@0 | 348 | //---------------------------------------------------------------------------- |
michael@0 | 349 | |
michael@0 | 350 | void |
michael@0 | 351 | nsStandardURL::Clear() |
michael@0 | 352 | { |
michael@0 | 353 | mSpec.Truncate(); |
michael@0 | 354 | |
michael@0 | 355 | mPort = -1; |
michael@0 | 356 | |
michael@0 | 357 | mScheme.Reset(); |
michael@0 | 358 | mAuthority.Reset(); |
michael@0 | 359 | mUsername.Reset(); |
michael@0 | 360 | mPassword.Reset(); |
michael@0 | 361 | mHost.Reset(); |
michael@0 | 362 | mHostEncoding = eEncoding_ASCII; |
michael@0 | 363 | |
michael@0 | 364 | mPath.Reset(); |
michael@0 | 365 | mFilepath.Reset(); |
michael@0 | 366 | mDirectory.Reset(); |
michael@0 | 367 | mBasename.Reset(); |
michael@0 | 368 | |
michael@0 | 369 | mExtension.Reset(); |
michael@0 | 370 | mQuery.Reset(); |
michael@0 | 371 | mRef.Reset(); |
michael@0 | 372 | |
michael@0 | 373 | InvalidateCache(); |
michael@0 | 374 | } |
michael@0 | 375 | |
michael@0 | 376 | void |
michael@0 | 377 | nsStandardURL::InvalidateCache(bool invalidateCachedFile) |
michael@0 | 378 | { |
michael@0 | 379 | if (invalidateCachedFile) |
michael@0 | 380 | mFile = 0; |
michael@0 | 381 | if (mHostA) { |
michael@0 | 382 | free(mHostA); |
michael@0 | 383 | mHostA = nullptr; |
michael@0 | 384 | } |
michael@0 | 385 | mSpecEncoding = eEncoding_Unknown; |
michael@0 | 386 | } |
michael@0 | 387 | |
michael@0 | 388 | bool |
michael@0 | 389 | nsStandardURL::EscapeIPv6(const char *host, nsCString &result) |
michael@0 | 390 | { |
michael@0 | 391 | // Escape IPv6 address literal by surrounding it with []'s |
michael@0 | 392 | if (host && (host[0] != '[') && PL_strchr(host, ':')) { |
michael@0 | 393 | result.Assign('['); |
michael@0 | 394 | result.Append(host); |
michael@0 | 395 | result.Append(']'); |
michael@0 | 396 | return true; |
michael@0 | 397 | } |
michael@0 | 398 | return false; |
michael@0 | 399 | } |
michael@0 | 400 | |
michael@0 | 401 | bool |
michael@0 | 402 | nsStandardURL::NormalizeIDN(const nsCSubstring &host, nsCString &result) |
michael@0 | 403 | { |
michael@0 | 404 | // If host is ACE, then convert to UTF-8. Else, if host is already UTF-8, |
michael@0 | 405 | // then make sure it is normalized per IDN. |
michael@0 | 406 | |
michael@0 | 407 | // this function returns true if normalization succeeds. |
michael@0 | 408 | |
michael@0 | 409 | // NOTE: As a side-effect this function sets mHostEncoding. While it would |
michael@0 | 410 | // be nice to avoid side-effects in this function, the implementation of |
michael@0 | 411 | // this function is already somewhat bound to the behavior of the |
michael@0 | 412 | // callsites. Anyways, this function exists to avoid code duplication, so |
michael@0 | 413 | // side-effects abound :-/ |
michael@0 | 414 | |
michael@0 | 415 | NS_ASSERTION(mHostEncoding == eEncoding_ASCII, "unexpected default encoding"); |
michael@0 | 416 | |
michael@0 | 417 | bool isASCII; |
michael@0 | 418 | if (!gIDN) { |
michael@0 | 419 | nsCOMPtr<nsIIDNService> serv(do_GetService(NS_IDNSERVICE_CONTRACTID)); |
michael@0 | 420 | if (serv) { |
michael@0 | 421 | NS_ADDREF(gIDN = serv.get()); |
michael@0 | 422 | } |
michael@0 | 423 | } |
michael@0 | 424 | |
michael@0 | 425 | if (gIDN && |
michael@0 | 426 | NS_SUCCEEDED(gIDN->ConvertToDisplayIDN(host, &isASCII, result))) { |
michael@0 | 427 | if (!isASCII) |
michael@0 | 428 | mHostEncoding = eEncoding_UTF8; |
michael@0 | 429 | |
michael@0 | 430 | return true; |
michael@0 | 431 | } |
michael@0 | 432 | |
michael@0 | 433 | result.Truncate(); |
michael@0 | 434 | return false; |
michael@0 | 435 | } |
michael@0 | 436 | |
michael@0 | 437 | void |
michael@0 | 438 | nsStandardURL::CoalescePath(netCoalesceFlags coalesceFlag, char *path) |
michael@0 | 439 | { |
michael@0 | 440 | net_CoalesceDirs(coalesceFlag, path); |
michael@0 | 441 | int32_t newLen = strlen(path); |
michael@0 | 442 | if (newLen < mPath.mLen) { |
michael@0 | 443 | int32_t diff = newLen - mPath.mLen; |
michael@0 | 444 | mPath.mLen = newLen; |
michael@0 | 445 | mDirectory.mLen += diff; |
michael@0 | 446 | mFilepath.mLen += diff; |
michael@0 | 447 | ShiftFromBasename(diff); |
michael@0 | 448 | } |
michael@0 | 449 | } |
michael@0 | 450 | |
michael@0 | 451 | uint32_t |
michael@0 | 452 | nsStandardURL::AppendSegmentToBuf(char *buf, uint32_t i, const char *str, URLSegment &seg, const nsCString *escapedStr, bool useEscaped) |
michael@0 | 453 | { |
michael@0 | 454 | if (seg.mLen > 0) { |
michael@0 | 455 | if (useEscaped) { |
michael@0 | 456 | seg.mLen = escapedStr->Length(); |
michael@0 | 457 | memcpy(buf + i, escapedStr->get(), seg.mLen); |
michael@0 | 458 | } |
michael@0 | 459 | else |
michael@0 | 460 | memcpy(buf + i, str + seg.mPos, seg.mLen); |
michael@0 | 461 | seg.mPos = i; |
michael@0 | 462 | i += seg.mLen; |
michael@0 | 463 | } else { |
michael@0 | 464 | seg.mPos = i; |
michael@0 | 465 | } |
michael@0 | 466 | return i; |
michael@0 | 467 | } |
michael@0 | 468 | |
michael@0 | 469 | uint32_t |
michael@0 | 470 | nsStandardURL::AppendToBuf(char *buf, uint32_t i, const char *str, uint32_t len) |
michael@0 | 471 | { |
michael@0 | 472 | memcpy(buf + i, str, len); |
michael@0 | 473 | return i + len; |
michael@0 | 474 | } |
michael@0 | 475 | |
michael@0 | 476 | // basic algorithm: |
michael@0 | 477 | // 1- escape url segments (for improved GetSpec efficiency) |
michael@0 | 478 | // 2- allocate spec buffer |
michael@0 | 479 | // 3- write url segments |
michael@0 | 480 | // 4- update url segment positions and lengths |
michael@0 | 481 | nsresult |
michael@0 | 482 | nsStandardURL::BuildNormalizedSpec(const char *spec) |
michael@0 | 483 | { |
michael@0 | 484 | // Assumptions: all member URLSegments must be relative the |spec| argument |
michael@0 | 485 | // passed to this function. |
michael@0 | 486 | |
michael@0 | 487 | // buffers for holding escaped url segments (these will remain empty unless |
michael@0 | 488 | // escaping is required). |
michael@0 | 489 | nsAutoCString encUsername, encPassword, encHost, encDirectory, |
michael@0 | 490 | encBasename, encExtension, encQuery, encRef; |
michael@0 | 491 | bool useEncUsername, useEncPassword, useEncHost = false, |
michael@0 | 492 | useEncDirectory, useEncBasename, useEncExtension, useEncQuery, useEncRef; |
michael@0 | 493 | nsAutoCString portbuf; |
michael@0 | 494 | |
michael@0 | 495 | // |
michael@0 | 496 | // escape each URL segment, if necessary, and calculate approximate normalized |
michael@0 | 497 | // spec length. |
michael@0 | 498 | // |
michael@0 | 499 | // [scheme://][username[:password]@]host[:port]/path[?query_string][#ref] |
michael@0 | 500 | |
michael@0 | 501 | uint32_t approxLen = 0; |
michael@0 | 502 | |
michael@0 | 503 | // the scheme is already ASCII |
michael@0 | 504 | if (mScheme.mLen > 0) |
michael@0 | 505 | approxLen += mScheme.mLen + 3; // includes room for "://", which we insert always |
michael@0 | 506 | |
michael@0 | 507 | // encode URL segments; convert UTF-8 to origin charset and possibly escape. |
michael@0 | 508 | // results written to encXXX variables only if |spec| is not already in the |
michael@0 | 509 | // appropriate encoding. |
michael@0 | 510 | { |
michael@0 | 511 | GET_SEGMENT_ENCODER(encoder); |
michael@0 | 512 | GET_QUERY_ENCODER(queryEncoder); |
michael@0 | 513 | // Items using an extraLen of 1 don't add anything unless mLen > 0 |
michael@0 | 514 | // Username@ |
michael@0 | 515 | approxLen += encoder.EncodeSegmentCount(spec, mUsername, esc_Username, encUsername, useEncUsername, 1); |
michael@0 | 516 | // :password - we insert the ':' even if there's no actual password if "user:@" was in the spec |
michael@0 | 517 | if (mPassword.mLen >= 0) |
michael@0 | 518 | approxLen += 1 + encoder.EncodeSegmentCount(spec, mPassword, esc_Password, encPassword, useEncPassword); |
michael@0 | 519 | // mHost is handled differently below due to encoding differences |
michael@0 | 520 | NS_ABORT_IF_FALSE(mPort >= -1, "Invalid negative mPort"); |
michael@0 | 521 | if (mPort != -1 && mPort != mDefaultPort) |
michael@0 | 522 | { |
michael@0 | 523 | // :port |
michael@0 | 524 | portbuf.AppendInt(mPort); |
michael@0 | 525 | approxLen += portbuf.Length() + 1; |
michael@0 | 526 | } |
michael@0 | 527 | |
michael@0 | 528 | approxLen += 1; // reserve space for possible leading '/' - may not be needed |
michael@0 | 529 | // Should just use mPath? These are pessimistic, and thus waste space |
michael@0 | 530 | approxLen += encoder.EncodeSegmentCount(spec, mDirectory, esc_Directory, encDirectory, useEncDirectory, 1); |
michael@0 | 531 | approxLen += encoder.EncodeSegmentCount(spec, mBasename, esc_FileBaseName, encBasename, useEncBasename); |
michael@0 | 532 | approxLen += encoder.EncodeSegmentCount(spec, mExtension, esc_FileExtension, encExtension, useEncExtension, 1); |
michael@0 | 533 | |
michael@0 | 534 | // These next ones *always* add their leading character even if length is 0 |
michael@0 | 535 | // Handles items like "http://#" |
michael@0 | 536 | // ?query |
michael@0 | 537 | if (mQuery.mLen >= 0) |
michael@0 | 538 | approxLen += 1 + queryEncoder.EncodeSegmentCount(spec, mQuery, esc_Query, encQuery, useEncQuery); |
michael@0 | 539 | // #ref |
michael@0 | 540 | if (mRef.mLen >= 0) |
michael@0 | 541 | approxLen += 1 + encoder.EncodeSegmentCount(spec, mRef, esc_Ref, encRef, useEncRef); |
michael@0 | 542 | } |
michael@0 | 543 | |
michael@0 | 544 | // do not escape the hostname, if IPv6 address literal, mHost will |
michael@0 | 545 | // already point to a [ ] delimited IPv6 address literal. |
michael@0 | 546 | // However, perform Unicode normalization on it, as IDN does. |
michael@0 | 547 | mHostEncoding = eEncoding_ASCII; |
michael@0 | 548 | // Note that we don't disallow URLs without a host - file:, etc |
michael@0 | 549 | if (mHost.mLen > 0) { |
michael@0 | 550 | const nsCSubstring& tempHost = |
michael@0 | 551 | Substring(spec + mHost.mPos, spec + mHost.mPos + mHost.mLen); |
michael@0 | 552 | if (tempHost.FindChar('\0') != kNotFound) |
michael@0 | 553 | return NS_ERROR_MALFORMED_URI; // null embedded in hostname |
michael@0 | 554 | if (tempHost.FindChar(' ') != kNotFound) |
michael@0 | 555 | return NS_ERROR_MALFORMED_URI; // don't allow spaces in the hostname |
michael@0 | 556 | if ((useEncHost = NormalizeIDN(tempHost, encHost))) |
michael@0 | 557 | approxLen += encHost.Length(); |
michael@0 | 558 | else |
michael@0 | 559 | approxLen += mHost.mLen; |
michael@0 | 560 | } |
michael@0 | 561 | |
michael@0 | 562 | // |
michael@0 | 563 | // generate the normalized URL string |
michael@0 | 564 | // |
michael@0 | 565 | // approxLen should be correct or 1 high |
michael@0 | 566 | if (!mSpec.SetLength(approxLen+1, mozilla::fallible_t())) // buf needs a trailing '\0' below |
michael@0 | 567 | return NS_ERROR_OUT_OF_MEMORY; |
michael@0 | 568 | char *buf; |
michael@0 | 569 | mSpec.BeginWriting(buf); |
michael@0 | 570 | uint32_t i = 0; |
michael@0 | 571 | |
michael@0 | 572 | if (mScheme.mLen > 0) { |
michael@0 | 573 | i = AppendSegmentToBuf(buf, i, spec, mScheme); |
michael@0 | 574 | net_ToLowerCase(buf + mScheme.mPos, mScheme.mLen); |
michael@0 | 575 | i = AppendToBuf(buf, i, "://", 3); |
michael@0 | 576 | } |
michael@0 | 577 | |
michael@0 | 578 | // record authority starting position |
michael@0 | 579 | mAuthority.mPos = i; |
michael@0 | 580 | |
michael@0 | 581 | // append authority |
michael@0 | 582 | if (mUsername.mLen > 0) { |
michael@0 | 583 | i = AppendSegmentToBuf(buf, i, spec, mUsername, &encUsername, useEncUsername); |
michael@0 | 584 | if (mPassword.mLen >= 0) { |
michael@0 | 585 | buf[i++] = ':'; |
michael@0 | 586 | i = AppendSegmentToBuf(buf, i, spec, mPassword, &encPassword, useEncPassword); |
michael@0 | 587 | } |
michael@0 | 588 | buf[i++] = '@'; |
michael@0 | 589 | } |
michael@0 | 590 | if (mHost.mLen > 0) { |
michael@0 | 591 | i = AppendSegmentToBuf(buf, i, spec, mHost, &encHost, useEncHost); |
michael@0 | 592 | net_ToLowerCase(buf + mHost.mPos, mHost.mLen); |
michael@0 | 593 | NS_ABORT_IF_FALSE(mPort >= -1, "Invalid negative mPort"); |
michael@0 | 594 | if (mPort != -1 && mPort != mDefaultPort) { |
michael@0 | 595 | buf[i++] = ':'; |
michael@0 | 596 | // Already formatted while building approxLen |
michael@0 | 597 | i = AppendToBuf(buf, i, portbuf.get(), portbuf.Length()); |
michael@0 | 598 | } |
michael@0 | 599 | } |
michael@0 | 600 | |
michael@0 | 601 | // record authority length |
michael@0 | 602 | mAuthority.mLen = i - mAuthority.mPos; |
michael@0 | 603 | |
michael@0 | 604 | // path must always start with a "/" |
michael@0 | 605 | if (mPath.mLen <= 0) { |
michael@0 | 606 | LOG(("setting path=/")); |
michael@0 | 607 | mDirectory.mPos = mFilepath.mPos = mPath.mPos = i; |
michael@0 | 608 | mDirectory.mLen = mFilepath.mLen = mPath.mLen = 1; |
michael@0 | 609 | // basename must exist, even if empty (bug 113508) |
michael@0 | 610 | mBasename.mPos = i+1; |
michael@0 | 611 | mBasename.mLen = 0; |
michael@0 | 612 | buf[i++] = '/'; |
michael@0 | 613 | } |
michael@0 | 614 | else { |
michael@0 | 615 | uint32_t leadingSlash = 0; |
michael@0 | 616 | if (spec[mPath.mPos] != '/') { |
michael@0 | 617 | LOG(("adding leading slash to path\n")); |
michael@0 | 618 | leadingSlash = 1; |
michael@0 | 619 | buf[i++] = '/'; |
michael@0 | 620 | // basename must exist, even if empty (bugs 113508, 429347) |
michael@0 | 621 | if (mBasename.mLen == -1) { |
michael@0 | 622 | mBasename.mPos = i; |
michael@0 | 623 | mBasename.mLen = 0; |
michael@0 | 624 | } |
michael@0 | 625 | } |
michael@0 | 626 | |
michael@0 | 627 | // record corrected (file)path starting position |
michael@0 | 628 | mPath.mPos = mFilepath.mPos = i - leadingSlash; |
michael@0 | 629 | |
michael@0 | 630 | i = AppendSegmentToBuf(buf, i, spec, mDirectory, &encDirectory, useEncDirectory); |
michael@0 | 631 | |
michael@0 | 632 | // the directory must end with a '/' |
michael@0 | 633 | if (buf[i-1] != '/') { |
michael@0 | 634 | buf[i++] = '/'; |
michael@0 | 635 | mDirectory.mLen++; |
michael@0 | 636 | } |
michael@0 | 637 | |
michael@0 | 638 | i = AppendSegmentToBuf(buf, i, spec, mBasename, &encBasename, useEncBasename); |
michael@0 | 639 | |
michael@0 | 640 | // make corrections to directory segment if leadingSlash |
michael@0 | 641 | if (leadingSlash) { |
michael@0 | 642 | mDirectory.mPos = mPath.mPos; |
michael@0 | 643 | if (mDirectory.mLen >= 0) |
michael@0 | 644 | mDirectory.mLen += leadingSlash; |
michael@0 | 645 | else |
michael@0 | 646 | mDirectory.mLen = 1; |
michael@0 | 647 | } |
michael@0 | 648 | |
michael@0 | 649 | if (mExtension.mLen >= 0) { |
michael@0 | 650 | buf[i++] = '.'; |
michael@0 | 651 | i = AppendSegmentToBuf(buf, i, spec, mExtension, &encExtension, useEncExtension); |
michael@0 | 652 | } |
michael@0 | 653 | // calculate corrected filepath length |
michael@0 | 654 | mFilepath.mLen = i - mFilepath.mPos; |
michael@0 | 655 | |
michael@0 | 656 | if (mQuery.mLen >= 0) { |
michael@0 | 657 | buf[i++] = '?'; |
michael@0 | 658 | i = AppendSegmentToBuf(buf, i, spec, mQuery, &encQuery, useEncQuery); |
michael@0 | 659 | } |
michael@0 | 660 | if (mRef.mLen >= 0) { |
michael@0 | 661 | buf[i++] = '#'; |
michael@0 | 662 | i = AppendSegmentToBuf(buf, i, spec, mRef, &encRef, useEncRef); |
michael@0 | 663 | } |
michael@0 | 664 | // calculate corrected path length |
michael@0 | 665 | mPath.mLen = i - mPath.mPos; |
michael@0 | 666 | } |
michael@0 | 667 | |
michael@0 | 668 | buf[i] = '\0'; |
michael@0 | 669 | |
michael@0 | 670 | if (mDirectory.mLen > 1) { |
michael@0 | 671 | netCoalesceFlags coalesceFlag = NET_COALESCE_NORMAL; |
michael@0 | 672 | if (SegmentIs(buf,mScheme,"ftp")) { |
michael@0 | 673 | coalesceFlag = (netCoalesceFlags) (coalesceFlag |
michael@0 | 674 | | NET_COALESCE_ALLOW_RELATIVE_ROOT |
michael@0 | 675 | | NET_COALESCE_DOUBLE_SLASH_IS_ROOT); |
michael@0 | 676 | } |
michael@0 | 677 | CoalescePath(coalesceFlag, buf + mDirectory.mPos); |
michael@0 | 678 | } |
michael@0 | 679 | mSpec.SetLength(strlen(buf)); |
michael@0 | 680 | NS_ASSERTION(mSpec.Length() <= approxLen, "We've overflowed the mSpec buffer!"); |
michael@0 | 681 | return NS_OK; |
michael@0 | 682 | } |
michael@0 | 683 | |
michael@0 | 684 | bool |
michael@0 | 685 | nsStandardURL::SegmentIs(const URLSegment &seg, const char *val, bool ignoreCase) |
michael@0 | 686 | { |
michael@0 | 687 | // one or both may be null |
michael@0 | 688 | if (!val || mSpec.IsEmpty()) |
michael@0 | 689 | return (!val && (mSpec.IsEmpty() || seg.mLen < 0)); |
michael@0 | 690 | if (seg.mLen < 0) |
michael@0 | 691 | return false; |
michael@0 | 692 | // if the first |seg.mLen| chars of |val| match, then |val| must |
michael@0 | 693 | // also be null terminated at |seg.mLen|. |
michael@0 | 694 | if (ignoreCase) |
michael@0 | 695 | return !PL_strncasecmp(mSpec.get() + seg.mPos, val, seg.mLen) |
michael@0 | 696 | && (val[seg.mLen] == '\0'); |
michael@0 | 697 | else |
michael@0 | 698 | return !strncmp(mSpec.get() + seg.mPos, val, seg.mLen) |
michael@0 | 699 | && (val[seg.mLen] == '\0'); |
michael@0 | 700 | } |
michael@0 | 701 | |
michael@0 | 702 | bool |
michael@0 | 703 | nsStandardURL::SegmentIs(const char* spec, const URLSegment &seg, const char *val, bool ignoreCase) |
michael@0 | 704 | { |
michael@0 | 705 | // one or both may be null |
michael@0 | 706 | if (!val || !spec) |
michael@0 | 707 | return (!val && (!spec || seg.mLen < 0)); |
michael@0 | 708 | if (seg.mLen < 0) |
michael@0 | 709 | return false; |
michael@0 | 710 | // if the first |seg.mLen| chars of |val| match, then |val| must |
michael@0 | 711 | // also be null terminated at |seg.mLen|. |
michael@0 | 712 | if (ignoreCase) |
michael@0 | 713 | return !PL_strncasecmp(spec + seg.mPos, val, seg.mLen) |
michael@0 | 714 | && (val[seg.mLen] == '\0'); |
michael@0 | 715 | else |
michael@0 | 716 | return !strncmp(spec + seg.mPos, val, seg.mLen) |
michael@0 | 717 | && (val[seg.mLen] == '\0'); |
michael@0 | 718 | } |
michael@0 | 719 | |
michael@0 | 720 | bool |
michael@0 | 721 | nsStandardURL::SegmentIs(const URLSegment &seg1, const char *val, const URLSegment &seg2, bool ignoreCase) |
michael@0 | 722 | { |
michael@0 | 723 | if (seg1.mLen != seg2.mLen) |
michael@0 | 724 | return false; |
michael@0 | 725 | if (seg1.mLen == -1 || (!val && mSpec.IsEmpty())) |
michael@0 | 726 | return true; // both are empty |
michael@0 | 727 | if (!val) |
michael@0 | 728 | return false; |
michael@0 | 729 | if (ignoreCase) |
michael@0 | 730 | return !PL_strncasecmp(mSpec.get() + seg1.mPos, val + seg2.mPos, seg1.mLen); |
michael@0 | 731 | else |
michael@0 | 732 | return !strncmp(mSpec.get() + seg1.mPos, val + seg2.mPos, seg1.mLen); |
michael@0 | 733 | } |
michael@0 | 734 | |
michael@0 | 735 | int32_t |
michael@0 | 736 | nsStandardURL::ReplaceSegment(uint32_t pos, uint32_t len, const char *val, uint32_t valLen) |
michael@0 | 737 | { |
michael@0 | 738 | if (val && valLen) { |
michael@0 | 739 | if (len == 0) |
michael@0 | 740 | mSpec.Insert(val, pos, valLen); |
michael@0 | 741 | else |
michael@0 | 742 | mSpec.Replace(pos, len, nsDependentCString(val, valLen)); |
michael@0 | 743 | return valLen - len; |
michael@0 | 744 | } |
michael@0 | 745 | |
michael@0 | 746 | // else remove the specified segment |
michael@0 | 747 | mSpec.Cut(pos, len); |
michael@0 | 748 | return -int32_t(len); |
michael@0 | 749 | } |
michael@0 | 750 | |
michael@0 | 751 | int32_t |
michael@0 | 752 | nsStandardURL::ReplaceSegment(uint32_t pos, uint32_t len, const nsACString &val) |
michael@0 | 753 | { |
michael@0 | 754 | if (len == 0) |
michael@0 | 755 | mSpec.Insert(val, pos); |
michael@0 | 756 | else |
michael@0 | 757 | mSpec.Replace(pos, len, val); |
michael@0 | 758 | return val.Length() - len; |
michael@0 | 759 | } |
michael@0 | 760 | |
michael@0 | 761 | nsresult |
michael@0 | 762 | nsStandardURL::ParseURL(const char *spec, int32_t specLen) |
michael@0 | 763 | { |
michael@0 | 764 | nsresult rv; |
michael@0 | 765 | |
michael@0 | 766 | // |
michael@0 | 767 | // parse given URL string |
michael@0 | 768 | // |
michael@0 | 769 | rv = mParser->ParseURL(spec, specLen, |
michael@0 | 770 | &mScheme.mPos, &mScheme.mLen, |
michael@0 | 771 | &mAuthority.mPos, &mAuthority.mLen, |
michael@0 | 772 | &mPath.mPos, &mPath.mLen); |
michael@0 | 773 | if (NS_FAILED(rv)) return rv; |
michael@0 | 774 | |
michael@0 | 775 | #ifdef DEBUG |
michael@0 | 776 | if (mScheme.mLen <= 0) { |
michael@0 | 777 | printf("spec=%s\n", spec); |
michael@0 | 778 | NS_WARNING("malformed url: no scheme"); |
michael@0 | 779 | } |
michael@0 | 780 | #endif |
michael@0 | 781 | |
michael@0 | 782 | if (mAuthority.mLen > 0) { |
michael@0 | 783 | rv = mParser->ParseAuthority(spec + mAuthority.mPos, mAuthority.mLen, |
michael@0 | 784 | &mUsername.mPos, &mUsername.mLen, |
michael@0 | 785 | &mPassword.mPos, &mPassword.mLen, |
michael@0 | 786 | &mHost.mPos, &mHost.mLen, |
michael@0 | 787 | &mPort); |
michael@0 | 788 | if (NS_FAILED(rv)) return rv; |
michael@0 | 789 | |
michael@0 | 790 | // Don't allow mPort to be set to this URI's default port |
michael@0 | 791 | if (mPort == mDefaultPort) |
michael@0 | 792 | mPort = -1; |
michael@0 | 793 | |
michael@0 | 794 | mUsername.mPos += mAuthority.mPos; |
michael@0 | 795 | mPassword.mPos += mAuthority.mPos; |
michael@0 | 796 | mHost.mPos += mAuthority.mPos; |
michael@0 | 797 | } |
michael@0 | 798 | |
michael@0 | 799 | if (mPath.mLen > 0) |
michael@0 | 800 | rv = ParsePath(spec, mPath.mPos, mPath.mLen); |
michael@0 | 801 | |
michael@0 | 802 | return rv; |
michael@0 | 803 | } |
michael@0 | 804 | |
michael@0 | 805 | nsresult |
michael@0 | 806 | nsStandardURL::ParsePath(const char *spec, uint32_t pathPos, int32_t pathLen) |
michael@0 | 807 | { |
michael@0 | 808 | LOG(("ParsePath: %s pathpos %d len %d\n",spec,pathPos,pathLen)); |
michael@0 | 809 | |
michael@0 | 810 | nsresult rv = mParser->ParsePath(spec + pathPos, pathLen, |
michael@0 | 811 | &mFilepath.mPos, &mFilepath.mLen, |
michael@0 | 812 | &mQuery.mPos, &mQuery.mLen, |
michael@0 | 813 | &mRef.mPos, &mRef.mLen); |
michael@0 | 814 | if (NS_FAILED(rv)) return rv; |
michael@0 | 815 | |
michael@0 | 816 | mFilepath.mPos += pathPos; |
michael@0 | 817 | mQuery.mPos += pathPos; |
michael@0 | 818 | mRef.mPos += pathPos; |
michael@0 | 819 | |
michael@0 | 820 | if (mFilepath.mLen > 0) { |
michael@0 | 821 | rv = mParser->ParseFilePath(spec + mFilepath.mPos, mFilepath.mLen, |
michael@0 | 822 | &mDirectory.mPos, &mDirectory.mLen, |
michael@0 | 823 | &mBasename.mPos, &mBasename.mLen, |
michael@0 | 824 | &mExtension.mPos, &mExtension.mLen); |
michael@0 | 825 | if (NS_FAILED(rv)) return rv; |
michael@0 | 826 | |
michael@0 | 827 | mDirectory.mPos += mFilepath.mPos; |
michael@0 | 828 | mBasename.mPos += mFilepath.mPos; |
michael@0 | 829 | mExtension.mPos += mFilepath.mPos; |
michael@0 | 830 | } |
michael@0 | 831 | return NS_OK; |
michael@0 | 832 | } |
michael@0 | 833 | |
michael@0 | 834 | char * |
michael@0 | 835 | nsStandardURL::AppendToSubstring(uint32_t pos, |
michael@0 | 836 | int32_t len, |
michael@0 | 837 | const char *tail) |
michael@0 | 838 | { |
michael@0 | 839 | // Verify pos and length are within boundaries |
michael@0 | 840 | if (pos > mSpec.Length()) |
michael@0 | 841 | return nullptr; |
michael@0 | 842 | if (len < 0) |
michael@0 | 843 | return nullptr; |
michael@0 | 844 | if ((uint32_t)len > (mSpec.Length() - pos)) |
michael@0 | 845 | return nullptr; |
michael@0 | 846 | if (!tail) |
michael@0 | 847 | return nullptr; |
michael@0 | 848 | |
michael@0 | 849 | uint32_t tailLen = strlen(tail); |
michael@0 | 850 | |
michael@0 | 851 | // Check for int overflow for proposed length of combined string |
michael@0 | 852 | if (UINT32_MAX - ((uint32_t)len + 1) < tailLen) |
michael@0 | 853 | return nullptr; |
michael@0 | 854 | |
michael@0 | 855 | char *result = (char *) NS_Alloc(len + tailLen + 1); |
michael@0 | 856 | if (result) { |
michael@0 | 857 | memcpy(result, mSpec.get() + pos, len); |
michael@0 | 858 | memcpy(result + len, tail, tailLen); |
michael@0 | 859 | result[len + tailLen] = '\0'; |
michael@0 | 860 | } |
michael@0 | 861 | return result; |
michael@0 | 862 | } |
michael@0 | 863 | |
michael@0 | 864 | nsresult |
michael@0 | 865 | nsStandardURL::ReadSegment(nsIBinaryInputStream *stream, URLSegment &seg) |
michael@0 | 866 | { |
michael@0 | 867 | nsresult rv; |
michael@0 | 868 | |
michael@0 | 869 | rv = stream->Read32(&seg.mPos); |
michael@0 | 870 | if (NS_FAILED(rv)) return rv; |
michael@0 | 871 | |
michael@0 | 872 | rv = stream->Read32((uint32_t *) &seg.mLen); |
michael@0 | 873 | if (NS_FAILED(rv)) return rv; |
michael@0 | 874 | |
michael@0 | 875 | return NS_OK; |
michael@0 | 876 | } |
michael@0 | 877 | |
michael@0 | 878 | nsresult |
michael@0 | 879 | nsStandardURL::WriteSegment(nsIBinaryOutputStream *stream, const URLSegment &seg) |
michael@0 | 880 | { |
michael@0 | 881 | nsresult rv; |
michael@0 | 882 | |
michael@0 | 883 | rv = stream->Write32(seg.mPos); |
michael@0 | 884 | if (NS_FAILED(rv)) return rv; |
michael@0 | 885 | |
michael@0 | 886 | rv = stream->Write32(uint32_t(seg.mLen)); |
michael@0 | 887 | if (NS_FAILED(rv)) return rv; |
michael@0 | 888 | |
michael@0 | 889 | return NS_OK; |
michael@0 | 890 | } |
michael@0 | 891 | |
michael@0 | 892 | /* static */ void |
michael@0 | 893 | nsStandardURL::PrefsChanged(nsIPrefBranch *prefs, const char *pref) |
michael@0 | 894 | { |
michael@0 | 895 | bool val; |
michael@0 | 896 | |
michael@0 | 897 | LOG(("nsStandardURL::PrefsChanged [pref=%s]\n", pref)); |
michael@0 | 898 | |
michael@0 | 899 | #define PREF_CHANGED(p) ((pref == nullptr) || !strcmp(pref, p)) |
michael@0 | 900 | #define GOT_PREF(p, b) (NS_SUCCEEDED(prefs->GetBoolPref(p, &b))) |
michael@0 | 901 | |
michael@0 | 902 | if (PREF_CHANGED(NS_NET_PREF_ESCAPEUTF8)) { |
michael@0 | 903 | if (GOT_PREF(NS_NET_PREF_ESCAPEUTF8, val)) |
michael@0 | 904 | gEscapeUTF8 = val; |
michael@0 | 905 | LOG(("escape UTF-8 %s\n", gEscapeUTF8 ? "enabled" : "disabled")); |
michael@0 | 906 | } |
michael@0 | 907 | |
michael@0 | 908 | if (PREF_CHANGED(NS_NET_PREF_ALWAYSENCODEINUTF8)) { |
michael@0 | 909 | if (GOT_PREF(NS_NET_PREF_ALWAYSENCODEINUTF8, val)) |
michael@0 | 910 | gAlwaysEncodeInUTF8 = val; |
michael@0 | 911 | LOG(("encode in UTF-8 %s\n", gAlwaysEncodeInUTF8 ? "enabled" : "disabled")); |
michael@0 | 912 | } |
michael@0 | 913 | #undef PREF_CHANGED |
michael@0 | 914 | #undef GOT_PREF |
michael@0 | 915 | } |
michael@0 | 916 | |
michael@0 | 917 | //---------------------------------------------------------------------------- |
michael@0 | 918 | // nsStandardURL::nsISupports |
michael@0 | 919 | //---------------------------------------------------------------------------- |
michael@0 | 920 | |
michael@0 | 921 | NS_IMPL_ADDREF(nsStandardURL) |
michael@0 | 922 | NS_IMPL_RELEASE(nsStandardURL) |
michael@0 | 923 | |
michael@0 | 924 | NS_INTERFACE_MAP_BEGIN(nsStandardURL) |
michael@0 | 925 | NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStandardURL) |
michael@0 | 926 | NS_INTERFACE_MAP_ENTRY(nsIURI) |
michael@0 | 927 | NS_INTERFACE_MAP_ENTRY(nsIURL) |
michael@0 | 928 | NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIFileURL, mSupportsFileURL) |
michael@0 | 929 | NS_INTERFACE_MAP_ENTRY(nsIStandardURL) |
michael@0 | 930 | NS_INTERFACE_MAP_ENTRY(nsISerializable) |
michael@0 | 931 | NS_INTERFACE_MAP_ENTRY(nsIClassInfo) |
michael@0 | 932 | NS_INTERFACE_MAP_ENTRY(nsIMutable) |
michael@0 | 933 | NS_INTERFACE_MAP_ENTRY(nsIIPCSerializableURI) |
michael@0 | 934 | // see nsStandardURL::Equals |
michael@0 | 935 | if (aIID.Equals(kThisImplCID)) |
michael@0 | 936 | foundInterface = static_cast<nsIURI *>(this); |
michael@0 | 937 | else |
michael@0 | 938 | NS_INTERFACE_MAP_ENTRY(nsISizeOf) |
michael@0 | 939 | NS_INTERFACE_MAP_END |
michael@0 | 940 | |
michael@0 | 941 | //---------------------------------------------------------------------------- |
michael@0 | 942 | // nsStandardURL::nsIURI |
michael@0 | 943 | //---------------------------------------------------------------------------- |
michael@0 | 944 | |
michael@0 | 945 | // result may contain unescaped UTF-8 characters |
michael@0 | 946 | NS_IMETHODIMP |
michael@0 | 947 | nsStandardURL::GetSpec(nsACString &result) |
michael@0 | 948 | { |
michael@0 | 949 | result = mSpec; |
michael@0 | 950 | return NS_OK; |
michael@0 | 951 | } |
michael@0 | 952 | |
michael@0 | 953 | // result may contain unescaped UTF-8 characters |
michael@0 | 954 | NS_IMETHODIMP |
michael@0 | 955 | nsStandardURL::GetSpecIgnoringRef(nsACString &result) |
michael@0 | 956 | { |
michael@0 | 957 | // URI without ref is 0 to one char before ref |
michael@0 | 958 | if (mRef.mLen >= 0) { |
michael@0 | 959 | URLSegment noRef(0, mRef.mPos - 1); |
michael@0 | 960 | |
michael@0 | 961 | result = Segment(noRef); |
michael@0 | 962 | } else { |
michael@0 | 963 | result = mSpec; |
michael@0 | 964 | } |
michael@0 | 965 | return NS_OK; |
michael@0 | 966 | } |
michael@0 | 967 | |
michael@0 | 968 | // result may contain unescaped UTF-8 characters |
michael@0 | 969 | NS_IMETHODIMP |
michael@0 | 970 | nsStandardURL::GetPrePath(nsACString &result) |
michael@0 | 971 | { |
michael@0 | 972 | result = Prepath(); |
michael@0 | 973 | return NS_OK; |
michael@0 | 974 | } |
michael@0 | 975 | |
michael@0 | 976 | // result is strictly US-ASCII |
michael@0 | 977 | NS_IMETHODIMP |
michael@0 | 978 | nsStandardURL::GetScheme(nsACString &result) |
michael@0 | 979 | { |
michael@0 | 980 | result = Scheme(); |
michael@0 | 981 | return NS_OK; |
michael@0 | 982 | } |
michael@0 | 983 | |
michael@0 | 984 | // result may contain unescaped UTF-8 characters |
michael@0 | 985 | NS_IMETHODIMP |
michael@0 | 986 | nsStandardURL::GetUserPass(nsACString &result) |
michael@0 | 987 | { |
michael@0 | 988 | result = Userpass(); |
michael@0 | 989 | return NS_OK; |
michael@0 | 990 | } |
michael@0 | 991 | |
michael@0 | 992 | // result may contain unescaped UTF-8 characters |
michael@0 | 993 | NS_IMETHODIMP |
michael@0 | 994 | nsStandardURL::GetUsername(nsACString &result) |
michael@0 | 995 | { |
michael@0 | 996 | result = Username(); |
michael@0 | 997 | return NS_OK; |
michael@0 | 998 | } |
michael@0 | 999 | |
michael@0 | 1000 | // result may contain unescaped UTF-8 characters |
michael@0 | 1001 | NS_IMETHODIMP |
michael@0 | 1002 | nsStandardURL::GetPassword(nsACString &result) |
michael@0 | 1003 | { |
michael@0 | 1004 | result = Password(); |
michael@0 | 1005 | return NS_OK; |
michael@0 | 1006 | } |
michael@0 | 1007 | |
michael@0 | 1008 | NS_IMETHODIMP |
michael@0 | 1009 | nsStandardURL::GetHostPort(nsACString &result) |
michael@0 | 1010 | { |
michael@0 | 1011 | result = Hostport(); |
michael@0 | 1012 | return NS_OK; |
michael@0 | 1013 | } |
michael@0 | 1014 | |
michael@0 | 1015 | NS_IMETHODIMP |
michael@0 | 1016 | nsStandardURL::GetHost(nsACString &result) |
michael@0 | 1017 | { |
michael@0 | 1018 | result = Host(); |
michael@0 | 1019 | return NS_OK; |
michael@0 | 1020 | } |
michael@0 | 1021 | |
michael@0 | 1022 | NS_IMETHODIMP |
michael@0 | 1023 | nsStandardURL::GetPort(int32_t *result) |
michael@0 | 1024 | { |
michael@0 | 1025 | *result = mPort; |
michael@0 | 1026 | return NS_OK; |
michael@0 | 1027 | } |
michael@0 | 1028 | |
michael@0 | 1029 | // result may contain unescaped UTF-8 characters |
michael@0 | 1030 | NS_IMETHODIMP |
michael@0 | 1031 | nsStandardURL::GetPath(nsACString &result) |
michael@0 | 1032 | { |
michael@0 | 1033 | result = Path(); |
michael@0 | 1034 | return NS_OK; |
michael@0 | 1035 | } |
michael@0 | 1036 | |
michael@0 | 1037 | // result is ASCII |
michael@0 | 1038 | NS_IMETHODIMP |
michael@0 | 1039 | nsStandardURL::GetAsciiSpec(nsACString &result) |
michael@0 | 1040 | { |
michael@0 | 1041 | if (mSpecEncoding == eEncoding_Unknown) { |
michael@0 | 1042 | if (IsASCII(mSpec)) |
michael@0 | 1043 | mSpecEncoding = eEncoding_ASCII; |
michael@0 | 1044 | else |
michael@0 | 1045 | mSpecEncoding = eEncoding_UTF8; |
michael@0 | 1046 | } |
michael@0 | 1047 | |
michael@0 | 1048 | if (mSpecEncoding == eEncoding_ASCII) { |
michael@0 | 1049 | result = mSpec; |
michael@0 | 1050 | return NS_OK; |
michael@0 | 1051 | } |
michael@0 | 1052 | |
michael@0 | 1053 | // try to guess the capacity required for result... |
michael@0 | 1054 | result.SetCapacity(mSpec.Length() + std::min<uint32_t>(32, mSpec.Length()/10)); |
michael@0 | 1055 | |
michael@0 | 1056 | result = Substring(mSpec, 0, mScheme.mLen + 3); |
michael@0 | 1057 | |
michael@0 | 1058 | NS_EscapeURL(Userpass(true), esc_OnlyNonASCII | esc_AlwaysCopy, result); |
michael@0 | 1059 | |
michael@0 | 1060 | // get escaped host |
michael@0 | 1061 | nsAutoCString escHostport; |
michael@0 | 1062 | if (mHost.mLen > 0) { |
michael@0 | 1063 | // this doesn't fail |
michael@0 | 1064 | (void) GetAsciiHost(escHostport); |
michael@0 | 1065 | |
michael@0 | 1066 | // escHostport = "hostA" + ":port" |
michael@0 | 1067 | uint32_t pos = mHost.mPos + mHost.mLen; |
michael@0 | 1068 | if (pos < mPath.mPos) |
michael@0 | 1069 | escHostport += Substring(mSpec, pos, mPath.mPos - pos); |
michael@0 | 1070 | } |
michael@0 | 1071 | result += escHostport; |
michael@0 | 1072 | |
michael@0 | 1073 | NS_EscapeURL(Path(), esc_OnlyNonASCII | esc_AlwaysCopy, result); |
michael@0 | 1074 | return NS_OK; |
michael@0 | 1075 | } |
michael@0 | 1076 | |
michael@0 | 1077 | // result is ASCII |
michael@0 | 1078 | NS_IMETHODIMP |
michael@0 | 1079 | nsStandardURL::GetAsciiHost(nsACString &result) |
michael@0 | 1080 | { |
michael@0 | 1081 | if (mHostEncoding == eEncoding_ASCII) { |
michael@0 | 1082 | result = Host(); |
michael@0 | 1083 | return NS_OK; |
michael@0 | 1084 | } |
michael@0 | 1085 | |
michael@0 | 1086 | // perhaps we have it cached... |
michael@0 | 1087 | if (mHostA) { |
michael@0 | 1088 | result = mHostA; |
michael@0 | 1089 | return NS_OK; |
michael@0 | 1090 | } |
michael@0 | 1091 | |
michael@0 | 1092 | if (gIDN) { |
michael@0 | 1093 | nsresult rv; |
michael@0 | 1094 | rv = gIDN->ConvertUTF8toACE(Host(), result); |
michael@0 | 1095 | if (NS_SUCCEEDED(rv)) { |
michael@0 | 1096 | mHostA = ToNewCString(result); |
michael@0 | 1097 | return NS_OK; |
michael@0 | 1098 | } |
michael@0 | 1099 | NS_WARNING("nsIDNService::ConvertUTF8toACE failed"); |
michael@0 | 1100 | } |
michael@0 | 1101 | |
michael@0 | 1102 | // something went wrong... guess all we can do is URL escape :-/ |
michael@0 | 1103 | NS_EscapeURL(Host(), esc_OnlyNonASCII | esc_AlwaysCopy, result); |
michael@0 | 1104 | return NS_OK; |
michael@0 | 1105 | } |
michael@0 | 1106 | |
michael@0 | 1107 | NS_IMETHODIMP |
michael@0 | 1108 | nsStandardURL::GetOriginCharset(nsACString &result) |
michael@0 | 1109 | { |
michael@0 | 1110 | if (mOriginCharset.IsEmpty()) |
michael@0 | 1111 | result.AssignLiteral("UTF-8"); |
michael@0 | 1112 | else |
michael@0 | 1113 | result = mOriginCharset; |
michael@0 | 1114 | return NS_OK; |
michael@0 | 1115 | } |
michael@0 | 1116 | |
michael@0 | 1117 | NS_IMETHODIMP |
michael@0 | 1118 | nsStandardURL::SetSpec(const nsACString &input) |
michael@0 | 1119 | { |
michael@0 | 1120 | ENSURE_MUTABLE(); |
michael@0 | 1121 | |
michael@0 | 1122 | const nsPromiseFlatCString &flat = PromiseFlatCString(input); |
michael@0 | 1123 | const char *spec = flat.get(); |
michael@0 | 1124 | int32_t specLength = flat.Length(); |
michael@0 | 1125 | |
michael@0 | 1126 | LOG(("nsStandardURL::SetSpec [spec=%s]\n", spec)); |
michael@0 | 1127 | |
michael@0 | 1128 | Clear(); |
michael@0 | 1129 | |
michael@0 | 1130 | if (!spec || !*spec) |
michael@0 | 1131 | return NS_OK; |
michael@0 | 1132 | |
michael@0 | 1133 | // filter out unexpected chars "\r\n\t" if necessary |
michael@0 | 1134 | nsAutoCString buf1; |
michael@0 | 1135 | if (net_FilterURIString(spec, buf1)) { |
michael@0 | 1136 | spec = buf1.get(); |
michael@0 | 1137 | specLength = buf1.Length(); |
michael@0 | 1138 | } |
michael@0 | 1139 | |
michael@0 | 1140 | // parse the given URL... |
michael@0 | 1141 | nsresult rv = ParseURL(spec, specLength); |
michael@0 | 1142 | if (NS_SUCCEEDED(rv)) { |
michael@0 | 1143 | // finally, use the URLSegment member variables to build a normalized |
michael@0 | 1144 | // copy of |spec| |
michael@0 | 1145 | rv = BuildNormalizedSpec(spec); |
michael@0 | 1146 | } |
michael@0 | 1147 | |
michael@0 | 1148 | if (NS_FAILED(rv)) { |
michael@0 | 1149 | Clear(); |
michael@0 | 1150 | return rv; |
michael@0 | 1151 | } |
michael@0 | 1152 | |
michael@0 | 1153 | #if defined(PR_LOGGING) |
michael@0 | 1154 | if (LOG_ENABLED()) { |
michael@0 | 1155 | LOG((" spec = %s\n", mSpec.get())); |
michael@0 | 1156 | LOG((" port = %d\n", mPort)); |
michael@0 | 1157 | LOG((" scheme = (%u,%d)\n", mScheme.mPos, mScheme.mLen)); |
michael@0 | 1158 | LOG((" authority = (%u,%d)\n", mAuthority.mPos, mAuthority.mLen)); |
michael@0 | 1159 | LOG((" username = (%u,%d)\n", mUsername.mPos, mUsername.mLen)); |
michael@0 | 1160 | LOG((" password = (%u,%d)\n", mPassword.mPos, mPassword.mLen)); |
michael@0 | 1161 | LOG((" hostname = (%u,%d)\n", mHost.mPos, mHost.mLen)); |
michael@0 | 1162 | LOG((" path = (%u,%d)\n", mPath.mPos, mPath.mLen)); |
michael@0 | 1163 | LOG((" filepath = (%u,%d)\n", mFilepath.mPos, mFilepath.mLen)); |
michael@0 | 1164 | LOG((" directory = (%u,%d)\n", mDirectory.mPos, mDirectory.mLen)); |
michael@0 | 1165 | LOG((" basename = (%u,%d)\n", mBasename.mPos, mBasename.mLen)); |
michael@0 | 1166 | LOG((" extension = (%u,%d)\n", mExtension.mPos, mExtension.mLen)); |
michael@0 | 1167 | LOG((" query = (%u,%d)\n", mQuery.mPos, mQuery.mLen)); |
michael@0 | 1168 | LOG((" ref = (%u,%d)\n", mRef.mPos, mRef.mLen)); |
michael@0 | 1169 | } |
michael@0 | 1170 | #endif |
michael@0 | 1171 | return rv; |
michael@0 | 1172 | } |
michael@0 | 1173 | |
michael@0 | 1174 | NS_IMETHODIMP |
michael@0 | 1175 | nsStandardURL::SetScheme(const nsACString &input) |
michael@0 | 1176 | { |
michael@0 | 1177 | ENSURE_MUTABLE(); |
michael@0 | 1178 | |
michael@0 | 1179 | const nsPromiseFlatCString &scheme = PromiseFlatCString(input); |
michael@0 | 1180 | |
michael@0 | 1181 | LOG(("nsStandardURL::SetScheme [scheme=%s]\n", scheme.get())); |
michael@0 | 1182 | |
michael@0 | 1183 | if (scheme.IsEmpty()) { |
michael@0 | 1184 | NS_WARNING("cannot remove the scheme from an url"); |
michael@0 | 1185 | return NS_ERROR_UNEXPECTED; |
michael@0 | 1186 | } |
michael@0 | 1187 | if (mScheme.mLen < 0) { |
michael@0 | 1188 | NS_WARNING("uninitialized"); |
michael@0 | 1189 | return NS_ERROR_NOT_INITIALIZED; |
michael@0 | 1190 | } |
michael@0 | 1191 | |
michael@0 | 1192 | if (!net_IsValidScheme(scheme)) { |
michael@0 | 1193 | NS_WARNING("the given url scheme contains invalid characters"); |
michael@0 | 1194 | return NS_ERROR_UNEXPECTED; |
michael@0 | 1195 | } |
michael@0 | 1196 | |
michael@0 | 1197 | InvalidateCache(); |
michael@0 | 1198 | |
michael@0 | 1199 | int32_t shift = ReplaceSegment(mScheme.mPos, mScheme.mLen, scheme); |
michael@0 | 1200 | |
michael@0 | 1201 | if (shift) { |
michael@0 | 1202 | mScheme.mLen = scheme.Length(); |
michael@0 | 1203 | ShiftFromAuthority(shift); |
michael@0 | 1204 | } |
michael@0 | 1205 | |
michael@0 | 1206 | // ensure new scheme is lowercase |
michael@0 | 1207 | // |
michael@0 | 1208 | // XXX the string code unfortunately doesn't provide a ToLowerCase |
michael@0 | 1209 | // that operates on a substring. |
michael@0 | 1210 | net_ToLowerCase((char *) mSpec.get(), mScheme.mLen); |
michael@0 | 1211 | return NS_OK; |
michael@0 | 1212 | } |
michael@0 | 1213 | |
michael@0 | 1214 | NS_IMETHODIMP |
michael@0 | 1215 | nsStandardURL::SetUserPass(const nsACString &input) |
michael@0 | 1216 | { |
michael@0 | 1217 | ENSURE_MUTABLE(); |
michael@0 | 1218 | |
michael@0 | 1219 | const nsPromiseFlatCString &userpass = PromiseFlatCString(input); |
michael@0 | 1220 | |
michael@0 | 1221 | LOG(("nsStandardURL::SetUserPass [userpass=%s]\n", userpass.get())); |
michael@0 | 1222 | |
michael@0 | 1223 | if (mURLType == URLTYPE_NO_AUTHORITY) { |
michael@0 | 1224 | if (userpass.IsEmpty()) |
michael@0 | 1225 | return NS_OK; |
michael@0 | 1226 | NS_WARNING("cannot set user:pass on no-auth url"); |
michael@0 | 1227 | return NS_ERROR_UNEXPECTED; |
michael@0 | 1228 | } |
michael@0 | 1229 | if (mAuthority.mLen < 0) { |
michael@0 | 1230 | NS_WARNING("uninitialized"); |
michael@0 | 1231 | return NS_ERROR_NOT_INITIALIZED; |
michael@0 | 1232 | } |
michael@0 | 1233 | |
michael@0 | 1234 | InvalidateCache(); |
michael@0 | 1235 | |
michael@0 | 1236 | if (userpass.IsEmpty()) { |
michael@0 | 1237 | // remove user:pass |
michael@0 | 1238 | if (mUsername.mLen > 0) { |
michael@0 | 1239 | if (mPassword.mLen > 0) |
michael@0 | 1240 | mUsername.mLen += (mPassword.mLen + 1); |
michael@0 | 1241 | mUsername.mLen++; |
michael@0 | 1242 | mSpec.Cut(mUsername.mPos, mUsername.mLen); |
michael@0 | 1243 | mAuthority.mLen -= mUsername.mLen; |
michael@0 | 1244 | ShiftFromHost(-mUsername.mLen); |
michael@0 | 1245 | mUsername.mLen = -1; |
michael@0 | 1246 | mPassword.mLen = -1; |
michael@0 | 1247 | } |
michael@0 | 1248 | return NS_OK; |
michael@0 | 1249 | } |
michael@0 | 1250 | |
michael@0 | 1251 | NS_ASSERTION(mHost.mLen >= 0, "uninitialized"); |
michael@0 | 1252 | |
michael@0 | 1253 | nsresult rv; |
michael@0 | 1254 | uint32_t usernamePos, passwordPos; |
michael@0 | 1255 | int32_t usernameLen, passwordLen; |
michael@0 | 1256 | |
michael@0 | 1257 | rv = mParser->ParseUserInfo(userpass.get(), userpass.Length(), |
michael@0 | 1258 | &usernamePos, &usernameLen, |
michael@0 | 1259 | &passwordPos, &passwordLen); |
michael@0 | 1260 | if (NS_FAILED(rv)) return rv; |
michael@0 | 1261 | |
michael@0 | 1262 | // build new user:pass in |buf| |
michael@0 | 1263 | nsAutoCString buf; |
michael@0 | 1264 | if (usernameLen > 0) { |
michael@0 | 1265 | GET_SEGMENT_ENCODER(encoder); |
michael@0 | 1266 | bool ignoredOut; |
michael@0 | 1267 | usernameLen = encoder.EncodeSegmentCount(userpass.get(), |
michael@0 | 1268 | URLSegment(usernamePos, |
michael@0 | 1269 | usernameLen), |
michael@0 | 1270 | esc_Username | esc_AlwaysCopy, |
michael@0 | 1271 | buf, ignoredOut); |
michael@0 | 1272 | if (passwordLen >= 0) { |
michael@0 | 1273 | buf.Append(':'); |
michael@0 | 1274 | passwordLen = encoder.EncodeSegmentCount(userpass.get(), |
michael@0 | 1275 | URLSegment(passwordPos, |
michael@0 | 1276 | passwordLen), |
michael@0 | 1277 | esc_Password | |
michael@0 | 1278 | esc_AlwaysCopy, buf, |
michael@0 | 1279 | ignoredOut); |
michael@0 | 1280 | } |
michael@0 | 1281 | if (mUsername.mLen < 0) |
michael@0 | 1282 | buf.Append('@'); |
michael@0 | 1283 | } |
michael@0 | 1284 | |
michael@0 | 1285 | uint32_t shift = 0; |
michael@0 | 1286 | |
michael@0 | 1287 | if (mUsername.mLen < 0) { |
michael@0 | 1288 | // no existing user:pass |
michael@0 | 1289 | if (!buf.IsEmpty()) { |
michael@0 | 1290 | mSpec.Insert(buf, mHost.mPos); |
michael@0 | 1291 | mUsername.mPos = mHost.mPos; |
michael@0 | 1292 | shift = buf.Length(); |
michael@0 | 1293 | } |
michael@0 | 1294 | } |
michael@0 | 1295 | else { |
michael@0 | 1296 | // replace existing user:pass |
michael@0 | 1297 | uint32_t userpassLen = mUsername.mLen; |
michael@0 | 1298 | if (mPassword.mLen >= 0) |
michael@0 | 1299 | userpassLen += (mPassword.mLen + 1); |
michael@0 | 1300 | mSpec.Replace(mUsername.mPos, userpassLen, buf); |
michael@0 | 1301 | shift = buf.Length() - userpassLen; |
michael@0 | 1302 | } |
michael@0 | 1303 | if (shift) { |
michael@0 | 1304 | ShiftFromHost(shift); |
michael@0 | 1305 | mAuthority.mLen += shift; |
michael@0 | 1306 | } |
michael@0 | 1307 | // update positions and lengths |
michael@0 | 1308 | mUsername.mLen = usernameLen; |
michael@0 | 1309 | mPassword.mLen = passwordLen; |
michael@0 | 1310 | if (passwordLen) |
michael@0 | 1311 | mPassword.mPos = mUsername.mPos + mUsername.mLen + 1; |
michael@0 | 1312 | return NS_OK; |
michael@0 | 1313 | } |
michael@0 | 1314 | |
michael@0 | 1315 | NS_IMETHODIMP |
michael@0 | 1316 | nsStandardURL::SetUsername(const nsACString &input) |
michael@0 | 1317 | { |
michael@0 | 1318 | ENSURE_MUTABLE(); |
michael@0 | 1319 | |
michael@0 | 1320 | const nsPromiseFlatCString &username = PromiseFlatCString(input); |
michael@0 | 1321 | |
michael@0 | 1322 | LOG(("nsStandardURL::SetUsername [username=%s]\n", username.get())); |
michael@0 | 1323 | |
michael@0 | 1324 | if (mURLType == URLTYPE_NO_AUTHORITY) { |
michael@0 | 1325 | if (username.IsEmpty()) |
michael@0 | 1326 | return NS_OK; |
michael@0 | 1327 | NS_WARNING("cannot set username on no-auth url"); |
michael@0 | 1328 | return NS_ERROR_UNEXPECTED; |
michael@0 | 1329 | } |
michael@0 | 1330 | |
michael@0 | 1331 | if (username.IsEmpty()) |
michael@0 | 1332 | return SetUserPass(username); |
michael@0 | 1333 | |
michael@0 | 1334 | InvalidateCache(); |
michael@0 | 1335 | |
michael@0 | 1336 | // escape username if necessary |
michael@0 | 1337 | nsAutoCString buf; |
michael@0 | 1338 | GET_SEGMENT_ENCODER(encoder); |
michael@0 | 1339 | const nsACString &escUsername = |
michael@0 | 1340 | encoder.EncodeSegment(username, esc_Username, buf); |
michael@0 | 1341 | |
michael@0 | 1342 | int32_t shift; |
michael@0 | 1343 | |
michael@0 | 1344 | if (mUsername.mLen < 0) { |
michael@0 | 1345 | mUsername.mPos = mAuthority.mPos; |
michael@0 | 1346 | mSpec.Insert(escUsername + NS_LITERAL_CSTRING("@"), mUsername.mPos); |
michael@0 | 1347 | shift = escUsername.Length() + 1; |
michael@0 | 1348 | } |
michael@0 | 1349 | else |
michael@0 | 1350 | shift = ReplaceSegment(mUsername.mPos, mUsername.mLen, escUsername); |
michael@0 | 1351 | |
michael@0 | 1352 | if (shift) { |
michael@0 | 1353 | mUsername.mLen = escUsername.Length(); |
michael@0 | 1354 | mAuthority.mLen += shift; |
michael@0 | 1355 | ShiftFromPassword(shift); |
michael@0 | 1356 | } |
michael@0 | 1357 | return NS_OK; |
michael@0 | 1358 | } |
michael@0 | 1359 | |
michael@0 | 1360 | NS_IMETHODIMP |
michael@0 | 1361 | nsStandardURL::SetPassword(const nsACString &input) |
michael@0 | 1362 | { |
michael@0 | 1363 | ENSURE_MUTABLE(); |
michael@0 | 1364 | |
michael@0 | 1365 | const nsPromiseFlatCString &password = PromiseFlatCString(input); |
michael@0 | 1366 | |
michael@0 | 1367 | LOG(("nsStandardURL::SetPassword [password=%s]\n", password.get())); |
michael@0 | 1368 | |
michael@0 | 1369 | if (mURLType == URLTYPE_NO_AUTHORITY) { |
michael@0 | 1370 | if (password.IsEmpty()) |
michael@0 | 1371 | return NS_OK; |
michael@0 | 1372 | NS_WARNING("cannot set password on no-auth url"); |
michael@0 | 1373 | return NS_ERROR_UNEXPECTED; |
michael@0 | 1374 | } |
michael@0 | 1375 | if (mUsername.mLen <= 0) { |
michael@0 | 1376 | NS_WARNING("cannot set password without existing username"); |
michael@0 | 1377 | return NS_ERROR_FAILURE; |
michael@0 | 1378 | } |
michael@0 | 1379 | |
michael@0 | 1380 | InvalidateCache(); |
michael@0 | 1381 | |
michael@0 | 1382 | if (password.IsEmpty()) { |
michael@0 | 1383 | if (mPassword.mLen >= 0) { |
michael@0 | 1384 | // cut(":password") |
michael@0 | 1385 | mSpec.Cut(mPassword.mPos - 1, mPassword.mLen + 1); |
michael@0 | 1386 | ShiftFromHost(-(mPassword.mLen + 1)); |
michael@0 | 1387 | mAuthority.mLen -= (mPassword.mLen + 1); |
michael@0 | 1388 | mPassword.mLen = -1; |
michael@0 | 1389 | } |
michael@0 | 1390 | return NS_OK; |
michael@0 | 1391 | } |
michael@0 | 1392 | |
michael@0 | 1393 | // escape password if necessary |
michael@0 | 1394 | nsAutoCString buf; |
michael@0 | 1395 | GET_SEGMENT_ENCODER(encoder); |
michael@0 | 1396 | const nsACString &escPassword = |
michael@0 | 1397 | encoder.EncodeSegment(password, esc_Password, buf); |
michael@0 | 1398 | |
michael@0 | 1399 | int32_t shift; |
michael@0 | 1400 | |
michael@0 | 1401 | if (mPassword.mLen < 0) { |
michael@0 | 1402 | mPassword.mPos = mUsername.mPos + mUsername.mLen + 1; |
michael@0 | 1403 | mSpec.Insert(NS_LITERAL_CSTRING(":") + escPassword, mPassword.mPos - 1); |
michael@0 | 1404 | shift = escPassword.Length() + 1; |
michael@0 | 1405 | } |
michael@0 | 1406 | else |
michael@0 | 1407 | shift = ReplaceSegment(mPassword.mPos, mPassword.mLen, escPassword); |
michael@0 | 1408 | |
michael@0 | 1409 | if (shift) { |
michael@0 | 1410 | mPassword.mLen = escPassword.Length(); |
michael@0 | 1411 | mAuthority.mLen += shift; |
michael@0 | 1412 | ShiftFromHost(shift); |
michael@0 | 1413 | } |
michael@0 | 1414 | return NS_OK; |
michael@0 | 1415 | } |
michael@0 | 1416 | |
michael@0 | 1417 | void |
michael@0 | 1418 | nsStandardURL::FindHostLimit(nsACString::const_iterator& aStart, |
michael@0 | 1419 | nsACString::const_iterator& aEnd) |
michael@0 | 1420 | { |
michael@0 | 1421 | for (int32_t i = 0; gHostLimitDigits[i]; ++i) { |
michael@0 | 1422 | nsACString::const_iterator c(aStart); |
michael@0 | 1423 | if (FindCharInReadable(gHostLimitDigits[i], c, aEnd)) { |
michael@0 | 1424 | aEnd = c; |
michael@0 | 1425 | } |
michael@0 | 1426 | } |
michael@0 | 1427 | } |
michael@0 | 1428 | |
michael@0 | 1429 | NS_IMETHODIMP |
michael@0 | 1430 | nsStandardURL::SetHostPort(const nsACString &aValue) |
michael@0 | 1431 | { |
michael@0 | 1432 | ENSURE_MUTABLE(); |
michael@0 | 1433 | |
michael@0 | 1434 | // We cannot simply call nsIURI::SetHost because that would treat the name as |
michael@0 | 1435 | // an IPv6 address (like http:://[server:443]/). We also cannot call |
michael@0 | 1436 | // nsIURI::SetHostPort because that isn't implemented. Sadfaces. |
michael@0 | 1437 | |
michael@0 | 1438 | // First set the hostname. |
michael@0 | 1439 | nsACString::const_iterator start, end; |
michael@0 | 1440 | aValue.BeginReading(start); |
michael@0 | 1441 | aValue.EndReading(end); |
michael@0 | 1442 | nsACString::const_iterator iter(start); |
michael@0 | 1443 | |
michael@0 | 1444 | FindHostLimit(iter, end); |
michael@0 | 1445 | FindCharInReadable(':', iter, end); |
michael@0 | 1446 | |
michael@0 | 1447 | nsresult rv = SetHost(Substring(start, iter)); |
michael@0 | 1448 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1449 | |
michael@0 | 1450 | // Also set the port if needed. |
michael@0 | 1451 | if (iter != end) { |
michael@0 | 1452 | iter++; |
michael@0 | 1453 | if (iter != end) { |
michael@0 | 1454 | nsCString portStr(Substring(iter, end)); |
michael@0 | 1455 | nsresult rv; |
michael@0 | 1456 | int32_t port = portStr.ToInteger(&rv); |
michael@0 | 1457 | if (NS_SUCCEEDED(rv)) { |
michael@0 | 1458 | rv = SetPort(port); |
michael@0 | 1459 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1460 | } |
michael@0 | 1461 | } |
michael@0 | 1462 | } |
michael@0 | 1463 | |
michael@0 | 1464 | return NS_OK; |
michael@0 | 1465 | } |
michael@0 | 1466 | |
michael@0 | 1467 | NS_IMETHODIMP |
michael@0 | 1468 | nsStandardURL::SetHost(const nsACString &input) |
michael@0 | 1469 | { |
michael@0 | 1470 | ENSURE_MUTABLE(); |
michael@0 | 1471 | |
michael@0 | 1472 | const nsPromiseFlatCString &hostname = PromiseFlatCString(input); |
michael@0 | 1473 | |
michael@0 | 1474 | nsACString::const_iterator start, end; |
michael@0 | 1475 | hostname.BeginReading(start); |
michael@0 | 1476 | hostname.EndReading(end); |
michael@0 | 1477 | |
michael@0 | 1478 | FindHostLimit(start, end); |
michael@0 | 1479 | |
michael@0 | 1480 | const nsCString flat(Substring(start, end)); |
michael@0 | 1481 | const char *host = flat.get(); |
michael@0 | 1482 | |
michael@0 | 1483 | LOG(("nsStandardURL::SetHost [host=%s]\n", host)); |
michael@0 | 1484 | |
michael@0 | 1485 | if (mURLType == URLTYPE_NO_AUTHORITY) { |
michael@0 | 1486 | if (flat.IsEmpty()) |
michael@0 | 1487 | return NS_OK; |
michael@0 | 1488 | NS_WARNING("cannot set host on no-auth url"); |
michael@0 | 1489 | return NS_ERROR_UNEXPECTED; |
michael@0 | 1490 | } else { |
michael@0 | 1491 | if (flat.IsEmpty()) { |
michael@0 | 1492 | // Setting an empty hostname is not allowed for |
michael@0 | 1493 | // URLTYPE_STANDARD and URLTYPE_AUTHORITY. |
michael@0 | 1494 | return NS_ERROR_UNEXPECTED; |
michael@0 | 1495 | } |
michael@0 | 1496 | } |
michael@0 | 1497 | |
michael@0 | 1498 | if (strlen(host) < flat.Length()) |
michael@0 | 1499 | return NS_ERROR_MALFORMED_URI; // found embedded null |
michael@0 | 1500 | |
michael@0 | 1501 | // For consistency with SetSpec/nsURLParsers, don't allow spaces |
michael@0 | 1502 | // in the hostname. |
michael@0 | 1503 | if (strchr(host, ' ')) |
michael@0 | 1504 | return NS_ERROR_MALFORMED_URI; |
michael@0 | 1505 | |
michael@0 | 1506 | InvalidateCache(); |
michael@0 | 1507 | mHostEncoding = eEncoding_ASCII; |
michael@0 | 1508 | |
michael@0 | 1509 | if (!*host) { |
michael@0 | 1510 | // remove existing hostname |
michael@0 | 1511 | if (mHost.mLen > 0) { |
michael@0 | 1512 | // remove entire authority |
michael@0 | 1513 | mSpec.Cut(mAuthority.mPos, mAuthority.mLen); |
michael@0 | 1514 | ShiftFromPath(-mAuthority.mLen); |
michael@0 | 1515 | mAuthority.mLen = 0; |
michael@0 | 1516 | mUsername.mLen = -1; |
michael@0 | 1517 | mPassword.mLen = -1; |
michael@0 | 1518 | mHost.mLen = -1; |
michael@0 | 1519 | mPort = -1; |
michael@0 | 1520 | } |
michael@0 | 1521 | return NS_OK; |
michael@0 | 1522 | } |
michael@0 | 1523 | |
michael@0 | 1524 | // handle IPv6 unescaped address literal |
michael@0 | 1525 | int32_t len; |
michael@0 | 1526 | nsAutoCString hostBuf; |
michael@0 | 1527 | if (EscapeIPv6(host, hostBuf)) { |
michael@0 | 1528 | host = hostBuf.get(); |
michael@0 | 1529 | len = hostBuf.Length(); |
michael@0 | 1530 | } |
michael@0 | 1531 | else if (NormalizeIDN(flat, hostBuf)) { |
michael@0 | 1532 | host = hostBuf.get(); |
michael@0 | 1533 | len = hostBuf.Length(); |
michael@0 | 1534 | } |
michael@0 | 1535 | else |
michael@0 | 1536 | len = flat.Length(); |
michael@0 | 1537 | |
michael@0 | 1538 | if (mHost.mLen < 0) { |
michael@0 | 1539 | int port_length = 0; |
michael@0 | 1540 | if (mPort != -1) { |
michael@0 | 1541 | nsAutoCString buf; |
michael@0 | 1542 | buf.Assign(':'); |
michael@0 | 1543 | buf.AppendInt(mPort); |
michael@0 | 1544 | port_length = buf.Length(); |
michael@0 | 1545 | } |
michael@0 | 1546 | if (mAuthority.mLen > 0) { |
michael@0 | 1547 | mHost.mPos = mAuthority.mPos + mAuthority.mLen - port_length; |
michael@0 | 1548 | mHost.mLen = 0; |
michael@0 | 1549 | } else if (mScheme.mLen > 0) { |
michael@0 | 1550 | mHost.mPos = mScheme.mPos + mScheme.mLen + 3; |
michael@0 | 1551 | mHost.mLen = 0; |
michael@0 | 1552 | } |
michael@0 | 1553 | } |
michael@0 | 1554 | |
michael@0 | 1555 | int32_t shift = ReplaceSegment(mHost.mPos, mHost.mLen, host, len); |
michael@0 | 1556 | |
michael@0 | 1557 | if (shift) { |
michael@0 | 1558 | mHost.mLen = len; |
michael@0 | 1559 | mAuthority.mLen += shift; |
michael@0 | 1560 | ShiftFromPath(shift); |
michael@0 | 1561 | } |
michael@0 | 1562 | |
michael@0 | 1563 | // Now canonicalize the host to lowercase |
michael@0 | 1564 | net_ToLowerCase(mSpec.BeginWriting() + mHost.mPos, mHost.mLen); |
michael@0 | 1565 | |
michael@0 | 1566 | return NS_OK; |
michael@0 | 1567 | } |
michael@0 | 1568 | |
michael@0 | 1569 | NS_IMETHODIMP |
michael@0 | 1570 | nsStandardURL::SetPort(int32_t port) |
michael@0 | 1571 | { |
michael@0 | 1572 | ENSURE_MUTABLE(); |
michael@0 | 1573 | |
michael@0 | 1574 | LOG(("nsStandardURL::SetPort [port=%d]\n", port)); |
michael@0 | 1575 | |
michael@0 | 1576 | if ((port == mPort) || (mPort == -1 && port == mDefaultPort)) |
michael@0 | 1577 | return NS_OK; |
michael@0 | 1578 | |
michael@0 | 1579 | // ports must be >= 0 |
michael@0 | 1580 | if (port < -1) // -1 == use default |
michael@0 | 1581 | return NS_ERROR_MALFORMED_URI; |
michael@0 | 1582 | |
michael@0 | 1583 | if (mURLType == URLTYPE_NO_AUTHORITY) { |
michael@0 | 1584 | NS_WARNING("cannot set port on no-auth url"); |
michael@0 | 1585 | return NS_ERROR_UNEXPECTED; |
michael@0 | 1586 | } |
michael@0 | 1587 | |
michael@0 | 1588 | InvalidateCache(); |
michael@0 | 1589 | |
michael@0 | 1590 | if (mPort == -1) { |
michael@0 | 1591 | // need to insert the port number in the URL spec |
michael@0 | 1592 | nsAutoCString buf; |
michael@0 | 1593 | buf.Assign(':'); |
michael@0 | 1594 | buf.AppendInt(port); |
michael@0 | 1595 | mSpec.Insert(buf, mAuthority.mPos + mAuthority.mLen); |
michael@0 | 1596 | mAuthority.mLen += buf.Length(); |
michael@0 | 1597 | ShiftFromPath(buf.Length()); |
michael@0 | 1598 | } |
michael@0 | 1599 | else if (port == -1 || port == mDefaultPort) { |
michael@0 | 1600 | // Don't allow mPort == mDefaultPort |
michael@0 | 1601 | port = -1; |
michael@0 | 1602 | |
michael@0 | 1603 | // compute length of the current port |
michael@0 | 1604 | nsAutoCString buf; |
michael@0 | 1605 | buf.Assign(':'); |
michael@0 | 1606 | buf.AppendInt(mPort); |
michael@0 | 1607 | |
michael@0 | 1608 | // need to remove the port number from the URL spec |
michael@0 | 1609 | uint32_t start = mAuthority.mPos + mAuthority.mLen - buf.Length(); |
michael@0 | 1610 | int32_t lengthToCut = buf.Length(); |
michael@0 | 1611 | mSpec.Cut(start, lengthToCut); |
michael@0 | 1612 | mAuthority.mLen -= lengthToCut; |
michael@0 | 1613 | ShiftFromPath(-lengthToCut); |
michael@0 | 1614 | } |
michael@0 | 1615 | else { |
michael@0 | 1616 | // need to replace the existing port |
michael@0 | 1617 | nsAutoCString buf; |
michael@0 | 1618 | buf.Assign(':'); |
michael@0 | 1619 | buf.AppendInt(mPort); |
michael@0 | 1620 | uint32_t start = mAuthority.mPos + mAuthority.mLen - buf.Length(); |
michael@0 | 1621 | uint32_t length = buf.Length(); |
michael@0 | 1622 | |
michael@0 | 1623 | buf.Assign(':'); |
michael@0 | 1624 | buf.AppendInt(port); |
michael@0 | 1625 | mSpec.Replace(start, length, buf); |
michael@0 | 1626 | if (buf.Length() != length) { |
michael@0 | 1627 | mAuthority.mLen += buf.Length() - length; |
michael@0 | 1628 | ShiftFromPath(buf.Length() - length); |
michael@0 | 1629 | } |
michael@0 | 1630 | } |
michael@0 | 1631 | |
michael@0 | 1632 | mPort = port; |
michael@0 | 1633 | return NS_OK; |
michael@0 | 1634 | } |
michael@0 | 1635 | |
michael@0 | 1636 | NS_IMETHODIMP |
michael@0 | 1637 | nsStandardURL::SetPath(const nsACString &input) |
michael@0 | 1638 | { |
michael@0 | 1639 | ENSURE_MUTABLE(); |
michael@0 | 1640 | |
michael@0 | 1641 | const nsPromiseFlatCString &path = PromiseFlatCString(input); |
michael@0 | 1642 | LOG(("nsStandardURL::SetPath [path=%s]\n", path.get())); |
michael@0 | 1643 | |
michael@0 | 1644 | InvalidateCache(); |
michael@0 | 1645 | |
michael@0 | 1646 | if (!path.IsEmpty()) { |
michael@0 | 1647 | nsAutoCString spec; |
michael@0 | 1648 | |
michael@0 | 1649 | spec.Assign(mSpec.get(), mPath.mPos); |
michael@0 | 1650 | if (path.First() != '/') |
michael@0 | 1651 | spec.Append('/'); |
michael@0 | 1652 | spec.Append(path); |
michael@0 | 1653 | |
michael@0 | 1654 | return SetSpec(spec); |
michael@0 | 1655 | } |
michael@0 | 1656 | else if (mPath.mLen >= 1) { |
michael@0 | 1657 | mSpec.Cut(mPath.mPos + 1, mPath.mLen - 1); |
michael@0 | 1658 | // these contain only a '/' |
michael@0 | 1659 | mPath.mLen = 1; |
michael@0 | 1660 | mDirectory.mLen = 1; |
michael@0 | 1661 | mFilepath.mLen = 1; |
michael@0 | 1662 | // these are no longer defined |
michael@0 | 1663 | mBasename.mLen = -1; |
michael@0 | 1664 | mExtension.mLen = -1; |
michael@0 | 1665 | mQuery.mLen = -1; |
michael@0 | 1666 | mRef.mLen = -1; |
michael@0 | 1667 | } |
michael@0 | 1668 | return NS_OK; |
michael@0 | 1669 | } |
michael@0 | 1670 | |
michael@0 | 1671 | NS_IMETHODIMP |
michael@0 | 1672 | nsStandardURL::Equals(nsIURI *unknownOther, bool *result) |
michael@0 | 1673 | { |
michael@0 | 1674 | return EqualsInternal(unknownOther, eHonorRef, result); |
michael@0 | 1675 | } |
michael@0 | 1676 | |
michael@0 | 1677 | NS_IMETHODIMP |
michael@0 | 1678 | nsStandardURL::EqualsExceptRef(nsIURI *unknownOther, bool *result) |
michael@0 | 1679 | { |
michael@0 | 1680 | return EqualsInternal(unknownOther, eIgnoreRef, result); |
michael@0 | 1681 | } |
michael@0 | 1682 | |
michael@0 | 1683 | nsresult |
michael@0 | 1684 | nsStandardURL::EqualsInternal(nsIURI *unknownOther, |
michael@0 | 1685 | nsStandardURL::RefHandlingEnum refHandlingMode, |
michael@0 | 1686 | bool *result) |
michael@0 | 1687 | { |
michael@0 | 1688 | NS_ENSURE_ARG_POINTER(unknownOther); |
michael@0 | 1689 | NS_PRECONDITION(result, "null pointer"); |
michael@0 | 1690 | |
michael@0 | 1691 | nsRefPtr<nsStandardURL> other; |
michael@0 | 1692 | nsresult rv = unknownOther->QueryInterface(kThisImplCID, |
michael@0 | 1693 | getter_AddRefs(other)); |
michael@0 | 1694 | if (NS_FAILED(rv)) { |
michael@0 | 1695 | *result = false; |
michael@0 | 1696 | return NS_OK; |
michael@0 | 1697 | } |
michael@0 | 1698 | |
michael@0 | 1699 | // First, check whether one URIs is an nsIFileURL while the other |
michael@0 | 1700 | // is not. If that's the case, they're different. |
michael@0 | 1701 | if (mSupportsFileURL != other->mSupportsFileURL) { |
michael@0 | 1702 | *result = false; |
michael@0 | 1703 | return NS_OK; |
michael@0 | 1704 | } |
michael@0 | 1705 | |
michael@0 | 1706 | // Next check parts of a URI that, if different, automatically make the |
michael@0 | 1707 | // URIs different |
michael@0 | 1708 | if (!SegmentIs(mScheme, other->mSpec.get(), other->mScheme) || |
michael@0 | 1709 | // Check for host manually, since conversion to file will |
michael@0 | 1710 | // ignore the host! |
michael@0 | 1711 | !SegmentIs(mHost, other->mSpec.get(), other->mHost) || |
michael@0 | 1712 | !SegmentIs(mQuery, other->mSpec.get(), other->mQuery) || |
michael@0 | 1713 | !SegmentIs(mUsername, other->mSpec.get(), other->mUsername) || |
michael@0 | 1714 | !SegmentIs(mPassword, other->mSpec.get(), other->mPassword) || |
michael@0 | 1715 | Port() != other->Port()) { |
michael@0 | 1716 | // No need to compare files or other URI parts -- these are different |
michael@0 | 1717 | // beasties |
michael@0 | 1718 | *result = false; |
michael@0 | 1719 | return NS_OK; |
michael@0 | 1720 | } |
michael@0 | 1721 | |
michael@0 | 1722 | if (refHandlingMode == eHonorRef && |
michael@0 | 1723 | !SegmentIs(mRef, other->mSpec.get(), other->mRef)) { |
michael@0 | 1724 | *result = false; |
michael@0 | 1725 | return NS_OK; |
michael@0 | 1726 | } |
michael@0 | 1727 | |
michael@0 | 1728 | // Then check for exact identity of URIs. If we have it, they're equal |
michael@0 | 1729 | if (SegmentIs(mDirectory, other->mSpec.get(), other->mDirectory) && |
michael@0 | 1730 | SegmentIs(mBasename, other->mSpec.get(), other->mBasename) && |
michael@0 | 1731 | SegmentIs(mExtension, other->mSpec.get(), other->mExtension)) { |
michael@0 | 1732 | *result = true; |
michael@0 | 1733 | return NS_OK; |
michael@0 | 1734 | } |
michael@0 | 1735 | |
michael@0 | 1736 | // At this point, the URIs are not identical, but they only differ in the |
michael@0 | 1737 | // directory/filename/extension. If these are file URLs, then get the |
michael@0 | 1738 | // corresponding file objects and compare those, since two filenames that |
michael@0 | 1739 | // differ, eg, only in case could still be equal. |
michael@0 | 1740 | if (mSupportsFileURL) { |
michael@0 | 1741 | // Assume not equal for failure cases... but failures in GetFile are |
michael@0 | 1742 | // really failures, more or less, so propagate them to caller. |
michael@0 | 1743 | *result = false; |
michael@0 | 1744 | |
michael@0 | 1745 | rv = EnsureFile(); |
michael@0 | 1746 | nsresult rv2 = other->EnsureFile(); |
michael@0 | 1747 | // special case for resource:// urls that don't resolve to files |
michael@0 | 1748 | if (rv == NS_ERROR_NO_INTERFACE && rv == rv2) |
michael@0 | 1749 | return NS_OK; |
michael@0 | 1750 | |
michael@0 | 1751 | if (NS_FAILED(rv)) { |
michael@0 | 1752 | LOG(("nsStandardURL::Equals [this=%p spec=%s] failed to ensure file", |
michael@0 | 1753 | this, mSpec.get())); |
michael@0 | 1754 | return rv; |
michael@0 | 1755 | } |
michael@0 | 1756 | NS_ASSERTION(mFile, "EnsureFile() lied!"); |
michael@0 | 1757 | rv = rv2; |
michael@0 | 1758 | if (NS_FAILED(rv)) { |
michael@0 | 1759 | LOG(("nsStandardURL::Equals [other=%p spec=%s] other failed to ensure file", |
michael@0 | 1760 | other.get(), other->mSpec.get())); |
michael@0 | 1761 | return rv; |
michael@0 | 1762 | } |
michael@0 | 1763 | NS_ASSERTION(other->mFile, "EnsureFile() lied!"); |
michael@0 | 1764 | return mFile->Equals(other->mFile, result); |
michael@0 | 1765 | } |
michael@0 | 1766 | |
michael@0 | 1767 | // The URLs are not identical, and they do not correspond to the |
michael@0 | 1768 | // same file, so they are different. |
michael@0 | 1769 | *result = false; |
michael@0 | 1770 | |
michael@0 | 1771 | return NS_OK; |
michael@0 | 1772 | } |
michael@0 | 1773 | |
michael@0 | 1774 | NS_IMETHODIMP |
michael@0 | 1775 | nsStandardURL::SchemeIs(const char *scheme, bool *result) |
michael@0 | 1776 | { |
michael@0 | 1777 | NS_PRECONDITION(result, "null pointer"); |
michael@0 | 1778 | |
michael@0 | 1779 | *result = SegmentIs(mScheme, scheme); |
michael@0 | 1780 | return NS_OK; |
michael@0 | 1781 | } |
michael@0 | 1782 | |
michael@0 | 1783 | /* virtual */ nsStandardURL* |
michael@0 | 1784 | nsStandardURL::StartClone() |
michael@0 | 1785 | { |
michael@0 | 1786 | nsStandardURL *clone = new nsStandardURL(); |
michael@0 | 1787 | return clone; |
michael@0 | 1788 | } |
michael@0 | 1789 | |
michael@0 | 1790 | NS_IMETHODIMP |
michael@0 | 1791 | nsStandardURL::Clone(nsIURI **result) |
michael@0 | 1792 | { |
michael@0 | 1793 | return CloneInternal(eHonorRef, result); |
michael@0 | 1794 | } |
michael@0 | 1795 | |
michael@0 | 1796 | |
michael@0 | 1797 | NS_IMETHODIMP |
michael@0 | 1798 | nsStandardURL::CloneIgnoringRef(nsIURI **result) |
michael@0 | 1799 | { |
michael@0 | 1800 | return CloneInternal(eIgnoreRef, result); |
michael@0 | 1801 | } |
michael@0 | 1802 | |
michael@0 | 1803 | nsresult |
michael@0 | 1804 | nsStandardURL::CloneInternal(nsStandardURL::RefHandlingEnum refHandlingMode, |
michael@0 | 1805 | nsIURI **result) |
michael@0 | 1806 | |
michael@0 | 1807 | { |
michael@0 | 1808 | nsRefPtr<nsStandardURL> clone = StartClone(); |
michael@0 | 1809 | if (!clone) |
michael@0 | 1810 | return NS_ERROR_OUT_OF_MEMORY; |
michael@0 | 1811 | |
michael@0 | 1812 | clone->mSpec = mSpec; |
michael@0 | 1813 | clone->mDefaultPort = mDefaultPort; |
michael@0 | 1814 | clone->mPort = mPort; |
michael@0 | 1815 | clone->mScheme = mScheme; |
michael@0 | 1816 | clone->mAuthority = mAuthority; |
michael@0 | 1817 | clone->mUsername = mUsername; |
michael@0 | 1818 | clone->mPassword = mPassword; |
michael@0 | 1819 | clone->mHost = mHost; |
michael@0 | 1820 | clone->mPath = mPath; |
michael@0 | 1821 | clone->mFilepath = mFilepath; |
michael@0 | 1822 | clone->mDirectory = mDirectory; |
michael@0 | 1823 | clone->mBasename = mBasename; |
michael@0 | 1824 | clone->mExtension = mExtension; |
michael@0 | 1825 | clone->mQuery = mQuery; |
michael@0 | 1826 | clone->mRef = mRef; |
michael@0 | 1827 | clone->mOriginCharset = mOriginCharset; |
michael@0 | 1828 | clone->mURLType = mURLType; |
michael@0 | 1829 | clone->mParser = mParser; |
michael@0 | 1830 | clone->mFile = mFile; |
michael@0 | 1831 | clone->mHostA = mHostA ? strdup(mHostA) : nullptr; |
michael@0 | 1832 | clone->mMutable = true; |
michael@0 | 1833 | clone->mSupportsFileURL = mSupportsFileURL; |
michael@0 | 1834 | clone->mHostEncoding = mHostEncoding; |
michael@0 | 1835 | clone->mSpecEncoding = mSpecEncoding; |
michael@0 | 1836 | |
michael@0 | 1837 | if (refHandlingMode == eIgnoreRef) { |
michael@0 | 1838 | clone->SetRef(EmptyCString()); |
michael@0 | 1839 | } |
michael@0 | 1840 | |
michael@0 | 1841 | clone.forget(result); |
michael@0 | 1842 | return NS_OK; |
michael@0 | 1843 | } |
michael@0 | 1844 | |
michael@0 | 1845 | NS_IMETHODIMP |
michael@0 | 1846 | nsStandardURL::Resolve(const nsACString &in, nsACString &out) |
michael@0 | 1847 | { |
michael@0 | 1848 | const nsPromiseFlatCString &flat = PromiseFlatCString(in); |
michael@0 | 1849 | const char *relpath = flat.get(); |
michael@0 | 1850 | |
michael@0 | 1851 | // filter out unexpected chars "\r\n\t" if necessary |
michael@0 | 1852 | nsAutoCString buf; |
michael@0 | 1853 | int32_t relpathLen; |
michael@0 | 1854 | if (net_FilterURIString(relpath, buf)) { |
michael@0 | 1855 | relpath = buf.get(); |
michael@0 | 1856 | relpathLen = buf.Length(); |
michael@0 | 1857 | } else |
michael@0 | 1858 | relpathLen = flat.Length(); |
michael@0 | 1859 | |
michael@0 | 1860 | char *result = nullptr; |
michael@0 | 1861 | |
michael@0 | 1862 | LOG(("nsStandardURL::Resolve [this=%p spec=%s relpath=%s]\n", |
michael@0 | 1863 | this, mSpec.get(), relpath)); |
michael@0 | 1864 | |
michael@0 | 1865 | NS_ASSERTION(mParser, "no parser: unitialized"); |
michael@0 | 1866 | |
michael@0 | 1867 | // NOTE: there is no need for this function to produce normalized |
michael@0 | 1868 | // output. normalization will occur when the result is used to |
michael@0 | 1869 | // initialize a nsStandardURL object. |
michael@0 | 1870 | |
michael@0 | 1871 | if (mScheme.mLen < 0) { |
michael@0 | 1872 | NS_WARNING("unable to Resolve URL: this URL not initialized"); |
michael@0 | 1873 | return NS_ERROR_NOT_INITIALIZED; |
michael@0 | 1874 | } |
michael@0 | 1875 | |
michael@0 | 1876 | nsresult rv; |
michael@0 | 1877 | URLSegment scheme; |
michael@0 | 1878 | char *resultPath = nullptr; |
michael@0 | 1879 | bool relative = false; |
michael@0 | 1880 | uint32_t offset = 0; |
michael@0 | 1881 | netCoalesceFlags coalesceFlag = NET_COALESCE_NORMAL; |
michael@0 | 1882 | |
michael@0 | 1883 | // relative urls should never contain a host, so we always want to use |
michael@0 | 1884 | // the noauth url parser. |
michael@0 | 1885 | // use it to extract a possible scheme |
michael@0 | 1886 | rv = mParser->ParseURL(relpath, |
michael@0 | 1887 | relpathLen, |
michael@0 | 1888 | &scheme.mPos, &scheme.mLen, |
michael@0 | 1889 | nullptr, nullptr, |
michael@0 | 1890 | nullptr, nullptr); |
michael@0 | 1891 | |
michael@0 | 1892 | // if the parser fails (for example because there is no valid scheme) |
michael@0 | 1893 | // reset the scheme and assume a relative url |
michael@0 | 1894 | if (NS_FAILED(rv)) scheme.Reset(); |
michael@0 | 1895 | |
michael@0 | 1896 | if (scheme.mLen >= 0) { |
michael@0 | 1897 | // add some flags to coalesceFlag if it is an ftp-url |
michael@0 | 1898 | // need this later on when coalescing the resulting URL |
michael@0 | 1899 | if (SegmentIs(relpath, scheme, "ftp", true)) { |
michael@0 | 1900 | coalesceFlag = (netCoalesceFlags) (coalesceFlag |
michael@0 | 1901 | | NET_COALESCE_ALLOW_RELATIVE_ROOT |
michael@0 | 1902 | | NET_COALESCE_DOUBLE_SLASH_IS_ROOT); |
michael@0 | 1903 | |
michael@0 | 1904 | } |
michael@0 | 1905 | // this URL appears to be absolute |
michael@0 | 1906 | // but try to find out more |
michael@0 | 1907 | if (SegmentIs(mScheme, relpath, scheme, true)) { |
michael@0 | 1908 | // mScheme and Scheme are the same |
michael@0 | 1909 | // but this can still be relative |
michael@0 | 1910 | if (nsCRT::strncmp(relpath + scheme.mPos + scheme.mLen, |
michael@0 | 1911 | "://",3) == 0) { |
michael@0 | 1912 | // now this is really absolute |
michael@0 | 1913 | // because a :// follows the scheme |
michael@0 | 1914 | result = NS_strdup(relpath); |
michael@0 | 1915 | } else { |
michael@0 | 1916 | // This is a deprecated form of relative urls like |
michael@0 | 1917 | // http:file or http:/path/file |
michael@0 | 1918 | // we will support it for now ... |
michael@0 | 1919 | relative = true; |
michael@0 | 1920 | offset = scheme.mLen + 1; |
michael@0 | 1921 | } |
michael@0 | 1922 | } else { |
michael@0 | 1923 | // the schemes are not the same, we are also done |
michael@0 | 1924 | // because we have to assume this is absolute |
michael@0 | 1925 | result = NS_strdup(relpath); |
michael@0 | 1926 | } |
michael@0 | 1927 | } else { |
michael@0 | 1928 | // add some flags to coalesceFlag if it is an ftp-url |
michael@0 | 1929 | // need this later on when coalescing the resulting URL |
michael@0 | 1930 | if (SegmentIs(mScheme,"ftp")) { |
michael@0 | 1931 | coalesceFlag = (netCoalesceFlags) (coalesceFlag |
michael@0 | 1932 | | NET_COALESCE_ALLOW_RELATIVE_ROOT |
michael@0 | 1933 | | NET_COALESCE_DOUBLE_SLASH_IS_ROOT); |
michael@0 | 1934 | } |
michael@0 | 1935 | if (relpath[0] == '/' && relpath[1] == '/') { |
michael@0 | 1936 | // this URL //host/path is almost absolute |
michael@0 | 1937 | result = AppendToSubstring(mScheme.mPos, mScheme.mLen + 1, relpath); |
michael@0 | 1938 | } else { |
michael@0 | 1939 | // then it must be relative |
michael@0 | 1940 | relative = true; |
michael@0 | 1941 | } |
michael@0 | 1942 | } |
michael@0 | 1943 | if (relative) { |
michael@0 | 1944 | uint32_t len = 0; |
michael@0 | 1945 | const char *realrelpath = relpath + offset; |
michael@0 | 1946 | switch (*realrelpath) { |
michael@0 | 1947 | case '/': |
michael@0 | 1948 | // overwrite everything after the authority |
michael@0 | 1949 | len = mAuthority.mPos + mAuthority.mLen; |
michael@0 | 1950 | break; |
michael@0 | 1951 | case '?': |
michael@0 | 1952 | // overwrite the existing ?query and #ref |
michael@0 | 1953 | if (mQuery.mLen >= 0) |
michael@0 | 1954 | len = mQuery.mPos - 1; |
michael@0 | 1955 | else if (mRef.mLen >= 0) |
michael@0 | 1956 | len = mRef.mPos - 1; |
michael@0 | 1957 | else |
michael@0 | 1958 | len = mPath.mPos + mPath.mLen; |
michael@0 | 1959 | break; |
michael@0 | 1960 | case '#': |
michael@0 | 1961 | case '\0': |
michael@0 | 1962 | // overwrite the existing #ref |
michael@0 | 1963 | if (mRef.mLen < 0) |
michael@0 | 1964 | len = mPath.mPos + mPath.mLen; |
michael@0 | 1965 | else |
michael@0 | 1966 | len = mRef.mPos - 1; |
michael@0 | 1967 | break; |
michael@0 | 1968 | default: |
michael@0 | 1969 | if (coalesceFlag & NET_COALESCE_DOUBLE_SLASH_IS_ROOT) { |
michael@0 | 1970 | if (Filename().Equals(NS_LITERAL_CSTRING("%2F"), |
michael@0 | 1971 | nsCaseInsensitiveCStringComparator())) { |
michael@0 | 1972 | // if ftp URL ends with %2F then simply |
michael@0 | 1973 | // append relative part because %2F also |
michael@0 | 1974 | // marks the root directory with ftp-urls |
michael@0 | 1975 | len = mFilepath.mPos + mFilepath.mLen; |
michael@0 | 1976 | } else { |
michael@0 | 1977 | // overwrite everything after the directory |
michael@0 | 1978 | len = mDirectory.mPos + mDirectory.mLen; |
michael@0 | 1979 | } |
michael@0 | 1980 | } else { |
michael@0 | 1981 | // overwrite everything after the directory |
michael@0 | 1982 | len = mDirectory.mPos + mDirectory.mLen; |
michael@0 | 1983 | } |
michael@0 | 1984 | } |
michael@0 | 1985 | result = AppendToSubstring(0, len, realrelpath); |
michael@0 | 1986 | // locate result path |
michael@0 | 1987 | resultPath = result + mPath.mPos; |
michael@0 | 1988 | } |
michael@0 | 1989 | if (!result) |
michael@0 | 1990 | return NS_ERROR_OUT_OF_MEMORY; |
michael@0 | 1991 | |
michael@0 | 1992 | if (resultPath) |
michael@0 | 1993 | net_CoalesceDirs(coalesceFlag, resultPath); |
michael@0 | 1994 | else { |
michael@0 | 1995 | // locate result path |
michael@0 | 1996 | resultPath = PL_strstr(result, "://"); |
michael@0 | 1997 | if (resultPath) { |
michael@0 | 1998 | resultPath = PL_strchr(resultPath + 3, '/'); |
michael@0 | 1999 | if (resultPath) |
michael@0 | 2000 | net_CoalesceDirs(coalesceFlag,resultPath); |
michael@0 | 2001 | } |
michael@0 | 2002 | } |
michael@0 | 2003 | out.Adopt(result); |
michael@0 | 2004 | return NS_OK; |
michael@0 | 2005 | } |
michael@0 | 2006 | |
michael@0 | 2007 | // result may contain unescaped UTF-8 characters |
michael@0 | 2008 | NS_IMETHODIMP |
michael@0 | 2009 | nsStandardURL::GetCommonBaseSpec(nsIURI *uri2, nsACString &aResult) |
michael@0 | 2010 | { |
michael@0 | 2011 | NS_ENSURE_ARG_POINTER(uri2); |
michael@0 | 2012 | |
michael@0 | 2013 | // if uri's are equal, then return uri as is |
michael@0 | 2014 | bool isEquals = false; |
michael@0 | 2015 | if (NS_SUCCEEDED(Equals(uri2, &isEquals)) && isEquals) |
michael@0 | 2016 | return GetSpec(aResult); |
michael@0 | 2017 | |
michael@0 | 2018 | aResult.Truncate(); |
michael@0 | 2019 | |
michael@0 | 2020 | // check pre-path; if they don't match, then return empty string |
michael@0 | 2021 | nsStandardURL *stdurl2; |
michael@0 | 2022 | nsresult rv = uri2->QueryInterface(kThisImplCID, (void **) &stdurl2); |
michael@0 | 2023 | isEquals = NS_SUCCEEDED(rv) |
michael@0 | 2024 | && SegmentIs(mScheme, stdurl2->mSpec.get(), stdurl2->mScheme) |
michael@0 | 2025 | && SegmentIs(mHost, stdurl2->mSpec.get(), stdurl2->mHost) |
michael@0 | 2026 | && SegmentIs(mUsername, stdurl2->mSpec.get(), stdurl2->mUsername) |
michael@0 | 2027 | && SegmentIs(mPassword, stdurl2->mSpec.get(), stdurl2->mPassword) |
michael@0 | 2028 | && (Port() == stdurl2->Port()); |
michael@0 | 2029 | if (!isEquals) |
michael@0 | 2030 | { |
michael@0 | 2031 | if (NS_SUCCEEDED(rv)) |
michael@0 | 2032 | NS_RELEASE(stdurl2); |
michael@0 | 2033 | return NS_OK; |
michael@0 | 2034 | } |
michael@0 | 2035 | |
michael@0 | 2036 | // scan for first mismatched character |
michael@0 | 2037 | const char *thisIndex, *thatIndex, *startCharPos; |
michael@0 | 2038 | startCharPos = mSpec.get() + mDirectory.mPos; |
michael@0 | 2039 | thisIndex = startCharPos; |
michael@0 | 2040 | thatIndex = stdurl2->mSpec.get() + mDirectory.mPos; |
michael@0 | 2041 | while ((*thisIndex == *thatIndex) && *thisIndex) |
michael@0 | 2042 | { |
michael@0 | 2043 | thisIndex++; |
michael@0 | 2044 | thatIndex++; |
michael@0 | 2045 | } |
michael@0 | 2046 | |
michael@0 | 2047 | // backup to just after previous slash so we grab an appropriate path |
michael@0 | 2048 | // segment such as a directory (not partial segments) |
michael@0 | 2049 | // todo: also check for file matches which include '?' and '#' |
michael@0 | 2050 | while ((thisIndex != startCharPos) && (*(thisIndex-1) != '/')) |
michael@0 | 2051 | thisIndex--; |
michael@0 | 2052 | |
michael@0 | 2053 | // grab spec from beginning to thisIndex |
michael@0 | 2054 | aResult = Substring(mSpec, mScheme.mPos, thisIndex - mSpec.get()); |
michael@0 | 2055 | |
michael@0 | 2056 | NS_RELEASE(stdurl2); |
michael@0 | 2057 | return rv; |
michael@0 | 2058 | } |
michael@0 | 2059 | |
michael@0 | 2060 | NS_IMETHODIMP |
michael@0 | 2061 | nsStandardURL::GetRelativeSpec(nsIURI *uri2, nsACString &aResult) |
michael@0 | 2062 | { |
michael@0 | 2063 | NS_ENSURE_ARG_POINTER(uri2); |
michael@0 | 2064 | |
michael@0 | 2065 | aResult.Truncate(); |
michael@0 | 2066 | |
michael@0 | 2067 | // if uri's are equal, then return empty string |
michael@0 | 2068 | bool isEquals = false; |
michael@0 | 2069 | if (NS_SUCCEEDED(Equals(uri2, &isEquals)) && isEquals) |
michael@0 | 2070 | return NS_OK; |
michael@0 | 2071 | |
michael@0 | 2072 | nsStandardURL *stdurl2; |
michael@0 | 2073 | nsresult rv = uri2->QueryInterface(kThisImplCID, (void **) &stdurl2); |
michael@0 | 2074 | isEquals = NS_SUCCEEDED(rv) |
michael@0 | 2075 | && SegmentIs(mScheme, stdurl2->mSpec.get(), stdurl2->mScheme) |
michael@0 | 2076 | && SegmentIs(mHost, stdurl2->mSpec.get(), stdurl2->mHost) |
michael@0 | 2077 | && SegmentIs(mUsername, stdurl2->mSpec.get(), stdurl2->mUsername) |
michael@0 | 2078 | && SegmentIs(mPassword, stdurl2->mSpec.get(), stdurl2->mPassword) |
michael@0 | 2079 | && (Port() == stdurl2->Port()); |
michael@0 | 2080 | if (!isEquals) |
michael@0 | 2081 | { |
michael@0 | 2082 | if (NS_SUCCEEDED(rv)) |
michael@0 | 2083 | NS_RELEASE(stdurl2); |
michael@0 | 2084 | |
michael@0 | 2085 | return uri2->GetSpec(aResult); |
michael@0 | 2086 | } |
michael@0 | 2087 | |
michael@0 | 2088 | // scan for first mismatched character |
michael@0 | 2089 | const char *thisIndex, *thatIndex, *startCharPos; |
michael@0 | 2090 | startCharPos = mSpec.get() + mDirectory.mPos; |
michael@0 | 2091 | thisIndex = startCharPos; |
michael@0 | 2092 | thatIndex = stdurl2->mSpec.get() + mDirectory.mPos; |
michael@0 | 2093 | |
michael@0 | 2094 | #ifdef XP_WIN |
michael@0 | 2095 | bool isFileScheme = SegmentIs(mScheme, "file"); |
michael@0 | 2096 | if (isFileScheme) |
michael@0 | 2097 | { |
michael@0 | 2098 | // on windows, we need to match the first segment of the path |
michael@0 | 2099 | // if these don't match then we need to return an absolute path |
michael@0 | 2100 | // skip over any leading '/' in path |
michael@0 | 2101 | while ((*thisIndex == *thatIndex) && (*thisIndex == '/')) |
michael@0 | 2102 | { |
michael@0 | 2103 | thisIndex++; |
michael@0 | 2104 | thatIndex++; |
michael@0 | 2105 | } |
michael@0 | 2106 | // look for end of first segment |
michael@0 | 2107 | while ((*thisIndex == *thatIndex) && *thisIndex && (*thisIndex != '/')) |
michael@0 | 2108 | { |
michael@0 | 2109 | thisIndex++; |
michael@0 | 2110 | thatIndex++; |
michael@0 | 2111 | } |
michael@0 | 2112 | |
michael@0 | 2113 | // if we didn't match through the first segment, return absolute path |
michael@0 | 2114 | if ((*thisIndex != '/') || (*thatIndex != '/')) |
michael@0 | 2115 | { |
michael@0 | 2116 | NS_RELEASE(stdurl2); |
michael@0 | 2117 | return uri2->GetSpec(aResult); |
michael@0 | 2118 | } |
michael@0 | 2119 | } |
michael@0 | 2120 | #endif |
michael@0 | 2121 | |
michael@0 | 2122 | while ((*thisIndex == *thatIndex) && *thisIndex) |
michael@0 | 2123 | { |
michael@0 | 2124 | thisIndex++; |
michael@0 | 2125 | thatIndex++; |
michael@0 | 2126 | } |
michael@0 | 2127 | |
michael@0 | 2128 | // backup to just after previous slash so we grab an appropriate path |
michael@0 | 2129 | // segment such as a directory (not partial segments) |
michael@0 | 2130 | // todo: also check for file matches with '#' and '?' |
michael@0 | 2131 | while ((*(thatIndex-1) != '/') && (thatIndex != startCharPos)) |
michael@0 | 2132 | thatIndex--; |
michael@0 | 2133 | |
michael@0 | 2134 | const char *limit = mSpec.get() + mFilepath.mPos + mFilepath.mLen; |
michael@0 | 2135 | |
michael@0 | 2136 | // need to account for slashes and add corresponding "../" |
michael@0 | 2137 | for (; thisIndex <= limit && *thisIndex; ++thisIndex) |
michael@0 | 2138 | { |
michael@0 | 2139 | if (*thisIndex == '/') |
michael@0 | 2140 | aResult.AppendLiteral("../"); |
michael@0 | 2141 | } |
michael@0 | 2142 | |
michael@0 | 2143 | // grab spec from thisIndex to end |
michael@0 | 2144 | uint32_t startPos = stdurl2->mScheme.mPos + thatIndex - stdurl2->mSpec.get(); |
michael@0 | 2145 | aResult.Append(Substring(stdurl2->mSpec, startPos, |
michael@0 | 2146 | stdurl2->mSpec.Length() - startPos)); |
michael@0 | 2147 | |
michael@0 | 2148 | NS_RELEASE(stdurl2); |
michael@0 | 2149 | return rv; |
michael@0 | 2150 | } |
michael@0 | 2151 | |
michael@0 | 2152 | //---------------------------------------------------------------------------- |
michael@0 | 2153 | // nsStandardURL::nsIURL |
michael@0 | 2154 | //---------------------------------------------------------------------------- |
michael@0 | 2155 | |
michael@0 | 2156 | // result may contain unescaped UTF-8 characters |
michael@0 | 2157 | NS_IMETHODIMP |
michael@0 | 2158 | nsStandardURL::GetFilePath(nsACString &result) |
michael@0 | 2159 | { |
michael@0 | 2160 | result = Filepath(); |
michael@0 | 2161 | return NS_OK; |
michael@0 | 2162 | } |
michael@0 | 2163 | |
michael@0 | 2164 | // result may contain unescaped UTF-8 characters |
michael@0 | 2165 | NS_IMETHODIMP |
michael@0 | 2166 | nsStandardURL::GetQuery(nsACString &result) |
michael@0 | 2167 | { |
michael@0 | 2168 | result = Query(); |
michael@0 | 2169 | return NS_OK; |
michael@0 | 2170 | } |
michael@0 | 2171 | |
michael@0 | 2172 | // result may contain unescaped UTF-8 characters |
michael@0 | 2173 | NS_IMETHODIMP |
michael@0 | 2174 | nsStandardURL::GetRef(nsACString &result) |
michael@0 | 2175 | { |
michael@0 | 2176 | result = Ref(); |
michael@0 | 2177 | return NS_OK; |
michael@0 | 2178 | } |
michael@0 | 2179 | |
michael@0 | 2180 | NS_IMETHODIMP |
michael@0 | 2181 | nsStandardURL::GetHasRef(bool *result) |
michael@0 | 2182 | { |
michael@0 | 2183 | *result = (mRef.mLen >= 0); |
michael@0 | 2184 | return NS_OK; |
michael@0 | 2185 | } |
michael@0 | 2186 | |
michael@0 | 2187 | // result may contain unescaped UTF-8 characters |
michael@0 | 2188 | NS_IMETHODIMP |
michael@0 | 2189 | nsStandardURL::GetDirectory(nsACString &result) |
michael@0 | 2190 | { |
michael@0 | 2191 | result = Directory(); |
michael@0 | 2192 | return NS_OK; |
michael@0 | 2193 | } |
michael@0 | 2194 | |
michael@0 | 2195 | // result may contain unescaped UTF-8 characters |
michael@0 | 2196 | NS_IMETHODIMP |
michael@0 | 2197 | nsStandardURL::GetFileName(nsACString &result) |
michael@0 | 2198 | { |
michael@0 | 2199 | result = Filename(); |
michael@0 | 2200 | return NS_OK; |
michael@0 | 2201 | } |
michael@0 | 2202 | |
michael@0 | 2203 | // result may contain unescaped UTF-8 characters |
michael@0 | 2204 | NS_IMETHODIMP |
michael@0 | 2205 | nsStandardURL::GetFileBaseName(nsACString &result) |
michael@0 | 2206 | { |
michael@0 | 2207 | result = Basename(); |
michael@0 | 2208 | return NS_OK; |
michael@0 | 2209 | } |
michael@0 | 2210 | |
michael@0 | 2211 | // result may contain unescaped UTF-8 characters |
michael@0 | 2212 | NS_IMETHODIMP |
michael@0 | 2213 | nsStandardURL::GetFileExtension(nsACString &result) |
michael@0 | 2214 | { |
michael@0 | 2215 | result = Extension(); |
michael@0 | 2216 | return NS_OK; |
michael@0 | 2217 | } |
michael@0 | 2218 | |
michael@0 | 2219 | NS_IMETHODIMP |
michael@0 | 2220 | nsStandardURL::SetFilePath(const nsACString &input) |
michael@0 | 2221 | { |
michael@0 | 2222 | ENSURE_MUTABLE(); |
michael@0 | 2223 | |
michael@0 | 2224 | const nsPromiseFlatCString &flat = PromiseFlatCString(input); |
michael@0 | 2225 | const char *filepath = flat.get(); |
michael@0 | 2226 | |
michael@0 | 2227 | LOG(("nsStandardURL::SetFilePath [filepath=%s]\n", filepath)); |
michael@0 | 2228 | |
michael@0 | 2229 | // if there isn't a filepath, then there can't be anything |
michael@0 | 2230 | // after the path either. this url is likely uninitialized. |
michael@0 | 2231 | if (mFilepath.mLen < 0) |
michael@0 | 2232 | return SetPath(flat); |
michael@0 | 2233 | |
michael@0 | 2234 | if (filepath && *filepath) { |
michael@0 | 2235 | nsAutoCString spec; |
michael@0 | 2236 | uint32_t dirPos, basePos, extPos; |
michael@0 | 2237 | int32_t dirLen, baseLen, extLen; |
michael@0 | 2238 | nsresult rv; |
michael@0 | 2239 | |
michael@0 | 2240 | rv = mParser->ParseFilePath(filepath, -1, |
michael@0 | 2241 | &dirPos, &dirLen, |
michael@0 | 2242 | &basePos, &baseLen, |
michael@0 | 2243 | &extPos, &extLen); |
michael@0 | 2244 | if (NS_FAILED(rv)) return rv; |
michael@0 | 2245 | |
michael@0 | 2246 | // build up new candidate spec |
michael@0 | 2247 | spec.Assign(mSpec.get(), mPath.mPos); |
michael@0 | 2248 | |
michael@0 | 2249 | // ensure leading '/' |
michael@0 | 2250 | if (filepath[dirPos] != '/') |
michael@0 | 2251 | spec.Append('/'); |
michael@0 | 2252 | |
michael@0 | 2253 | GET_SEGMENT_ENCODER(encoder); |
michael@0 | 2254 | |
michael@0 | 2255 | // append encoded filepath components |
michael@0 | 2256 | if (dirLen > 0) |
michael@0 | 2257 | encoder.EncodeSegment(Substring(filepath + dirPos, |
michael@0 | 2258 | filepath + dirPos + dirLen), |
michael@0 | 2259 | esc_Directory | esc_AlwaysCopy, spec); |
michael@0 | 2260 | if (baseLen > 0) |
michael@0 | 2261 | encoder.EncodeSegment(Substring(filepath + basePos, |
michael@0 | 2262 | filepath + basePos + baseLen), |
michael@0 | 2263 | esc_FileBaseName | esc_AlwaysCopy, spec); |
michael@0 | 2264 | if (extLen >= 0) { |
michael@0 | 2265 | spec.Append('.'); |
michael@0 | 2266 | if (extLen > 0) |
michael@0 | 2267 | encoder.EncodeSegment(Substring(filepath + extPos, |
michael@0 | 2268 | filepath + extPos + extLen), |
michael@0 | 2269 | esc_FileExtension | esc_AlwaysCopy, |
michael@0 | 2270 | spec); |
michael@0 | 2271 | } |
michael@0 | 2272 | |
michael@0 | 2273 | // compute the ending position of the current filepath |
michael@0 | 2274 | if (mFilepath.mLen >= 0) { |
michael@0 | 2275 | uint32_t end = mFilepath.mPos + mFilepath.mLen; |
michael@0 | 2276 | if (mSpec.Length() > end) |
michael@0 | 2277 | spec.Append(mSpec.get() + end, mSpec.Length() - end); |
michael@0 | 2278 | } |
michael@0 | 2279 | |
michael@0 | 2280 | return SetSpec(spec); |
michael@0 | 2281 | } |
michael@0 | 2282 | else if (mPath.mLen > 1) { |
michael@0 | 2283 | mSpec.Cut(mPath.mPos + 1, mFilepath.mLen - 1); |
michael@0 | 2284 | // left shift query, and ref |
michael@0 | 2285 | ShiftFromQuery(1 - mFilepath.mLen); |
michael@0 | 2286 | // these contain only a '/' |
michael@0 | 2287 | mPath.mLen = 1; |
michael@0 | 2288 | mDirectory.mLen = 1; |
michael@0 | 2289 | mFilepath.mLen = 1; |
michael@0 | 2290 | // these are no longer defined |
michael@0 | 2291 | mBasename.mLen = -1; |
michael@0 | 2292 | mExtension.mLen = -1; |
michael@0 | 2293 | } |
michael@0 | 2294 | return NS_OK; |
michael@0 | 2295 | } |
michael@0 | 2296 | |
michael@0 | 2297 | NS_IMETHODIMP |
michael@0 | 2298 | nsStandardURL::SetQuery(const nsACString &input) |
michael@0 | 2299 | { |
michael@0 | 2300 | ENSURE_MUTABLE(); |
michael@0 | 2301 | |
michael@0 | 2302 | const nsPromiseFlatCString &flat = PromiseFlatCString(input); |
michael@0 | 2303 | const char *query = flat.get(); |
michael@0 | 2304 | |
michael@0 | 2305 | LOG(("nsStandardURL::SetQuery [query=%s]\n", query)); |
michael@0 | 2306 | |
michael@0 | 2307 | if (mPath.mLen < 0) |
michael@0 | 2308 | return SetPath(flat); |
michael@0 | 2309 | |
michael@0 | 2310 | InvalidateCache(); |
michael@0 | 2311 | |
michael@0 | 2312 | if (!query || !*query) { |
michael@0 | 2313 | // remove existing query |
michael@0 | 2314 | if (mQuery.mLen >= 0) { |
michael@0 | 2315 | // remove query and leading '?' |
michael@0 | 2316 | mSpec.Cut(mQuery.mPos - 1, mQuery.mLen + 1); |
michael@0 | 2317 | ShiftFromRef(-(mQuery.mLen + 1)); |
michael@0 | 2318 | mPath.mLen -= (mQuery.mLen + 1); |
michael@0 | 2319 | mQuery.mPos = 0; |
michael@0 | 2320 | mQuery.mLen = -1; |
michael@0 | 2321 | } |
michael@0 | 2322 | return NS_OK; |
michael@0 | 2323 | } |
michael@0 | 2324 | |
michael@0 | 2325 | int32_t queryLen = strlen(query); |
michael@0 | 2326 | if (query[0] == '?') { |
michael@0 | 2327 | query++; |
michael@0 | 2328 | queryLen--; |
michael@0 | 2329 | } |
michael@0 | 2330 | |
michael@0 | 2331 | if (mQuery.mLen < 0) { |
michael@0 | 2332 | if (mRef.mLen < 0) |
michael@0 | 2333 | mQuery.mPos = mSpec.Length(); |
michael@0 | 2334 | else |
michael@0 | 2335 | mQuery.mPos = mRef.mPos - 1; |
michael@0 | 2336 | mSpec.Insert('?', mQuery.mPos); |
michael@0 | 2337 | mQuery.mPos++; |
michael@0 | 2338 | mQuery.mLen = 0; |
michael@0 | 2339 | // the insertion pushes these out by 1 |
michael@0 | 2340 | mPath.mLen++; |
michael@0 | 2341 | mRef.mPos++; |
michael@0 | 2342 | } |
michael@0 | 2343 | |
michael@0 | 2344 | // encode query if necessary |
michael@0 | 2345 | nsAutoCString buf; |
michael@0 | 2346 | bool encoded; |
michael@0 | 2347 | GET_QUERY_ENCODER(encoder); |
michael@0 | 2348 | encoder.EncodeSegmentCount(query, URLSegment(0, queryLen), esc_Query, |
michael@0 | 2349 | buf, encoded); |
michael@0 | 2350 | if (encoded) { |
michael@0 | 2351 | query = buf.get(); |
michael@0 | 2352 | queryLen = buf.Length(); |
michael@0 | 2353 | } |
michael@0 | 2354 | |
michael@0 | 2355 | int32_t shift = ReplaceSegment(mQuery.mPos, mQuery.mLen, query, queryLen); |
michael@0 | 2356 | |
michael@0 | 2357 | if (shift) { |
michael@0 | 2358 | mQuery.mLen = queryLen; |
michael@0 | 2359 | mPath.mLen += shift; |
michael@0 | 2360 | ShiftFromRef(shift); |
michael@0 | 2361 | } |
michael@0 | 2362 | return NS_OK; |
michael@0 | 2363 | } |
michael@0 | 2364 | |
michael@0 | 2365 | NS_IMETHODIMP |
michael@0 | 2366 | nsStandardURL::SetRef(const nsACString &input) |
michael@0 | 2367 | { |
michael@0 | 2368 | ENSURE_MUTABLE(); |
michael@0 | 2369 | |
michael@0 | 2370 | const nsPromiseFlatCString &flat = PromiseFlatCString(input); |
michael@0 | 2371 | const char *ref = flat.get(); |
michael@0 | 2372 | |
michael@0 | 2373 | LOG(("nsStandardURL::SetRef [ref=%s]\n", ref)); |
michael@0 | 2374 | |
michael@0 | 2375 | if (mPath.mLen < 0) |
michael@0 | 2376 | return SetPath(flat); |
michael@0 | 2377 | |
michael@0 | 2378 | InvalidateCache(); |
michael@0 | 2379 | |
michael@0 | 2380 | if (!ref || !*ref) { |
michael@0 | 2381 | // remove existing ref |
michael@0 | 2382 | if (mRef.mLen >= 0) { |
michael@0 | 2383 | // remove ref and leading '#' |
michael@0 | 2384 | mSpec.Cut(mRef.mPos - 1, mRef.mLen + 1); |
michael@0 | 2385 | mPath.mLen -= (mRef.mLen + 1); |
michael@0 | 2386 | mRef.mPos = 0; |
michael@0 | 2387 | mRef.mLen = -1; |
michael@0 | 2388 | } |
michael@0 | 2389 | return NS_OK; |
michael@0 | 2390 | } |
michael@0 | 2391 | |
michael@0 | 2392 | int32_t refLen = strlen(ref); |
michael@0 | 2393 | if (ref[0] == '#') { |
michael@0 | 2394 | ref++; |
michael@0 | 2395 | refLen--; |
michael@0 | 2396 | } |
michael@0 | 2397 | |
michael@0 | 2398 | if (mRef.mLen < 0) { |
michael@0 | 2399 | mSpec.Append('#'); |
michael@0 | 2400 | ++mPath.mLen; // Include the # in the path. |
michael@0 | 2401 | mRef.mPos = mSpec.Length(); |
michael@0 | 2402 | mRef.mLen = 0; |
michael@0 | 2403 | } |
michael@0 | 2404 | |
michael@0 | 2405 | // encode ref if necessary |
michael@0 | 2406 | nsAutoCString buf; |
michael@0 | 2407 | bool encoded; |
michael@0 | 2408 | GET_SEGMENT_ENCODER(encoder); |
michael@0 | 2409 | encoder.EncodeSegmentCount(ref, URLSegment(0, refLen), esc_Ref, |
michael@0 | 2410 | buf, encoded); |
michael@0 | 2411 | if (encoded) { |
michael@0 | 2412 | ref = buf.get(); |
michael@0 | 2413 | refLen = buf.Length(); |
michael@0 | 2414 | } |
michael@0 | 2415 | |
michael@0 | 2416 | int32_t shift = ReplaceSegment(mRef.mPos, mRef.mLen, ref, refLen); |
michael@0 | 2417 | mPath.mLen += shift; |
michael@0 | 2418 | mRef.mLen = refLen; |
michael@0 | 2419 | return NS_OK; |
michael@0 | 2420 | } |
michael@0 | 2421 | |
michael@0 | 2422 | NS_IMETHODIMP |
michael@0 | 2423 | nsStandardURL::SetDirectory(const nsACString &input) |
michael@0 | 2424 | { |
michael@0 | 2425 | NS_NOTYETIMPLEMENTED(""); |
michael@0 | 2426 | return NS_ERROR_NOT_IMPLEMENTED; |
michael@0 | 2427 | } |
michael@0 | 2428 | |
michael@0 | 2429 | NS_IMETHODIMP |
michael@0 | 2430 | nsStandardURL::SetFileName(const nsACString &input) |
michael@0 | 2431 | { |
michael@0 | 2432 | ENSURE_MUTABLE(); |
michael@0 | 2433 | |
michael@0 | 2434 | const nsPromiseFlatCString &flat = PromiseFlatCString(input); |
michael@0 | 2435 | const char *filename = flat.get(); |
michael@0 | 2436 | |
michael@0 | 2437 | LOG(("nsStandardURL::SetFileName [filename=%s]\n", filename)); |
michael@0 | 2438 | |
michael@0 | 2439 | if (mPath.mLen < 0) |
michael@0 | 2440 | return SetPath(flat); |
michael@0 | 2441 | |
michael@0 | 2442 | int32_t shift = 0; |
michael@0 | 2443 | |
michael@0 | 2444 | if (!(filename && *filename)) { |
michael@0 | 2445 | // remove the filename |
michael@0 | 2446 | if (mBasename.mLen > 0) { |
michael@0 | 2447 | if (mExtension.mLen >= 0) |
michael@0 | 2448 | mBasename.mLen += (mExtension.mLen + 1); |
michael@0 | 2449 | mSpec.Cut(mBasename.mPos, mBasename.mLen); |
michael@0 | 2450 | shift = -mBasename.mLen; |
michael@0 | 2451 | mBasename.mLen = 0; |
michael@0 | 2452 | mExtension.mLen = -1; |
michael@0 | 2453 | } |
michael@0 | 2454 | } |
michael@0 | 2455 | else { |
michael@0 | 2456 | nsresult rv; |
michael@0 | 2457 | URLSegment basename, extension; |
michael@0 | 2458 | |
michael@0 | 2459 | // let the parser locate the basename and extension |
michael@0 | 2460 | rv = mParser->ParseFileName(filename, -1, |
michael@0 | 2461 | &basename.mPos, &basename.mLen, |
michael@0 | 2462 | &extension.mPos, &extension.mLen); |
michael@0 | 2463 | if (NS_FAILED(rv)) return rv; |
michael@0 | 2464 | |
michael@0 | 2465 | if (basename.mLen < 0) { |
michael@0 | 2466 | // remove existing filename |
michael@0 | 2467 | if (mBasename.mLen >= 0) { |
michael@0 | 2468 | uint32_t len = mBasename.mLen; |
michael@0 | 2469 | if (mExtension.mLen >= 0) |
michael@0 | 2470 | len += (mExtension.mLen + 1); |
michael@0 | 2471 | mSpec.Cut(mBasename.mPos, len); |
michael@0 | 2472 | shift = -int32_t(len); |
michael@0 | 2473 | mBasename.mLen = 0; |
michael@0 | 2474 | mExtension.mLen = -1; |
michael@0 | 2475 | } |
michael@0 | 2476 | } |
michael@0 | 2477 | else { |
michael@0 | 2478 | nsAutoCString newFilename; |
michael@0 | 2479 | bool ignoredOut; |
michael@0 | 2480 | GET_SEGMENT_ENCODER(encoder); |
michael@0 | 2481 | basename.mLen = encoder.EncodeSegmentCount(filename, basename, |
michael@0 | 2482 | esc_FileBaseName | |
michael@0 | 2483 | esc_AlwaysCopy, |
michael@0 | 2484 | newFilename, |
michael@0 | 2485 | ignoredOut); |
michael@0 | 2486 | if (extension.mLen >= 0) { |
michael@0 | 2487 | newFilename.Append('.'); |
michael@0 | 2488 | extension.mLen = encoder.EncodeSegmentCount(filename, extension, |
michael@0 | 2489 | esc_FileExtension | |
michael@0 | 2490 | esc_AlwaysCopy, |
michael@0 | 2491 | newFilename, |
michael@0 | 2492 | ignoredOut); |
michael@0 | 2493 | } |
michael@0 | 2494 | |
michael@0 | 2495 | if (mBasename.mLen < 0) { |
michael@0 | 2496 | // insert new filename |
michael@0 | 2497 | mBasename.mPos = mDirectory.mPos + mDirectory.mLen; |
michael@0 | 2498 | mSpec.Insert(newFilename, mBasename.mPos); |
michael@0 | 2499 | shift = newFilename.Length(); |
michael@0 | 2500 | } |
michael@0 | 2501 | else { |
michael@0 | 2502 | // replace existing filename |
michael@0 | 2503 | uint32_t oldLen = uint32_t(mBasename.mLen); |
michael@0 | 2504 | if (mExtension.mLen >= 0) |
michael@0 | 2505 | oldLen += (mExtension.mLen + 1); |
michael@0 | 2506 | mSpec.Replace(mBasename.mPos, oldLen, newFilename); |
michael@0 | 2507 | shift = newFilename.Length() - oldLen; |
michael@0 | 2508 | } |
michael@0 | 2509 | |
michael@0 | 2510 | mBasename.mLen = basename.mLen; |
michael@0 | 2511 | mExtension.mLen = extension.mLen; |
michael@0 | 2512 | if (mExtension.mLen >= 0) |
michael@0 | 2513 | mExtension.mPos = mBasename.mPos + mBasename.mLen + 1; |
michael@0 | 2514 | } |
michael@0 | 2515 | } |
michael@0 | 2516 | if (shift) { |
michael@0 | 2517 | ShiftFromQuery(shift); |
michael@0 | 2518 | mFilepath.mLen += shift; |
michael@0 | 2519 | mPath.mLen += shift; |
michael@0 | 2520 | } |
michael@0 | 2521 | return NS_OK; |
michael@0 | 2522 | } |
michael@0 | 2523 | |
michael@0 | 2524 | NS_IMETHODIMP |
michael@0 | 2525 | nsStandardURL::SetFileBaseName(const nsACString &input) |
michael@0 | 2526 | { |
michael@0 | 2527 | nsAutoCString extension; |
michael@0 | 2528 | nsresult rv = GetFileExtension(extension); |
michael@0 | 2529 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 2530 | |
michael@0 | 2531 | nsAutoCString newFileName(input); |
michael@0 | 2532 | |
michael@0 | 2533 | if (!extension.IsEmpty()) { |
michael@0 | 2534 | newFileName.Append('.'); |
michael@0 | 2535 | newFileName.Append(extension); |
michael@0 | 2536 | } |
michael@0 | 2537 | |
michael@0 | 2538 | return SetFileName(newFileName); |
michael@0 | 2539 | } |
michael@0 | 2540 | |
michael@0 | 2541 | NS_IMETHODIMP |
michael@0 | 2542 | nsStandardURL::SetFileExtension(const nsACString &input) |
michael@0 | 2543 | { |
michael@0 | 2544 | nsAutoCString newFileName; |
michael@0 | 2545 | nsresult rv = GetFileBaseName(newFileName); |
michael@0 | 2546 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 2547 | |
michael@0 | 2548 | if (!input.IsEmpty()) { |
michael@0 | 2549 | newFileName.Append('.'); |
michael@0 | 2550 | newFileName.Append(input); |
michael@0 | 2551 | } |
michael@0 | 2552 | |
michael@0 | 2553 | return SetFileName(newFileName); |
michael@0 | 2554 | } |
michael@0 | 2555 | |
michael@0 | 2556 | //---------------------------------------------------------------------------- |
michael@0 | 2557 | // nsStandardURL::nsIFileURL |
michael@0 | 2558 | //---------------------------------------------------------------------------- |
michael@0 | 2559 | |
michael@0 | 2560 | nsresult |
michael@0 | 2561 | nsStandardURL::EnsureFile() |
michael@0 | 2562 | { |
michael@0 | 2563 | NS_PRECONDITION(mSupportsFileURL, |
michael@0 | 2564 | "EnsureFile() called on a URL that doesn't support files!"); |
michael@0 | 2565 | if (mFile) { |
michael@0 | 2566 | // Nothing to do |
michael@0 | 2567 | return NS_OK; |
michael@0 | 2568 | } |
michael@0 | 2569 | |
michael@0 | 2570 | // Parse the spec if we don't have a cached result |
michael@0 | 2571 | if (mSpec.IsEmpty()) { |
michael@0 | 2572 | NS_WARNING("url not initialized"); |
michael@0 | 2573 | return NS_ERROR_NOT_INITIALIZED; |
michael@0 | 2574 | } |
michael@0 | 2575 | |
michael@0 | 2576 | if (!SegmentIs(mScheme, "file")) { |
michael@0 | 2577 | NS_WARNING("not a file URL"); |
michael@0 | 2578 | return NS_ERROR_FAILURE; |
michael@0 | 2579 | } |
michael@0 | 2580 | |
michael@0 | 2581 | return net_GetFileFromURLSpec(mSpec, getter_AddRefs(mFile)); |
michael@0 | 2582 | } |
michael@0 | 2583 | |
michael@0 | 2584 | NS_IMETHODIMP |
michael@0 | 2585 | nsStandardURL::GetFile(nsIFile **result) |
michael@0 | 2586 | { |
michael@0 | 2587 | NS_PRECONDITION(mSupportsFileURL, |
michael@0 | 2588 | "GetFile() called on a URL that doesn't support files!"); |
michael@0 | 2589 | nsresult rv = EnsureFile(); |
michael@0 | 2590 | if (NS_FAILED(rv)) |
michael@0 | 2591 | return rv; |
michael@0 | 2592 | |
michael@0 | 2593 | #if defined(PR_LOGGING) |
michael@0 | 2594 | if (LOG_ENABLED()) { |
michael@0 | 2595 | nsAutoCString path; |
michael@0 | 2596 | mFile->GetNativePath(path); |
michael@0 | 2597 | LOG(("nsStandardURL::GetFile [this=%p spec=%s resulting_path=%s]\n", |
michael@0 | 2598 | this, mSpec.get(), path.get())); |
michael@0 | 2599 | } |
michael@0 | 2600 | #endif |
michael@0 | 2601 | |
michael@0 | 2602 | // clone the file, so the caller can modify it. |
michael@0 | 2603 | // XXX nsIFileURL.idl specifies that the consumer must _not_ modify the |
michael@0 | 2604 | // nsIFile returned from this method; but it seems that some folks do |
michael@0 | 2605 | // (see bug 161921). until we can be sure that all the consumers are |
michael@0 | 2606 | // behaving themselves, we'll stay on the safe side and clone the file. |
michael@0 | 2607 | // see bug 212724 about fixing the consumers. |
michael@0 | 2608 | return mFile->Clone(result); |
michael@0 | 2609 | } |
michael@0 | 2610 | |
michael@0 | 2611 | NS_IMETHODIMP |
michael@0 | 2612 | nsStandardURL::SetFile(nsIFile *file) |
michael@0 | 2613 | { |
michael@0 | 2614 | ENSURE_MUTABLE(); |
michael@0 | 2615 | |
michael@0 | 2616 | NS_ENSURE_ARG_POINTER(file); |
michael@0 | 2617 | |
michael@0 | 2618 | nsresult rv; |
michael@0 | 2619 | nsAutoCString url; |
michael@0 | 2620 | |
michael@0 | 2621 | rv = net_GetURLSpecFromFile(file, url); |
michael@0 | 2622 | if (NS_FAILED(rv)) return rv; |
michael@0 | 2623 | |
michael@0 | 2624 | SetSpec(url); |
michael@0 | 2625 | |
michael@0 | 2626 | rv = Init(mURLType, mDefaultPort, url, nullptr, nullptr); |
michael@0 | 2627 | |
michael@0 | 2628 | // must clone |file| since its value is not guaranteed to remain constant |
michael@0 | 2629 | if (NS_SUCCEEDED(rv)) { |
michael@0 | 2630 | InvalidateCache(); |
michael@0 | 2631 | if (NS_FAILED(file->Clone(getter_AddRefs(mFile)))) { |
michael@0 | 2632 | NS_WARNING("nsIFile::Clone failed"); |
michael@0 | 2633 | // failure to clone is not fatal (GetFile will generate mFile) |
michael@0 | 2634 | mFile = 0; |
michael@0 | 2635 | } |
michael@0 | 2636 | } |
michael@0 | 2637 | return rv; |
michael@0 | 2638 | } |
michael@0 | 2639 | |
michael@0 | 2640 | //---------------------------------------------------------------------------- |
michael@0 | 2641 | // nsStandardURL::nsIStandardURL |
michael@0 | 2642 | //---------------------------------------------------------------------------- |
michael@0 | 2643 | |
michael@0 | 2644 | inline bool |
michael@0 | 2645 | IsUTFCharset(const char *aCharset) |
michael@0 | 2646 | { |
michael@0 | 2647 | return ((aCharset[0] == 'U' || aCharset[0] == 'u') && |
michael@0 | 2648 | (aCharset[1] == 'T' || aCharset[1] == 't') && |
michael@0 | 2649 | (aCharset[2] == 'F' || aCharset[2] == 'f')); |
michael@0 | 2650 | } |
michael@0 | 2651 | |
michael@0 | 2652 | NS_IMETHODIMP |
michael@0 | 2653 | nsStandardURL::Init(uint32_t urlType, |
michael@0 | 2654 | int32_t defaultPort, |
michael@0 | 2655 | const nsACString &spec, |
michael@0 | 2656 | const char *charset, |
michael@0 | 2657 | nsIURI *baseURI) |
michael@0 | 2658 | { |
michael@0 | 2659 | ENSURE_MUTABLE(); |
michael@0 | 2660 | |
michael@0 | 2661 | InvalidateCache(); |
michael@0 | 2662 | |
michael@0 | 2663 | switch (urlType) { |
michael@0 | 2664 | case URLTYPE_STANDARD: |
michael@0 | 2665 | mParser = net_GetStdURLParser(); |
michael@0 | 2666 | break; |
michael@0 | 2667 | case URLTYPE_AUTHORITY: |
michael@0 | 2668 | mParser = net_GetAuthURLParser(); |
michael@0 | 2669 | break; |
michael@0 | 2670 | case URLTYPE_NO_AUTHORITY: |
michael@0 | 2671 | mParser = net_GetNoAuthURLParser(); |
michael@0 | 2672 | break; |
michael@0 | 2673 | default: |
michael@0 | 2674 | NS_NOTREACHED("bad urlType"); |
michael@0 | 2675 | return NS_ERROR_INVALID_ARG; |
michael@0 | 2676 | } |
michael@0 | 2677 | mDefaultPort = defaultPort; |
michael@0 | 2678 | mURLType = urlType; |
michael@0 | 2679 | |
michael@0 | 2680 | mOriginCharset.Truncate(); |
michael@0 | 2681 | |
michael@0 | 2682 | if (charset == nullptr || *charset == '\0') { |
michael@0 | 2683 | // check if baseURI provides an origin charset and use that. |
michael@0 | 2684 | if (baseURI) |
michael@0 | 2685 | baseURI->GetOriginCharset(mOriginCharset); |
michael@0 | 2686 | |
michael@0 | 2687 | // URI can't be encoded in UTF-16, UTF-16BE, UTF-16LE, UTF-32, |
michael@0 | 2688 | // UTF-32-LE, UTF-32LE, UTF-32BE (yet?). Truncate mOriginCharset if |
michael@0 | 2689 | // it starts with "utf" (since an empty mOriginCharset implies |
michael@0 | 2690 | // UTF-8, this is safe even if mOriginCharset is UTF-8). |
michael@0 | 2691 | |
michael@0 | 2692 | if (mOriginCharset.Length() > 3 && |
michael@0 | 2693 | IsUTFCharset(mOriginCharset.get())) { |
michael@0 | 2694 | mOriginCharset.Truncate(); |
michael@0 | 2695 | } |
michael@0 | 2696 | } |
michael@0 | 2697 | else if (!IsUTFCharset(charset)) { |
michael@0 | 2698 | mOriginCharset = charset; |
michael@0 | 2699 | } |
michael@0 | 2700 | |
michael@0 | 2701 | if (baseURI) { |
michael@0 | 2702 | uint32_t start, end; |
michael@0 | 2703 | // pull out the scheme and where it ends |
michael@0 | 2704 | nsresult rv = net_ExtractURLScheme(spec, &start, &end, nullptr); |
michael@0 | 2705 | if (NS_SUCCEEDED(rv) && spec.Length() > end+2) { |
michael@0 | 2706 | nsACString::const_iterator slash; |
michael@0 | 2707 | spec.BeginReading(slash); |
michael@0 | 2708 | slash.advance(end+1); |
michael@0 | 2709 | // then check if // follows |
michael@0 | 2710 | // if it follows, aSpec is really absolute ... |
michael@0 | 2711 | // ignore aBaseURI in this case |
michael@0 | 2712 | if (*slash == '/' && *(++slash) == '/') |
michael@0 | 2713 | baseURI = nullptr; |
michael@0 | 2714 | } |
michael@0 | 2715 | } |
michael@0 | 2716 | |
michael@0 | 2717 | if (!baseURI) |
michael@0 | 2718 | return SetSpec(spec); |
michael@0 | 2719 | |
michael@0 | 2720 | nsAutoCString buf; |
michael@0 | 2721 | nsresult rv = baseURI->Resolve(spec, buf); |
michael@0 | 2722 | if (NS_FAILED(rv)) return rv; |
michael@0 | 2723 | |
michael@0 | 2724 | return SetSpec(buf); |
michael@0 | 2725 | } |
michael@0 | 2726 | |
michael@0 | 2727 | NS_IMETHODIMP |
michael@0 | 2728 | nsStandardURL::GetMutable(bool *value) |
michael@0 | 2729 | { |
michael@0 | 2730 | *value = mMutable; |
michael@0 | 2731 | return NS_OK; |
michael@0 | 2732 | } |
michael@0 | 2733 | |
michael@0 | 2734 | NS_IMETHODIMP |
michael@0 | 2735 | nsStandardURL::SetMutable(bool value) |
michael@0 | 2736 | { |
michael@0 | 2737 | NS_ENSURE_ARG(mMutable || !value); |
michael@0 | 2738 | |
michael@0 | 2739 | mMutable = value; |
michael@0 | 2740 | return NS_OK; |
michael@0 | 2741 | } |
michael@0 | 2742 | |
michael@0 | 2743 | //---------------------------------------------------------------------------- |
michael@0 | 2744 | // nsStandardURL::nsISerializable |
michael@0 | 2745 | //---------------------------------------------------------------------------- |
michael@0 | 2746 | |
michael@0 | 2747 | NS_IMETHODIMP |
michael@0 | 2748 | nsStandardURL::Read(nsIObjectInputStream *stream) |
michael@0 | 2749 | { |
michael@0 | 2750 | NS_PRECONDITION(!mHostA, "Shouldn't have cached ASCII host"); |
michael@0 | 2751 | NS_PRECONDITION(mSpecEncoding == eEncoding_Unknown, |
michael@0 | 2752 | "Shouldn't have spec encoding here"); |
michael@0 | 2753 | |
michael@0 | 2754 | nsresult rv; |
michael@0 | 2755 | |
michael@0 | 2756 | uint32_t urlType; |
michael@0 | 2757 | rv = stream->Read32(&urlType); |
michael@0 | 2758 | if (NS_FAILED(rv)) return rv; |
michael@0 | 2759 | mURLType = urlType; |
michael@0 | 2760 | switch (mURLType) { |
michael@0 | 2761 | case URLTYPE_STANDARD: |
michael@0 | 2762 | mParser = net_GetStdURLParser(); |
michael@0 | 2763 | break; |
michael@0 | 2764 | case URLTYPE_AUTHORITY: |
michael@0 | 2765 | mParser = net_GetAuthURLParser(); |
michael@0 | 2766 | break; |
michael@0 | 2767 | case URLTYPE_NO_AUTHORITY: |
michael@0 | 2768 | mParser = net_GetNoAuthURLParser(); |
michael@0 | 2769 | break; |
michael@0 | 2770 | default: |
michael@0 | 2771 | NS_NOTREACHED("bad urlType"); |
michael@0 | 2772 | return NS_ERROR_FAILURE; |
michael@0 | 2773 | } |
michael@0 | 2774 | |
michael@0 | 2775 | rv = stream->Read32((uint32_t *) &mPort); |
michael@0 | 2776 | if (NS_FAILED(rv)) return rv; |
michael@0 | 2777 | |
michael@0 | 2778 | rv = stream->Read32((uint32_t *) &mDefaultPort); |
michael@0 | 2779 | if (NS_FAILED(rv)) return rv; |
michael@0 | 2780 | |
michael@0 | 2781 | rv = NS_ReadOptionalCString(stream, mSpec); |
michael@0 | 2782 | if (NS_FAILED(rv)) return rv; |
michael@0 | 2783 | |
michael@0 | 2784 | rv = ReadSegment(stream, mScheme); |
michael@0 | 2785 | if (NS_FAILED(rv)) return rv; |
michael@0 | 2786 | |
michael@0 | 2787 | rv = ReadSegment(stream, mAuthority); |
michael@0 | 2788 | if (NS_FAILED(rv)) return rv; |
michael@0 | 2789 | |
michael@0 | 2790 | rv = ReadSegment(stream, mUsername); |
michael@0 | 2791 | if (NS_FAILED(rv)) return rv; |
michael@0 | 2792 | |
michael@0 | 2793 | rv = ReadSegment(stream, mPassword); |
michael@0 | 2794 | if (NS_FAILED(rv)) return rv; |
michael@0 | 2795 | |
michael@0 | 2796 | rv = ReadSegment(stream, mHost); |
michael@0 | 2797 | if (NS_FAILED(rv)) return rv; |
michael@0 | 2798 | |
michael@0 | 2799 | rv = ReadSegment(stream, mPath); |
michael@0 | 2800 | if (NS_FAILED(rv)) return rv; |
michael@0 | 2801 | |
michael@0 | 2802 | rv = ReadSegment(stream, mFilepath); |
michael@0 | 2803 | if (NS_FAILED(rv)) return rv; |
michael@0 | 2804 | |
michael@0 | 2805 | rv = ReadSegment(stream, mDirectory); |
michael@0 | 2806 | if (NS_FAILED(rv)) return rv; |
michael@0 | 2807 | |
michael@0 | 2808 | rv = ReadSegment(stream, mBasename); |
michael@0 | 2809 | if (NS_FAILED(rv)) return rv; |
michael@0 | 2810 | |
michael@0 | 2811 | rv = ReadSegment(stream, mExtension); |
michael@0 | 2812 | if (NS_FAILED(rv)) return rv; |
michael@0 | 2813 | |
michael@0 | 2814 | // handle forward compatibility from older serializations that included mParam |
michael@0 | 2815 | URLSegment old_param; |
michael@0 | 2816 | rv = ReadSegment(stream, old_param); |
michael@0 | 2817 | if (NS_FAILED(rv)) return rv; |
michael@0 | 2818 | |
michael@0 | 2819 | rv = ReadSegment(stream, mQuery); |
michael@0 | 2820 | if (NS_FAILED(rv)) return rv; |
michael@0 | 2821 | |
michael@0 | 2822 | rv = ReadSegment(stream, mRef); |
michael@0 | 2823 | if (NS_FAILED(rv)) return rv; |
michael@0 | 2824 | |
michael@0 | 2825 | rv = NS_ReadOptionalCString(stream, mOriginCharset); |
michael@0 | 2826 | if (NS_FAILED(rv)) return rv; |
michael@0 | 2827 | |
michael@0 | 2828 | bool isMutable; |
michael@0 | 2829 | rv = stream->ReadBoolean(&isMutable); |
michael@0 | 2830 | if (NS_FAILED(rv)) return rv; |
michael@0 | 2831 | mMutable = isMutable; |
michael@0 | 2832 | |
michael@0 | 2833 | bool supportsFileURL; |
michael@0 | 2834 | rv = stream->ReadBoolean(&supportsFileURL); |
michael@0 | 2835 | if (NS_FAILED(rv)) return rv; |
michael@0 | 2836 | mSupportsFileURL = supportsFileURL; |
michael@0 | 2837 | |
michael@0 | 2838 | uint32_t hostEncoding; |
michael@0 | 2839 | rv = stream->Read32(&hostEncoding); |
michael@0 | 2840 | if (NS_FAILED(rv)) return rv; |
michael@0 | 2841 | if (hostEncoding != eEncoding_ASCII && hostEncoding != eEncoding_UTF8) { |
michael@0 | 2842 | NS_WARNING("Unexpected host encoding"); |
michael@0 | 2843 | return NS_ERROR_UNEXPECTED; |
michael@0 | 2844 | } |
michael@0 | 2845 | mHostEncoding = hostEncoding; |
michael@0 | 2846 | |
michael@0 | 2847 | // wait until object is set up, then modify path to include the param |
michael@0 | 2848 | if (old_param.mLen >= 0) { // note that mLen=0 is ";" |
michael@0 | 2849 | // If this wasn't empty, it marks characters between the end of the |
michael@0 | 2850 | // file and start of the query - mPath should include the param, |
michael@0 | 2851 | // query and ref already. Bump the mFilePath and |
michael@0 | 2852 | // directory/basename/extension components to include this. |
michael@0 | 2853 | mFilepath.Merge(mSpec, ';', old_param); |
michael@0 | 2854 | mDirectory.Merge(mSpec, ';', old_param); |
michael@0 | 2855 | mBasename.Merge(mSpec, ';', old_param); |
michael@0 | 2856 | mExtension.Merge(mSpec, ';', old_param); |
michael@0 | 2857 | } |
michael@0 | 2858 | |
michael@0 | 2859 | return NS_OK; |
michael@0 | 2860 | } |
michael@0 | 2861 | |
michael@0 | 2862 | NS_IMETHODIMP |
michael@0 | 2863 | nsStandardURL::Write(nsIObjectOutputStream *stream) |
michael@0 | 2864 | { |
michael@0 | 2865 | nsresult rv; |
michael@0 | 2866 | |
michael@0 | 2867 | rv = stream->Write32(mURLType); |
michael@0 | 2868 | if (NS_FAILED(rv)) return rv; |
michael@0 | 2869 | |
michael@0 | 2870 | rv = stream->Write32(uint32_t(mPort)); |
michael@0 | 2871 | if (NS_FAILED(rv)) return rv; |
michael@0 | 2872 | |
michael@0 | 2873 | rv = stream->Write32(uint32_t(mDefaultPort)); |
michael@0 | 2874 | if (NS_FAILED(rv)) return rv; |
michael@0 | 2875 | |
michael@0 | 2876 | rv = NS_WriteOptionalStringZ(stream, mSpec.get()); |
michael@0 | 2877 | if (NS_FAILED(rv)) return rv; |
michael@0 | 2878 | |
michael@0 | 2879 | rv = WriteSegment(stream, mScheme); |
michael@0 | 2880 | if (NS_FAILED(rv)) return rv; |
michael@0 | 2881 | |
michael@0 | 2882 | rv = WriteSegment(stream, mAuthority); |
michael@0 | 2883 | if (NS_FAILED(rv)) return rv; |
michael@0 | 2884 | |
michael@0 | 2885 | rv = WriteSegment(stream, mUsername); |
michael@0 | 2886 | if (NS_FAILED(rv)) return rv; |
michael@0 | 2887 | |
michael@0 | 2888 | rv = WriteSegment(stream, mPassword); |
michael@0 | 2889 | if (NS_FAILED(rv)) return rv; |
michael@0 | 2890 | |
michael@0 | 2891 | rv = WriteSegment(stream, mHost); |
michael@0 | 2892 | if (NS_FAILED(rv)) return rv; |
michael@0 | 2893 | |
michael@0 | 2894 | rv = WriteSegment(stream, mPath); |
michael@0 | 2895 | if (NS_FAILED(rv)) return rv; |
michael@0 | 2896 | |
michael@0 | 2897 | rv = WriteSegment(stream, mFilepath); |
michael@0 | 2898 | if (NS_FAILED(rv)) return rv; |
michael@0 | 2899 | |
michael@0 | 2900 | rv = WriteSegment(stream, mDirectory); |
michael@0 | 2901 | if (NS_FAILED(rv)) return rv; |
michael@0 | 2902 | |
michael@0 | 2903 | rv = WriteSegment(stream, mBasename); |
michael@0 | 2904 | if (NS_FAILED(rv)) return rv; |
michael@0 | 2905 | |
michael@0 | 2906 | rv = WriteSegment(stream, mExtension); |
michael@0 | 2907 | if (NS_FAILED(rv)) return rv; |
michael@0 | 2908 | |
michael@0 | 2909 | // for backwards compatibility since we removed mParam. Note that this will mean that |
michael@0 | 2910 | // an older browser will read "" for mParam, and the param(s) will be part of mPath (as they |
michael@0 | 2911 | // after the removal of special handling). It only matters if you downgrade a browser to before |
michael@0 | 2912 | // the patch. |
michael@0 | 2913 | URLSegment empty; |
michael@0 | 2914 | rv = WriteSegment(stream, empty); |
michael@0 | 2915 | if (NS_FAILED(rv)) return rv; |
michael@0 | 2916 | |
michael@0 | 2917 | rv = WriteSegment(stream, mQuery); |
michael@0 | 2918 | if (NS_FAILED(rv)) return rv; |
michael@0 | 2919 | |
michael@0 | 2920 | rv = WriteSegment(stream, mRef); |
michael@0 | 2921 | if (NS_FAILED(rv)) return rv; |
michael@0 | 2922 | |
michael@0 | 2923 | rv = NS_WriteOptionalStringZ(stream, mOriginCharset.get()); |
michael@0 | 2924 | if (NS_FAILED(rv)) return rv; |
michael@0 | 2925 | |
michael@0 | 2926 | rv = stream->WriteBoolean(mMutable); |
michael@0 | 2927 | if (NS_FAILED(rv)) return rv; |
michael@0 | 2928 | |
michael@0 | 2929 | rv = stream->WriteBoolean(mSupportsFileURL); |
michael@0 | 2930 | if (NS_FAILED(rv)) return rv; |
michael@0 | 2931 | |
michael@0 | 2932 | rv = stream->Write32(mHostEncoding); |
michael@0 | 2933 | if (NS_FAILED(rv)) return rv; |
michael@0 | 2934 | |
michael@0 | 2935 | // mSpecEncoding and mHostA are just caches that can be recovered as needed. |
michael@0 | 2936 | |
michael@0 | 2937 | return NS_OK; |
michael@0 | 2938 | } |
michael@0 | 2939 | |
michael@0 | 2940 | //--------------------------------------------------------------------------- |
michael@0 | 2941 | // nsStandardURL::nsIIPCSerializableURI |
michael@0 | 2942 | //--------------------------------------------------------------------------- |
michael@0 | 2943 | |
michael@0 | 2944 | inline |
michael@0 | 2945 | mozilla::ipc::StandardURLSegment |
michael@0 | 2946 | ToIPCSegment(const nsStandardURL::URLSegment& aSegment) |
michael@0 | 2947 | { |
michael@0 | 2948 | return mozilla::ipc::StandardURLSegment(aSegment.mPos, aSegment.mLen); |
michael@0 | 2949 | } |
michael@0 | 2950 | |
michael@0 | 2951 | inline |
michael@0 | 2952 | nsStandardURL::URLSegment |
michael@0 | 2953 | FromIPCSegment(const mozilla::ipc::StandardURLSegment& aSegment) |
michael@0 | 2954 | { |
michael@0 | 2955 | return nsStandardURL::URLSegment(aSegment.position(), aSegment.length()); |
michael@0 | 2956 | } |
michael@0 | 2957 | |
michael@0 | 2958 | void |
michael@0 | 2959 | nsStandardURL::Serialize(URIParams& aParams) |
michael@0 | 2960 | { |
michael@0 | 2961 | StandardURLParams params; |
michael@0 | 2962 | |
michael@0 | 2963 | params.urlType() = mURLType; |
michael@0 | 2964 | params.port() = mPort; |
michael@0 | 2965 | params.defaultPort() = mDefaultPort; |
michael@0 | 2966 | params.spec() = mSpec; |
michael@0 | 2967 | params.scheme() = ToIPCSegment(mScheme); |
michael@0 | 2968 | params.authority() = ToIPCSegment(mAuthority); |
michael@0 | 2969 | params.username() = ToIPCSegment(mUsername); |
michael@0 | 2970 | params.password() = ToIPCSegment(mPassword); |
michael@0 | 2971 | params.host() = ToIPCSegment(mHost); |
michael@0 | 2972 | params.path() = ToIPCSegment(mPath); |
michael@0 | 2973 | params.filePath() = ToIPCSegment(mFilepath); |
michael@0 | 2974 | params.directory() = ToIPCSegment(mDirectory); |
michael@0 | 2975 | params.baseName() = ToIPCSegment(mBasename); |
michael@0 | 2976 | params.extension() = ToIPCSegment(mExtension); |
michael@0 | 2977 | params.query() = ToIPCSegment(mQuery); |
michael@0 | 2978 | params.ref() = ToIPCSegment(mRef); |
michael@0 | 2979 | params.originCharset() = mOriginCharset; |
michael@0 | 2980 | params.isMutable() = !!mMutable; |
michael@0 | 2981 | params.supportsFileURL() = !!mSupportsFileURL; |
michael@0 | 2982 | params.hostEncoding() = mHostEncoding; |
michael@0 | 2983 | // mSpecEncoding and mHostA are just caches that can be recovered as needed. |
michael@0 | 2984 | |
michael@0 | 2985 | aParams = params; |
michael@0 | 2986 | } |
michael@0 | 2987 | |
michael@0 | 2988 | bool |
michael@0 | 2989 | nsStandardURL::Deserialize(const URIParams& aParams) |
michael@0 | 2990 | { |
michael@0 | 2991 | NS_PRECONDITION(!mHostA, "Shouldn't have cached ASCII host"); |
michael@0 | 2992 | NS_PRECONDITION(mSpecEncoding == eEncoding_Unknown, |
michael@0 | 2993 | "Shouldn't have spec encoding here"); |
michael@0 | 2994 | NS_PRECONDITION(!mFile, "Shouldn't have cached file"); |
michael@0 | 2995 | |
michael@0 | 2996 | if (aParams.type() != URIParams::TStandardURLParams) { |
michael@0 | 2997 | NS_ERROR("Received unknown parameters from the other process!"); |
michael@0 | 2998 | return false; |
michael@0 | 2999 | } |
michael@0 | 3000 | |
michael@0 | 3001 | const StandardURLParams& params = aParams.get_StandardURLParams(); |
michael@0 | 3002 | |
michael@0 | 3003 | mURLType = params.urlType(); |
michael@0 | 3004 | switch (mURLType) { |
michael@0 | 3005 | case URLTYPE_STANDARD: |
michael@0 | 3006 | mParser = net_GetStdURLParser(); |
michael@0 | 3007 | break; |
michael@0 | 3008 | case URLTYPE_AUTHORITY: |
michael@0 | 3009 | mParser = net_GetAuthURLParser(); |
michael@0 | 3010 | break; |
michael@0 | 3011 | case URLTYPE_NO_AUTHORITY: |
michael@0 | 3012 | mParser = net_GetNoAuthURLParser(); |
michael@0 | 3013 | break; |
michael@0 | 3014 | default: |
michael@0 | 3015 | NS_NOTREACHED("bad urlType"); |
michael@0 | 3016 | return false; |
michael@0 | 3017 | } |
michael@0 | 3018 | |
michael@0 | 3019 | if (params.hostEncoding() != eEncoding_ASCII && |
michael@0 | 3020 | params.hostEncoding() != eEncoding_UTF8) { |
michael@0 | 3021 | NS_WARNING("Unexpected host encoding"); |
michael@0 | 3022 | return false; |
michael@0 | 3023 | } |
michael@0 | 3024 | |
michael@0 | 3025 | mPort = params.port(); |
michael@0 | 3026 | mDefaultPort = params.defaultPort(); |
michael@0 | 3027 | mSpec = params.spec(); |
michael@0 | 3028 | mScheme = FromIPCSegment(params.scheme()); |
michael@0 | 3029 | mAuthority = FromIPCSegment(params.authority()); |
michael@0 | 3030 | mUsername = FromIPCSegment(params.username()); |
michael@0 | 3031 | mPassword = FromIPCSegment(params.password()); |
michael@0 | 3032 | mHost = FromIPCSegment(params.host()); |
michael@0 | 3033 | mPath = FromIPCSegment(params.path()); |
michael@0 | 3034 | mFilepath = FromIPCSegment(params.filePath()); |
michael@0 | 3035 | mDirectory = FromIPCSegment(params.directory()); |
michael@0 | 3036 | mBasename = FromIPCSegment(params.baseName()); |
michael@0 | 3037 | mExtension = FromIPCSegment(params.extension()); |
michael@0 | 3038 | mQuery = FromIPCSegment(params.query()); |
michael@0 | 3039 | mRef = FromIPCSegment(params.ref()); |
michael@0 | 3040 | mOriginCharset = params.originCharset(); |
michael@0 | 3041 | mMutable = params.isMutable(); |
michael@0 | 3042 | mSupportsFileURL = params.supportsFileURL(); |
michael@0 | 3043 | mHostEncoding = params.hostEncoding(); |
michael@0 | 3044 | |
michael@0 | 3045 | // mSpecEncoding and mHostA are just caches that can be recovered as needed. |
michael@0 | 3046 | return true; |
michael@0 | 3047 | } |
michael@0 | 3048 | |
michael@0 | 3049 | //---------------------------------------------------------------------------- |
michael@0 | 3050 | // nsStandardURL::nsIClassInfo |
michael@0 | 3051 | //---------------------------------------------------------------------------- |
michael@0 | 3052 | |
michael@0 | 3053 | NS_IMETHODIMP |
michael@0 | 3054 | nsStandardURL::GetInterfaces(uint32_t *count, nsIID * **array) |
michael@0 | 3055 | { |
michael@0 | 3056 | *count = 0; |
michael@0 | 3057 | *array = nullptr; |
michael@0 | 3058 | return NS_OK; |
michael@0 | 3059 | } |
michael@0 | 3060 | |
michael@0 | 3061 | NS_IMETHODIMP |
michael@0 | 3062 | nsStandardURL::GetHelperForLanguage(uint32_t language, nsISupports **_retval) |
michael@0 | 3063 | { |
michael@0 | 3064 | *_retval = nullptr; |
michael@0 | 3065 | return NS_OK; |
michael@0 | 3066 | } |
michael@0 | 3067 | |
michael@0 | 3068 | NS_IMETHODIMP |
michael@0 | 3069 | nsStandardURL::GetContractID(char * *aContractID) |
michael@0 | 3070 | { |
michael@0 | 3071 | *aContractID = nullptr; |
michael@0 | 3072 | return NS_OK; |
michael@0 | 3073 | } |
michael@0 | 3074 | |
michael@0 | 3075 | NS_IMETHODIMP |
michael@0 | 3076 | nsStandardURL::GetClassDescription(char * *aClassDescription) |
michael@0 | 3077 | { |
michael@0 | 3078 | *aClassDescription = nullptr; |
michael@0 | 3079 | return NS_OK; |
michael@0 | 3080 | } |
michael@0 | 3081 | |
michael@0 | 3082 | NS_IMETHODIMP |
michael@0 | 3083 | nsStandardURL::GetClassID(nsCID * *aClassID) |
michael@0 | 3084 | { |
michael@0 | 3085 | *aClassID = (nsCID*) nsMemory::Alloc(sizeof(nsCID)); |
michael@0 | 3086 | if (!*aClassID) |
michael@0 | 3087 | return NS_ERROR_OUT_OF_MEMORY; |
michael@0 | 3088 | return GetClassIDNoAlloc(*aClassID); |
michael@0 | 3089 | } |
michael@0 | 3090 | |
michael@0 | 3091 | NS_IMETHODIMP |
michael@0 | 3092 | nsStandardURL::GetImplementationLanguage(uint32_t *aImplementationLanguage) |
michael@0 | 3093 | { |
michael@0 | 3094 | *aImplementationLanguage = nsIProgrammingLanguage::CPLUSPLUS; |
michael@0 | 3095 | return NS_OK; |
michael@0 | 3096 | } |
michael@0 | 3097 | |
michael@0 | 3098 | NS_IMETHODIMP |
michael@0 | 3099 | nsStandardURL::GetFlags(uint32_t *aFlags) |
michael@0 | 3100 | { |
michael@0 | 3101 | *aFlags = nsIClassInfo::MAIN_THREAD_ONLY; |
michael@0 | 3102 | return NS_OK; |
michael@0 | 3103 | } |
michael@0 | 3104 | |
michael@0 | 3105 | NS_IMETHODIMP |
michael@0 | 3106 | nsStandardURL::GetClassIDNoAlloc(nsCID *aClassIDNoAlloc) |
michael@0 | 3107 | { |
michael@0 | 3108 | *aClassIDNoAlloc = kStandardURLCID; |
michael@0 | 3109 | return NS_OK; |
michael@0 | 3110 | } |
michael@0 | 3111 | |
michael@0 | 3112 | //---------------------------------------------------------------------------- |
michael@0 | 3113 | // nsStandardURL::nsISizeOf |
michael@0 | 3114 | //---------------------------------------------------------------------------- |
michael@0 | 3115 | |
michael@0 | 3116 | size_t |
michael@0 | 3117 | nsStandardURL::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const |
michael@0 | 3118 | { |
michael@0 | 3119 | return mSpec.SizeOfExcludingThisIfUnshared(aMallocSizeOf) + |
michael@0 | 3120 | mOriginCharset.SizeOfExcludingThisIfUnshared(aMallocSizeOf) + |
michael@0 | 3121 | aMallocSizeOf(mHostA); |
michael@0 | 3122 | |
michael@0 | 3123 | // Measurement of the following members may be added later if DMD finds it is |
michael@0 | 3124 | // worthwhile: |
michael@0 | 3125 | // - mParser |
michael@0 | 3126 | // - mFile |
michael@0 | 3127 | } |
michael@0 | 3128 | |
michael@0 | 3129 | size_t |
michael@0 | 3130 | nsStandardURL::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const { |
michael@0 | 3131 | return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); |
michael@0 | 3132 | } |