tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

RemoteImageProtocolHandler.cpp (7613B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* This Source Code Form is subject to the terms of the Mozilla Public
      3 * License, v. 2.0. If a copy of the MPL was not distributed with this
      4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      5 
      6 #include "RemoteImageProtocolHandler.h"
      7 
      8 #include "imgITools.h"
      9 #include "nsContentUtils.h"
     10 #include "nsIPipe.h"
     11 #include "nsIURI.h"
     12 #include "nsMimeTypes.h"
     13 #include "nsNetUtil.h"
     14 #include "nsStreamUtils.h"
     15 #include "nsURLHelper.h"
     16 #include "mozilla/dom/ipc/IdType.h"
     17 #include "mozilla/dom/ContentParent.h"
     18 #include "mozilla/dom/ContentProcessManager.h"
     19 #include "mozilla/gfx/2D.h"
     20 
     21 namespace mozilla::image {
     22 
     23 using mozilla::dom::ContentParent;
     24 using mozilla::dom::ContentParentId;
     25 using mozilla::dom::ContentProcessManager;
     26 using mozilla::dom::UniqueContentParentKeepAlive;
     27 
     28 StaticRefPtr<RemoteImageProtocolHandler> RemoteImageProtocolHandler::sSingleton;
     29 
     30 NS_IMPL_ISUPPORTS(RemoteImageProtocolHandler, nsIProtocolHandler,
     31                  nsISupportsWeakReference);
     32 
     33 NS_IMETHODIMP RemoteImageProtocolHandler::GetScheme(nsACString& aScheme) {
     34  aScheme.AssignLiteral("moz-remote-image");
     35  return NS_OK;
     36 }
     37 
     38 NS_IMETHODIMP RemoteImageProtocolHandler::AllowPort(int32_t, const char*,
     39                                                    bool* aAllow) {
     40  *aAllow = false;
     41  return NS_OK;
     42 }
     43 
     44 static UniqueContentParentKeepAlive GetLaunchingContentParentForDecode(
     45    const Maybe<ContentParentId>& aContentParentId) {
     46  if (aContentParentId.isSome()) {
     47    if (ContentProcessManager* cpm = ContentProcessManager::GetSingleton()) {
     48      if (ContentParent* cp = cpm->GetContentProcessById(*aContentParentId)) {
     49        return cp->TryAddKeepAlive(/* aBrowserId */ 0);
     50      }
     51    }
     52  }
     53 
     54  // We use the extension process as a fallback, because
     55  // it is usually running, and should be OK to parse images.
     56  return ContentParent::GetNewOrUsedLaunchingBrowserProcess(
     57      EXTENSION_REMOTE_TYPE,
     58      /* aGroup */ nullptr,
     59      /* aPriority */ hal::PROCESS_PRIORITY_FOREGROUND,
     60      /* aPreferUsed */ true);
     61 }
     62 
     63 static nsresult EncodeImage(const dom::IPCImage& aImage,
     64                            nsIAsyncOutputStream* aOutputStream) {
     65  // TODO(Bug 1997538): Use the internal image/icon format for the
     66  // moz-remote-image: protocol
     67  nsresult rv;
     68  nsCOMPtr<imgITools> imgTools =
     69      do_GetService("@mozilla.org/image/tools;1", &rv);
     70  MOZ_TRY(rv);
     71 
     72  nsCOMPtr<imgIContainer> imgContainer =
     73      nsContentUtils::IPCImageToImage(aImage);
     74  if (!imgContainer) {
     75    return NS_ERROR_FAILURE;
     76  }
     77 
     78  nsCOMPtr<nsIInputStream> stream;
     79  MOZ_TRY(imgTools->EncodeImage(imgContainer, nsLiteralCString(IMAGE_PNG),
     80                                u"png-zlib-level=0"_ns,
     81                                getter_AddRefs(stream)));
     82 
     83  nsCOMPtr<nsIEventTarget> target =
     84      do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv);
     85  MOZ_TRY(rv);
     86 
     87  return NS_AsyncCopy(stream, aOutputStream, target);
     88 }
     89 
     90 static void AsyncReEncodeImage(nsIURI* aRemoteURI, ImageIntSize aSize,
     91                               const Maybe<ContentParentId> aContentParentId,
     92                               nsIAsyncOutputStream* aOutputStream) {
     93  UniqueContentParentKeepAlive cp =
     94      GetLaunchingContentParentForDecode(aContentParentId);
     95  if (NS_WARN_IF(!cp)) {
     96    aOutputStream->CloseWithStatus(NS_ERROR_FAILURE);
     97    return;
     98  }
     99 
    100  cp->WaitForLaunchAsync()
    101      ->Then(
    102          GetCurrentSerialEventTarget(), __func__,
    103          [remoteURI = nsCOMPtr{aRemoteURI},
    104           aSize](UniqueContentParentKeepAlive&& aCp) {
    105            return aCp->SendDecodeImage(WrapNotNull(remoteURI), aSize);
    106          },
    107          [](nsresult aError) {
    108            return ContentParent::DecodeImagePromise::CreateAndReject(
    109                ipc::ResponseRejectReason::SendError, __func__);
    110          })
    111      ->Then(
    112          GetCurrentSerialEventTarget(), __func__,
    113          [cp = std::move(cp), outputStream = nsCOMPtr{aOutputStream},
    114           aSize](const std::tuple<nsresult, mozilla::Maybe<dom::IPCImage>>&
    115                      aResult) {
    116            nsresult rv = std::get<0>(aResult);
    117            const mozilla::Maybe<dom::IPCImage>& image = std::get<1>(aResult);
    118 
    119            if (NS_FAILED(rv)) {
    120              outputStream->CloseWithStatus(rv);
    121              return;
    122            }
    123 
    124            if (image.isNothing()) {
    125              outputStream->CloseWithStatus(NS_ERROR_UNEXPECTED);
    126              return;
    127            }
    128 
    129            // Make sure the image size matches if a specific size was
    130            // requested.
    131            if (aSize.Width() && aSize.Height() && image->size() != aSize) {
    132              outputStream->CloseWithStatus(NS_ERROR_UNEXPECTED);
    133              return;
    134            }
    135 
    136            rv = EncodeImage(*image, outputStream);
    137            if (NS_FAILED(rv)) {
    138              outputStream->CloseWithStatus(rv);
    139            }
    140          },
    141          [outputStream =
    142               nsCOMPtr{aOutputStream}](mozilla::ipc::ResponseRejectReason) {
    143            outputStream->CloseWithStatus(NS_ERROR_FAILURE);
    144          });
    145 }
    146 
    147 // Parse out the relevant parts of the moz-remote-image URL
    148 static nsresult ParseURI(nsIURI* aURI, nsIURI** aRemoteURI, ImageIntSize* aSize,
    149                         Maybe<ContentParentId>& aContentParentId) {
    150  MOZ_ASSERT(aURI->SchemeIs("moz-remote-image"));
    151 
    152  nsAutoCString query;
    153  MOZ_TRY(aURI->GetQuery(query));
    154 
    155  bool hasURL;
    156  int32_t width = 0;
    157  int32_t height = 0;
    158 
    159  bool ok = URLParams::Parse(
    160      query, true, [&](const nsACString& aName, const nsACString& aValue) {
    161        nsresult rv;
    162        if (aName.EqualsLiteral("url")) {
    163          hasURL = true;
    164          rv = NS_NewURI(aRemoteURI, aValue);
    165          if (NS_FAILED(rv)) {
    166            return false;
    167          }
    168        } else if (aName.EqualsLiteral("width")) {
    169          width = aValue.ToInteger(&rv);
    170          if (NS_FAILED(rv) || width < 0) {
    171            return false;
    172          }
    173        } else if (aName.EqualsLiteral("height")) {
    174          height = aValue.ToInteger(&rv);
    175          if (NS_FAILED(rv) || height < 0) {
    176            return false;
    177          }
    178        } else if (aName.EqualsLiteral("contentParentId")) {
    179          int64_t id = aValue.ToInteger(&rv);
    180          if (NS_FAILED(rv) || id < 0) {
    181            return false;
    182          }
    183          aContentParentId = Some(ContentParentId(uint64_t(id)));
    184        }
    185        return true;
    186      });
    187  if (NS_WARN_IF(!ok || !hasURL)) {
    188    return NS_ERROR_DOM_MALFORMED_URI;
    189  }
    190 
    191  *aSize = ImageIntSize(width, height);
    192  return NS_OK;
    193 }
    194 
    195 NS_IMETHODIMP RemoteImageProtocolHandler::NewChannel(nsIURI* aURI,
    196                                                     nsILoadInfo* aLoadInfo,
    197                                                     nsIChannel** aOutChannel) {
    198  if (!aLoadInfo->TriggeringPrincipal()->IsSystemPrincipal()) {
    199    return NS_ERROR_UNEXPECTED;
    200  }
    201 
    202  nsCOMPtr<nsIURI> remoteURI;
    203  ImageIntSize size;
    204  Maybe<ContentParentId> contentParentId;
    205  MOZ_TRY(ParseURI(aURI, getter_AddRefs(remoteURI), &size, contentParentId));
    206 
    207  nsCOMPtr<nsIAsyncInputStream> pipeIn;
    208  nsCOMPtr<nsIAsyncOutputStream> pipeOut;
    209  NS_NewPipe2(getter_AddRefs(pipeIn), getter_AddRefs(pipeOut), true, true);
    210 
    211  nsCOMPtr<nsIChannel> channel;
    212  MOZ_TRY(NS_NewInputStreamChannelInternal(
    213      getter_AddRefs(channel), aURI, pipeIn.forget(),
    214      /* aContentType */ nsLiteralCString(IMAGE_PNG),
    215      /* aContentCharset */ ""_ns, aLoadInfo));
    216 
    217  AsyncReEncodeImage(remoteURI, size, contentParentId, pipeOut);
    218 
    219  channel.forget(aOutChannel);
    220  return NS_OK;
    221 }
    222 
    223 }  // namespace mozilla::image