Bug 1209184: Part 2 - [webext] Localize CSS files served from moz-extension: channels. r=jdm
☠☠ backed out by cbf641f8da0a ☠ ☠
authorKris Maglione <maglione.k@gmail.com>
Fri, 27 Nov 2015 16:03:01 -0800
changeset 274660 a517959befe9b9e426380771c8712e568bb62832
parent 274659 a4f1765a6cdf7bca6ed2d22788be9e03e6ed8c24
child 274661 399404ff25e40003c620cd1d6193790127a5c121
push id68637
push usercbook@mozilla.com
push dateMon, 30 Nov 2015 12:45:36 +0000
treeherdermozilla-inbound@5a8a532f97cd [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjdm
bugs1209184
milestone45.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1209184: Part 2 - [webext] Localize CSS files served from moz-extension: channels. r=jdm
netwerk/protocol/res/ExtensionProtocolHandler.cpp
netwerk/protocol/res/ExtensionProtocolHandler.h
netwerk/protocol/res/SubstitutingProtocolHandler.cpp
netwerk/protocol/res/SubstitutingProtocolHandler.h
--- a/netwerk/protocol/res/ExtensionProtocolHandler.cpp
+++ b/netwerk/protocol/res/ExtensionProtocolHandler.cpp
@@ -3,16 +3,26 @@
 /* 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/. */
 
 #include "ExtensionProtocolHandler.h"
 
 #include "nsIAddonPolicyService.h"
 #include "nsServiceManagerUtils.h"
+#include "nsIURL.h"
+#include "nsIChannel.h"
+#include "nsIStreamListener.h"
+#include "nsIRequestObserver.h"
+#include "nsIInputStreamChannel.h"
+#include "nsIInputStream.h"
+#include "nsIOutputStream.h"
+#include "nsIStreamConverterService.h"
+#include "nsIPipe.h"
+#include "nsNetUtil.h"
 
 namespace mozilla {
 
 NS_IMPL_QUERY_INTERFACE(ExtensionProtocolHandler, nsISubstitutingProtocolHandler,
                         nsIProtocolHandler, nsIProtocolHandlerWithDynamicFlags,
                         nsISupportsWeakReference)
 NS_IMPL_ADDREF_INHERITED(ExtensionProtocolHandler, SubstitutingProtocolHandler)
 NS_IMPL_RELEASE_INHERITED(ExtensionProtocolHandler, SubstitutingProtocolHandler)
@@ -28,9 +38,112 @@ ExtensionProtocolHandler::GetFlagsForURI
     nsresult rv = aps->ExtensionURILoadableByAnyone(aURI, &loadableByAnyone);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   *aFlags = URI_STD | URI_IS_LOCAL_RESOURCE | (loadableByAnyone ? (URI_LOADABLE_BY_ANYONE | URI_FETCHABLE_BY_ANYONE) : URI_DANGEROUS_TO_LOAD);
   return NS_OK;
 }
 
+class PipeCloser : public nsIRequestObserver
+{
+public:
+  NS_DECL_ISUPPORTS
+
+  explicit PipeCloser(nsIOutputStream* aOutputStream) :
+    mOutputStream(aOutputStream)
+  {
+  }
+
+  NS_IMETHOD OnStartRequest(nsIRequest*, nsISupports*) override
+  {
+    return NS_OK;
+  }
+
+  NS_IMETHOD OnStopRequest(nsIRequest*, nsISupports*, nsresult aStatusCode) override
+  {
+    NS_ENSURE_TRUE(mOutputStream, NS_ERROR_UNEXPECTED);
+
+    nsresult rv = mOutputStream->Close();
+    mOutputStream = nullptr;
+    return rv;
+  }
+
+protected:
+  virtual ~PipeCloser() {}
+
+private:
+  nsCOMPtr<nsIOutputStream> mOutputStream;
+};
+
+NS_IMPL_ISUPPORTS(PipeCloser, nsIRequestObserver)
+
+nsresult
+ExtensionProtocolHandler::SubstituteChannel(nsIURI* aURI,
+                                            nsILoadInfo* aLoadInfo,
+                                            nsIChannel** result)
+{
+  nsresult rv;
+  nsCOMPtr<nsIURL> url = do_QueryInterface(aURI, &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsAutoCString ext;
+  rv = url->GetFileExtension(ext);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (!ext.LowerCaseEqualsLiteral("css")) {
+    return NS_OK;
+  }
+
+  // Filter CSS files to replace locale message tokens with localized strings.
+
+  nsCOMPtr<nsIStreamConverterService> convService =
+    do_GetService(NS_STREAMCONVERTERSERVICE_CONTRACTID, &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  const char* kFromType = "application/vnd.mozilla.webext.unlocalized";
+  const char* kToType = "text/css";
+
+  nsCOMPtr<nsIInputStream> inputStream;
+  if (aLoadInfo && aLoadInfo->GetSecurityMode()) {
+    // Certain security checks require an async channel.
+
+    nsCOMPtr<nsIOutputStream> outputStream;
+    rv = NS_NewPipe(getter_AddRefs(inputStream), getter_AddRefs(outputStream),
+                    0, UINT32_MAX, true, false);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    nsCOMPtr<nsIStreamListener> listener;
+    nsCOMPtr<nsIRequestObserver> observer = new PipeCloser(outputStream);
+    rv = NS_NewSimpleStreamListener(getter_AddRefs(listener), outputStream, observer);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    nsCOMPtr<nsIStreamListener> converter;
+    rv = convService->AsyncConvertData(kFromType, kToType, listener,
+                                       aURI, getter_AddRefs(converter));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = (*result)->AsyncOpen2(converter);
+  } else {
+    // Stylesheet loads for extension content scripts require a sync channel,
+    // but fortunately do not invoke security checks.
+
+    nsCOMPtr<nsIInputStream> sourceStream;
+    rv = (*result)->Open(getter_AddRefs(sourceStream));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = convService->Convert(sourceStream, kFromType, kToType,
+                              aURI, getter_AddRefs(inputStream));
+  }
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsIChannel> channel;
+  rv = NS_NewInputStreamChannelInternal(getter_AddRefs(channel), aURI, inputStream,
+                                        NS_LITERAL_CSTRING("text/css"),
+                                        NS_LITERAL_CSTRING("utf-8"),
+                                        aLoadInfo);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  channel.swap(*result);
+  return NS_OK;
+}
+
 } // namespace mozilla
--- a/netwerk/protocol/res/ExtensionProtocolHandler.h
+++ b/netwerk/protocol/res/ExtensionProtocolHandler.h
@@ -34,13 +34,15 @@ protected:
     // substitutions can only match on host.
     if (SubstitutingProtocolHandler::HasSubstitution(aHost) && aPath.EqualsLiteral("/_blank.html")) {
       aResult.AssignLiteral("about:blank");
       return true;
     }
 
     return false;
   }
+
+  virtual nsresult SubstituteChannel(nsIURI* uri, nsILoadInfo* aLoadInfo, nsIChannel** result) override;
 };
 
 } // namespace mozilla
 
 #endif /* ExtensionProtocolHandler_h___ */
--- a/netwerk/protocol/res/SubstitutingProtocolHandler.cpp
+++ b/netwerk/protocol/res/SubstitutingProtocolHandler.cpp
@@ -248,17 +248,20 @@ SubstitutingProtocolHandler::NewChannel2
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = NS_NewChannelInternal(result, newURI, aLoadInfo);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsLoadFlags loadFlags = 0;
   (*result)->GetLoadFlags(&loadFlags);
   (*result)->SetLoadFlags(loadFlags & ~nsIChannel::LOAD_REPLACE);
-  return (*result)->SetOriginalURI(uri);
+  rv = (*result)->SetOriginalURI(uri);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return SubstituteChannel(uri, aLoadInfo, result);
 }
 
 nsresult
 SubstitutingProtocolHandler::NewChannel(nsIURI* uri, nsIChannel* *result)
 {
   return NewChannel2(uri, nullptr, result);
 }
 
--- a/netwerk/protocol/res/SubstitutingProtocolHandler.h
+++ b/netwerk/protocol/res/SubstitutingProtocolHandler.h
@@ -54,16 +54,23 @@ protected:
 
   // Override this in the subclass to check for special case when resolving URIs
   // _before_ checking substitutions.
   virtual bool ResolveSpecialCases(const nsACString& aHost, const nsACString& aPath, nsACString& aResult)
   {
     return false;
   }
 
+  // Override this in the subclass to check for special case when opening
+  // channels.
+  virtual nsresult SubstituteChannel(nsIURI* uri, nsILoadInfo* aLoadInfo, nsIChannel** result)
+  {
+    return NS_OK;
+  }
+
   nsIIOService* IOService() { return mIOService; }
 
 private:
   nsCString mScheme;
   Maybe<uint32_t> mFlags;
   nsInterfaceHashtable<nsCStringHashKey,nsIURI> mSubstitutions;
   nsCOMPtr<nsIIOService> mIOService;