image/ImageFactory.cpp
author Morten Stenshorne <mstensho@chromium.org>
Tue, 09 Oct 2018 04:14:13 +0000
changeset 495959 13844f50ee32e17f513b7c1013c2348200e21b8c
parent 475740 7597bbcc9d66111326aa69048ad5847bdc7497f2
child 505383 6f3709b3878117466168c40affa7bca0b60cf75b
permissions -rw-r--r--
Bug 1496274 [wpt PR 13345] - [LayoutNG] Correct clip-path reference box calculation., a=testonly Automatic update from web-platform-tests[LayoutNG] Correct clip-path reference box calculation. We used coordinates relatively to the line box, while we were expected by the caller to be relative to the containing block. Flipping for writing mode was bogus for NG (but needed by legacy), since NG uses truly physical coordinates. Hardened tests to contain a leading line and padding, and leading content on the first line of the clipped child. Bug: 641907 Change-Id: I2b1b9ff4ea92a6405fcdffcf139842458b46442f Cq-Include-Trybots: luci.chromium.try​:linux_layout_tests_layout_ng Reviewed-on: https://chromium-review.googlesource.com/c/1257913 Reviewed-by: Koji Ishii <kojii@chromium.org> Reviewed-by: Fredrik Söderquist <fs@opera.com> Commit-Queue: Morten Stenshorne <mstensho@chromium.org> Cr-Commit-Position: refs/heads/master@{#596554} -- wpt-commits: e9a0828c85819340f721f121aac19ab8eefa3439 wpt-pr: 13345

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 *
 * 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 "ImageFactory.h"

#include <algorithm>

#include "mozilla/Likely.h"

#include "nsIHttpChannel.h"
#include "nsIFileChannel.h"
#include "nsIFile.h"
#include "nsMimeTypes.h"
#include "nsIRequest.h"

#include "MultipartImage.h"
#include "RasterImage.h"
#include "VectorImage.h"
#include "Image.h"
#include "nsMediaFragmentURIParser.h"
#include "nsContentUtils.h"
#include "nsIScriptSecurityManager.h"

#include "gfxPrefs.h"

namespace mozilla {
namespace image {

/*static*/ void
ImageFactory::Initialize()
{ }

static uint32_t
ComputeImageFlags(nsIURI* uri, const nsCString& aMimeType, bool isMultiPart)
{
  nsresult rv;

  // We default to the static globals.
  bool isDiscardable = gfxPrefs::ImageMemDiscardable();
  bool doDecodeImmediately = gfxPrefs::ImageDecodeImmediatelyEnabled();

  // We want UI to be as snappy as possible and not to flicker. Disable
  // discarding for chrome URLS.
  bool isChrome = false;
  rv = uri->SchemeIs("chrome", &isChrome);
  if (NS_SUCCEEDED(rv) && isChrome) {
    isDiscardable = false;
  }

  // We don't want resources like the "loading" icon to be discardable either.
  bool isResource = false;
  rv = uri->SchemeIs("resource", &isResource);
  if (NS_SUCCEEDED(rv) && isResource) {
    isDiscardable = false;
  }

  // For multipart/x-mixed-replace, we basically want a direct channel to the
  // decoder. Disable everything for this case.
  if (isMultiPart) {
    isDiscardable = false;
  }

  // We have all the information we need.
  uint32_t imageFlags = Image::INIT_FLAG_NONE;
  if (isDiscardable) {
    imageFlags |= Image::INIT_FLAG_DISCARDABLE;
  }
  if (doDecodeImmediately) {
    imageFlags |= Image::INIT_FLAG_DECODE_IMMEDIATELY;
  }
  if (isMultiPart) {
    imageFlags |= Image::INIT_FLAG_TRANSIENT;
  }

  // Synchronously decode metadata (including size) if we have a data URI since
  // the data is immediately available.
  bool isDataURI = false;
  rv = uri->SchemeIs("data", &isDataURI);
  if (NS_SUCCEEDED(rv) && isDataURI) {
    imageFlags |= Image::INIT_FLAG_SYNC_LOAD;
  }

  return imageFlags;
}

#ifdef DEBUG
static void
NotifyImageLoading(nsIURI* aURI)
{
  if (!NS_IsMainThread()) {
    nsCOMPtr<nsIURI> uri(aURI);
    nsCOMPtr<nsIRunnable> ev =
      NS_NewRunnableFunction("NotifyImageLoading", [uri] () -> void {
        NotifyImageLoading(uri);
    });
    SystemGroup::Dispatch(TaskCategory::Other, ev.forget());
    return;
  }

  nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
  NS_WARNING_ASSERTION(obs, "Can't get an observer service handle");
  if (obs) {
    nsAutoCString spec;
    aURI->GetSpec(spec);
    obs->NotifyObservers(nullptr, "image-loading", NS_ConvertUTF8toUTF16(spec).get());
  }
}
#endif

/* static */ already_AddRefed<Image>
ImageFactory::CreateImage(nsIRequest* aRequest,
                          ProgressTracker* aProgressTracker,
                          const nsCString& aMimeType,
                          nsIURI* aURI,
                          bool aIsMultiPart,
                          uint32_t aInnerWindowId)
{
  MOZ_ASSERT(gfxPrefs::SingletonExists(),
             "Pref observers should have been initialized already");

  // Compute the image's initialization flags.
  uint32_t imageFlags = ComputeImageFlags(aURI, aMimeType, aIsMultiPart);

#ifdef DEBUG
  // Record the image load for startup performance testing.
  bool match = false;
  if ((NS_SUCCEEDED(aURI->SchemeIs("resource", &match)) && match) ||
      (NS_SUCCEEDED(aURI->SchemeIs("chrome", &match)) && match)) {
    NotifyImageLoading(aURI);
  }
#endif

  // Select the type of image to create based on MIME type.
  if (aMimeType.EqualsLiteral(IMAGE_SVG_XML)) {
    return CreateVectorImage(aRequest, aProgressTracker, aMimeType,
                             aURI, imageFlags, aInnerWindowId);
  } else {
    return CreateRasterImage(aRequest, aProgressTracker, aMimeType,
                             aURI, imageFlags, aInnerWindowId);
  }
}

// Marks an image as having an error before returning it.
template <typename T>
static already_AddRefed<Image>
BadImage(const char* aMessage, RefPtr<T>& aImage)
{
  aImage->SetHasError();
  return aImage.forget();
}

/* static */ already_AddRefed<Image>
ImageFactory::CreateAnonymousImage(const nsCString& aMimeType,
                                   uint32_t aSizeHint /* = 0 */)
{
  nsresult rv;

  RefPtr<RasterImage> newImage = new RasterImage();

  RefPtr<ProgressTracker> newTracker = new ProgressTracker();
  newTracker->SetImage(newImage);
  newImage->SetProgressTracker(newTracker);

  rv = newImage->Init(aMimeType.get(), Image::INIT_FLAG_SYNC_LOAD);
  if (NS_FAILED(rv)) {
    return BadImage("RasterImage::Init failed", newImage);
  }

  rv = newImage->SetSourceSizeHint(aSizeHint);
  if (NS_FAILED(rv)) {
    return BadImage("RasterImage::SetSourceSizeHint failed", newImage);
  }

  return newImage.forget();
}

/* static */ already_AddRefed<MultipartImage>
ImageFactory::CreateMultipartImage(Image* aFirstPart,
                                   ProgressTracker* aProgressTracker)
{
  MOZ_ASSERT(aFirstPart);
  MOZ_ASSERT(aProgressTracker);

  RefPtr<MultipartImage> newImage = new MultipartImage(aFirstPart);
  aProgressTracker->SetImage(newImage);
  newImage->SetProgressTracker(aProgressTracker);

  newImage->Init();

  return newImage.forget();
}

int32_t
SaturateToInt32(int64_t val)
{
  if (val > INT_MAX) {
    return INT_MAX;
  }
  if (val < INT_MIN) {
    return INT_MIN;
  }

  return static_cast<int32_t>(val);
}

uint32_t
GetContentSize(nsIRequest* aRequest)
{
  nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
  if (channel) {
    int64_t size;
    nsresult rv = channel->GetContentLength(&size);
    if (NS_SUCCEEDED(rv)) {
      return std::max(SaturateToInt32(size), 0);
    }
  }

  // Use the file size as a size hint for file channels.
  nsCOMPtr<nsIFileChannel> fileChannel(do_QueryInterface(aRequest));
  if (fileChannel) {
    nsCOMPtr<nsIFile> file;
    nsresult rv = fileChannel->GetFile(getter_AddRefs(file));
    if (NS_SUCCEEDED(rv)) {
      int64_t filesize;
      rv = file->GetFileSize(&filesize);
      if (NS_SUCCEEDED(rv)) {
        return std::max(SaturateToInt32(filesize), 0);
      }
    }
  }

  // Fallback - neither http nor file. We'll use dynamic allocation.
  return 0;
}

/* static */ already_AddRefed<Image>
ImageFactory::CreateRasterImage(nsIRequest* aRequest,
                                ProgressTracker* aProgressTracker,
                                const nsCString& aMimeType,
                                nsIURI* aURI,
                                uint32_t aImageFlags,
                                uint32_t aInnerWindowId)
{
  MOZ_ASSERT(aProgressTracker);

  nsresult rv;

  RefPtr<RasterImage> newImage = new RasterImage(aURI);
  aProgressTracker->SetImage(newImage);
  newImage->SetProgressTracker(aProgressTracker);

  rv = newImage->Init(aMimeType.get(), aImageFlags);
  if (NS_FAILED(rv)) {
    return BadImage("RasterImage::Init failed", newImage);
  }

  newImage->SetInnerWindowID(aInnerWindowId);

  rv = newImage->SetSourceSizeHint(GetContentSize(aRequest));
  if (NS_FAILED(rv)) {
    return BadImage("RasterImage::SetSourceSizeHint failed", newImage);
  }

  return newImage.forget();
}

/* static */ already_AddRefed<Image>
ImageFactory::CreateVectorImage(nsIRequest* aRequest,
                                ProgressTracker* aProgressTracker,
                                const nsCString& aMimeType,
                                nsIURI* aURI,
                                uint32_t aImageFlags,
                                uint32_t aInnerWindowId)
{
  MOZ_ASSERT(aProgressTracker);

  nsresult rv;

  RefPtr<VectorImage> newImage = new VectorImage(aURI);
  aProgressTracker->SetImage(newImage);
  newImage->SetProgressTracker(aProgressTracker);

  rv = newImage->Init(aMimeType.get(), aImageFlags);
  if (NS_FAILED(rv)) {
    return BadImage("VectorImage::Init failed", newImage);
  }

  newImage->SetInnerWindowID(aInnerWindowId);

  rv = newImage->OnStartRequest(aRequest, nullptr);
  if (NS_FAILED(rv)) {
    return BadImage("VectorImage::OnStartRequest failed", newImage);
  }

  return newImage.forget();
}

} // namespace image
} // namespace mozilla