forked from mirrors/gecko-dev
Bug 196078 - Part 2: Support displaying arbitrary text/* MIME types as plain text, r=smaug,necko-reviewers,valentin
This patch refactors how we check for text formats when deciding how to handle resources, such that more text MIME types will be rendered in-browser, rather than downloaded. This change requires us to move more away from using the Gecko-Content-Viewers category in the category manager for this decision, as we need to handle an unlimited number of MIME types behind the scenes. Support for Gecko-Content-Viewers was left in for both the in-tree use for application/http-index-format and dynamically determining whether image/avif and image/jxl are supported, as well as for the message/rfc822 type used by Thunderbird. Differential Revision: https://phabricator.services.mozilla.com/D212078
This commit is contained in:
parent
c613d7241c
commit
855d5761c0
9 changed files with 111 additions and 89 deletions
|
|
@ -17,10 +17,9 @@ webidl Document;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* To get a component that implements nsIDocumentLoaderFactory
|
* To get a component that implements nsIDocumentLoaderFactory
|
||||||
* for a given mimetype, use nsICategoryManager to find an entry
|
* for a given mimetype, use nsContentUtils::FindInternalDocumentViewer.
|
||||||
* with the mimetype as its name in the category "Gecko-Content-Viewers".
|
* This will look up the MIME type within the "Gecko-Content-Viewers" category,
|
||||||
* The value of the entry is the contractid of the component.
|
* with additional handlers for other content types.
|
||||||
* The component is a service, so use GetService, not CreateInstance to get it.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
[scriptable, uuid(e795239e-9d3c-47c4-b063-9e600fb3b287)]
|
[scriptable, uuid(e795239e-9d3c-47c4-b063-9e600fb3b287)]
|
||||||
|
|
|
||||||
|
|
@ -1081,21 +1081,13 @@ nsresult ExternalResourceMap::PendingLoad::SetupViewer(
|
||||||
new LoadgroupCallbacks(callbacks);
|
new LoadgroupCallbacks(callbacks);
|
||||||
newLoadGroup->SetNotificationCallbacks(newCallbacks);
|
newLoadGroup->SetNotificationCallbacks(newCallbacks);
|
||||||
|
|
||||||
// This is some serious hackery cribbed from docshell
|
|
||||||
nsCOMPtr<nsICategoryManager> catMan =
|
|
||||||
do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
|
|
||||||
NS_ENSURE_TRUE(catMan, NS_ERROR_NOT_AVAILABLE);
|
|
||||||
nsCString contractId;
|
|
||||||
nsresult rv =
|
|
||||||
catMan->GetCategoryEntry("Gecko-Content-Viewers", type, contractId);
|
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
|
||||||
nsCOMPtr<nsIDocumentLoaderFactory> docLoaderFactory =
|
nsCOMPtr<nsIDocumentLoaderFactory> docLoaderFactory =
|
||||||
do_GetService(contractId.get());
|
nsContentUtils::FindInternalDocumentViewer(type);
|
||||||
NS_ENSURE_TRUE(docLoaderFactory, NS_ERROR_NOT_AVAILABLE);
|
NS_ENSURE_TRUE(docLoaderFactory, NS_ERROR_NOT_AVAILABLE);
|
||||||
|
|
||||||
nsCOMPtr<nsIDocumentViewer> viewer;
|
nsCOMPtr<nsIDocumentViewer> viewer;
|
||||||
nsCOMPtr<nsIStreamListener> listener;
|
nsCOMPtr<nsIStreamListener> listener;
|
||||||
rv = docLoaderFactory->CreateInstance(
|
nsresult rv = docLoaderFactory->CreateInstance(
|
||||||
"external-resource", chan, newLoadGroup, type, nullptr, nullptr,
|
"external-resource", chan, newLoadGroup, type, nullptr, nullptr,
|
||||||
getter_AddRefs(listener), getter_AddRefs(viewer));
|
getter_AddRefs(listener), getter_AddRefs(viewer));
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
|
||||||
|
|
@ -4631,28 +4631,66 @@ bool nsContentUtils::IsChildOfSameType(Document* aDoc) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool IsJSONType(const nsACString& aContentType) {
|
||||||
|
return aContentType.EqualsLiteral(TEXT_JSON) ||
|
||||||
|
aContentType.EqualsLiteral(APPLICATION_JSON);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool IsNonPlainTextType(const nsACString& aContentType) {
|
||||||
|
// MIME type suffixes which should not be plain text.
|
||||||
|
static constexpr std::string_view kNonPlainTextTypes[] = {
|
||||||
|
"html",
|
||||||
|
"xml",
|
||||||
|
"xsl",
|
||||||
|
"calendar",
|
||||||
|
"x-calendar",
|
||||||
|
"x-vcalendar",
|
||||||
|
"vcalendar",
|
||||||
|
"vcard",
|
||||||
|
"x-vcard",
|
||||||
|
"directory",
|
||||||
|
"ldif",
|
||||||
|
"qif",
|
||||||
|
"x-qif",
|
||||||
|
"x-csv",
|
||||||
|
"x-vcf",
|
||||||
|
"rtf",
|
||||||
|
"comma-separated-values",
|
||||||
|
"csv",
|
||||||
|
"tab-separated-values",
|
||||||
|
"tsv",
|
||||||
|
"ofx",
|
||||||
|
"vnd.sun.j2me.app-descriptor",
|
||||||
|
"x-ms-iqy",
|
||||||
|
"x-ms-odc",
|
||||||
|
"x-ms-rqy",
|
||||||
|
"x-ms-contact"};
|
||||||
|
|
||||||
|
// Trim off the "text/" prefix for comparison.
|
||||||
|
MOZ_ASSERT(StringBeginsWith(aContentType, "text/"_ns));
|
||||||
|
std::string_view suffix = aContentType;
|
||||||
|
suffix.remove_prefix(5);
|
||||||
|
|
||||||
|
for (std::string_view type : kNonPlainTextTypes) {
|
||||||
|
if (type == suffix) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool nsContentUtils::IsPlainTextType(const nsACString& aContentType) {
|
bool nsContentUtils::IsPlainTextType(const nsACString& aContentType) {
|
||||||
// NOTE: if you add a type here, add it to the content_types array in
|
// All `text/*`, any JSON type and any JavaScript type are considered "plain
|
||||||
// layout/build/components.conf as well.
|
// text" types for the purposes of how to render them as a document.
|
||||||
return aContentType.EqualsLiteral(TEXT_PLAIN) ||
|
return (StringBeginsWith(aContentType, "text/"_ns) &&
|
||||||
aContentType.EqualsLiteral(TEXT_CSS) ||
|
!IsNonPlainTextType(aContentType)) ||
|
||||||
aContentType.EqualsLiteral(TEXT_CACHE_MANIFEST) ||
|
IsJSONType(aContentType) || IsJavascriptMIMEType(aContentType);
|
||||||
aContentType.EqualsLiteral(TEXT_VTT) ||
|
|
||||||
aContentType.EqualsLiteral(APPLICATION_JAVASCRIPT) ||
|
|
||||||
aContentType.EqualsLiteral(APPLICATION_XJAVASCRIPT) ||
|
|
||||||
aContentType.EqualsLiteral(TEXT_ECMASCRIPT) ||
|
|
||||||
aContentType.EqualsLiteral(APPLICATION_ECMASCRIPT) ||
|
|
||||||
aContentType.EqualsLiteral(TEXT_JAVASCRIPT) ||
|
|
||||||
aContentType.EqualsLiteral(APPLICATION_JSON) ||
|
|
||||||
aContentType.EqualsLiteral(TEXT_JSON) ||
|
|
||||||
aContentType.EqualsLiteral(TEXT_EVENT_STREAM);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool nsContentUtils::IsUtf8OnlyPlainTextType(const nsACString& aContentType) {
|
bool nsContentUtils::IsUtf8OnlyPlainTextType(const nsACString& aContentType) {
|
||||||
// NOTE: This must be a subset of the list in IsPlainTextType().
|
// NOTE: This must be a subset of the list in IsPlainTextType().
|
||||||
return aContentType.EqualsLiteral(TEXT_CACHE_MANIFEST) ||
|
return IsJSONType(aContentType) ||
|
||||||
aContentType.EqualsLiteral(APPLICATION_JSON) ||
|
aContentType.EqualsLiteral(TEXT_CACHE_MANIFEST) ||
|
||||||
aContentType.EqualsLiteral(TEXT_JSON) ||
|
|
||||||
aContentType.EqualsLiteral(TEXT_VTT);
|
aContentType.EqualsLiteral(TEXT_VTT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -7218,9 +7256,12 @@ nsContentUtils::FindInternalDocumentViewer(const nsACString& aType,
|
||||||
return docFactory.forget();
|
return docFactory.forget();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (DecoderTraits::IsSupportedInVideoDocument(aType)) {
|
// If the type wasn't registered in `Gecko-Content-Viewers`, check if it's
|
||||||
docFactory =
|
// another type which we may dynamically support, such as `text/*` types or
|
||||||
do_GetService("@mozilla.org/content/document-loader-factory;1");
|
// video document types. These types are all backed by the nsContentDLF.
|
||||||
|
if (IsPlainTextType(aType) ||
|
||||||
|
DecoderTraits::IsSupportedInVideoDocument(aType)) {
|
||||||
|
docFactory = do_GetService(CONTENT_DLF_CONTRACTID);
|
||||||
if (docFactory && aLoaderType) {
|
if (docFactory && aLoaderType) {
|
||||||
*aLoaderType = TYPE_CONTENT;
|
*aLoaderType = TYPE_CONTENT;
|
||||||
}
|
}
|
||||||
|
|
@ -7930,32 +7971,40 @@ void nsContentUtils::DestroyMatchString(void* aData) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool nsContentUtils::IsJavascriptMIMEType(const nsAString& aMIMEType) {
|
// Table ordered from most to least likely JS MIME types.
|
||||||
// Table ordered from most to least likely JS MIME types.
|
static constexpr std::string_view kJavascriptMIMETypes[] = {
|
||||||
static const char* jsTypes[] = {"text/javascript",
|
"text/javascript",
|
||||||
"text/ecmascript",
|
"text/ecmascript",
|
||||||
"application/javascript",
|
"application/javascript",
|
||||||
"application/ecmascript",
|
"application/ecmascript",
|
||||||
"application/x-javascript",
|
"application/x-javascript",
|
||||||
"application/x-ecmascript",
|
"application/x-ecmascript",
|
||||||
"text/javascript1.0",
|
"text/javascript1.0",
|
||||||
"text/javascript1.1",
|
"text/javascript1.1",
|
||||||
"text/javascript1.2",
|
"text/javascript1.2",
|
||||||
"text/javascript1.3",
|
"text/javascript1.3",
|
||||||
"text/javascript1.4",
|
"text/javascript1.4",
|
||||||
"text/javascript1.5",
|
"text/javascript1.5",
|
||||||
"text/jscript",
|
"text/jscript",
|
||||||
"text/livescript",
|
"text/livescript",
|
||||||
"text/x-ecmascript",
|
"text/x-ecmascript",
|
||||||
"text/x-javascript",
|
"text/x-javascript"};
|
||||||
nullptr};
|
|
||||||
|
|
||||||
for (uint32_t i = 0; jsTypes[i]; ++i) {
|
bool nsContentUtils::IsJavascriptMIMEType(const nsAString& aMIMEType) {
|
||||||
if (aMIMEType.LowerCaseEqualsASCII(jsTypes[i])) {
|
for (std::string_view type : kJavascriptMIMETypes) {
|
||||||
|
if (aMIMEType.LowerCaseEqualsASCII(type.data(), type.length())) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool nsContentUtils::IsJavascriptMIMEType(const nsACString& aMIMEType) {
|
||||||
|
for (std::string_view type : kJavascriptMIMETypes) {
|
||||||
|
if (aMIMEType.LowerCaseEqualsASCII(type.data(), type.length())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2769,6 +2769,7 @@ class nsContentUtils {
|
||||||
static bool IsJavaScriptLanguage(const nsString& aName);
|
static bool IsJavaScriptLanguage(const nsString& aName);
|
||||||
|
|
||||||
static bool IsJavascriptMIMEType(const nsAString& aMIMEType);
|
static bool IsJavascriptMIMEType(const nsAString& aMIMEType);
|
||||||
|
static bool IsJavascriptMIMEType(const nsACString& aMIMEType);
|
||||||
|
|
||||||
static void SplitMimeType(const nsAString& aValue, nsString& aType,
|
static void SplitMimeType(const nsAString& aValue, nsString& aType,
|
||||||
nsString& aParams);
|
nsString& aParams);
|
||||||
|
|
|
||||||
|
|
@ -131,20 +131,13 @@ gfxSVGGlyphsDocument* gfxSVGGlyphs::FindOrCreateGlyphsDocument(
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult gfxSVGGlyphsDocument::SetupPresentation() {
|
nsresult gfxSVGGlyphsDocument::SetupPresentation() {
|
||||||
nsCOMPtr<nsICategoryManager> catMan =
|
|
||||||
do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
|
|
||||||
nsCString contractId;
|
|
||||||
nsresult rv = catMan->GetCategoryEntry("Gecko-Content-Viewers",
|
|
||||||
"image/svg+xml", contractId);
|
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
|
||||||
|
|
||||||
nsCOMPtr<nsIDocumentLoaderFactory> docLoaderFactory =
|
nsCOMPtr<nsIDocumentLoaderFactory> docLoaderFactory =
|
||||||
do_GetService(contractId.get());
|
nsContentUtils::FindInternalDocumentViewer("image/svg+xml"_ns);
|
||||||
NS_ASSERTION(docLoaderFactory, "Couldn't get DocumentLoaderFactory");
|
NS_ASSERTION(docLoaderFactory, "Couldn't get DocumentLoaderFactory");
|
||||||
|
|
||||||
nsCOMPtr<nsIDocumentViewer> viewer;
|
nsCOMPtr<nsIDocumentViewer> viewer;
|
||||||
rv = docLoaderFactory->CreateInstanceForDocument(nullptr, mDocument, nullptr,
|
nsresult rv = docLoaderFactory->CreateInstanceForDocument(
|
||||||
getter_AddRefs(viewer));
|
nullptr, mDocument, nullptr, getter_AddRefs(viewer));
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
auto upem = mOwner->FontEntry()->UnitsPerEm();
|
auto upem = mOwner->FontEntry()->UnitsPerEm();
|
||||||
|
|
|
||||||
|
|
@ -283,20 +283,14 @@ nsresult SVGDocumentWrapper::SetupViewer(nsIRequest* aRequest,
|
||||||
NS_ENSURE_TRUE(newLoadGroup, NS_ERROR_OUT_OF_MEMORY);
|
NS_ENSURE_TRUE(newLoadGroup, NS_ERROR_OUT_OF_MEMORY);
|
||||||
newLoadGroup->SetLoadGroup(loadGroup);
|
newLoadGroup->SetLoadGroup(loadGroup);
|
||||||
|
|
||||||
nsCOMPtr<nsICategoryManager> catMan =
|
|
||||||
do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
|
|
||||||
NS_ENSURE_TRUE(catMan, NS_ERROR_NOT_AVAILABLE);
|
|
||||||
nsCString contractId;
|
|
||||||
nsresult rv = catMan->GetCategoryEntry("Gecko-Content-Viewers", IMAGE_SVG_XML,
|
|
||||||
contractId);
|
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
|
||||||
nsCOMPtr<nsIDocumentLoaderFactory> docLoaderFactory =
|
nsCOMPtr<nsIDocumentLoaderFactory> docLoaderFactory =
|
||||||
do_GetService(contractId.get());
|
nsContentUtils::FindInternalDocumentViewer(
|
||||||
|
nsLiteralCString(IMAGE_SVG_XML));
|
||||||
NS_ENSURE_TRUE(docLoaderFactory, NS_ERROR_NOT_AVAILABLE);
|
NS_ENSURE_TRUE(docLoaderFactory, NS_ERROR_NOT_AVAILABLE);
|
||||||
|
|
||||||
nsCOMPtr<nsIDocumentViewer> viewer;
|
nsCOMPtr<nsIDocumentViewer> viewer;
|
||||||
nsCOMPtr<nsIStreamListener> listener;
|
nsCOMPtr<nsIStreamListener> listener;
|
||||||
rv = docLoaderFactory->CreateInstance(
|
nsresult rv = docLoaderFactory->CreateInstance(
|
||||||
"external-resource", chan, newLoadGroup, nsLiteralCString(IMAGE_SVG_XML),
|
"external-resource", chan, newLoadGroup, nsLiteralCString(IMAGE_SVG_XML),
|
||||||
nullptr, nullptr, getter_AddRefs(listener), getter_AddRefs(viewer));
|
nullptr, nullptr, getter_AddRefs(listener), getter_AddRefs(viewer));
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
|
||||||
|
|
@ -14,24 +14,14 @@ UnloadFunc = 'nsLayoutModuleDtor'
|
||||||
Priority = 100
|
Priority = 100
|
||||||
|
|
||||||
content_types = [
|
content_types = [
|
||||||
'application/ecmascript',
|
|
||||||
'application/javascript',
|
|
||||||
'application/json',
|
|
||||||
'application/mathml+xml',
|
'application/mathml+xml',
|
||||||
'application/rdf+xml',
|
'application/rdf+xml',
|
||||||
'application/vnd.wap.xhtml+xml',
|
'application/vnd.wap.xhtml+xml',
|
||||||
'application/x-javascript',
|
|
||||||
'application/x-view-source',
|
'application/x-view-source',
|
||||||
'application/xhtml+xml',
|
'application/xhtml+xml',
|
||||||
'application/xml',
|
'application/xml',
|
||||||
'image/svg+xml',
|
'image/svg+xml',
|
||||||
'text/cache-manifest',
|
|
||||||
'text/css',
|
|
||||||
'text/ecmascript',
|
|
||||||
'text/event-stream',
|
|
||||||
'text/html',
|
'text/html',
|
||||||
'text/javascript',
|
|
||||||
'text/json',
|
|
||||||
'text/plain',
|
'text/plain',
|
||||||
'text/rdf',
|
'text/rdf',
|
||||||
'text/vtt',
|
'text/vtt',
|
||||||
|
|
|
||||||
|
|
@ -61,4 +61,7 @@ add_task(async function test_display_plaintext_type() {
|
||||||
await expectOutcome("application/json", "jsonviewer");
|
await expectOutcome("application/json", "jsonviewer");
|
||||||
// NOTE: text/json does not load JSON viewer?
|
// NOTE: text/json does not load JSON viewer?
|
||||||
await expectOutcome("text/json", "text");
|
await expectOutcome("text/json", "text");
|
||||||
|
|
||||||
|
// Unknown text/ types should be loadable as plain text documents.
|
||||||
|
await expectOutcome("text/unknown-type", "text");
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -166,11 +166,12 @@ HttpIndexViewer.prototype = {
|
||||||
|
|
||||||
aChannel.contentType = contentType;
|
aChannel.contentType = contentType;
|
||||||
|
|
||||||
let contract = Services.catMan.getCategoryEntry(
|
// NOTE: This assumes that both text/html and text/plain will continue to be
|
||||||
"Gecko-Content-Viewers",
|
// handled by nsContentDLF. If this ever changes this logic will need to be
|
||||||
contentType
|
// updated.
|
||||||
);
|
let factory = Cc[
|
||||||
let factory = Cc[contract].getService(Ci.nsIDocumentLoaderFactory);
|
"@mozilla.org/content/document-loader-factory;1"
|
||||||
|
].getService(Ci.nsIDocumentLoaderFactory);
|
||||||
|
|
||||||
let listener = {};
|
let listener = {};
|
||||||
let res = factory.createInstance(
|
let res = factory.createInstance(
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue