diff --git a/docshell/base/nsIDocumentLoaderFactory.idl b/docshell/base/nsIDocumentLoaderFactory.idl index e3df2b2241eb..2bc607c9a517 100644 --- a/docshell/base/nsIDocumentLoaderFactory.idl +++ b/docshell/base/nsIDocumentLoaderFactory.idl @@ -17,10 +17,9 @@ webidl Document; /** * To get a component that implements nsIDocumentLoaderFactory - * for a given mimetype, use nsICategoryManager to find an entry - * with the mimetype as its name in the category "Gecko-Content-Viewers". - * The value of the entry is the contractid of the component. - * The component is a service, so use GetService, not CreateInstance to get it. + * for a given mimetype, use nsContentUtils::FindInternalDocumentViewer. + * This will look up the MIME type within the "Gecko-Content-Viewers" category, + * with additional handlers for other content types. */ [scriptable, uuid(e795239e-9d3c-47c4-b063-9e600fb3b287)] diff --git a/dom/base/Document.cpp b/dom/base/Document.cpp index b933faf0c335..450e83209fb2 100644 --- a/dom/base/Document.cpp +++ b/dom/base/Document.cpp @@ -1081,21 +1081,13 @@ nsresult ExternalResourceMap::PendingLoad::SetupViewer( new LoadgroupCallbacks(callbacks); newLoadGroup->SetNotificationCallbacks(newCallbacks); - // This is some serious hackery cribbed from docshell - nsCOMPtr 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 docLoaderFactory = - do_GetService(contractId.get()); + nsContentUtils::FindInternalDocumentViewer(type); NS_ENSURE_TRUE(docLoaderFactory, NS_ERROR_NOT_AVAILABLE); nsCOMPtr viewer; nsCOMPtr listener; - rv = docLoaderFactory->CreateInstance( + nsresult rv = docLoaderFactory->CreateInstance( "external-resource", chan, newLoadGroup, type, nullptr, nullptr, getter_AddRefs(listener), getter_AddRefs(viewer)); NS_ENSURE_SUCCESS(rv, rv); diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp index fbcd9c6b23cd..23e60fb8e11e 100644 --- a/dom/base/nsContentUtils.cpp +++ b/dom/base/nsContentUtils.cpp @@ -4626,28 +4626,66 @@ bool nsContentUtils::IsChildOfSameType(Document* aDoc) { 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) { - // NOTE: if you add a type here, add it to the content_types array in - // layout/build/components.conf as well. - return aContentType.EqualsLiteral(TEXT_PLAIN) || - aContentType.EqualsLiteral(TEXT_CSS) || - aContentType.EqualsLiteral(TEXT_CACHE_MANIFEST) || - 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); + // All `text/*`, any JSON type and any JavaScript type are considered "plain + // text" types for the purposes of how to render them as a document. + return (StringBeginsWith(aContentType, "text/"_ns) && + !IsNonPlainTextType(aContentType)) || + IsJSONType(aContentType) || IsJavascriptMIMEType(aContentType); } bool nsContentUtils::IsUtf8OnlyPlainTextType(const nsACString& aContentType) { // NOTE: This must be a subset of the list in IsPlainTextType(). - return aContentType.EqualsLiteral(TEXT_CACHE_MANIFEST) || - aContentType.EqualsLiteral(APPLICATION_JSON) || - aContentType.EqualsLiteral(TEXT_JSON) || + return IsJSONType(aContentType) || + aContentType.EqualsLiteral(TEXT_CACHE_MANIFEST) || aContentType.EqualsLiteral(TEXT_VTT); } @@ -7213,9 +7251,12 @@ nsContentUtils::FindInternalDocumentViewer(const nsACString& aType, return docFactory.forget(); } - if (DecoderTraits::IsSupportedInVideoDocument(aType)) { - docFactory = - do_GetService("@mozilla.org/content/document-loader-factory;1"); + // If the type wasn't registered in `Gecko-Content-Viewers`, check if it's + // another type which we may dynamically support, such as `text/*` types or + // 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) { *aLoaderType = TYPE_CONTENT; } @@ -7925,32 +7966,40 @@ void nsContentUtils::DestroyMatchString(void* aData) { } } -bool nsContentUtils::IsJavascriptMIMEType(const nsAString& aMIMEType) { - // Table ordered from most to least likely JS MIME types. - static const char* jsTypes[] = {"text/javascript", - "text/ecmascript", - "application/javascript", - "application/ecmascript", - "application/x-javascript", - "application/x-ecmascript", - "text/javascript1.0", - "text/javascript1.1", - "text/javascript1.2", - "text/javascript1.3", - "text/javascript1.4", - "text/javascript1.5", - "text/jscript", - "text/livescript", - "text/x-ecmascript", - "text/x-javascript", - nullptr}; +// Table ordered from most to least likely JS MIME types. +static constexpr std::string_view kJavascriptMIMETypes[] = { + "text/javascript", + "text/ecmascript", + "application/javascript", + "application/ecmascript", + "application/x-javascript", + "application/x-ecmascript", + "text/javascript1.0", + "text/javascript1.1", + "text/javascript1.2", + "text/javascript1.3", + "text/javascript1.4", + "text/javascript1.5", + "text/jscript", + "text/livescript", + "text/x-ecmascript", + "text/x-javascript"}; - for (uint32_t i = 0; jsTypes[i]; ++i) { - if (aMIMEType.LowerCaseEqualsASCII(jsTypes[i])) { +bool nsContentUtils::IsJavascriptMIMEType(const nsAString& aMIMEType) { + for (std::string_view type : kJavascriptMIMETypes) { + if (aMIMEType.LowerCaseEqualsASCII(type.data(), type.length())) { 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; } diff --git a/dom/base/nsContentUtils.h b/dom/base/nsContentUtils.h index bb35e4d98929..4f9abc2f0985 100644 --- a/dom/base/nsContentUtils.h +++ b/dom/base/nsContentUtils.h @@ -2764,6 +2764,7 @@ class nsContentUtils { static bool IsJavaScriptLanguage(const nsString& aName); static bool IsJavascriptMIMEType(const nsAString& aMIMEType); + static bool IsJavascriptMIMEType(const nsACString& aMIMEType); static void SplitMimeType(const nsAString& aValue, nsString& aType, nsString& aParams); diff --git a/gfx/thebes/gfxSVGGlyphs.cpp b/gfx/thebes/gfxSVGGlyphs.cpp index 4d688f5b8c16..4ee5b5ab32e4 100644 --- a/gfx/thebes/gfxSVGGlyphs.cpp +++ b/gfx/thebes/gfxSVGGlyphs.cpp @@ -131,20 +131,13 @@ gfxSVGGlyphsDocument* gfxSVGGlyphs::FindOrCreateGlyphsDocument( } nsresult gfxSVGGlyphsDocument::SetupPresentation() { - nsCOMPtr 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 docLoaderFactory = - do_GetService(contractId.get()); + nsContentUtils::FindInternalDocumentViewer("image/svg+xml"_ns); NS_ASSERTION(docLoaderFactory, "Couldn't get DocumentLoaderFactory"); nsCOMPtr viewer; - rv = docLoaderFactory->CreateInstanceForDocument(nullptr, mDocument, nullptr, - getter_AddRefs(viewer)); + nsresult rv = docLoaderFactory->CreateInstanceForDocument( + nullptr, mDocument, nullptr, getter_AddRefs(viewer)); NS_ENSURE_SUCCESS(rv, rv); auto upem = mOwner->FontEntry()->UnitsPerEm(); diff --git a/image/SVGDocumentWrapper.cpp b/image/SVGDocumentWrapper.cpp index 08228c9c708c..bf29d8d64dd7 100644 --- a/image/SVGDocumentWrapper.cpp +++ b/image/SVGDocumentWrapper.cpp @@ -283,20 +283,14 @@ nsresult SVGDocumentWrapper::SetupViewer(nsIRequest* aRequest, NS_ENSURE_TRUE(newLoadGroup, NS_ERROR_OUT_OF_MEMORY); newLoadGroup->SetLoadGroup(loadGroup); - nsCOMPtr 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 docLoaderFactory = - do_GetService(contractId.get()); + nsContentUtils::FindInternalDocumentViewer( + nsLiteralCString(IMAGE_SVG_XML)); NS_ENSURE_TRUE(docLoaderFactory, NS_ERROR_NOT_AVAILABLE); nsCOMPtr viewer; nsCOMPtr listener; - rv = docLoaderFactory->CreateInstance( + nsresult rv = docLoaderFactory->CreateInstance( "external-resource", chan, newLoadGroup, nsLiteralCString(IMAGE_SVG_XML), nullptr, nullptr, getter_AddRefs(listener), getter_AddRefs(viewer)); NS_ENSURE_SUCCESS(rv, rv); diff --git a/layout/build/components.conf b/layout/build/components.conf index ec9eb24aeb0f..007876db2ce5 100644 --- a/layout/build/components.conf +++ b/layout/build/components.conf @@ -14,24 +14,14 @@ UnloadFunc = 'nsLayoutModuleDtor' Priority = 100 content_types = [ - 'application/ecmascript', - 'application/javascript', - 'application/json', 'application/mathml+xml', 'application/rdf+xml', 'application/vnd.wap.xhtml+xml', - 'application/x-javascript', 'application/x-view-source', 'application/xhtml+xml', 'application/xml', 'image/svg+xml', - 'text/cache-manifest', - 'text/css', - 'text/ecmascript', - 'text/event-stream', 'text/html', - 'text/javascript', - 'text/json', 'text/plain', 'text/rdf', 'text/vtt', diff --git a/netwerk/test/browser/browser_display_plaintext_types.js b/netwerk/test/browser/browser_display_plaintext_types.js index 1b47be6f0c91..6e5eb71687bd 100644 --- a/netwerk/test/browser/browser_display_plaintext_types.js +++ b/netwerk/test/browser/browser_display_plaintext_types.js @@ -61,4 +61,7 @@ add_task(async function test_display_plaintext_type() { await expectOutcome("application/json", "jsonviewer"); // NOTE: text/json does not load JSON viewer? await expectOutcome("text/json", "text"); + + // Unknown text/ types should be loadable as plain text documents. + await expectOutcome("text/unknown-type", "text"); }); diff --git a/toolkit/components/utils/SimpleServices.sys.mjs b/toolkit/components/utils/SimpleServices.sys.mjs index 7c872cc7a68d..5280d381fe87 100644 --- a/toolkit/components/utils/SimpleServices.sys.mjs +++ b/toolkit/components/utils/SimpleServices.sys.mjs @@ -166,11 +166,12 @@ HttpIndexViewer.prototype = { aChannel.contentType = contentType; - let contract = Services.catMan.getCategoryEntry( - "Gecko-Content-Viewers", - contentType - ); - let factory = Cc[contract].getService(Ci.nsIDocumentLoaderFactory); + // NOTE: This assumes that both text/html and text/plain will continue to be + // handled by nsContentDLF. If this ever changes this logic will need to be + // updated. + let factory = Cc[ + "@mozilla.org/content/document-loader-factory;1" + ].getService(Ci.nsIDocumentLoaderFactory); let listener = {}; let res = factory.createInstance(