Add API to test stream converters to find out their output type draft
authorMatt Woodrow <mwoodrow@mozilla.com>
Thu, 05 Dec 2019 16:15:12 +1300
changeset 2517420 dcb9d0517d82e54ceece41ddb5cf383dd97112a1
parent 2517419 e19cf79e46cded18e373aeaceed4ed8aaaf4e3d5
child 2517421 340b11aac1bca6cee9dd1a6dc438a1c136926e16
push id460521
push usermwoodrow@mozilla.com
push dateThu, 05 Dec 2019 03:16:46 +0000
treeherdertry@340b11aac1bc [default view] [failures only]
milestone73.0a1
Add API to test stream converters to find out their output type
browser/extensions/pdfjs/content/PdfStreamConverter.jsm
devtools/client/jsonview/converter-child.js
modules/libjar/zipwriter/nsDeflateConverter.cpp
netwerk/streamconv/converters/mozTXTToHTMLConv.cpp
netwerk/streamconv/converters/nsFTPDirListingConv.cpp
netwerk/streamconv/converters/nsHTTPCompressConv.cpp
netwerk/streamconv/converters/nsIndexedToHTML.cpp
netwerk/streamconv/converters/nsMultiMixedConv.cpp
netwerk/streamconv/converters/nsUnknownDecoder.cpp
netwerk/streamconv/nsIStreamConverter.idl
netwerk/streamconv/nsIStreamConverterService.idl
netwerk/streamconv/nsStreamConverterService.cpp
uriloader/base/nsURILoader.cpp
--- a/browser/extensions/pdfjs/content/PdfStreamConverter.jsm
+++ b/browser/extensions/pdfjs/content/PdfStreamConverter.jsm
@@ -1003,16 +1003,20 @@ PdfStreamConverter.prototype = {
   },
 
   // nsIStreamConverter::asyncConvertData
   asyncConvertData(aFromType, aToType, aListener, aCtxt) {
     // Store the listener passed to us
     this.listener = aListener;
   },
 
+  getConvertedType(aFromType) {
+    return "text/html";
+  },
+
   // nsIStreamListener::onDataAvailable
   onDataAvailable(aRequest, aInputStream, aOffset, aCount) {
     if (!this.dataListener) {
       return;
     }
 
     var binaryStream = this.binaryStream;
     binaryStream.setInputStream(aInputStream);
--- a/devtools/client/jsonview/converter-child.js
+++ b/devtools/client/jsonview/converter-child.js
@@ -70,16 +70,19 @@ Converter.prototype = {
    */
   convert: function(fromStream, fromType, toType, ctx) {
     return fromStream;
   },
 
   asyncConvertData: function(fromType, toType, listener, ctx) {
     this.listener = listener;
   },
+  getConvertedType: function(fromType) {
+    return "text/html";
+  },
 
   onDataAvailable: function(request, inputStream, offset, count) {
     // Decode and insert data.
     const buffer = new ArrayBuffer(count);
     new BinaryInput(inputStream).readArrayBuffer(count, buffer);
     this.decodeAndInsertBuffer(buffer);
   },
 
--- a/modules/libjar/zipwriter/nsDeflateConverter.cpp
+++ b/modules/libjar/zipwriter/nsDeflateConverter.cpp
@@ -87,16 +87,22 @@ NS_IMETHODIMP nsDeflateConverter::AsyncC
   nsresult rv = Init();
   NS_ENSURE_SUCCESS(rv, rv);
 
   mListener = aListener;
   mContext = aCtxt;
   return rv;
 }
 
+NS_IMETHODIMP
+nsDeflateConverter::GetConvertedType(const char* aFromType,
+                                     nsACString& aToType) {
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
 NS_IMETHODIMP nsDeflateConverter::OnDataAvailable(nsIRequest* aRequest,
                                                   nsIInputStream* aInputStream,
                                                   uint64_t aOffset,
                                                   uint32_t aCount) {
   if (!mListener) return NS_ERROR_NOT_INITIALIZED;
 
   auto buffer = MakeUnique<char[]>(aCount);
   NS_ENSURE_TRUE(buffer, NS_ERROR_OUT_OF_MEMORY);
--- a/netwerk/streamconv/converters/mozTXTToHTMLConv.cpp
+++ b/netwerk/streamconv/converters/mozTXTToHTMLConv.cpp
@@ -1214,16 +1214,21 @@ mozTXTToHTMLConv::Convert(nsIInputStream
 NS_IMETHODIMP
 mozTXTToHTMLConv::AsyncConvertData(const char* aFromType, const char* aToType,
                                    nsIStreamListener* aListener,
                                    nsISupports* aCtxt) {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
+mozTXTToHTMLConv::GetConvertedType(const char* aFromType, nsACString& aToType) {
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
 mozTXTToHTMLConv::OnDataAvailable(nsIRequest* request, nsIInputStream* inStr,
                                   uint64_t sourceOffset, uint32_t count) {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 mozTXTToHTMLConv::OnStartRequest(nsIRequest* request) {
   return NS_ERROR_NOT_IMPLEMENTED;
--- a/netwerk/streamconv/converters/nsFTPDirListingConv.cpp
+++ b/netwerk/streamconv/converters/nsFTPDirListingConv.cpp
@@ -65,16 +65,22 @@ nsFTPDirListingConv::AsyncConvertData(co
 
   MOZ_LOG(gFTPDirListConvLog, LogLevel::Debug,
           ("nsFTPDirListingConv::AsyncConvertData() converting FROM raw, TO "
            "application/http-index-format\n"));
 
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsFTPDirListingConv::GetConvertedType(const char* aFromType,
+                                      nsACString& aToType) {
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
 // nsIStreamListener implementation
 NS_IMETHODIMP
 nsFTPDirListingConv::OnDataAvailable(nsIRequest* request, nsIInputStream* inStr,
                                      uint64_t sourceOffset, uint32_t count) {
   NS_ASSERTION(request, "FTP dir listing stream converter needs a request");
 
   nsresult rv;
 
--- a/netwerk/streamconv/converters/nsHTTPCompressConv.cpp
+++ b/netwerk/streamconv/converters/nsHTTPCompressConv.cpp
@@ -111,16 +111,22 @@ nsHTTPCompressConv::AsyncConvertData(con
   MutexAutoLock lock(mMutex);
   // hook ourself up with the receiving listener.
   mListener = aListener;
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsHTTPCompressConv::GetConvertedType(const char* aFromType,
+                                     nsACString& aToType) {
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
 nsHTTPCompressConv::OnStartRequest(nsIRequest* request) {
   LOG(("nsHttpCompresssConv %p onstart\n", this));
   nsCOMPtr<nsIStreamListener> listener;
   {
     MutexAutoLock lock(mMutex);
     listener = mListener;
   }
   return listener->OnStartRequest(request);
--- a/netwerk/streamconv/converters/nsIndexedToHTML.cpp
+++ b/netwerk/streamconv/converters/nsIndexedToHTML.cpp
@@ -91,16 +91,21 @@ nsIndexedToHTML::Convert(nsIInputStream*
 NS_IMETHODIMP
 nsIndexedToHTML::AsyncConvertData(const char* aFromType, const char* aToType,
                                   nsIStreamListener* aListener,
                                   nsISupports* aCtxt) {
   return Init(aListener);
 }
 
 NS_IMETHODIMP
+nsIndexedToHTML::GetConvertedType(const char* aFromType, nsACString& aToType) {
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
 nsIndexedToHTML::OnStartRequest(nsIRequest* request) {
   nsCString buffer;
   nsresult rv = DoOnStartRequest(request, nullptr, buffer);
   if (NS_FAILED(rv)) {
     request->Cancel(rv);
   }
 
   rv = mListener->OnStartRequest(request);
--- a/netwerk/streamconv/converters/nsMultiMixedConv.cpp
+++ b/netwerk/streamconv/converters/nsMultiMixedConv.cpp
@@ -402,16 +402,21 @@ nsMultiMixedConv::AsyncConvertData(const
   // WARNING: this listener must be able to handle multiple OnStartRequest,
   // OnDataAvail() and OnStopRequest() call combinations. We call of series
   // of these for each sub-part in the raw stream.
   mFinalListener = aListener;
 
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsMultiMixedConv::GetConvertedType(const char* aFromType, nsACString& aToType) {
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
 // nsIRequestObserver implementation
 NS_IMETHODIMP
 nsMultiMixedConv::OnStartRequest(nsIRequest* request) {
   // we're assuming the content-type is available at this stage
   NS_ASSERTION(mBoundary.IsEmpty(), "a second on start???");
 
   nsresult rv;
 
--- a/netwerk/streamconv/converters/nsUnknownDecoder.cpp
+++ b/netwerk/streamconv/converters/nsUnknownDecoder.cpp
@@ -139,16 +139,21 @@ nsUnknownDecoder::AsyncConvertData(const
   // to throw at him.
   //
 
   MutexAutoLock lock(mMutex);
   mNextListener = aListener;
   return (aListener) ? NS_OK : NS_ERROR_FAILURE;
 }
 
+NS_IMETHODIMP
+nsUnknownDecoder::GetConvertedType(const char* aFromType, nsACString& aToType) {
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
 // ----
 //
 // nsIStreamListener methods...
 //
 // ----
 
 NS_IMETHODIMP
 nsUnknownDecoder::OnDataAvailable(nsIRequest* request, nsIInputStream* aStream,
--- a/netwerk/streamconv/nsIStreamConverter.idl
+++ b/netwerk/streamconv/nsIStreamConverter.idl
@@ -88,13 +88,18 @@ interface nsIStreamConverter : nsIStream
      * @param aListener     The listener who receives the converted data.
      * @param aCtxt         Either an opaque context, or a converter specific context
      *                      (implementation specific).
      */
     void asyncConvertData(in string aFromType,
                           in string aToType,
                           in nsIStreamListener aListener,
                           in nsISupports aCtxt);
+
+    /**
+     * Compute the resulting output from when using aFromType and *\*
+     */
+    ACString getConvertedType(in string aFromType);
 };
 
 %{C++
 #define NS_ISTREAMCONVERTER_KEY         "@mozilla.org/streamconv;1"
 %}
--- a/netwerk/streamconv/nsIStreamConverterService.idl
+++ b/netwerk/streamconv/nsIStreamConverterService.idl
@@ -29,16 +29,18 @@ interface nsIStreamListener;
 interface nsIStreamConverterService : nsISupports {
     /**
      * Tests whether conversion between the two specified types is possible.
      * This is cheaper than calling convert()/asyncConvertData(); it is not
      * necessary to call this function before calling one of those, though.
      */
     boolean canConvert(in string aFromType, in string aToType);
 
+    ACString convertedType(in string aFromType);
+
     /**
      * <b>SYNCHRONOUS VERSION</b>
      * Converts a stream of one type, to a stream of another type.
      *
      * Use this method when you have a stream you want to convert.
      *
      * @param aFromStream   The stream representing the original/raw data.
      * @param aFromType     The MIME type of aFromStream.
--- a/netwerk/streamconv/nsStreamConverterService.cpp
+++ b/netwerk/streamconv/nsStreamConverterService.cpp
@@ -347,16 +347,35 @@ nsStreamConverterService::CanConvert(con
   rv = FindConverter(contractID.get(), &converterChain);
   *_retval = NS_SUCCEEDED(rv);
 
   delete converterChain;
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsStreamConverterService::ConvertedType(const char* aFromType,
+                                        nsACString& aOutToType) {
+  // first determine whether we can even handle this conversion
+  // build a CONTRACTID
+  nsAutoCString contractID;
+  contractID.AssignLiteral(NS_ISTREAMCONVERTER_KEY "?from=");
+  contractID.Append(aFromType);
+  contractID.AppendLiteral("&to=*/*");
+  const char* cContractID = contractID.get();
+
+  nsresult rv;
+  nsCOMPtr<nsIStreamConverter> converter(do_CreateInstance(cContractID, &rv));
+  if (NS_SUCCEEDED(rv)) {
+    return converter->GetConvertedType(aFromType, aOutToType);
+  }
+  return rv;
+}
+
+NS_IMETHODIMP
 nsStreamConverterService::Convert(nsIInputStream* aFromStream,
                                   const char* aFromType, const char* aToType,
                                   nsISupports* aContext,
                                   nsIInputStream** _retval) {
   if (!aFromStream || !aFromType || !aToType || !_retval)
     return NS_ERROR_NULL_POINTER;
   nsresult rv;
 
--- a/uriloader/base/nsURILoader.cpp
+++ b/uriloader/base/nsURILoader.cpp
@@ -488,17 +488,33 @@ nsresult nsDocumentOpenInfo::DispatchCon
     // Fifth step:  If no listener prefers this type, see if any stream
     //              converters exist to transform this content type into
     //              some other.
     //
     // Don't do this if the server sent us a MIME type of "*/*" because they saw
     // it in our Accept header and got confused.
     // XXXbz have to be careful here; may end up in some sort of bizarre
     // infinite decoding loop.
-    if (mContentType != anyType && !(mFlags & nsIURILoader::DONT_CONVERT)) {
+    if (RefPtr<mozilla::net::DocumentLoadListener> doc =
+            do_GetInterface(m_originalContext)) {
+      LOG(("Attempting stream conversion"));
+      nsCOMPtr<nsIStreamConverterService> StreamConvService =
+          do_GetService(NS_STREAMCONVERTERSERVICE_CONTRACTID, &rv);
+      nsAutoCString str;
+      if (NS_SUCCEEDED(
+              StreamConvService->ConvertedType(mContentType.get(), str))) {
+        LOG(("Found converter into type %s", str.get()));
+        mContentType = str;
+        if (m_contentListener &&
+            TryContentListener(m_contentListener, aChannel)) {
+          LOG(("Success!"));
+          return NS_OK;
+        }
+      }
+    } else if (mContentType != anyType) {
       rv = ConvertData(request, m_contentListener, mContentType, anyType);
       if (NS_FAILED(rv)) {
         m_targetStreamListener = nullptr;
       } else if (m_targetStreamListener) {
         // We found a converter for this MIME type.  We'll just pump data into
         // it and let the downstream nsDocumentOpenInfo handle things.
         LOG(("  Converter taking over now"));
         return NS_OK;