nsresult
nsAboutCacheEntry::Channel::WriteCacheEntryDescription(nsICacheEntry *entry)
{
    nsresult rv;
    nsCString buffer;
    uint32_t n;

    nsAutoCString str;

    rv = entry->GetKey(str);
    if (NS_FAILED(rv)) return rv;

    buffer.SetCapacity(4096);
    buffer.AssignLiteral("<table>\n"
                         "  <tr>\n"
                         "    <th>key:</th>\n"
                         "    <td id=\"td-key\">");

    // Test if the key is actually a URI
    nsCOMPtr<nsIURI> uri;
    bool isJS = false;
    bool isData = false;

    rv = NS_NewURI(getter_AddRefs(uri), str);
    // javascript: and data: URLs should not be linkified
    // since clicking them can cause scripts to run - bug 162584
    if (NS_SUCCEEDED(rv)) {
        uri->SchemeIs("javascript", &isJS);
        uri->SchemeIs("data", &isData);
    }
    char* escapedStr = nsEscapeHTML(str.get());
    if (NS_SUCCEEDED(rv) && !(isJS || isData)) {
        buffer.AppendLiteral("<a href=\"");
        buffer.Append(escapedStr);
        buffer.AppendLiteral("\">");
        buffer.Append(escapedStr);
        buffer.AppendLiteral("</a>");
        uri = 0;
    } else {
        buffer.Append(escapedStr);
    }
    free(escapedStr);
    buffer.AppendLiteral("</td>\n"
                         "  </tr>\n");

    // temp vars for reporting
    char timeBuf[255];
    uint32_t u = 0;
    int32_t  i = 0;
    nsAutoCString s;

    // Fetch Count
    s.Truncate();
    entry->GetFetchCount(&i);
    s.AppendInt(i);
    APPEND_ROW("fetch count", s);

    // Last Fetched
    entry->GetLastFetched(&u);
    if (u) {
        PrintTimeString(timeBuf, sizeof(timeBuf), u);
        APPEND_ROW("last fetched", timeBuf);
    } else {
        APPEND_ROW("last fetched", "No last fetch time (bug 1000338)");
    }

    // Last Modified
    entry->GetLastModified(&u);
    if (u) {
        PrintTimeString(timeBuf, sizeof(timeBuf), u);
        APPEND_ROW("last modified", timeBuf);
    } else {
        APPEND_ROW("last modified", "No last modified time (bug 1000338)");
    }

    // Expiration Time
    entry->GetExpirationTime(&u);
    if (u < 0xFFFFFFFF) {
        PrintTimeString(timeBuf, sizeof(timeBuf), u);
        APPEND_ROW("expires", timeBuf);
    } else {
        APPEND_ROW("expires", "No expiration time");
    }

    // Data Size
    s.Truncate();
    uint32_t dataSize;
    if (NS_FAILED(entry->GetStorageDataSize(&dataSize)))
        dataSize = 0;
    s.AppendInt((int32_t)dataSize);     // XXX nsICacheEntryInfo interfaces should be fixed.
    s.AppendLiteral(" B");
    APPEND_ROW("Data size", s);

    // TODO - mayhemer
    // Here used to be a link to the disk file (in the old cache for entries that
    // did not fit any of the block files, in the new cache every time).
    // I'd rather have a small set of buttons here to action on the entry:
    // 1. save the content
    // 2. save as a complete HTTP response (response head, headers, content)
    // 3. doom the entry
    // A new bug(s) should be filed here.

    // Security Info
    nsCOMPtr<nsISupports> securityInfo;
    entry->GetSecurityInfo(getter_AddRefs(securityInfo));
    if (securityInfo) {
        APPEND_ROW("Security", "This is a secure document.");
    } else {
        APPEND_ROW("Security",
                   "This document does not have any security info associated with it.");
    }

    buffer.AppendLiteral("</table>\n"
                         "<hr/>\n"
                         "<table>\n");

    mBuffer = &buffer;  // make it available for OnMetaDataElement().
    entry->VisitMetaData(this);
    mBuffer = nullptr;

    buffer.AppendLiteral("</table>\n");
    mOutputStream->Write(buffer.get(), buffer.Length(), &n);
    buffer.Truncate();

    // Provide a hexdump of the data
    if (!dataSize) {
        return NS_OK;
    }

    nsCOMPtr<nsIInputStream> stream;
    entry->OpenInputStream(0, getter_AddRefs(stream));
    if (!stream) {
        return NS_OK;
    }

    RefPtr<nsInputStreamPump> pump;
    rv = nsInputStreamPump::Create(getter_AddRefs(pump), stream);
    if (NS_FAILED(rv)) {
        return NS_OK; // just ignore
    }

    rv = pump->AsyncRead(this, nullptr);
    if (NS_FAILED(rv)) {
        return NS_OK; // just ignore
    }

    mWaitingForData = true;
    return NS_OK;
}