LCOV - code coverage report
Current view: top level - netwerk/protocol/res - ExtensionProtocolHandler.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 1 295 0.3 %
Date: 2017-07-14 16:53:18 Functions: 0 48 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
       5             :  * You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include "ExtensionProtocolHandler.h"
       8             : 
       9             : #include "mozilla/AbstractThread.h"
      10             : #include "mozilla/ClearOnShutdown.h"
      11             : #include "mozilla/ExtensionPolicyService.h"
      12             : #include "mozilla/FileUtils.h"
      13             : #include "mozilla/ipc/IPCStreamUtils.h"
      14             : #include "mozilla/ipc/URIParams.h"
      15             : #include "mozilla/ipc/URIUtils.h"
      16             : #include "mozilla/net/NeckoChild.h"
      17             : #include "mozilla/RefPtr.h"
      18             : 
      19             : #include "FileDescriptor.h"
      20             : #include "FileDescriptorFile.h"
      21             : #include "LoadInfo.h"
      22             : #include "nsServiceManagerUtils.h"
      23             : #include "nsIFile.h"
      24             : #include "nsIFileChannel.h"
      25             : #include "nsIFileStreams.h"
      26             : #include "nsIFileURL.h"
      27             : #include "nsIJARChannel.h"
      28             : #include "nsIMIMEService.h"
      29             : #include "nsIURL.h"
      30             : #include "nsIChannel.h"
      31             : #include "nsIInputStreamPump.h"
      32             : #include "nsIJARURI.h"
      33             : #include "nsIStreamListener.h"
      34             : #include "nsIThread.h"
      35             : #include "nsIInputStream.h"
      36             : #include "nsIOutputStream.h"
      37             : #include "nsIStreamConverterService.h"
      38             : #include "nsNetUtil.h"
      39             : #include "prio.h"
      40             : #include "SimpleChannel.h"
      41             : 
      42             : #if defined(XP_WIN)
      43             : #include "nsILocalFileWin.h"
      44             : #endif
      45             : 
      46             : #if !defined(XP_WIN) && defined(MOZ_CONTENT_SANDBOX)
      47             : #include "mozilla/SandboxSettings.h"
      48             : #endif
      49             : 
      50             : #define EXTENSION_SCHEME "moz-extension"
      51             : using mozilla::ipc::FileDescriptor;
      52             : using OptionalIPCStream = mozilla::ipc::OptionalIPCStream;
      53             : 
      54             : namespace mozilla {
      55             : 
      56             : template <>
      57             : class MOZ_MUST_USE_TYPE GenericErrorResult<nsresult>
      58             : {
      59             :   nsresult mErrorValue;
      60             : 
      61             :   template<typename V, typename E2> friend class Result;
      62             : 
      63             : public:
      64           0 :   explicit GenericErrorResult(nsresult aErrorValue) : mErrorValue(aErrorValue) {}
      65             : 
      66           0 :   operator nsresult() { return mErrorValue; }
      67             : };
      68             : 
      69             : namespace net {
      70             : 
      71             : using extensions::URLInfo;
      72             : 
      73           3 : StaticRefPtr<ExtensionProtocolHandler> ExtensionProtocolHandler::sSingleton;
      74             : 
      75             : static inline Result<Ok, nsresult>
      76             : WrapNSResult(PRStatus aRv)
      77             : {
      78             :     if (aRv != PR_SUCCESS) {
      79             :         return Err(NS_ERROR_FAILURE);
      80             :     }
      81             :     return Ok();
      82             : }
      83             : 
      84             : static inline Result<Ok, nsresult>
      85           0 : WrapNSResult(nsresult aRv)
      86             : {
      87           0 :     if (NS_FAILED(aRv)) {
      88           0 :         return Err(aRv);
      89             :     }
      90           0 :     return Ok();
      91             : }
      92             : 
      93             : #define NS_TRY(expr) MOZ_TRY(WrapNSResult(expr))
      94             : 
      95             : /**
      96             :  * Helper class used with SimpleChannel to asynchronously obtain an input
      97             :  * stream or file descriptor from the parent for a remote moz-extension load
      98             :  * from the child.
      99             :  */
     100             : class ExtensionStreamGetter : public RefCounted<ExtensionStreamGetter>
     101             : {
     102             :   public:
     103             :     // To use when getting a remote input stream for a resource
     104             :     // in an unpacked extension.
     105           0 :     ExtensionStreamGetter(nsIURI* aURI, nsILoadInfo* aLoadInfo)
     106           0 :       : mURI(aURI)
     107             :       , mLoadInfo(aLoadInfo)
     108           0 :       , mIsJarChannel(false)
     109             :     {
     110           0 :       MOZ_ASSERT(aURI);
     111           0 :       MOZ_ASSERT(aLoadInfo);
     112           0 :     }
     113             : 
     114             :     // To use when getting an FD for a packed extension JAR file
     115             :     // in order to load a resource.
     116           0 :     ExtensionStreamGetter(nsIURI* aURI, nsILoadInfo* aLoadInfo,
     117             :                           already_AddRefed<nsIJARChannel>&& aJarChannel,
     118             :                           nsIFile* aJarFile)
     119           0 :       : mURI(aURI)
     120             :       , mLoadInfo(aLoadInfo)
     121           0 :       , mJarChannel(Move(aJarChannel))
     122             :       , mJarFile(aJarFile)
     123           0 :       , mIsJarChannel(true)
     124             :     {
     125           0 :       MOZ_ASSERT(aURI);
     126           0 :       MOZ_ASSERT(aLoadInfo);
     127           0 :       MOZ_ASSERT(mJarChannel);
     128           0 :       MOZ_ASSERT(aJarFile);
     129           0 :     }
     130             : 
     131           0 :     ~ExtensionStreamGetter() {}
     132             : 
     133             :     // Get an input stream or file descriptor from the parent asynchronously.
     134             :     Result<Ok, nsresult> GetAsync(nsIStreamListener* aListener,
     135             :                                   nsIChannel* aChannel);
     136             : 
     137             :     // Handle an input stream being returned from the parent
     138             :     void OnStream(nsIInputStream* aStream);
     139             : 
     140             :     // Handle file descriptor being returned from the parent
     141             :     void OnFD(const FileDescriptor& aFD);
     142             : 
     143           0 :     MOZ_DECLARE_REFCOUNTED_TYPENAME(ExtensionStreamGetter)
     144             : 
     145             :   private:
     146             :     nsCOMPtr<nsIURI> mURI;
     147             :     nsCOMPtr<nsILoadInfo> mLoadInfo;
     148             :     nsCOMPtr<nsIJARChannel> mJarChannel;
     149             :     nsCOMPtr<nsIFile> mJarFile;
     150             :     nsCOMPtr<nsIStreamListener> mListener;
     151             :     nsCOMPtr<nsIChannel> mChannel;
     152             :     bool mIsJarChannel;
     153             : };
     154             : 
     155             : class ExtensionJARFileOpener : public nsISupports
     156             : {
     157             : public:
     158           0 :   ExtensionJARFileOpener(nsIFile* aFile,
     159           0 :                          NeckoParent::GetExtensionFDResolver& aResolve) :
     160             :     mFile(aFile),
     161           0 :     mResolve(aResolve)
     162             :   {
     163           0 :     MOZ_ASSERT(aFile);
     164           0 :     MOZ_ASSERT(aResolve);
     165           0 :   }
     166             : 
     167           0 :   NS_IMETHOD OpenFile()
     168             :   {
     169           0 :     MOZ_ASSERT(!NS_IsMainThread());
     170           0 :     AutoFDClose prFileDesc;
     171             : 
     172             : #if defined(XP_WIN)
     173             :     nsresult rv;
     174             :     nsCOMPtr<nsILocalFileWin> winFile = do_QueryInterface(mFile, &rv);
     175             :     MOZ_ASSERT(winFile);
     176             :     if (NS_SUCCEEDED(rv)) {
     177             :       rv = winFile->OpenNSPRFileDescShareDelete(PR_RDONLY, 0,
     178             :                                                 &prFileDesc.rwget());
     179             :     }
     180             : #else
     181           0 :     nsresult rv = mFile->OpenNSPRFileDesc(PR_RDONLY, 0, &prFileDesc.rwget());
     182             : #endif /* XP_WIN */
     183             : 
     184           0 :     if (NS_SUCCEEDED(rv)) {
     185           0 :       mFD = FileDescriptor(FileDescriptor::PlatformHandleType(
     186           0 :                            PR_FileDesc2NativeHandle(prFileDesc)));
     187             :     }
     188             : 
     189             :     nsCOMPtr<nsIRunnable> event =
     190           0 :       mozilla::NewRunnableMethod("ExtensionJarFileFDResolver",
     191           0 :         this, &ExtensionJARFileOpener::SendBackFD);
     192             : 
     193           0 :     rv = NS_DispatchToMainThread(event, nsIEventTarget::DISPATCH_NORMAL);
     194           0 :     NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "NS_DispatchToMainThread");
     195           0 :     return NS_OK;
     196             :   }
     197             : 
     198           0 :   NS_IMETHOD SendBackFD()
     199             :   {
     200           0 :     MOZ_ASSERT(NS_IsMainThread());
     201           0 :     mResolve(mFD);
     202           0 :     return NS_OK;
     203             :   }
     204             : 
     205             :   NS_DECL_THREADSAFE_ISUPPORTS
     206             : 
     207             : private:
     208           0 :   virtual ~ExtensionJARFileOpener() {}
     209             : 
     210             :   nsCOMPtr<nsIFile> mFile;
     211             :   NeckoParent::GetExtensionFDResolver mResolve;
     212             :   FileDescriptor mFD;
     213             : };
     214             : 
     215           0 : NS_IMPL_ISUPPORTS(ExtensionJARFileOpener, nsISupports)
     216             : 
     217             : // The amount of time, in milliseconds, that the file opener thread will remain
     218             : // allocated after it is used. This value chosen because to match other uses
     219             : // of LazyIdleThread.
     220             : #define DEFAULT_THREAD_TIMEOUT_MS 30000
     221             : 
     222             : // Request an FD or input stream from the parent.
     223             : Result<Ok, nsresult>
     224           0 : ExtensionStreamGetter::GetAsync(nsIStreamListener* aListener,
     225             :                                 nsIChannel* aChannel)
     226             : {
     227           0 :   MOZ_ASSERT(IsNeckoChild());
     228             : 
     229           0 :   mListener = aListener;
     230           0 :   mChannel = aChannel;
     231             : 
     232             :   // Serialize the URI to send to parent
     233           0 :   mozilla::ipc::URIParams uri;
     234           0 :   SerializeURI(mURI, uri);
     235             : 
     236             :   // Serialize the LoadInfo to send to parent
     237           0 :   OptionalLoadInfoArgs loadInfo;
     238           0 :   NS_TRY(mozilla::ipc::LoadInfoToLoadInfoArgs(mLoadInfo, &loadInfo));
     239             : 
     240           0 :   RefPtr<ExtensionStreamGetter> self = this;
     241           0 :   if (mIsJarChannel) {
     242             :     // Request an FD for this moz-extension URI
     243           0 :     gNeckoChild->SendGetExtensionFD(uri, loadInfo)->Then(
     244           0 :       AbstractThread::MainThread(),
     245             :       __func__,
     246           0 :       [self] (const FileDescriptor& fd) {
     247           0 :         self->OnFD(fd);
     248           0 :       },
     249           0 :       [self] (const mozilla::ipc::PromiseRejectReason) {
     250           0 :         self->OnFD(FileDescriptor());
     251           0 :       }
     252           0 :     );
     253           0 :     return Ok();
     254             :   }
     255             : 
     256             :   // Request an input stream for this moz-extension URI
     257           0 :   gNeckoChild->SendGetExtensionStream(uri, loadInfo)->Then(
     258           0 :     AbstractThread::MainThread(),
     259             :     __func__,
     260           0 :     [self] (const OptionalIPCStream& stream) {
     261           0 :       nsCOMPtr<nsIInputStream> inputStream;
     262           0 :       if (stream.type() == OptionalIPCStream::OptionalIPCStream::TIPCStream) {
     263           0 :         inputStream = ipc::DeserializeIPCStream(stream);
     264             :       }
     265           0 :       self->OnStream(inputStream);
     266           0 :     },
     267           0 :     [self] (const mozilla::ipc::PromiseRejectReason) {
     268           0 :       self->OnStream(nullptr);
     269           0 :     }
     270           0 :   );
     271           0 :   return Ok();
     272             : }
     273             : 
     274             : // Handle an input stream sent from the parent.
     275             : void
     276           0 : ExtensionStreamGetter::OnStream(nsIInputStream* aStream)
     277             : {
     278           0 :   MOZ_ASSERT(IsNeckoChild());
     279           0 :   MOZ_ASSERT(mListener);
     280             : 
     281             :   // We must keep an owning reference to the listener
     282             :   // until we pass it on to AsyncRead.
     283           0 :   nsCOMPtr<nsIStreamListener> listener = mListener.forget();
     284             : 
     285           0 :   MOZ_ASSERT(mChannel);
     286             : 
     287           0 :   if (!aStream) {
     288             :     // The parent didn't send us back a stream.
     289           0 :     listener->OnStartRequest(mChannel, nullptr);
     290           0 :     listener->OnStopRequest(mChannel, nullptr, NS_ERROR_FILE_ACCESS_DENIED);
     291           0 :     mChannel->Cancel(NS_BINDING_ABORTED);
     292           0 :     return;
     293             :   }
     294             : 
     295           0 :   nsCOMPtr<nsIInputStreamPump> pump;
     296           0 :   nsresult rv = NS_NewInputStreamPump(getter_AddRefs(pump), aStream);
     297           0 :   if (NS_FAILED(rv)) {
     298           0 :     mChannel->Cancel(NS_BINDING_ABORTED);
     299           0 :     return;
     300             :   }
     301             : 
     302           0 :   rv = pump->AsyncRead(listener, nullptr);
     303           0 :   if (NS_FAILED(rv)) {
     304           0 :     mChannel->Cancel(NS_BINDING_ABORTED);
     305             :   }
     306             : }
     307             : 
     308             : // Handle an FD sent from the parent.
     309             : void
     310           0 : ExtensionStreamGetter::OnFD(const FileDescriptor& aFD)
     311             : {
     312           0 :   MOZ_ASSERT(IsNeckoChild());
     313           0 :   MOZ_ASSERT(mListener);
     314           0 :   MOZ_ASSERT(mChannel);
     315             : 
     316           0 :   if (!aFD.IsValid()) {
     317           0 :     OnStream(nullptr);
     318           0 :     return;
     319             :   }
     320             : 
     321             :   // We must keep an owning reference to the listener
     322             :   // until we pass it on to AsyncOpen2.
     323           0 :   nsCOMPtr<nsIStreamListener> listener = mListener.forget();
     324             : 
     325           0 :   RefPtr<FileDescriptorFile> fdFile = new FileDescriptorFile(aFD, mJarFile);
     326           0 :   mJarChannel->SetJarFile(fdFile);
     327           0 :   nsresult rv = mJarChannel->AsyncOpen2(listener);
     328           0 :   if (NS_FAILED(rv)) {
     329           0 :     mChannel->Cancel(NS_BINDING_ABORTED);
     330             :   }
     331             : }
     332             : 
     333           0 : NS_IMPL_QUERY_INTERFACE(ExtensionProtocolHandler, nsISubstitutingProtocolHandler,
     334             :                         nsIProtocolHandler, nsIProtocolHandlerWithDynamicFlags,
     335             :                         nsISupportsWeakReference)
     336           0 : NS_IMPL_ADDREF_INHERITED(ExtensionProtocolHandler, SubstitutingProtocolHandler)
     337           0 : NS_IMPL_RELEASE_INHERITED(ExtensionProtocolHandler, SubstitutingProtocolHandler)
     338             : 
     339             : already_AddRefed<ExtensionProtocolHandler>
     340           0 : ExtensionProtocolHandler::GetSingleton()
     341             : {
     342           0 :   if (!sSingleton) {
     343           0 :     sSingleton = new ExtensionProtocolHandler();
     344           0 :     ClearOnShutdown(&sSingleton);
     345             :   }
     346           0 :   return do_AddRef(sSingleton.get());
     347             : }
     348             : 
     349           0 : ExtensionProtocolHandler::ExtensionProtocolHandler()
     350           0 :   : SubstitutingProtocolHandler(EXTENSION_SCHEME)
     351             : #if !defined(XP_WIN) && defined(MOZ_CONTENT_SANDBOX)
     352             :   , mAlreadyCheckedDevRepo(false)
     353             : #endif
     354             : {
     355           0 :   mUseRemoteFileChannels = IsNeckoChild() &&
     356           0 :     Preferences::GetBool("extensions.webextensions.protocol.remote");
     357           0 : }
     358             : 
     359             : static inline ExtensionPolicyService&
     360           0 : EPS()
     361             : {
     362           0 :   return ExtensionPolicyService::GetSingleton();
     363             : }
     364             : 
     365             : nsresult
     366           0 : ExtensionProtocolHandler::GetFlagsForURI(nsIURI* aURI, uint32_t* aFlags)
     367             : {
     368             :   // In general a moz-extension URI is only loadable by chrome, but a whitelisted
     369             :   // subset are web-accessible (and cross-origin fetchable). Check that whitelist.
     370           0 :   bool loadableByAnyone = false;
     371             : 
     372           0 :   URLInfo url(aURI);
     373           0 :   if (auto* policy = EPS().GetByURL(url)) {
     374           0 :     loadableByAnyone = policy->IsPathWebAccessible(url.FilePath());
     375             :   }
     376             : 
     377           0 :   *aFlags = URI_STD | URI_IS_LOCAL_RESOURCE | (loadableByAnyone ? (URI_LOADABLE_BY_ANYONE | URI_FETCHABLE_BY_ANYONE) : URI_DANGEROUS_TO_LOAD);
     378           0 :   return NS_OK;
     379             : }
     380             : 
     381             : bool
     382           0 : ExtensionProtocolHandler::ResolveSpecialCases(const nsACString& aHost,
     383             :                                               const nsACString& aPath,
     384             :                                               const nsACString& aPathname,
     385             :                                               nsACString& aResult)
     386             : {
     387             :   // Create special moz-extension:-pages such as moz-extension://foo/_blank.html
     388             :   // for all registered extensions. We can't just do this as a substitution
     389             :   // because substitutions can only match on host.
     390           0 :   if (!SubstitutingProtocolHandler::HasSubstitution(aHost)) {
     391           0 :     return false;
     392             :   }
     393             : 
     394           0 :   if (aPathname.EqualsLiteral("/_blank.html")) {
     395           0 :     aResult.AssignLiteral("about:blank");
     396           0 :     return true;
     397             :   }
     398             : 
     399           0 :   if (aPathname.EqualsLiteral("/_generated_background_page.html")) {
     400           0 :     Unused << EPS().GetGeneratedBackgroundPageUrl(aHost, aResult);
     401           0 :     return !aResult.IsEmpty();
     402             :   }
     403             : 
     404           0 :   return false;
     405             : }
     406             : 
     407             : // For file or JAR URI's, substitute in a remote channel.
     408             : Result<Ok, nsresult>
     409           0 : ExtensionProtocolHandler::SubstituteRemoteChannel(nsIURI* aURI,
     410             :                                                   nsILoadInfo* aLoadInfo,
     411             :                                                   nsIChannel** aRetVal)
     412             : {
     413           0 :   MOZ_ASSERT(IsNeckoChild());
     414           0 :   NS_TRY(aURI ? NS_OK : NS_ERROR_INVALID_ARG);
     415           0 :   NS_TRY(aLoadInfo ? NS_OK : NS_ERROR_INVALID_ARG);
     416             : 
     417           0 :   nsAutoCString unResolvedSpec;
     418           0 :   NS_TRY(aURI->GetSpec(unResolvedSpec));
     419             : 
     420           0 :   nsAutoCString resolvedSpec;
     421           0 :   NS_TRY(ResolveURI(aURI, resolvedSpec));
     422             : 
     423             :   // Use the target URI scheme to determine if this is a packed or unpacked
     424             :   // extension URI. For unpacked extensions, we'll request an input stream
     425             :   // from the parent. For a packed extension, we'll request a file descriptor
     426             :   // for the JAR file.
     427           0 :   nsAutoCString scheme;
     428           0 :   NS_TRY(net_ExtractURLScheme(resolvedSpec, scheme));
     429             : 
     430           0 :   if (scheme.EqualsLiteral("file")) {
     431             :     // Unpacked extension
     432           0 :     SubstituteRemoteFileChannel(aURI, aLoadInfo, resolvedSpec, aRetVal);
     433           0 :     return Ok();
     434             :   }
     435             : 
     436           0 :   if (scheme.EqualsLiteral("jar")) {
     437             :     // Packed extension
     438           0 :     return SubstituteRemoteJarChannel(aURI, aLoadInfo, resolvedSpec, aRetVal);
     439             :   }
     440             : 
     441             :   // Only unpacked resource files and JAR files are remoted.
     442             :   // No other moz-extension loads should be reading from the filesystem.
     443           0 :   return Ok();
     444             : }
     445             : 
     446             : nsresult
     447           0 : ExtensionProtocolHandler::SubstituteChannel(nsIURI* aURI,
     448             :                                             nsILoadInfo* aLoadInfo,
     449             :                                             nsIChannel** result)
     450             : {
     451             :   nsresult rv;
     452           0 :   nsCOMPtr<nsIURL> url = do_QueryInterface(aURI, &rv);
     453           0 :   NS_ENSURE_SUCCESS(rv, rv);
     454             : 
     455           0 :   if (mUseRemoteFileChannels) {
     456           0 :     MOZ_TRY(SubstituteRemoteChannel(aURI, aLoadInfo, result));
     457             :   }
     458             : 
     459           0 :   nsAutoCString ext;
     460           0 :   rv = url->GetFileExtension(ext);
     461           0 :   NS_ENSURE_SUCCESS(rv, rv);
     462             : 
     463           0 :   if (!ext.LowerCaseEqualsLiteral("css")) {
     464           0 :     return NS_OK;
     465             :   }
     466             : 
     467             :   // Filter CSS files to replace locale message tokens with localized strings.
     468             : 
     469           0 :   bool haveLoadInfo = aLoadInfo;
     470           0 :   nsCOMPtr<nsIChannel> channel = NS_NewSimpleChannel(
     471             :     aURI, aLoadInfo, *result,
     472           0 :     [haveLoadInfo] (nsIStreamListener* listener, nsIChannel* channel, nsIChannel* origChannel) -> RequestOrReason {
     473             :       nsresult rv;
     474             :       nsCOMPtr<nsIStreamConverterService> convService =
     475           0 :         do_GetService(NS_STREAMCONVERTERSERVICE_CONTRACTID, &rv);
     476           0 :       NS_TRY(rv);
     477             : 
     478           0 :       nsCOMPtr<nsIURI> uri;
     479           0 :       NS_TRY(channel->GetURI(getter_AddRefs(uri)));
     480             : 
     481           0 :       const char* kFromType = "application/vnd.mozilla.webext.unlocalized";
     482           0 :       const char* kToType = "text/css";
     483             : 
     484           0 :       nsCOMPtr<nsIStreamListener> converter;
     485           0 :       NS_TRY(convService->AsyncConvertData(kFromType, kToType, listener,
     486             :                                         uri, getter_AddRefs(converter)));
     487           0 :       if (haveLoadInfo) {
     488           0 :         NS_TRY(origChannel->AsyncOpen2(converter));
     489             :       } else {
     490           0 :         NS_TRY(origChannel->AsyncOpen(converter, nullptr));
     491             :       }
     492             : 
     493           0 :       return RequestOrReason(origChannel);
     494           0 :     });
     495           0 :   NS_ENSURE_TRUE(channel, NS_ERROR_OUT_OF_MEMORY);
     496             : 
     497           0 :   if (aLoadInfo) {
     498             :     nsCOMPtr<nsILoadInfo> loadInfo =
     499           0 :         static_cast<LoadInfo*>(aLoadInfo)->CloneForNewRequest();
     500           0 :     (*result)->SetLoadInfo(loadInfo);
     501             :   }
     502             : 
     503           0 :   channel.swap(*result);
     504             : 
     505           0 :   return NS_OK;
     506             : }
     507             : 
     508             : #if !defined(XP_WIN) && defined(MOZ_CONTENT_SANDBOX)
     509             : // The |aRequestedFile| argument must already be Normalize()'d
     510             : Result<Ok, nsresult>
     511             : ExtensionProtocolHandler::DevRepoContains(nsIFile* aRequestedFile,
     512             :                                           bool *aResult)
     513             : {
     514             :   MOZ_ASSERT(!IsNeckoChild());
     515             :   MOZ_ASSERT(aResult);
     516             :   *aResult = false;
     517             : 
     518             :   // On the first invocation, set mDevRepo if this is a
     519             :   // development build with MOZ_DEVELOPER_REPO_DIR set.
     520             :   if (!mAlreadyCheckedDevRepo) {
     521             :     mAlreadyCheckedDevRepo = true;
     522             :     if (mozilla::IsDevelopmentBuild()) {
     523             :       char *developer_repo_dir = PR_GetEnv("MOZ_DEVELOPER_REPO_DIR");
     524             :       if (developer_repo_dir) {
     525             :         NS_TRY(NS_NewLocalFile(NS_ConvertUTF8toUTF16(developer_repo_dir),
     526             :                                false, getter_AddRefs(mDevRepo)));
     527             :         NS_TRY(mDevRepo->Normalize());
     528             :       }
     529             :     }
     530             :   }
     531             : 
     532             :   if (mDevRepo) {
     533             :     // This is a development build
     534             :     NS_TRY(mDevRepo->Contains(aRequestedFile, aResult));
     535             :   }
     536             : 
     537             :   return Ok();
     538             : }
     539             : #endif /* !defined(XP_WIN) && defined(MOZ_CONTENT_SANDBOX) */
     540             : 
     541             : Result<nsCOMPtr<nsIInputStream>, nsresult>
     542           0 : ExtensionProtocolHandler::NewStream(nsIURI* aChildURI,
     543             :                                     nsILoadInfo* aChildLoadInfo,
     544             :                                     bool* aTerminateSender)
     545             : {
     546           0 :   MOZ_ASSERT(!IsNeckoChild());
     547           0 :   NS_TRY(aChildURI ? NS_OK : NS_ERROR_INVALID_ARG);
     548           0 :   NS_TRY(aChildLoadInfo ? NS_OK : NS_ERROR_INVALID_ARG);
     549           0 :   NS_TRY(aTerminateSender ? NS_OK : NS_ERROR_INVALID_ARG);
     550             : 
     551           0 :   *aTerminateSender = true;
     552             :   nsresult rv;
     553             : 
     554             :   // We should never receive a URI that isn't for a moz-extension because
     555             :   // these requests ordinarily come from the child's ExtensionProtocolHandler.
     556             :   // Ensure this request is for a moz-extension URI. A rogue child process
     557             :   // could send us any URI.
     558           0 :   bool isExtScheme = false;
     559           0 :   if (NS_FAILED(aChildURI->SchemeIs(EXTENSION_SCHEME, &isExtScheme)) ||
     560           0 :       !isExtScheme) {
     561           0 :     return Err(NS_ERROR_UNKNOWN_PROTOCOL);
     562             :   }
     563             : 
     564             :   // For errors after this point, we want to propagate the error to
     565             :   // the child, but we don't force the child to be terminated because
     566             :   // the error is likely to be due to a bug in the extension.
     567           0 :   *aTerminateSender = false;
     568             : 
     569             :   /*
     570             :    * Make sure there is a substitution installed for the host found
     571             :    * in the child's request URI and make sure the host resolves to
     572             :    * a directory.
     573             :    */
     574             : 
     575           0 :   nsAutoCString host;
     576           0 :   NS_TRY(aChildURI->GetAsciiHost(host));
     577             : 
     578             :   // Lookup the directory this host string resolves to
     579           0 :   nsCOMPtr<nsIURI> baseURI;
     580           0 :   NS_TRY(GetSubstitution(host, getter_AddRefs(baseURI)));
     581             : 
     582             :   // The result should be a file URL for the extension base dir
     583           0 :   nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(baseURI, &rv);
     584           0 :   NS_TRY(rv);
     585             : 
     586           0 :   nsCOMPtr<nsIFile> extensionDir;
     587           0 :   NS_TRY(fileURL->GetFile(getter_AddRefs(extensionDir)));
     588             : 
     589           0 :   bool isDirectory = false;
     590           0 :   NS_TRY(extensionDir->IsDirectory(&isDirectory));
     591           0 :   if (!isDirectory) {
     592             :     // The host should map to a directory for unpacked extensions
     593           0 :     return Err(NS_ERROR_FILE_NOT_DIRECTORY);
     594             :   }
     595             : 
     596             :   /*
     597             :    * Now get a channel for the resolved child URI and make sure the
     598             :    * channel is a file channel.
     599             :    */
     600             : 
     601           0 :   nsCOMPtr<nsIChannel> channel;
     602           0 :   NS_TRY(NS_NewChannelInternal(getter_AddRefs(channel),
     603             :                                aChildURI,
     604             :                                aChildLoadInfo));
     605             : 
     606             :   // Channel should be a file channel. It should never be a JAR
     607             :   // channel because we only request remote streams for unpacked
     608             :   // extension resource loads where the URI resolves to a file.
     609           0 :   nsCOMPtr<nsIFileChannel> fileChannel = do_QueryInterface(channel, &rv);
     610           0 :   NS_TRY(rv);
     611             : 
     612           0 :   nsCOMPtr<nsIFile> requestedFile;
     613           0 :   NS_TRY(fileChannel->GetFile(getter_AddRefs(requestedFile)));
     614             : 
     615             :   /*
     616             :    * Make sure the file we resolved to is within the extension directory.
     617             :    */
     618             : 
     619             :   // Normalize paths for sane comparisons. nsIFile::Contains depends on
     620             :   // it for reliable subpath checks.
     621           0 :   NS_TRY(extensionDir->Normalize());
     622           0 :   NS_TRY(requestedFile->Normalize());
     623             : 
     624           0 :   bool isResourceFromExtensionDir = false;
     625           0 :   NS_TRY(extensionDir->Contains(requestedFile, &isResourceFromExtensionDir));
     626           0 :   if (!isResourceFromExtensionDir) {
     627             : #if defined(XP_WIN)
     628             :     return Err(NS_ERROR_FILE_ACCESS_DENIED);
     629             : #elif defined(MOZ_CONTENT_SANDBOX)
     630             :     // On a dev build, we allow an unpacked resource that isn't
     631             :     // from the extension directory as long as it is from the repo.
     632             :     bool isResourceFromDevRepo = false;
     633             :     MOZ_TRY(DevRepoContains(requestedFile, &isResourceFromDevRepo));
     634             :     if (!isResourceFromDevRepo) {
     635             :       return Err(NS_ERROR_FILE_ACCESS_DENIED);
     636             :     }
     637             : #endif /* defined(XP_WIN) */
     638             :   }
     639             : 
     640           0 :   nsCOMPtr<nsIInputStream> inputStream;
     641           0 :   NS_TRY(NS_NewLocalFileInputStream(getter_AddRefs(inputStream),
     642             :                                     requestedFile,
     643             :                                     PR_RDONLY,
     644             :                                     -1,
     645             :                                     nsIFileInputStream::DEFER_OPEN));
     646             : 
     647           0 :   return inputStream;
     648             : }
     649             : 
     650             : Result<Ok, nsresult>
     651           0 : ExtensionProtocolHandler::NewFD(nsIURI* aChildURI,
     652             :                                 nsILoadInfo* aChildLoadInfo,
     653             :                                 bool* aTerminateSender,
     654             :                                 NeckoParent::GetExtensionFDResolver& aResolve)
     655             : {
     656           0 :   MOZ_ASSERT(!IsNeckoChild());
     657           0 :   NS_TRY(aChildURI ? NS_OK : NS_ERROR_INVALID_ARG);
     658           0 :   NS_TRY(aChildLoadInfo ? NS_OK : NS_ERROR_INVALID_ARG);
     659           0 :   NS_TRY(aTerminateSender ? NS_OK : NS_ERROR_INVALID_ARG);
     660             : 
     661           0 :   *aTerminateSender = true;
     662             :   nsresult rv;
     663             : 
     664             :   // Ensure this is a moz-extension URI
     665           0 :   bool isExtScheme = false;
     666           0 :   if (NS_FAILED(aChildURI->SchemeIs(EXTENSION_SCHEME, &isExtScheme)) ||
     667           0 :       !isExtScheme) {
     668           0 :     return Err(NS_ERROR_UNKNOWN_PROTOCOL);
     669             :   }
     670             : 
     671             :   // For errors after this point, we want to propagate the error to
     672             :   // the child, but we don't force the child to be terminated.
     673           0 :   *aTerminateSender = false;
     674             : 
     675           0 :   nsAutoCString host;
     676           0 :   NS_TRY(aChildURI->GetAsciiHost(host));
     677             : 
     678             :   // We expect the host string to map to a JAR file because the URI
     679             :   // should refer to a web accessible resource for an enabled extension.
     680           0 :   nsCOMPtr<nsIURI> subURI;
     681           0 :   NS_TRY(GetSubstitution(host, getter_AddRefs(subURI)));
     682             : 
     683           0 :   nsCOMPtr<nsIJARURI> jarURI = do_QueryInterface(subURI, &rv);
     684           0 :   NS_TRY(rv);
     685             : 
     686           0 :   nsCOMPtr<nsIURI> innerFileURI;
     687           0 :   NS_TRY(jarURI->GetJARFile(getter_AddRefs(innerFileURI)));
     688             : 
     689           0 :   nsCOMPtr<nsIFileURL> innerFileURL = do_QueryInterface(innerFileURI, &rv);
     690           0 :   NS_TRY(rv);
     691             : 
     692           0 :   nsCOMPtr<nsIFile> jarFile;
     693           0 :   NS_TRY(innerFileURL->GetFile(getter_AddRefs(jarFile)));
     694             : 
     695           0 :   if (!mFileOpenerThread) {
     696             :     mFileOpenerThread =
     697             :       new LazyIdleThread(DEFAULT_THREAD_TIMEOUT_MS,
     698           0 :                          NS_LITERAL_CSTRING("ExtensionProtocolHandler"));
     699             :   }
     700             : 
     701             :   RefPtr<ExtensionJARFileOpener> fileOpener =
     702           0 :     new ExtensionJARFileOpener(jarFile, aResolve);
     703             : 
     704             :   nsCOMPtr<nsIRunnable> event =
     705           0 :     mozilla::NewRunnableMethod("ExtensionJarFileOpener",
     706           0 :         fileOpener, &ExtensionJARFileOpener::OpenFile);
     707             : 
     708           0 :   NS_TRY(mFileOpenerThread->Dispatch(event, nsIEventTarget::DISPATCH_NORMAL));
     709             : 
     710           0 :   return Ok();
     711             : }
     712             : 
     713             : static void
     714           0 : NewSimpleChannel(nsIURI* aURI,
     715             :                  nsILoadInfo* aLoadinfo,
     716             :                  ExtensionStreamGetter* aStreamGetter,
     717             :                  nsIChannel** aRetVal)
     718             : {
     719           0 :   nsCOMPtr<nsIChannel> channel = NS_NewSimpleChannel(
     720             :     aURI, aLoadinfo, aStreamGetter,
     721             :     [] (nsIStreamListener* listener, nsIChannel* channel,
     722           0 :         ExtensionStreamGetter* getter) -> RequestOrReason {
     723           0 :       MOZ_TRY(getter->GetAsync(listener, channel));
     724           0 :       return RequestOrReason(nullptr);
     725           0 :     });
     726             : 
     727             :   nsresult rv;
     728           0 :   nsCOMPtr<nsIMIMEService> mime = do_GetService("@mozilla.org/mime;1", &rv);
     729           0 :   if (NS_SUCCEEDED(rv)) {
     730           0 :     nsAutoCString contentType;
     731           0 :     rv = mime->GetTypeFromURI(aURI, contentType);
     732           0 :     if (NS_SUCCEEDED(rv)) {
     733           0 :       Unused << channel->SetContentType(contentType);
     734             :     }
     735             :   }
     736             : 
     737           0 :   channel.swap(*aRetVal);
     738           0 : }
     739             : 
     740             : void
     741           0 : ExtensionProtocolHandler::SubstituteRemoteFileChannel(nsIURI* aURI,
     742             :                                                       nsILoadInfo* aLoadinfo,
     743             :                                                       nsACString& aResolvedFileSpec,
     744             :                                                       nsIChannel** aRetVal)
     745             : {
     746           0 :   MOZ_ASSERT(IsNeckoChild());
     747             : 
     748             :   RefPtr<ExtensionStreamGetter> streamGetter =
     749           0 :     new ExtensionStreamGetter(aURI, aLoadinfo);
     750             : 
     751           0 :   NewSimpleChannel(aURI, aLoadinfo, streamGetter, aRetVal);
     752           0 : }
     753             : 
     754             : Result<Ok, nsresult>
     755           0 : ExtensionProtocolHandler::SubstituteRemoteJarChannel(nsIURI* aURI,
     756             :                                                      nsILoadInfo* aLoadinfo,
     757             :                                                      nsACString& aResolvedSpec,
     758             :                                                      nsIChannel** aRetVal)
     759             : {
     760           0 :   MOZ_ASSERT(IsNeckoChild());
     761             :   nsresult rv;
     762             : 
     763             :   // Build a JAR URI for this jar:file:// URI and use it to extract the
     764             :   // inner file URI.
     765           0 :   nsCOMPtr<nsIURI> uri;
     766           0 :   NS_TRY(NS_NewURI(getter_AddRefs(uri), aResolvedSpec));
     767             : 
     768           0 :   nsCOMPtr<nsIJARURI> jarURI = do_QueryInterface(uri, &rv);
     769           0 :   NS_TRY(rv);
     770             : 
     771           0 :   nsCOMPtr<nsIURI> innerFileURI;
     772           0 :   NS_TRY(jarURI->GetJARFile(getter_AddRefs(innerFileURI)));
     773             : 
     774           0 :   nsCOMPtr<nsIFileURL> innerFileURL = do_QueryInterface(innerFileURI, &rv);
     775           0 :   NS_TRY(rv);
     776             : 
     777           0 :   nsCOMPtr<nsIFile> jarFile;
     778           0 :   NS_TRY(innerFileURL->GetFile(getter_AddRefs(jarFile)));
     779             : 
     780           0 :   nsCOMPtr<nsIJARChannel> jarChannel = do_QueryInterface(*aRetVal, &rv);
     781           0 :   NS_TRY(rv);
     782             : 
     783             :   RefPtr<ExtensionStreamGetter> streamGetter =
     784           0 :     new ExtensionStreamGetter(aURI, aLoadinfo, jarChannel.forget(), jarFile);
     785             : 
     786           0 :   NewSimpleChannel(aURI, aLoadinfo, streamGetter, aRetVal);
     787           0 :   return Ok();
     788             : }
     789             : 
     790             : #undef NS_TRY
     791             : 
     792             : } // namespace net
     793             : } // namespace mozilla

Generated by: LCOV version 1.13