Bug 1209184: Part 2 - [webext] Localize CSS files served from moz-extension: channels. r=jdm
authorKris Maglione <maglione.k@gmail.com>
Fri, 27 Nov 2015 16:03:01 -0800
changeset 309165 ceef1da728bf078fe100f35d80ff237ac7f3b7ba
parent 309164 62ff0d59cd62619bede132718cf524f2a142646d
child 309166 b8fe968c1775a69b166b5c4fae1684d06efec74a
push id5513
push userraliiev@mozilla.com
push dateMon, 25 Jan 2016 13:55:34 +0000
treeherdermozilla-beta@5ee97dd05b5c [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;