/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set sw=2 ts=8 et tw=80 : */ /* 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 entry for a directory will either be empty (in the case of the // top-level directory) or will end with a slash #define ENTRY_IS_DIRECTORY(_entry) \
((_entry).IsEmpty() || '/' == (_entry).Last())
//----------------------------------------------------------------------------- // nsJARInputThunk // // this class allows us to do some extra work on the stream transport thread. //-----------------------------------------------------------------------------
class nsJARInputThunk : public nsIInputStream { public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIINPUTSTREAM
nsJARInputThunk(nsIZipReader* zipReader, const nsACString& jarEntry, bool usingJarCache)
: mUsingJarCache(usingJarCache),
mJarReader(zipReader),
mJarEntry(jarEntry),
mContentLength(-1) {
MOZ_DIAGNOSTIC_ASSERT(zipReader, "zipReader must not be null");
}
nsJARChannel::nsJARChannel() {
LOG(("nsJARChannel::nsJARChannel [this=%p]\n", this)); // hold an owning reference to the jar handler
mJarHandler = gJarHandler;
}
nsJARChannel::~nsJARChannel() {
LOG(("nsJARChannel::~nsJARChannel [this=%p]\n", this)); if (NS_IsMainThread()) { return;
}
// Proxy release the following members to main thread.
NS_ReleaseOnMainThread("nsJARChannel::mLoadInfo", mLoadInfo.forget());
NS_ReleaseOnMainThread("nsJARChannel::mCallbacks", mCallbacks.forget());
NS_ReleaseOnMainThread("nsJARChannel::mProgressSink", mProgressSink.forget());
NS_ReleaseOnMainThread("nsJARChannel::mLoadGroup", mLoadGroup.forget());
NS_ReleaseOnMainThread("nsJARChannel::mListener", mListener.forget());
}
// important to pass a clone of the file since the nsIFile impl is not // necessarily MT-safe
nsCOMPtr<nsIFile> clonedFile;
nsresult rv = NS_OK; if (mJarFile) {
rv = mJarFile->Clone(getter_AddRefs(clonedFile)); if (NS_FAILED(rv)) return rv;
}
nsCOMPtr<nsIZipReader> reader; if (mPreCachedJarReader) {
reader = mPreCachedJarReader;
} elseif (jarCache) { if (mInnerJarEntry.IsEmpty())
rv = jarCache->GetZip(clonedFile, getter_AddRefs(reader)); else
rv = jarCache->GetInnerZip(clonedFile, mInnerJarEntry,
getter_AddRefs(reader));
} else { // create an uncached jar reader
nsCOMPtr<nsIZipReader> outerReader = do_CreateInstance(kZipReaderCID, &rv); if (NS_FAILED(rv)) return rv;
rv = outerReader->Open(clonedFile); if (NS_FAILED(rv)) return rv;
if (mInnerJarEntry.IsEmpty())
reader = outerReader; else {
reader = do_CreateInstance(kZipReaderCID, &rv); if (NS_FAILED(rv)) return rv;
rv = reader->OpenInner(outerReader, mInnerJarEntry);
}
} if (NS_FAILED(rv)) return rv;
RefPtr<nsJARInputThunk> input = new nsJARInputThunk(reader, mJarEntry, jarCache != nullptr);
rv = input->Init(); if (NS_FAILED(rv)) { return rv;
}
// Make GetContentLength meaningful
mContentLength = input->GetContentLength();
rv = mJarURI->GetJARFile(getter_AddRefs(mJarBaseURI)); if (NS_FAILED(rv)) return rv;
rv = mJarURI->GetJAREntry(mJarEntry); if (NS_FAILED(rv)) return rv;
// The name of the JAR entry must not contain URL-escaped characters: // we're moving from URL domain to a filename domain here. nsStandardURL // does basic escaping by default, which breaks reading zipped files which // have e.g. spaces in their filenames.
NS_UnescapeURL(mJarEntry);
// try to get a nsIFile directly from the url, which will often succeed.
{
nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(mJarBaseURI); if (fileURL) fileURL->GetFile(getter_AddRefs(mJarFile));
}
// try to handle a nested jar if (!mJarFile) {
nsCOMPtr<nsIJARURI> jarURI = do_QueryInterface(mJarBaseURI); if (jarURI) {
nsCOMPtr<nsIFileURL> fileURL;
nsCOMPtr<nsIURI> innerJarURI;
rv = jarURI->GetJARFile(getter_AddRefs(innerJarURI)); if (NS_SUCCEEDED(rv)) fileURL = do_QueryInterface(innerJarURI); if (fileURL) {
fileURL->GetFile(getter_AddRefs(mJarFile));
jarURI->GetJAREntry(mInnerJarEntry);
}
}
}
// Set mLoadGroup and mOpened before AsyncOpen return, and set back if // if failed when callback. if (mLoadGroup) {
mLoadGroup->AddRequest(this, nullptr);
}
SetOpened();
// check if we're displaying a directory // mJarEntry will be empty if we're trying to display // the topmost directory in a zip, e.g. jar:foo.zip!/ if (ENTRY_IS_DIRECTORY(mJarEntry)) {
aResult.AssignLiteral(APPLICATION_HTTP_INDEX_FORMAT); returntrue;
}
// Not a directory, take a guess by its extension for (int32_t i = len - 1; i >= 0; i--) { if (fileName[i] == '.') {
ext = &fileName[i + 1]; break;
}
} if (!ext) { returnfalse;
}
nsIMIMEService* mimeServ = gJarHandler->MimeService(); if (!mimeServ) { returnfalse;
}
mimeServ->GetTypeFromExtension(nsDependentCString(ext), aResult); return !aResult.IsEmpty();
}
NS_IMETHODIMP
nsJARChannel::GetContentType(nsACString& aResult) { // If the Jar file has not been open yet, // We return application/x-unknown-content-type if (!mOpened) {
aResult.AssignLiteral(UNKNOWN_CONTENT_TYPE); return NS_OK;
}
aResult = mContentType; return NS_OK;
}
NS_IMETHODIMP
nsJARChannel::SetContentType(const nsACString& aContentType) { // We behave like HTTP channels (treat this as a hint if called before open, // and override the charset if called after open). // mContentCharset is unchanged if not parsed
NS_ParseResponseContentType(aContentType, mContentType, mContentCharset); return NS_OK;
}
NS_IMETHODIMP
nsJARChannel::GetContentCharset(nsACString& aContentCharset) { // If someone gives us a charset hint we should just use that charset. // So we don't care when this is being called.
aContentCharset = mContentCharset; if (mContentCharset.IsEmpty() && (mOriginalURI->SchemeIs("chrome") ||
mOriginalURI->SchemeIs("resource"))) {
aContentCharset.AssignLiteral("UTF-8");
} return NS_OK;
}
NS_IMETHODIMP
nsJARChannel::SetContentLength(int64_t aContentLength) { // XXX does this really make any sense at all?
mContentLength = aContentLength; return NS_OK;
}
// The Legacy Telemetry event can only hold 80 characters. // We only save the file name and path inside the jar. auto findFilenameStart = [](const nsCString& aSpec) -> uint32_t {
int32_t pos = aSpec.Find("!/"); if (pos == kNotFound) {
MOZ_ASSERT(false, "This should not happen"); return 0;
}
int32_t from = aSpec.RFindChar('/', pos); if (from == kNotFound) {
MOZ_ASSERT(false, "This should not happen"); return 0;
} // Skip over the slash
from++; return from;
};
// If for some reason we are unable to extract the filename we report the // entire string, or 80 characters of it, to make sure we don't miss any // events.
uint32_t from = findFilenameStart(aSpec); constauto fileName = Substring(aSpec, from);
// To test this telemetry we use a zip file and we want to make // sure don't filter it out. bool isTest = fileName.Find("test_empty_file.zip!") != -1; bool isOmniJa = StringBeginsWith(fileName, "omni.ja!"_ns);
if (StringEndsWith(fileName, ".ftl"_ns)) { // FTL uses I/O to test for file presence, so we get // a high volume of events from it, but it is not erronous. // Also, Fluent is resilient to empty loads, so even if any // of the errors are real errors, they don't cause YSOD. // We can investigate them separately. if (!isTest && aStatus == NS_ERROR_FILE_NOT_FOUND) { return;
}
glean::zero_byte_load::LoadFtlExtra extra = {
.cancelled = Some(aCanceled),
.fileName = Some(fileName),
.status = Some(errorCString),
.sync = Some(aIsSync),
};
glean::zero_byte_load::load_ftl.Record(Some(extra));
} elseif (StringEndsWith(fileName, ".dtd"_ns)) { // We're going to skip reporting telemetry on res DTDs. // See Bug 1693711 for investigation into those empty loads. if (!isTest && StringBeginsWith(fileName, "omni.ja!/res/dtd"_ns)) { return;
}
glean::zero_byte_load::LoadDtdExtra extra = {
.cancelled = Some(aCanceled),
.fileName = Some(fileName),
.status = Some(errorCString),
.sync = Some(aIsSync),
};
glean::zero_byte_load::load_dtd.Record(Some(extra));
} elseif (StringEndsWith(fileName, ".properties"_ns)) {
glean::zero_byte_load::LoadPropertiesExtra extra = {
.cancelled = Some(aCanceled),
.fileName = Some(fileName),
.status = Some(errorCString),
.sync = Some(aIsSync),
};
glean::zero_byte_load::load_properties.Record(Some(extra));
} elseif (StringEndsWith(fileName, ".js"_ns) ||
StringEndsWith(fileName, ".jsm"_ns) ||
StringEndsWith(fileName, ".mjs"_ns)) { // We're going to skip reporting telemetry on JS loads // coming not from omni.ja. // See Bug 1693711 for investigation into those empty loads. if (!isTest && !isOmniJa) { return;
}
glean::zero_byte_load::LoadJsExtra extra = {
.cancelled = Some(aCanceled),
.fileName = Some(fileName),
.status = Some(errorCString),
.sync = Some(aIsSync),
};
glean::zero_byte_load::load_js.Record(Some(extra));
} elseif (StringEndsWith(fileName, ".xml"_ns)) {
glean::zero_byte_load::LoadXmlExtra extra = {
.cancelled = Some(aCanceled),
.fileName = Some(fileName),
.status = Some(errorCString),
.sync = Some(aIsSync),
};
glean::zero_byte_load::load_xml.Record(Some(extra));
} elseif (StringEndsWith(fileName, ".xhtml"_ns)) { // This error seems to be very common and is not strongly // correlated to YSOD. if (aStatus == NS_ERROR_PARSED_DATA_CACHED) { return;
}
// We're not investigating YSODs from extensions for now. if (!isOmniJa) { return;
}
// Bug 1702937: Filter css/NS_ERROR_CORRUPTED_CONTENT that is coming from // outside of omni.ja. if (!isOmniJa && aStatus == NS_ERROR_CORRUPTED_CONTENT) { return;
}
glean::zero_byte_load::LoadCssExtra extra = {
.cancelled = Some(aCanceled),
.fileName = Some(fileName),
.status = Some(errorCString),
.sync = Some(aIsSync),
};
glean::zero_byte_load::load_css.Record(Some(extra));
} elseif (StringEndsWith(fileName, ".json"_ns)) { // FTL uses I/O to test for file presence, so we get // a high volume of events from it, but it is not erronous. // Also, Fluent is resilient to empty loads, so even if any // of the errors are real errors, they don't cause YSOD. // We can investigate them separately. if (!isTest && aStatus == NS_ERROR_FILE_NOT_FOUND) { return;
}
glean::zero_byte_load::LoadJsonExtra extra = {
.cancelled = Some(aCanceled),
.fileName = Some(fileName),
.status = Some(errorCString),
.sync = Some(aIsSync),
};
glean::zero_byte_load::load_json.Record(Some(extra));
} elseif (StringEndsWith(fileName, ".html"_ns)) { // See bug 1695560. Filter out non-omni.ja HTML. if (!isOmniJa) { return;
}
// See bug 1695560. "activity-stream-noscripts.html" with NS_ERROR_FAILURE // is filtered out. if (fileName.EqualsLiteral("omni.ja!/chrome/browser/res/activity-stream/" "prerendered/activity-stream-noscripts.html") &&
aStatus == NS_ERROR_FAILURE) { return;
}
glean::zero_byte_load::LoadPngExtra extra = {
.cancelled = Some(aCanceled),
.fileName = Some(fileName),
.status = Some(errorCString),
.sync = Some(aIsSync),
};
glean::zero_byte_load::load_png.Record(Some(extra));
} elseif (StringEndsWith(fileName, ".svg"_ns)) { // See bug 1695560. // Bug 1702937: Filter out svg+'css'+'png'/NS_BINDING_ABORTED combo. if (!isOmniJa || aStatus == NS_BINDING_ABORTED) { return;
}
glean::zero_byte_load::LoadSvgExtra extra = {
.cancelled = Some(aCanceled),
.fileName = Some(fileName),
.status = Some(errorCString),
.sync = Some(aIsSync),
};
glean::zero_byte_load::load_svg.Record(Some(extra));
} else { // All others // We're going to, for now, filter out `other` category. // See Bug 1693711 for investigation into those empty loads. // Bug 1702937: Filter other/*.ico/NS_BINDING_ABORTED. if (!isTest && (!isOmniJa || (aStatus == NS_BINDING_ABORTED &&
StringEndsWith(fileName, ".ico"_ns)))) { return;
}
// See bug 1695560. "search-extensions/google/favicon.ico" with // NS_BINDING_ABORTED is filtered out. if (fileName.EqualsLiteral( "omni.ja!/chrome/browser/search-extensions/google/favicon.ico") &&
aStatus == NS_BINDING_ABORTED) { return;
}
// See bug 1695560. "update.locale" with // NS_ERROR_FILE_NOT_FOUND is filtered out. if (fileName.EqualsLiteral("omni.ja!/update.locale") &&
aStatus == NS_ERROR_FILE_NOT_FOUND) { return;
}
// If mJarFile was not set by LookupFile, we can't open a channel. if (!mJarFile) {
MOZ_ASSERT_UNREACHABLE("only file-backed jars are supported"); return NS_ERROR_NOT_IMPLEMENTED;
}
RefPtr<nsJARInputThunk> input;
rv = CreateJarInput(gJarHandler->JarCache(), getter_AddRefs(input)); if (NS_FAILED(rv)) return rv;
input.forget(aStream);
SetOpened();
return NS_OK;
}
void nsJARChannel::SetOpened() {
MOZ_ASSERT(!mOpened, "Opening channel twice?");
mOpened = true; // Compute the content type now. if (!GetContentTypeGuess(mContentType)) {
mContentType.Assign(UNKNOWN_CONTENT_TYPE);
}
}
// simply report progress here instead of hooking ourselves up as a // nsITransportEventSink implementation. // XXX do the 64-bit stuff for real if (mProgressSink && NS_SUCCEEDED(rv)) { if (NS_IsMainThread()) {
FireOnProgress(offset + count);
} else {
NS_DispatchToMainThread(NewRunnableMethod<uint64_t>( "nsJARChannel::FireOnProgress", this, &nsJARChannel::FireOnProgress,
offset + count));
}
}
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.