tor-browser

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

ImageFactory.cpp (8006B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
      2 *
      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
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "ImageFactory.h"
      8 
      9 #include <algorithm>
     10 
     11 #include "nsIChannel.h"
     12 #include "nsIFileChannel.h"
     13 #include "nsIObserverService.h"
     14 #include "nsIFile.h"
     15 #include "nsMimeTypes.h"
     16 #include "nsIRequest.h"
     17 
     18 #include "MultipartImage.h"
     19 #include "RasterImage.h"
     20 #include "VectorImage.h"
     21 #include "Image.h"
     22 #include "mozilla/MediaFragmentURIParser.h"
     23 #include "nsContentUtils.h"
     24 
     25 #include "mozilla/SchedulerGroup.h"
     26 #include "mozilla/StaticPrefs_image.h"
     27 #include "mozilla/ProfilerMarkers.h"
     28 
     29 namespace mozilla {
     30 namespace image {
     31 
     32 /*static*/
     33 void ImageFactory::Initialize() {}
     34 
     35 static uint32_t ComputeImageFlags(nsIURI* uri, const nsCString& aMimeType,
     36                                  bool isMultiPart) {
     37  // We default to the static globals.
     38  bool isDiscardable = StaticPrefs::image_mem_discardable();
     39  bool doDecodeImmediately = StaticPrefs::image_decode_immediately_enabled();
     40 
     41  // We want UI to be as snappy as possible and not to flicker. Disable
     42  // discarding for chrome URLS.
     43  if (uri->SchemeIs("chrome")) {
     44    isDiscardable = false;
     45  }
     46 
     47  // We don't want resources like the "loading" icon to be discardable either.
     48  if (uri->SchemeIs("resource")) {
     49    isDiscardable = false;
     50  }
     51 
     52  // For multipart/x-mixed-replace, we basically want a direct channel to the
     53  // decoder. Disable everything for this case.
     54  if (isMultiPart) {
     55    isDiscardable = false;
     56  }
     57 
     58  // We have all the information we need.
     59  uint32_t imageFlags = Image::INIT_FLAG_NONE;
     60  if (isDiscardable) {
     61    imageFlags |= Image::INIT_FLAG_DISCARDABLE;
     62  }
     63  if (doDecodeImmediately) {
     64    imageFlags |= Image::INIT_FLAG_DECODE_IMMEDIATELY;
     65  }
     66  if (isMultiPart) {
     67    imageFlags |= Image::INIT_FLAG_TRANSIENT;
     68  }
     69 
     70  // Synchronously decode metadata (including size) if we have a data URI since
     71  // the data is immediately available.
     72  if (uri->SchemeIs("data")) {
     73    imageFlags |= Image::INIT_FLAG_SYNC_LOAD;
     74  }
     75 
     76  return imageFlags;
     77 }
     78 
     79 #ifdef DEBUG
     80 static void NotifyImageLoading(nsIURI* aURI) {
     81  if (!NS_IsMainThread()) {
     82    nsCOMPtr<nsIURI> uri(aURI);
     83    nsCOMPtr<nsIRunnable> ev = NS_NewRunnableFunction(
     84        "NotifyImageLoading", [uri]() -> void { NotifyImageLoading(uri); });
     85    NS_DispatchToMainThread(ev.forget());
     86    return;
     87  }
     88 
     89  nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
     90  NS_WARNING_ASSERTION(obs, "Can't get an observer service handle");
     91  if (obs) {
     92    nsAutoCString spec;
     93    aURI->GetSpec(spec);
     94    obs->NotifyObservers(nullptr, "image-loading",
     95                         NS_ConvertUTF8toUTF16(spec).get());
     96  }
     97 }
     98 #endif
     99 
    100 /* static */
    101 already_AddRefed<Image> ImageFactory::CreateImage(
    102    nsIRequest* aRequest, ProgressTracker* aProgressTracker,
    103    const nsCString& aMimeType, nsIURI* aURI, bool aIsMultiPart,
    104    uint64_t aInnerWindowId) {
    105  // Compute the image's initialization flags.
    106  uint32_t imageFlags = ComputeImageFlags(aURI, aMimeType, aIsMultiPart);
    107 
    108 #ifdef DEBUG
    109  // Record the image load for startup performance testing.
    110  if (aURI->SchemeIs("resource") || aURI->SchemeIs("chrome")) {
    111    NotifyImageLoading(aURI);
    112  }
    113 #endif
    114 
    115  if (profiler_thread_is_being_profiled_for_markers()) {
    116    static const size_t sMaxTruncatedLength = 1024;
    117    PROFILER_MARKER_TEXT(
    118        "Image Load", GRAPHICS, MarkerInnerWindowId(aInnerWindowId),
    119        nsContentUtils::TruncatedURLForDisplay(aURI, sMaxTruncatedLength));
    120  }
    121 
    122  // Select the type of image to create based on MIME type.
    123  if (aMimeType.EqualsLiteral(IMAGE_SVG_XML)) {
    124    return CreateVectorImage(aRequest, aProgressTracker, aMimeType, aURI,
    125                             imageFlags, aInnerWindowId);
    126  } else {
    127    return CreateRasterImage(aRequest, aProgressTracker, aMimeType, aURI,
    128                             imageFlags, aInnerWindowId);
    129  }
    130 }
    131 
    132 // Marks an image as having an error before returning it.
    133 template <typename T>
    134 static already_AddRefed<Image> BadImage(const char* aMessage,
    135                                        RefPtr<T>& aImage) {
    136  aImage->SetHasError();
    137  return aImage.forget();
    138 }
    139 
    140 /* static */
    141 already_AddRefed<Image> ImageFactory::CreateAnonymousImage(
    142    const nsCString& aMimeType, uint32_t aSizeHint /* = 0 */) {
    143  nsresult rv;
    144 
    145  RefPtr<RasterImage> newImage = new RasterImage();
    146 
    147  RefPtr<ProgressTracker> newTracker = new ProgressTracker();
    148  newTracker->SetImage(newImage);
    149  newImage->SetProgressTracker(newTracker);
    150 
    151  rv = newImage->Init(aMimeType.get(), Image::INIT_FLAG_SYNC_LOAD);
    152  if (NS_FAILED(rv)) {
    153    return BadImage("RasterImage::Init failed", newImage);
    154  }
    155 
    156  rv = newImage->SetSourceSizeHint(aSizeHint);
    157  if (NS_FAILED(rv)) {
    158    return BadImage("RasterImage::SetSourceSizeHint failed", newImage);
    159  }
    160 
    161  return newImage.forget();
    162 }
    163 
    164 /* static */
    165 already_AddRefed<MultipartImage> ImageFactory::CreateMultipartImage(
    166    Image* aFirstPart, ProgressTracker* aProgressTracker) {
    167  MOZ_ASSERT(aFirstPart);
    168  MOZ_ASSERT(aProgressTracker);
    169 
    170  RefPtr<MultipartImage> newImage = new MultipartImage(aFirstPart);
    171  aProgressTracker->SetImage(newImage);
    172  newImage->SetProgressTracker(aProgressTracker);
    173 
    174  newImage->Init();
    175 
    176  return newImage.forget();
    177 }
    178 
    179 int32_t SaturateToInt32(int64_t val) {
    180  if (val > INT_MAX) {
    181    return INT_MAX;
    182  }
    183  if (val < INT_MIN) {
    184    return INT_MIN;
    185  }
    186 
    187  return static_cast<int32_t>(val);
    188 }
    189 
    190 uint32_t GetContentSize(nsIRequest* aRequest) {
    191  nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
    192  if (channel) {
    193    int64_t size;
    194    nsresult rv = channel->GetContentLength(&size);
    195    if (NS_SUCCEEDED(rv)) {
    196      return std::max(SaturateToInt32(size), 0);
    197    }
    198  }
    199 
    200  // Use the file size as a size hint for file channels.
    201  nsCOMPtr<nsIFileChannel> fileChannel(do_QueryInterface(aRequest));
    202  if (fileChannel) {
    203    nsCOMPtr<nsIFile> file;
    204    nsresult rv = fileChannel->GetFile(getter_AddRefs(file));
    205    if (NS_SUCCEEDED(rv)) {
    206      int64_t filesize;
    207      rv = file->GetFileSize(&filesize);
    208      if (NS_SUCCEEDED(rv)) {
    209        return std::max(SaturateToInt32(filesize), 0);
    210      }
    211    }
    212  }
    213 
    214  // Fallback - neither http nor file. We'll use dynamic allocation.
    215  return 0;
    216 }
    217 
    218 /* static */
    219 already_AddRefed<Image> ImageFactory::CreateRasterImage(
    220    nsIRequest* aRequest, ProgressTracker* aProgressTracker,
    221    const nsCString& aMimeType, nsIURI* aURI, uint32_t aImageFlags,
    222    uint64_t aInnerWindowId) {
    223  MOZ_ASSERT(aProgressTracker);
    224 
    225  nsresult rv;
    226 
    227  RefPtr<RasterImage> newImage = new RasterImage(aURI);
    228  aProgressTracker->SetImage(newImage);
    229  newImage->SetProgressTracker(aProgressTracker);
    230 
    231  rv = newImage->Init(aMimeType.get(), aImageFlags);
    232  if (NS_FAILED(rv)) {
    233    return BadImage("RasterImage::Init failed", newImage);
    234  }
    235 
    236  newImage->SetInnerWindowID(aInnerWindowId);
    237 
    238  rv = newImage->SetSourceSizeHint(GetContentSize(aRequest));
    239  if (NS_FAILED(rv)) {
    240    return BadImage("RasterImage::SetSourceSizeHint failed", newImage);
    241  }
    242 
    243  return newImage.forget();
    244 }
    245 
    246 /* static */
    247 already_AddRefed<Image> ImageFactory::CreateVectorImage(
    248    nsIRequest* aRequest, ProgressTracker* aProgressTracker,
    249    const nsCString& aMimeType, nsIURI* aURI, uint32_t aImageFlags,
    250    uint64_t aInnerWindowId) {
    251  MOZ_ASSERT(aProgressTracker);
    252 
    253  nsresult rv;
    254 
    255  RefPtr<VectorImage> newImage = new VectorImage(aURI);
    256  aProgressTracker->SetImage(newImage);
    257  newImage->SetProgressTracker(aProgressTracker);
    258 
    259  rv = newImage->Init(aMimeType.get(), aImageFlags);
    260  if (NS_FAILED(rv)) {
    261    return BadImage("VectorImage::Init failed", newImage);
    262  }
    263 
    264  newImage->SetInnerWindowID(aInnerWindowId);
    265 
    266  rv = newImage->OnStartRequest(aRequest);
    267  if (NS_FAILED(rv)) {
    268    return BadImage("VectorImage::OnStartRequest failed", newImage);
    269  }
    270 
    271  return newImage.forget();
    272 }
    273 
    274 }  // namespace image
    275 }  // namespace mozilla