Ejemplo n.º 1
0
bool
net_IsValidHostName(const nsCSubstring &host)
{
    const char *end = host.EndReading();
    // Use explicit whitelists to select which characters we are
    // willing to send to lower-level DNS logic. This is more
    // self-documenting, and can also be slightly faster than the
    // blacklist approach, since DNS names are the common case, and
    // the commonest characters will tend to be near the start of
    // the list.

    // Whitelist for DNS names (RFC 1035) with extra characters added 
    // for pragmatic reasons "$+_"
    // see https://bugzilla.mozilla.org/show_bug.cgi?id=355181#c2
    if (net_FindCharNotInSet(host.BeginReading(), end,
                             "abcdefghijklmnopqrstuvwxyz"
                             ".-0123456789"
                             "ABCDEFGHIJKLMNOPQRSTUVWXYZ$+_") == end)
        return true;

    // Might be a valid IPv6 link-local address containing a percent sign
    nsCAutoString strhost(host);
    PRNetAddr addr;
    return PR_StringToNetAddr(strhost.get(), &addr) == PR_SUCCESS;
}
Ejemplo n.º 2
0
nsresult
CacheStorageService::DoomStorageEntries(nsCSubstring const& aContextKey,
                                        bool aDiskStorage,
                                        nsICacheEntryDoomCallback* aCallback)
{
  mLock.AssertCurrentThreadOwns();

  NS_ENSURE_TRUE(!mShutdown, NS_ERROR_NOT_INITIALIZED);

  nsAutoCString memoryStorageID(aContextKey);
  AppendMemoryStorageID(memoryStorageID);

  nsAutoPtr<CacheEntryTable> entries;
  if (aDiskStorage) {
    LOG(("  dooming disk+memory storage of %s", aContextKey.BeginReading()));
    // Grab all entries in this storage
    sGlobalEntryTables->RemoveAndForget(aContextKey, entries);
    // Just remove the memory-only records table
    sGlobalEntryTables->Remove(memoryStorageID);
  }
  else {
    LOG(("  dooming memory-only storage of %s", aContextKey.BeginReading()));
    // Grab the memory-only records table, EvictionRunnable will safely remove
    // entries one by one from the master hashtable on the background management
    // thread.  Code at AddStorageEntry ensures a new entry will always replace
    // memory only entries that EvictionRunnable yet didn't manage to remove.
    sGlobalEntryTables->RemoveAndForget(memoryStorageID, entries);
  }

  nsRefPtr<EvictionRunnable> evict = new EvictionRunnable(
    aContextKey, entries.forget(), aDiskStorage, aCallback);

  return Dispatch(evict);
}
Ejemplo n.º 3
0
void
ToLowerCase( nsCSubstring& aCString )
  {
    ConvertToLowerCase converter;
    char* start;
    converter.write(aCString.BeginWriting(start), aCString.Length());
  }
Ejemplo n.º 4
0
static void
MorkUnescape(const nsCSubstring &aString, nsCString &aResult)
{
    PRUint32 len = aString.Length();

    // We optimize for speed over space here -- size the result buffer to
    // the size of the source, which is an upper bound on the size of the
    // unescaped string.
    // FIXME: Mork assume there will never be errors
    if (!EnsureStringLength(aResult, len)) {
        aResult.Truncate();
        return; // out of memory.
    }

    char *result = aResult.BeginWriting();
    const char *source = aString.BeginReading();
    const char *sourceEnd = source + len;

    const char *startPos = nsnull;
    PRUint32 bytes;
    for (; source < sourceEnd; ++source) {
        char c = *source;
        if (c == '\\') {
            if (startPos) {
                bytes = source - startPos;
                memcpy(result, startPos, bytes);
                result += bytes;
                startPos = nsnull;
            }
            if (source < sourceEnd - 1) {
                *(result++) = *(++source);
            }
        } else if (c == '$') {
            if (startPos) {
                bytes = source - startPos;
                memcpy(result, startPos, bytes);
                result += bytes;
                startPos = nsnull;
            }
            if (source < sourceEnd - 2) {
                // Would be nice to use ToInteger() here, but it currently
                // requires a null-terminated string.
                char c2 = *(++source);
                char c3 = *(++source);
                if (ConvertChar(&c2) && ConvertChar(&c3)) {
                    *(result++) = ((c2 << 4) | c3);
                }
            }
        } else if (!startPos) {
            startPos = source;
        }
    }
    if (startPos) {
        bytes = source - startPos;
        memcpy(result, startPos, bytes);
        result += bytes;
    }
    aResult.SetLength(result - aResult.BeginReading());
}
bool
nsHttpNegotiateAuth::MatchesBaseURI(const nsCSubstring &matchScheme,
                                    const nsCSubstring &matchHost,
                                    PRInt32             matchPort,
                                    const char         *baseStart,
                                    const char         *baseEnd)
{
    // check if scheme://host:port matches baseURI

    // parse the base URI
    const char *hostStart, *schemeEnd = strstr(baseStart, "://");
    if (schemeEnd) {
        // the given scheme must match the parsed scheme exactly
        if (!matchScheme.Equals(Substring(baseStart, schemeEnd)))
            return false;
        hostStart = schemeEnd + 3;
    }
    else
        hostStart = baseStart;

    // XXX this does not work for IPv6-literals
    const char *hostEnd = strchr(hostStart, ':');
    if (hostEnd && hostEnd < baseEnd) {
        // the given port must match the parsed port exactly
        int port = atoi(hostEnd + 1);
        if (matchPort != (PRInt32) port)
            return false;
    }
    else
        hostEnd = baseEnd;


    // if we didn't parse out a host, then assume we got a match.
    if (hostStart == hostEnd)
        return true;

    PRUint32 hostLen = hostEnd - hostStart;

    // matchHost must either equal host or be a subdomain of host
    if (matchHost.Length() < hostLen)
        return false;

    const char *end = matchHost.EndReading();
    if (PL_strncasecmp(end - hostLen, hostStart, hostLen) == 0) {
        // if matchHost ends with host from the base URI, then make sure it is
        // either an exact match, or prefixed with a dot.  we don't want
        // "foobar.com" to match "bar.com"
        if (matchHost.Length() == hostLen ||
            *(end - hostLen) == '.' ||
            *(end - hostLen - 1) == '.')
            return true;
    }

    return false;
}
void ReverseString(const nsCSubstring& source, nsCSubstring& result)
{
  nsACString::const_iterator sourceBegin, sourceEnd;
  source.BeginReading(sourceBegin);
  source.EndReading(sourceEnd);

  result.SetLength(source.Length());
  nsACString::iterator destEnd;
  result.EndWriting(destEnd);

  while (sourceBegin != sourceEnd) {
    *(--destEnd) = *sourceBegin;
    ++sourceBegin;
  }
}
Ejemplo n.º 7
0
nsresult
nsFtpControlConnection::Write(const nsCSubstring& command)
{
    NS_ENSURE_STATE(mSocketOutput);

    PRUint32 len = command.Length();
    PRUint32 cnt;
    nsresult rv = mSocketOutput->Write(command.Data(), len, &cnt);

    if (NS_FAILED(rv))
        return rv;

    if (len != cnt)
        return NS_ERROR_FAILURE;

    return NS_OK;
}
Ejemplo n.º 8
0
void
AppendTagWithValue(nsACString & aTarget, char const aTag, nsCSubstring const & aValue)
{
  aTarget.Append(aTag);

  // First check the value string to save some memory copying
  // for cases we don't need to escape at all (most likely).
  if (!aValue.IsEmpty()) {
    if (!aValue.Contains(',')) {
      // No need to escape
      aTarget.Append(aValue);
    } else {
      nsAutoCString escapedValue(aValue);
      escapedValue.ReplaceSubstring(
        NS_LITERAL_CSTRING(","), NS_LITERAL_CSTRING(",,"));
      aTarget.Append(escapedValue);
    }
  }

  aTarget.Append(',');
}
Ejemplo n.º 9
0
PRBool
IsValidHTTPToken(const nsCSubstring& aToken)
{
    if (aToken.IsEmpty()) {
        return PR_FALSE;
    }

    nsCSubstring::const_char_iterator iter, end;

    aToken.BeginReading(iter);
    aToken.EndReading(end);

    while (iter != end) {
        if (*iter <= 32 ||
                *iter >= 127 ||
                *iter == '(' ||
                *iter == ')' ||
                *iter == '<' ||
                *iter == '>' ||
                *iter == '@' ||
                *iter == ',' ||
                *iter == ';' ||
                *iter == ':' ||
                *iter == '\\' ||
                *iter == '\"' ||
                *iter == '/' ||
                *iter == '[' ||
                *iter == ']' ||
                *iter == '?' ||
                *iter == '=' ||
                *iter == '{' ||
                *iter == '}') {
            return PR_FALSE;
        }
        ++iter;
    }

    return PR_TRUE;
}
Ejemplo n.º 10
0
nsresult
nsBase64Encoder::Finish(nsCSubstring& result)
{
  char* b64 = PL_Base64Encode(mData.get(), mData.Length(), nsnull);
  if (!b64)
    return NS_ERROR_OUT_OF_MEMORY;

  result.Assign(b64);
  PR_Free(b64);
  // Free unneeded memory and allow reusing the object
  mData.Truncate();
  return NS_OK;
}
nsresult
NS_DeserializeObject(const nsCSubstring& str, nsISupports** obj)
{
  // Base64 maps 3 binary bytes -> 4 ASCII bytes, so this calculation gives us
  // the right size. Compare also the comment in plbase64.h.
  PRUint32 size = (str.Length() * 3) / 4;
  char* buf = PL_Base64Decode(str.BeginReading(), str.Length(), nsnull);
  if (!buf)
    return NS_ERROR_OUT_OF_MEMORY;
  nsCOMPtr<nsIInputStream> stream;
  nsresult rv = NS_NewCStringInputStream(getter_AddRefs(stream),
                                         Substring(buf, buf + size));
  PR_Free(buf);
  NS_ENSURE_SUCCESS(rv, rv);

  nsCOMPtr<nsIObjectInputStream> objstream =
      do_CreateInstance("@mozilla.org/binaryinputstream;1");
  if (!objstream)
    return NS_ERROR_OUT_OF_MEMORY;

  objstream->SetInputStream(stream);
  return objstream->ReadObject(PR_TRUE, obj);
}
nsresult
NS_DeserializeObject(const nsCSubstring& str, nsISupports** obj)
{
  // Base64 maps 3 binary bytes -> 4 ASCII bytes.  If the original byte array
  // does not have length 0 mod 3, the input is padded with zeros and the
  // output is padded with a corresponding number of trailing '=' (which are
  // then sometimes dropped).  To compute the correct length of the original
  // byte array, we have to subtract the number of trailing '=' and then
  // multiply by 3 and then divide by 4 (making sure this is an integer
  // division).

  PRUint32 size = str.Length();
  if (size > 0 && str[size-1] == '=') {
    if (size > 1 && str[size-2] == '=') {
      size -= 2;
    } else {
      size -= 1;
    }
  }
  size = (size * 3) / 4;
  char* buf = PL_Base64Decode(str.BeginReading(), str.Length(), nullptr);
  if (!buf)
    return NS_ERROR_OUT_OF_MEMORY;
  nsCOMPtr<nsIInputStream> stream;
  nsresult rv = NS_NewCStringInputStream(getter_AddRefs(stream),
                                         Substring(buf, size));
  PR_Free(buf);
  NS_ENSURE_SUCCESS(rv, rv);

  nsCOMPtr<nsIObjectInputStream> objstream =
      do_CreateInstance("@mozilla.org/binaryinputstream;1");
  if (!objstream)
    return NS_ERROR_OUT_OF_MEMORY;

  objstream->SetInputStream(stream);
  return objstream->ReadObject(true, obj);
}
Ejemplo n.º 13
0
nsresult
rdf_MakeRelativeRef(const nsCSubstring& aBaseURI, nsCString& aURI)
{
    // This implementation is extremely simple: e.g., it can't compute
    // relative paths, or anything fancy like that. If the context URI
    // is not a prefix of the URI in question, we'll just bail.
    PRUint32 prefixLen = aBaseURI.Length();
    if (prefixLen != 0 && StringBeginsWith(aURI, aBaseURI)) {
        if (prefixLen < aURI.Length() && aURI.CharAt(prefixLen) == '/')
            ++prefixLen; // chop the leading slash so it's not `absolute'

        aURI.Cut(0, prefixLen);
    }

    return NS_OK;
}
nsresult
nsHttpChannelAuthProvider::GetAuthorizationMembers(bool                 proxyAuth,
                                                   nsCSubstring&        scheme,
                                                   const char*&         host,
                                                   int32_t&             port,
                                                   nsCSubstring&        path,
                                                   nsHttpAuthIdentity*& ident,
                                                   nsISupports**&       continuationState)
{
    if (proxyAuth) {
        NS_ASSERTION (UsingHttpProxy(),
                      "proxyAuth is true, but no HTTP proxy is configured!");

        host = ProxyHost();
        port = ProxyPort();
        ident = &mProxyIdent;
        scheme.AssignLiteral("http");

        continuationState = &mProxyAuthContinuationState;
    }
    else {
        host = Host();
        port = Port();
        ident = &mIdent;

        nsresult rv;
        rv = GetCurrentPath(path);
        if (NS_FAILED(rv)) return rv;

        rv = mURI->GetScheme(scheme);
        if (NS_FAILED(rv)) return rv;

        continuationState = &mAuthContinuationState;
    }

    return NS_OK;
}
Ejemplo n.º 15
0
// Find the storage ID for the storage area that contains aPath.
MtpStorageID
MozMtpDatabase::FindStorageIDFor(const nsACString& aPath, nsCSubstring& aRemainder)
{
  MutexAutoLock lock(mMutex);

  aRemainder.Truncate();

  StorageArray::size_type numStorages = mStorage.Length();
  StorageArray::index_type storageIndex;

  for (storageIndex = 0; storageIndex < numStorages; storageIndex++) {
    RefPtr<StorageEntry> storage = mStorage[storageIndex];
    if (StringHead(aPath, storage->mStoragePath.Length()).Equals(storage->mStoragePath)) {
      if (aPath.Length() == storage->mStoragePath.Length()) {
        return storage->mStorageID;
      }
      if (aPath[storage->mStoragePath.Length()] == '/') {
        aRemainder = Substring(aPath, storage->mStoragePath.Length() + 1);
        return storage->mStorageID;
      }
    }
  }
  return 0;
}
Ejemplo n.º 16
0
nsresult
CacheStorageService::DoomStorageEntries(nsCSubstring const& aContextKey,
                                        nsILoadContextInfo* aContext,
                                        bool aDiskStorage,
                                        nsICacheEntryDoomCallback* aCallback)
{
  mLock.AssertCurrentThreadOwns();

  NS_ENSURE_TRUE(!mShutdown, NS_ERROR_NOT_INITIALIZED);

  nsAutoCString memoryStorageID(aContextKey);
  AppendMemoryStorageID(memoryStorageID);

  if (aDiskStorage) {
    LOG(("  dooming disk+memory storage of %s", aContextKey.BeginReading()));

    // Just remove all entries, CacheFileIOManager will take care of the files.
    sGlobalEntryTables->Remove(aContextKey);
    sGlobalEntryTables->Remove(memoryStorageID);

    if (aContext && !aContext->IsPrivate()) {
      LOG(("  dooming disk entries"));
      CacheFileIOManager::EvictByContext(aContext);
    }
  } else {
    LOG(("  dooming memory-only storage of %s", aContextKey.BeginReading()));

    class MemoryEntriesRemoval {
    public:
      static PLDHashOperator EvictEntry(const nsACString& aKey,
                                        CacheEntry* aEntry,
                                        void* aClosure)
      {
        CacheEntryTable* entries = static_cast<CacheEntryTable*>(aClosure);
        nsCString key(aKey);
        RemoveExactEntry(entries, key, aEntry, false);
        return PL_DHASH_NEXT;
      }
    };

    // Remove the memory entries table from the global tables.
    // Since we store memory entries also in the disk entries table
    // we need to remove the memory entries from the disk table one
    // by one manually.
    nsAutoPtr<CacheEntryTable> memoryEntries;
    sGlobalEntryTables->RemoveAndForget(memoryStorageID, memoryEntries);

    CacheEntryTable* entries;
    sGlobalEntryTables->Get(aContextKey, &entries);
    if (memoryEntries && entries)
      memoryEntries->EnumerateRead(&MemoryEntriesRemoval::EvictEntry, entries);
  }

  // An artificial callback.  This is a candidate for removal tho.  In the new
  // cache any 'doom' or 'evict' function ensures that the entry or entries
  // being doomed is/are not accessible after the function returns.  So there is
  // probably no need for a callback - has no meaning.  But for compatibility
  // with the old cache that is still in the tree we keep the API similar to be
  // able to make tests as well as other consumers work for now.
  class Callback : public nsRunnable
  {
  public:
    Callback(nsICacheEntryDoomCallback* aCallback) : mCallback(aCallback) { }
    NS_IMETHODIMP Run()
    {
      mCallback->OnCacheEntryDoomed(NS_OK);
      return NS_OK;
    }
    nsCOMPtr<nsICacheEntryDoomCallback> mCallback;
  };

  if (aCallback) {
    nsRefPtr<nsRunnable> callback = new Callback(aCallback);
    return NS_DispatchToCurrentThread(callback);
  }

  return NS_OK;
}
Ejemplo n.º 17
0
nsresult
CacheStorageService::AddStorageEntry(nsCSubstring const& aContextKey,
                                     nsIURI* aURI,
                                     const nsACString & aIdExtension,
                                     bool aWriteToDisk,
                                     bool aCreateIfNotExist,
                                     bool aReplace,
                                     CacheEntryHandle** aResult)
{
  NS_ENSURE_ARG(aURI);

  nsresult rv;

  nsAutoCString entryKey;
  rv = CacheEntry::HashingKey(EmptyCString(), aIdExtension, aURI, entryKey);
  NS_ENSURE_SUCCESS(rv, rv);

  LOG(("CacheStorageService::AddStorageEntry [entryKey=%s, contextKey=%s]",
    entryKey.get(), aContextKey.BeginReading()));

  nsRefPtr<CacheEntry> entry;
  nsRefPtr<CacheEntryHandle> handle;

  {
    mozilla::MutexAutoLock lock(mLock);

    NS_ENSURE_FALSE(mShutdown, NS_ERROR_NOT_INITIALIZED);

    // Ensure storage table
    CacheEntryTable* entries;
    if (!sGlobalEntryTables->Get(aContextKey, &entries)) {
      entries = new CacheEntryTable(CacheEntryTable::ALL_ENTRIES);
      sGlobalEntryTables->Put(aContextKey, entries);
      LOG(("  new storage entries table for context %s", aContextKey.BeginReading()));
    }

    bool entryExists = entries->Get(entryKey, getter_AddRefs(entry));

    // check whether the file is already doomed
    if (entryExists && entry->IsFileDoomed() && !aReplace) {
      aReplace = true;
    }

    // If truncate is demanded, delete and doom the current entry
    if (entryExists && aReplace) {
      entries->Remove(entryKey);

      LOG(("  dooming entry %p for %s because of OPEN_TRUNCATE", entry.get(), entryKey.get()));
      // On purpose called under the lock to prevent races of doom and open on I/O thread
      // No need to remove from both memory-only and all-entries tables.  The new entry
      // will overwrite the shadow entry in its ctor.
      entry->DoomAlreadyRemoved();

      entry = nullptr;
      entryExists = false;
    }

    if (entryExists && entry->SetUsingDisk(aWriteToDisk)) {
      RecordMemoryOnlyEntry(entry, !aWriteToDisk, true /* overwrite */);
    }

    // Ensure entry for the particular URL, if not read/only
    if (!entryExists && (aCreateIfNotExist || aReplace)) {
      // Entry is not in the hashtable or has just been truncated...
      entry = new CacheEntry(aContextKey, aURI, aIdExtension, aWriteToDisk);
      entries->Put(entryKey, entry);
      LOG(("  new entry %p for %s", entry.get(), entryKey.get()));
    }

    if (entry) {
      // Here, if this entry was not for a long time referenced by any consumer,
      // gets again first 'handles count' reference.
      handle = entry->NewHandle();
    }
  }

  handle.forget(aResult);
  return NS_OK;
}
Ejemplo n.º 18
0
static nsresult
rdf_BlockingWrite(nsIOutputStream* stream, const nsCSubstring& s)
{
    return rdf_BlockingWrite(stream, s.BeginReading(), s.Length());
}
Ejemplo n.º 19
0
bool
MatchesBaseURI(const nsCSubstring &matchScheme,
               const nsCSubstring &matchHost,
               int32_t             matchPort,
               nsDependentCSubstring const& url)
{
  // check if scheme://host:port matches baseURI

  // parse the base URI
  mozilla::Tokenizer t(url);
  mozilla::Tokenizer::Token token;

  t.SkipWhites();

  // We don't know if the url to check against starts with scheme
  // or a host name.  Start recording here.
  t.Record();

  mozilla::Unused << t.Next(token);

  // The ipv6 literals MUST be enclosed with [] in the preference.
  bool ipv6 = false;
  if (token.Equals(mozilla::Tokenizer::Token::Char('['))) {
    nsDependentCSubstring ipv6BareLiteral;
    if (!t.ReadUntil(mozilla::Tokenizer::Token::Char(']'), ipv6BareLiteral)) {
      // Broken ipv6 literal
      return false;
    }

    nsDependentCSubstring ipv6Literal;
    t.Claim(ipv6Literal, mozilla::Tokenizer::INCLUDE_LAST);
    if (!matchHost.Equals(ipv6Literal, nsCaseInsensitiveUTF8StringComparator()) &&
        !matchHost.Equals(ipv6BareLiteral, nsCaseInsensitiveUTF8StringComparator())) {
      return false;
    }

    ipv6 = true;
  } else if (t.CheckChar(':') && t.CheckChar('/') && t.CheckChar('/')) {
    if (!matchScheme.Equals(token.Fragment())) {
      return false;
    }
    // Re-start recording the hostname from the point after scheme://.
    t.Record();
  }

  while (t.Next(token)) {
    bool eof = token.Equals(mozilla::Tokenizer::Token::EndOfFile());
    bool port = token.Equals(mozilla::Tokenizer::Token::Char(':'));

    if (eof || port) {
      if (!ipv6) { // Match already performed above.
        nsDependentCSubstring hostName;
        t.Claim(hostName);

        // An empty hostname means to accept everything for the schema
        if (!hostName.IsEmpty()) {
          /*
           host:      bar.com   foo.bar.com   foobar.com  foo.bar.com    bar.com
           pref:      bar.com       bar.com      bar.com     .bar.com   .bar.com
           result:     accept        accept       reject       accept     reject
          */
          if (!StringEndsWith(matchHost, hostName, nsCaseInsensitiveUTF8StringComparator())) {
            return false;
          }
          if (matchHost.Length() > hostName.Length() &&
              matchHost[matchHost.Length() - hostName.Length() - 1] != '.' &&
              hostName[0] != '.') {
            return false;
          }
        }
      }

      if (port) {
        uint16_t portNumber;
        if (!t.ReadInteger(&portNumber)) {
          // Missing port number
          return false;
        }
        if (matchPort != portNumber) {
          return false;
        }
        if (!t.CheckEOF()) {
          return false;
        }
      }
    } else if (ipv6) {
      // After an ipv6 literal there can only be EOF or :port.  Everything else
      // must be treated as non-match/broken input.
      return false;
    }
  }

  // All negative checks has passed positively.
  return true;
}
Ejemplo n.º 20
0
nsresult
CacheStorageService::AddStorageEntry(nsCSubstring const& aContextKey,
                                     nsIURI* aURI,
                                     const nsACString & aIdExtension,
                                     bool aWriteToDisk,
                                     bool aCreateIfNotExist,
                                     bool aReplace,
                                     CacheEntry** aResult)
{
  NS_ENSURE_ARG(aURI);

  nsresult rv;

  nsAutoCString entryKey;
  rv = CacheEntry::HashingKey(EmptyCString(), aIdExtension, aURI, entryKey);
  NS_ENSURE_SUCCESS(rv, rv);

  LOG(("CacheStorageService::AddStorageEntry [entryKey=%s, contextKey=%s]",
    entryKey.get(), aContextKey.BeginReading()));

  nsRefPtr<CacheEntry> entry;

  {
    mozilla::MutexAutoLock lock(mLock);

    NS_ENSURE_FALSE(mShutdown, NS_ERROR_NOT_INITIALIZED);

    // Ensure storage table
    CacheEntryTable* entries;
    if (!sGlobalEntryTables->Get(aContextKey, &entries)) {
      entries = new CacheEntryTable();
      sGlobalEntryTables->Put(aContextKey, entries);
      LOG(("  new storage entries table for context %s", aContextKey.BeginReading()));
    }

    bool entryExists = entries->Get(entryKey, getter_AddRefs(entry));

    // Check entry that is memory-only is also in related memory-only hashtable.
    // If not, it has been evicted and we will truncate it ; doom is pending for it,
    // this consumer just made it sooner then the entry has actually been removed
    // from the master hash table.
    // (This can be bypassed when entry is about to be replaced anyway.)
    if (entryExists && !entry->UsingDisk() && !aReplace) {
      nsAutoCString memoryStorageID(aContextKey);
      AppendMemoryStorageID(memoryStorageID);
      CacheEntryTable* memoryEntries;
      aReplace = sGlobalEntryTables->Get(memoryStorageID, &memoryEntries) &&
                 memoryEntries->GetWeak(entryKey) != entry;

#ifdef MOZ_LOGGING
      if (aReplace) {
        LOG(("  memory-only entry %p for %s already doomed, replacing", entry.get(), entryKey.get()));
      }
#endif
    }

    // If truncate is demanded, delete and doom the current entry
    if (entryExists && aReplace) {
      entries->Remove(entryKey);

      LOG(("  dooming entry %p for %s because of OPEN_TRUNCATE", entry.get(), entryKey.get()));
      // On purpose called under the lock to prevent races of doom and open on I/O thread
      entry->DoomAlreadyRemoved();

      entry = nullptr;
      entryExists = false;
    }

    if (entryExists && entry->SetUsingDisk(aWriteToDisk)) {
      RecordMemoryOnlyEntry(entry, !aWriteToDisk, true /* overwrite */);
    }

    // Ensure entry for the particular URL, if not read/only
    if (!entryExists && (aCreateIfNotExist || aReplace)) {
      // Entry is not in the hashtable or has just been truncated...
      entry = new CacheEntry(aContextKey, aURI, aIdExtension, aWriteToDisk);
      entries->Put(entryKey, entry);
      LOG(("  new entry %p for %s", entry.get(), entryKey.get()));
    }
  }

  entry.forget(aResult);
  return NS_OK;
}