/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim:set ts=4 sw=2 sts=2 et cindent: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// The Chromium code defines its own LOG macro which we don't want #undef LOG #define LOG(args) MOZ_LOG(gStandardURLLog, LogLevel::Debug, args) #undef LOG_ENABLED #define LOG_ENABLED() MOZ_LOG_TEST(gStandardURLLog, LogLevel::Debug)
usingnamespace mozilla::ipc;
/** * The UTS #46 ToUnicode operation as parametrized by the WHATWG URL Standard, * except potentially misleading labels are treated according to ToASCII * instead. Combined with the ToASCII operation without rerunning the expensive * part. * * NOTE: This function performs percent-decoding on the argument unlike * the other `NS_DomainTo` functions! * * If upon successfull return `aASCII` is empty, it is the caller's * responsibility to treat the value of `aDisplay` also as the value of * `aASCII`. (The weird semantics avoid useless allocation / copying.) * * Rust callers that don't happen to be using XPCOM strings are better * off using the `idna` crate directly. (See `idna_glue` for what policy * closure to use.)
*/ inline nsresult NS_DomainToDisplayAndASCII(const nsACString& aDomain,
nsACString& aDisplay,
nsACString& aASCII) { return mozilla_net_domain_to_display_and_ascii_impl(&aDomain, &aDisplay,
&aASCII);
}
// This will always be initialized and destroyed on the main thread, but // can be safely used on other threads.
StaticRefPtr<nsIIDNService> nsStandardURL::gIDN;
int32_t nsStandardURL::nsSegmentEncoder::EncodeSegmentCount( constchar* aStr, const URLSegment& aSeg, int16_t aMask, nsCString& aOut, bool& aAppended, uint32_t aExtraLen) { // aExtraLen is characters outside the segment that will be // added when the segment is not empty (like the @ following // a username). if (!aStr || aSeg.mLen <= 0) { // Empty segment, so aExtraLen not added per above.
aAppended = false; return 0;
}
// first honor the origin charset if appropriate. as an optimization, // only do this if the segment is non-ASCII. Further, if mEncoding is // null, then the origin charset is UTF-8 and there is nothing to do. if (mEncoding) {
size_t upTo; if (MOZ_UNLIKELY(mEncoding == ISO_2022_JP_ENCODING)) {
upTo = Encoding::ISO2022JPASCIIValidUpTo(AsBytes(span));
} else {
upTo = Encoding::ASCIIValidUpTo(AsBytes(span));
} if (upTo != span.Length()) { // we have to encode this segment char bufferArr[512];
Span<char> buffer = Span(bufferArr);
auto encoder = mEncoding->NewEncoder();
nsAutoCString valid; // has to be declared in this scope if (MOZ_UNLIKELY(!IsUtf8(span.From(upTo)))) {
MOZ_ASSERT_UNREACHABLE("Invalid UTF-8 passed to nsStandardURL."); // It's UB to pass invalid UTF-8 to // EncodeFromUTF8WithoutReplacement(), so let's make our input valid // UTF-8 by replacing invalid sequences with the REPLACEMENT // CHARACTER.
UTF_8_ENCODING->Decode(
nsDependentCSubstring(span.Elements(), span.Length()), valid); // This assigment is OK. `span` can't be used after `valid` has // been destroyed because the only way out of the scope that `valid` // was declared in is via return inside the loop below. Specifically, // the return is the only way out of the loop.
span = valid;
}
size_t totalRead = 0; for (;;) { auto [encoderResult, read, written] =
encoder->EncodeFromUTF8WithoutReplacement(
AsBytes(span.From(totalRead)), AsWritableBytes(buffer), true);
totalRead += read; auto bufferWritten = buffer.To(written); if (!NS_EscapeURLSpan(bufferWritten, aMask, aOut)) {
aOut.Append(bufferWritten);
} if (encoderResult == kInputEmpty) {
aAppended = true; // Difference between original and current output // string lengths plus extra length return aOut.Length() - origLen + aExtraLen;
} if (encoderResult == kOutputFull) { continue;
}
aOut.AppendLiteral("%26%23");
aOut.AppendInt(encoderResult);
aOut.AppendLiteral("%3B");
}
MOZ_RELEASE_ASSERT( false, "There's supposed to be no way out of the above loop except return.");
}
}
if (NS_EscapeURLSpan(span, aMask, aOut)) {
aAppended = true; // Difference between original and current output // string lengths plus extra length return aOut.Length() - origLen + aExtraLen;
}
aAppended = false; // Original segment length plus extra length return span.Length() + aExtraLen;
}
// gInitialized changes value only once (false->true) on the main thread. // It's OK to race here because in the worst case we'll just // dispatch a noop runnable to the main thread.
MOZ_ASSERT(gInitialized);
// default parser in case nsIStandardURL::Init is never called
mParser = net_GetStdURLParser();
bool nsStandardURL::IsValid() { auto checkSegment = [&](const nsStandardURL::URLSegment& aSeg) { #ifdef EARLY_BETA_OR_EARLIER // If the parity is not the same, we assume that this is caused by a memory // error. In this case, we think this URLSegment is valid. if ((aSeg.mPos.Parity() != aSeg.mPos.CalculateParity()) ||
(aSeg.mLen.Parity() != aSeg.mLen.CalculateParity())) {
MOZ_ASSERT(false); returntrue;
} #endif // Bad value if (NS_WARN_IF(aSeg.mLen < -1)) { returnfalse;
} if (aSeg.mLen == -1) { returntrue;
}
// Points out of string if (NS_WARN_IF(aSeg.mPos + aSeg.mLen > mSpec.Length())) { returnfalse;
}
nsCOMPtr<nsIIDNService> serv(do_GetService(NS_IDNSERVICE_CONTRACTID)); if (serv) {
gIDN = serv;
}
MOZ_DIAGNOSTIC_ASSERT(gIDN);
// Make sure nsURLHelper::InitGlobals() gets called on the main thread
nsCOMPtr<nsIURLParser> parser = net_GetStdURLParser();
MOZ_DIAGNOSTIC_ASSERT(parser);
Unused << parser;
}
#ifdef DEBUG_DUMP_URLS_AT_SHUTDOWN if (gInitialized) { // This instanciates a dummy class, and will trigger the class // destructor when libxul is unloaded. This is equivalent to atexit(), // but gracefully handles dlclose().
StaticMutexAutoLock lock(gAllURLsMutex); static DumpLeakedURLs d;
} #endif
}
nsresult nsStandardURL::NormalizeIDN(const nsACString& aHost,
nsACString& aResult) {
mDisplayHost.Truncate();
mCheckedIfHostA = true;
nsCString displayHost; // Intentionally not nsAutoCString to avoid copy when // assigning to field
nsresult rv = NS_DomainToDisplayAndASCII(aHost, displayHost, aResult); if (NS_FAILED(rv)) { return rv;
} if (aResult.IsEmpty()) {
aResult.Assign(displayHost);
} else {
mDisplayHost = displayHost;
} return NS_OK;
}
void nsStandardURL::CoalescePath(netCoalesceFlags coalesceFlag, char* path) { auto resultCoalesceDirs = net_CoalesceDirs(coalesceFlag, path);
int32_t newLen = strlen(path); if (newLen < mPath.mLen && resultCoalesceDirs) { // Obtain indices for the last slash and the end of the basename
uint32_t lastSlash = resultCoalesceDirs->first();
uint32_t endOfBasename = resultCoalesceDirs->second();
// The directory length includes all characters up to and // including the last slash
mDirectory.mLen = static_cast<int32_t>(lastSlash) + 1;
// basename length includes everything after the last slash // until hash, query, or the null char. However, if there was an extension // we must make sure to update the length.
mBasename.mLen = static_cast<int32_t>(endOfBasename - mDirectory.mLen); if (mExtension.mLen >= 0) {
mBasename.mLen -= 1; // Length of the . character
mBasename.mLen -= mExtension.mLen;
}
mBasename.mPos = mDirectory.mPos + mDirectory.mLen;
// Adjust the positions of extension, query, and ref as needed // This is possible because net_CoalesceDirs does not modify their lengths
ShiftFromExtension(diff);
// // escape each URL segment, if necessary, and calculate approximate normalized // spec length. // // [scheme://][username[:password]@]host[:port]/path[?query_string][#ref]
uint32_t approxLen = 0;
// the scheme is already ASCII if (mScheme.mLen > 0) {
approxLen +=
mScheme.mLen + 3; // includes room for "://", which we insert always
}
// encode URL segments; convert UTF-8 to origin charset and possibly escape. // results written to encXXX variables only if |spec| is not already in the // appropriate encoding.
{
nsSegmentEncoder encoder;
nsSegmentEncoder queryEncoder(encoding); // Username@
approxLen += encoder.EncodeSegmentCount(spec, mUsername, esc_Username,
encUsername, useEncUsername, 0);
approxLen += 1; // reserve length for @ // :password - we insert the ':' even if there's no actual password if // "user:@" was in the spec if (mPassword.mLen > 0) {
approxLen += 1 + encoder.EncodeSegmentCount(spec, mPassword, esc_Password,
encPassword, useEncPassword);
} // mHost is handled differently below due to encoding differences
MOZ_ASSERT(mPort >= -1, "Invalid negative mPort"); if (mPort != -1 && mPort != mDefaultPort) { // :port
portbuf.AppendInt(mPort);
approxLen += portbuf.Length() + 1;
}
approxLen +=
1; // reserve space for possible leading '/' - may not be needed // Should just use mPath? These are pessimistic, and thus waste space
approxLen += encoder.EncodeSegmentCount(spec, mDirectory, esc_Directory,
encDirectory, useEncDirectory, 1);
approxLen += encoder.EncodeSegmentCount(spec, mBasename, esc_FileBaseName,
encBasename, useEncBasename);
approxLen += encoder.EncodeSegmentCount(spec, mExtension, esc_FileExtension,
encExtension, useEncExtension, 1);
// These next ones *always* add their leading character even if length is 0 // Handles items like "http://#" // ?query if (mQuery.mLen >= 0) {
approxLen += 1 + queryEncoder.EncodeSegmentCount(spec, mQuery, esc_Query,
encQuery, useEncQuery);
} // #ref
// do not escape the hostname, if IPv6 address literal, mHost will // already point to a [ ] delimited IPv6 address literal. // However, perform Unicode normalization on it, as IDN does. // Note that we don't disallow URLs without a host - file:, etc if (mHost.mLen > 0) {
nsDependentCSubstring tempHost(spec + mHost.mPos, mHost.mLen);
nsresult rv; bool allowIp = !SegmentIs(spec, mScheme, "resource") &&
!SegmentIs(spec, mScheme, "chrome"); if (tempHost.First() == '[' && allowIp) {
mCheckedIfHostA = true;
rv = (nsresult)rusturl_parse_ipv6addr(&tempHost, &encHost); if (NS_FAILED(rv)) { return rv;
}
} else {
rv = NormalizeIDN(tempHost, encHost); if (NS_FAILED(rv)) { return rv;
} if (IPv4Parser::EndsInANumber(encHost) && allowIp) {
nsAutoCString ipString;
rv = IPv4Parser::NormalizeIPv4(encHost, ipString); if (NS_FAILED(rv)) { return rv;
}
encHost = ipString;
}
}
// NormalizeIDN always copies, if the call was successful.
useEncHost = true;
approxLen += encHost.Length();
} else { // empty host means empty mDisplayHost
mDisplayHost.Truncate();
mCheckedIfHostA = true;
}
// We must take a copy of every single segment because they are pointing to // the |spec| while we are changing their value, in case we must use // encoded strings.
URLSegment username(mUsername);
URLSegment password(mPassword);
URLSegment host(mHost);
URLSegment path(mPath);
URLSegment directory(mDirectory);
URLSegment basename(mBasename);
URLSegment extension(mExtension);
URLSegment query(mQuery);
URLSegment ref(mRef);
// The encoded string could be longer than the original input, so we need // to check the final URI isn't longer than the max length. if (approxLen + 1 > StaticPrefs::network_standard_url_max_length()) { return NS_ERROR_MALFORMED_URI;
}
// // generate the normalized URL string // // approxLen should be correct or 1 high if (!mSpec.SetLength(approxLen + 1,
fallible)) { // buf needs a trailing '\0' below return NS_ERROR_OUT_OF_MEMORY;
} char* buf = mSpec.BeginWriting();
uint32_t i = 0;
int32_t diff = 0;
if (mScheme.mLen > 0) {
i = AppendSegmentToBuf(buf, i, spec, mScheme, mScheme);
net_ToLowerCase(buf + mScheme.mPos, mScheme.mLen);
i = AppendToBuf(buf, i, "://", 3);
}
// record authority starting position
mAuthority.mPos = i;
// append authority if (mUsername.mLen > 0 || mPassword.mLen > 0) { if (mUsername.mLen > 0) {
i = AppendSegmentToBuf(buf, i, spec, username, mUsername, &encUsername,
useEncUsername, &diff);
ShiftFromPassword(diff);
} else {
mUsername.mLen = -1;
} if (password.mLen > 0) {
buf[i++] = ':';
i = AppendSegmentToBuf(buf, i, spec, password, mPassword, &encPassword,
useEncPassword, &diff);
ShiftFromHost(diff);
} else {
mPassword.mLen = -1;
}
buf[i++] = '@';
} else {
mUsername.mLen = -1;
mPassword.mLen = -1;
} if (host.mLen > 0) {
i = AppendSegmentToBuf(buf, i, spec, host, mHost, &encHost, useEncHost,
&diff);
ShiftFromPath(diff);
MOZ_ASSERT(mPort >= -1, "Invalid negative mPort"); if (mPort != -1 && mPort != mDefaultPort) {
buf[i++] = ':'; // Already formatted while building approxLen
i = AppendToBuf(buf, i, portbuf.get(), portbuf.Length());
}
}
// record authority length
mAuthority.mLen = i - mAuthority.mPos;
// path must always start with a "/" if (mPath.mLen <= 0) {
LOG(("setting path=/"));
mDirectory.mPos = mFilepath.mPos = mPath.mPos = i;
mDirectory.mLen = mFilepath.mLen = mPath.mLen = 1; // basename must exist, even if empty (bug 113508)
mBasename.mPos = i + 1;
mBasename.mLen = 0;
buf[i++] = '/';
} else {
uint32_t leadingSlash = 0; if (spec[path.mPos] != '/') {
LOG(("adding leading slash to path\n"));
leadingSlash = 1;
buf[i++] = '/'; // basename must exist, even if empty (bugs 113508, 429347) if (mBasename.mLen == -1) {
mBasename.mPos = basename.mPos = i;
mBasename.mLen = basename.mLen = 0;
}
}
// record corrected (file)path starting position
mPath.mPos = mFilepath.mPos = i - leadingSlash;
i = AppendSegmentToBuf(buf, i, spec, directory, mDirectory, &encDirectory,
useEncDirectory, &diff);
ShiftFromBasename(diff);
// the directory must end with a '/' if (buf[i - 1] != '/') {
buf[i++] = '/';
mDirectory.mLen++;
}
i = AppendSegmentToBuf(buf, i, spec, basename, mBasename, &encBasename,
useEncBasename, &diff);
ShiftFromExtension(diff);
// make corrections to directory segment if leadingSlash if (leadingSlash) {
mDirectory.mPos = mPath.mPos; if (mDirectory.mLen >= 0) {
mDirectory.mLen += leadingSlash;
} else {
mDirectory.mLen = 1;
}
}
if (mExtension.mLen >= 0) {
buf[i++] = '.';
i = AppendSegmentToBuf(buf, i, spec, extension, mExtension, &encExtension,
useEncExtension, &diff);
ShiftFromQuery(diff);
} // calculate corrected filepath length
mFilepath.mLen = i - mFilepath.mPos;
if (mQuery.mLen >= 0) {
buf[i++] = '?';
i = AppendSegmentToBuf(buf, i, spec, query, mQuery, &encQuery,
useEncQuery, &diff);
ShiftFromRef(diff);
} if (mRef.mLen >= 0) {
buf[i++] = '#';
i = AppendSegmentToBuf(buf, i, spec, ref, mRef, &encRef, useEncRef,
&diff);
} // calculate corrected path length
mPath.mLen = i - mPath.mPos;
}
buf[i] = '\0';
// https://url.spec.whatwg.org/#path-state (1.4.1.2) // https://url.spec.whatwg.org/#windows-drive-letter if (SegmentIs(buf, mScheme, "file")) { char* path = &buf[mPath.mPos]; // To account for cases like file:///w|/m and file:///c| if (mPath.mLen >= 3 && path[0] == '/' && IsAsciiAlpha(path[1]) &&
path[2] == '|' && (mPath.mLen == 3 || path[3] == '/')) {
buf[mPath.mPos + 2] = ':';
}
}
if (mDirectory.mLen > 0) {
netCoalesceFlags coalesceFlag = NET_COALESCE_NORMAL; if (SegmentIs(buf, mScheme, "ftp")) {
coalesceFlag =
(netCoalesceFlags)(coalesceFlag | NET_COALESCE_ALLOW_RELATIVE_ROOT |
NET_COALESCE_DOUBLE_SLASH_IS_ROOT);
}
CoalescePath(coalesceFlag, buf + mDirectory.mPos);
}
mSpec.Truncate(strlen(buf));
NS_ASSERTION(mSpec.Length() <= approxLen, "We've overflowed the mSpec buffer!");
MOZ_ASSERT(mSpec.Length() <= StaticPrefs::network_standard_url_max_length(), "The spec should never be this long, we missed a check.");
bool nsStandardURL::SegmentIs(const URLSegment& seg, constchar* val, bool ignoreCase) { // one or both may be null if (!val || mSpec.IsEmpty()) { return (!val && (mSpec.IsEmpty() || seg.mLen < 0));
} if (seg.mLen < 0) { returnfalse;
} // if the first |seg.mLen| chars of |val| match, then |val| must // also be null terminated at |seg.mLen|. if (ignoreCase) { return !nsCRT::strncasecmp(mSpec.get() + seg.mPos, val, seg.mLen) &&
(val[seg.mLen] == '\0');
}
bool nsStandardURL::SegmentIs(constchar* spec, const URLSegment& seg, constchar* val, bool ignoreCase) { // one or both may be null if (!val || !spec) { return (!val && (!spec || seg.mLen < 0));
} if (seg.mLen < 0) { returnfalse;
} // if the first |seg.mLen| chars of |val| match, then |val| must // also be null terminated at |seg.mLen|. if (ignoreCase) { return !nsCRT::strncasecmp(spec + seg.mPos, val, seg.mLen) &&
(val[seg.mLen] == '\0');
}
NS_IMPL_CLASSINFO(nsStandardURL, nullptr, nsIClassInfo::THREADSAFE,
NS_STANDARDURL_CID) // Empty CI getter. We only need nsIClassInfo for Serialization
NS_IMPL_CI_INTERFACE_GETTER0(nsStandardURL)
// result may contain unescaped UTF-8 characters
NS_IMETHODIMP
nsStandardURL::GetSpec(nsACString& result) {
MOZ_ASSERT(mSpec.Length() <= StaticPrefs::network_standard_url_max_length(), "The spec should never be this long, we missed a check.");
result = mSpec; return NS_OK;
}
// result may contain unescaped UTF-8 characters
NS_IMETHODIMP
nsStandardURL::GetSensitiveInfoHiddenSpec(nsACString& result) {
nsresult rv = GetSpec(result); if (NS_FAILED(rv)) { return rv;
} if (mPassword.mLen > 0) {
result.ReplaceLiteral(mPassword.mPos, mPassword.mLen, "****");
} return NS_OK;
}
// result may contain unescaped UTF-8 characters
NS_IMETHODIMP
nsStandardURL::GetSpecIgnoringRef(nsACString& result) { // URI without ref is 0 to one char before ref if (mRef.mLen < 0) { return GetSpec(result);
}
NS_IMETHODIMP
nsStandardURL::GetPort(int32_t* result) { // should never be more than 16 bit
MOZ_ASSERT(mPort <= std::numeric_limits<uint16_t>::max());
*result = mPort; return NS_OK;
}
// result may contain unescaped UTF-8 characters
NS_IMETHODIMP
nsStandardURL::GetPathQueryRef(nsACString& result) {
result = Path(); return NS_OK;
}
// result is ASCII
NS_IMETHODIMP
nsStandardURL::GetAsciiSpec(nsACString& result) {
result = mSpec; return NS_OK;
}
// result is ASCII
NS_IMETHODIMP
nsStandardURL::GetAsciiHostPort(nsACString& result) {
result = Hostport(); return NS_OK;
}
// result is ASCII
NS_IMETHODIMP
nsStandardURL::GetAsciiHost(nsACString& result) {
result = Host(); return NS_OK;
}
if (input.Length() > StaticPrefs::network_standard_url_max_length()) { return NS_ERROR_MALFORMED_URI;
}
// filter out unexpected chars "\r\n\t" if necessary
nsAutoCString filteredURI;
net_FilterURIString(flat, filteredURI);
if (filteredURI.Length() == 0) { return NS_ERROR_MALFORMED_URI;
}
// Make a backup of the current URL
nsStandardURL prevURL(false, false);
prevURL.CopyMembers(this, eHonorRef, ""_ns);
Clear();
if (IsSpecialProtocol(filteredURI)) { // Bug 652186: Replace all backslashes with slashes when parsing paths // Stop when we reach the query or the hash. auto* start = filteredURI.BeginWriting(); auto* end = filteredURI.EndWriting(); while (start != end) { if (*start == '?' || *start == '#') { break;
} if (*start == '\\') {
*start = '/';
}
start++;
}
}
// parse the given URL...
nsresult rv = ParseURL(spec, specLength); if (mScheme.mLen <= 0) {
rv = NS_ERROR_MALFORMED_URI;
} if (NS_SUCCEEDED(rv)) { // finally, use the URLSegment member variables to build a normalized // copy of |spec|
rv = BuildNormalizedSpec(spec, encoding);
}
// Make sure that a URLTYPE_AUTHORITY has a non-empty hostname. if (mURLType == URLTYPE_AUTHORITY && mHost.mLen <= 0) {
rv = NS_ERROR_MALFORMED_URI;
}
if (NS_FAILED(rv)) {
Clear(); // If parsing the spec has failed, restore the old URL // so we don't end up with an empty URL.
CopyMembers(&prevURL, eHonorRef, ""_ns); return rv;
}
if (scheme.IsEmpty()) {
NS_WARNING("cannot remove the scheme from an url"); return NS_ERROR_UNEXPECTED;
} if (mScheme.mLen < 0) {
NS_WARNING("uninitialized"); return NS_ERROR_NOT_INITIALIZED;
}
if (!net_IsValidScheme(scheme)) {
NS_WARNING("the given url scheme contains invalid characters"); return NS_ERROR_UNEXPECTED;
}
if (shift) {
mScheme.mLen = scheme.Length();
ShiftFromAuthority(shift);
}
// ensure new scheme is lowercase // // XXX the string code unfortunately doesn't provide a ToLowerCase // that operates on a substring.
net_ToLowerCase((char*)mSpec.get(), mScheme.mLen);
// If the scheme changes the default port also changes. if (Scheme() == "http"_ns || Scheme() == "ws"_ns) {
mDefaultPort = 80;
} elseif (Scheme() == "https"_ns || Scheme() == "wss"_ns) {
mDefaultPort = 443;
} if (mPort == mDefaultPort) {
MOZ_ALWAYS_SUCCEEDS(SetPort(-1));
}
if (mURLType == URLTYPE_NO_AUTHORITY) { if (userpass.IsEmpty()) { return NS_OK;
}
NS_WARNING("cannot set user:pass on no-auth url"); return NS_ERROR_UNEXPECTED;
} if (mAuthority.mLen < 0) {
NS_WARNING("uninitialized"); return NS_ERROR_NOT_INITIALIZED;
} if (mAuthority.mLen == 0) { // If the URL doesn't have a hostname then setting the userpass to // empty string is a no-op. But setting it to anything else should // return an error. if (input.Length() == 0) { return NS_OK;
} else { return NS_ERROR_UNEXPECTED;
}
}
if (mURLType == URLTYPE_NO_AUTHORITY) { if (username.IsEmpty()) { return NS_OK;
}
NS_WARNING("cannot set username on no-auth url"); return NS_ERROR_UNEXPECTED;
} if (mAuthority.mLen == 0) { // If the URL doesn't have a hostname then setting the username to // empty string is a no-op. But setting it to anything else should // return an error. if (input.Length() == 0) { return NS_OK;
} else { return NS_ERROR_UNEXPECTED;
}
}
auto clearedPassword = MakeScopeExit([&password, this]() { // Check that if this method is called with the empty string then the // password is definitely cleared when exiting this method. if (password.IsEmpty()) {
MOZ_DIAGNOSTIC_ASSERT(this->Password().IsEmpty());
}
Unused << this; // silence compiler -Wunused-lambda-capture
});
auto onExitGuard = MakeScopeExit([&] { SanityCheck(); });
if (mURLType == URLTYPE_NO_AUTHORITY) { if (password.IsEmpty()) { return NS_OK;
}
NS_WARNING("cannot set password on no-auth url"); return NS_ERROR_UNEXPECTED;
} if (mAuthority.mLen == 0) { // If the URL doesn't have a hostname then setting the password to // empty string is a no-op. But setting it to anything else should // return an error. if (input.Length() == 0) { return NS_OK;
} else { return NS_ERROR_UNEXPECTED;
}
}
void nsStandardURL::FindHostLimit(nsACString::const_iterator& aStart,
nsACString::const_iterator& aEnd) { for (int32_t i = 0; gHostLimitDigits[i]; ++i) {
nsACString::const_iterator c(aStart); if (FindCharInReadable(gHostLimitDigits[i], c, aEnd)) {
aEnd = c;
}
}
}
// If aValue only has a host part and no port number, the port // will not be reset!!!
nsresult nsStandardURL::SetHostPort(const nsACString& aValue) { // We cannot simply call nsIURI::SetHost because that would treat the name as // an IPv6 address (like http:://[server:443]/). We also cannot call // nsIURI::SetHostPort because that isn't implemented. Sadfaces.
if (*start == '[') { // IPv6 address if (!FindCharInReadable(']', iter, end)) { // the ] character is missing return NS_ERROR_MALFORMED_URI;
} // iter now at the ']' character
isIPv6 = true;
} else {
nsACString::const_iterator iter2(start); if (FindCharInReadable(']', iter2, end)) { // if the first char isn't [ then there should be no ] character return NS_ERROR_MALFORMED_URI;
}
}
FindCharInReadable(':', iter, end);
if (!isIPv6 && iter != end) {
nsACString::const_iterator iter2(iter);
iter2++; // Skip over the first ':' character if (FindCharInReadable(':', iter2, end)) { // If there is more than one ':' character it suggests an IPv6 // The format should be [2001::1]:80 where the port is optional return NS_ERROR_MALFORMED_URI;
}
}
auto onExitGuard = MakeScopeExit([&] { SanityCheck(); });
if (iter == end) { // does not end in colon return NS_OK;
}
iter++; // advance over the colon if (iter == end) { // port number is missing return NS_OK;
}
nsCString portStr(Substring(iter, end));
int32_t port = portStr.ToInteger(&rv); if (NS_FAILED(rv)) { // Failure parsing the port number return NS_OK;
}
if (mURLType == URLTYPE_NO_AUTHORITY) { if (flat.IsEmpty()) { return NS_OK;
}
NS_WARNING("cannot set host on no-auth url"); return NS_ERROR_UNEXPECTED;
}
if (mURLType == URLTYPE_AUTHORITY && flat.IsEmpty()) { // Setting an empty hostname is not allowed for URLTYPE_AUTHORITY. return NS_ERROR_UNEXPECTED;
}
if ((port == mPort) || (mPort == -1 && port == mDefaultPort)) { return NS_OK;
}
// ports must be >= 0 and 16 bit // -1 == use default if (port < -1 || port > std::numeric_limits<uint16_t>::max()) { return NS_ERROR_MALFORMED_URI;
}
if (mURLType == URLTYPE_NO_AUTHORITY) {
NS_WARNING("cannot set port on no-auth url"); return NS_ERROR_UNEXPECTED;
} if (mAuthority.mLen == 0) { // If the URL doesn't have a hostname then setting the port to // -1 is a no-op. But setting it to anything else should // return an error. if (port == -1) { return NS_OK;
} else { return NS_ERROR_UNEXPECTED;
}
}
auto onExitGuard = MakeScopeExit([&] { SanityCheck(); });
InvalidateCache(); if (port == mDefaultPort) {
port = -1;
}
ReplacePortInSpec(port);
mPort = port; return NS_OK;
}
/** * Replaces the existing port in mSpec with aNewPort. * * The caller is responsible for: * - Calling InvalidateCache (since our mSpec is changing). * - Checking whether aNewPort is mDefaultPort (in which case the * caller should pass aNewPort=-1).
*/ void nsStandardURL::ReplacePortInSpec(int32_t aNewPort) {
NS_ASSERTION(aNewPort != mDefaultPort || mDefaultPort == -1, "Caller should check its passed-in value and pass -1 instead of " "mDefaultPort, to avoid encoding default port into mSpec");
auto onExitGuard = MakeScopeExit([&] { SanityCheck(); });
// Create the (possibly empty) string that we're planning to replace:
nsAutoCString buf; if (mPort != -1) {
buf.Assign(':');
buf.AppendInt(mPort);
} // Find the position & length of that string: const uint32_t replacedLen = buf.Length(); const uint32_t replacedStart =
mAuthority.mPos + mAuthority.mLen - replacedLen;
// Create the (possibly empty) replacement string: if (aNewPort == -1) {
buf.Truncate();
} else {
buf.Assign(':');
buf.AppendInt(aNewPort);
} // Perform the replacement:
mSpec.Replace(replacedStart, replacedLen, buf);
// Bookkeeping to reflect the new length:
int32_t shift = buf.Length() - replacedLen;
mAuthority.mLen += shift;
ShiftFromPath(shift);
}
spec.Assign(mSpec.get(), mPath.mPos); if (path.First() != '/') {
spec.Append('/');
}
spec.Append(path);
return SetSpecInternal(spec);
} if (mPath.mLen >= 1) {
mSpec.Cut(mPath.mPos + 1, mPath.mLen - 1); // these contain only a '/'
mPath.mLen = 1;
mDirectory.mLen = 1;
mFilepath.mLen = 1; // these are no longer defined
mBasename.mLen = -1;
mExtension.mLen = -1;
mQuery.mLen = -1;
mRef.mLen = -1;
} return NS_OK;
}
// When updating this also update SubstitutingURL::Mutator // Queries this list of interfaces. If none match, it queries mURI.
NS_IMPL_NSIURIMUTATOR_ISUPPORTS(nsStandardURL::Mutator, nsIURISetters,
nsIURIMutator, nsIStandardURLMutator,
nsIURLMutator, nsIFileURLMutator,
nsISerializable)
// First, check whether one URIs is an nsIFileURL while the other // is not. If that's the case, they're different. if (mSupportsFileURL != other->mSupportsFileURL) {
*result = false; return NS_OK;
}
// Next check parts of a URI that, if different, automatically make the // URIs different if (!SegmentIs(mScheme, other->mSpec.get(), other->mScheme) || // Check for host manually, since conversion to file will // ignore the host!
!SegmentIs(mHost, other->mSpec.get(), other->mHost) ||
!SegmentIs(mQuery, other->mSpec.get(), other->mQuery) ||
!SegmentIs(mUsername, other->mSpec.get(), other->mUsername) ||
!SegmentIs(mPassword, other->mSpec.get(), other->mPassword) ||
Port() != other->Port()) { // No need to compare files or other URI parts -- these are different // beasties
*result = false; return NS_OK;
}
// Then check for exact identity of URIs. If we have it, they're equal if (SegmentIs(mDirectory, other->mSpec.get(), other->mDirectory) &&
--> --------------------
--> maximum size reached
--> --------------------
¤ Dauer der Verarbeitung: 0.76 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.