<commit_before>// Copyright (c) 2015 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.

#include "shell/common/api/electron_api_native_image.h"

#include <memory>
#include <string>
#include <utility>
#include <vector>

#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/strings/pattern.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/thread_restrictions.h"
#include "gin/arguments.h"
#include "gin/object_template_builder.h"
#include "gin/per_isolate_data.h"
#include "gin/wrappable.h"
#include "net/base/data_url.h"
#include "shell/common/asar/asar_util.h"
#include "shell/common/gin_converters/file_path_converter.h"
#include "shell/common/gin_converters/gfx_converter.h"
#include "shell/common/gin_converters/gurl_converter.h"
#include "shell/common/gin_converters/value_converter.h"
#include "shell/common/gin_helper/dictionary.h"
#include "shell/common/gin_helper/function_template_extensions.h"
#include "shell/common/gin_helper/object_template_builder.h"
#include "shell/common/node_includes.h"
#include "shell/common/skia_util.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "third_party/skia/include/core/SkImageInfo.h"
#include "third_party/skia/include/core/SkPixelRef.h"
#include "ui/base/layout.h"
#include "ui/base/webui/web_ui_util.h"
#include "ui/gfx/codec/jpeg_codec.h"
#include "ui/gfx/codec/png_codec.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/image/image_skia.h"
#include "ui/gfx/image/image_skia_operations.h"
#include "ui/gfx/image/image_util.h"

#if defined(OS_WIN)
#include "base/win/scoped_gdi_object.h"
#include "shell/common/asar/archive.h"
#include "ui/gfx/icon_util.h"
#endif

namespace electron {

namespace api {

namespace {

// Get the scale factor from options object at the first argument
float GetScaleFactorFromOptions(gin::Arguments* args) {
  float scale_factor = 1.0f;
  gin_helper::Dictionary options;
  if (args->GetNext(&options))
    options.Get("scaleFactor", &scale_factor);
  return scale_factor;
}

base::FilePath NormalizePath(const base::FilePath& path) {
  if (!path.ReferencesParent()) {
    return path;
  }

  base::ThreadRestrictions::ScopedAllowIO allow_blocking;
  base::FilePath absolute_path = MakeAbsoluteFilePath(path);
  // MakeAbsoluteFilePath returns an empty path on failures so use original path
  if (absolute_path.empty()) {
    return path;
  } else {
    return absolute_path;
  }
}

#if defined(OS_MAC)
bool IsTemplateFilename(const base::FilePath& path) {
  return (base::MatchPattern(path.value(), "*Template.*") ||
          base::MatchPattern(path.value(), "*Template@*x.*"));
}
#endif

#if defined(OS_WIN)
base::win::ScopedHICON ReadICOFromPath(int size, const base::FilePath& path) {
  // If file is in asar archive, we extract it to a temp file so LoadImage can
  // load it.
  base::FilePath asar_path, relative_path;
  base::FilePath image_path(path);
  if (asar::GetAsarArchivePath(image_path, &asar_path, &relative_path)) {
    std::shared_ptr<asar::Archive> archive =
        asar::GetOrCreateAsarArchive(asar_path);
    if (archive)
      archive->CopyFileOut(relative_path, &image_path);
  }

  // Load the icon from file.
  return base::win::ScopedHICON(
      static_cast<HICON>(LoadImage(NULL, image_path.value().c_str(), IMAGE_ICON,
                                   size, size, LR_LOADFROMFILE)));
}
#endif

}  // namespace

NativeImage::NativeImage(v8::Isolate* isolate, const gfx::Image& image)
    : image_(image), isolate_(isolate) {
  if (image_.HasRepresentation(gfx::Image::kImageRepSkia)) {
    auto* const image_skia = image_.ToImageSkia();
    if (!image_skia->isNull()) {
      isolate_->AdjustAmountOfExternalAllocatedMemory(
          image_skia->bitmap()->computeByteSize());
    }
  }
}

#if defined(OS_WIN)
NativeImage::NativeImage(v8::Isolate* isolate, const base::FilePath& hicon_path)
    : hicon_path_(hicon_path), isolate_(isolate) {
  // Use the 256x256 icon as fallback icon.
  gfx::ImageSkia image_skia;
  electron::util::ReadImageSkiaFromICO(&image_skia, GetHICON(256));
  image_ = gfx::Image(image_skia);
  if (image_.HasRepresentation(gfx::Image::kImageRepSkia)) {
    if (!image_skia.isNull()) {
      isolate_->AdjustAmountOfExternalAllocatedMemory(
          image_.ToImageSkia()->bitmap()->computeByteSize());
    }
  }
}
#endif

NativeImage::~NativeImage() {
  if (image_.HasRepresentation(gfx::Image::kImageRepSkia)) {
    auto* const image_skia = image_.ToImageSkia();
    if (!image_skia->isNull()) {
      isolate_->AdjustAmountOfExternalAllocatedMemory(
          image_skia->bitmap()->computeByteSize());
    }
  }
}

// static
bool NativeImage::TryConvertNativeImage(v8::Isolate* isolate,
                                        v8::Local<v8::Value> image,
                                        NativeImage** native_image,
                                        OnConvertError on_error) {
  std::string error_message;

  base::FilePath icon_path;
  if (gin::ConvertFromV8(isolate, image, &icon_path)) {
    *native_image = NativeImage::CreateFromPath(isolate, icon_path).get();
    if ((*native_image)->image().IsEmpty()) {
#if defined(OS_WIN)
      const auto img_path = base::WideToUTF8(icon_path.value());
#else
      const auto img_path = icon_path.value();
#endif
      error_message = "Failed to load image from path '" + img_path + "'";
    }
  } else {
    if (!gin::ConvertFromV8(isolate, image, native_image)) {
      error_message = "Argument must be a file path or a NativeImage";
    }
  }<commit_msg>fix: AdjustAmountOfExternalAllocatedMemory regression in NativeImage destructor (#29179)<commit_after>// Copyright (c) 2015 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.

#include "shell/common/api/electron_api_native_image.h"

#include <memory>
#include <string>
#include <utility>
#include <vector>

#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/strings/pattern.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/thread_restrictions.h"
#include "gin/arguments.h"
#include "gin/object_template_builder.h"
#include "gin/per_isolate_data.h"
#include "gin/wrappable.h"
#include "net/base/data_url.h"
#include "shell/common/asar/asar_util.h"
#include "shell/common/gin_converters/file_path_converter.h"
#include "shell/common/gin_converters/gfx_converter.h"
#include "shell/common/gin_converters/gurl_converter.h"
#include "shell/common/gin_converters/value_converter.h"
#include "shell/common/gin_helper/dictionary.h"
#include "shell/common/gin_helper/function_template_extensions.h"
#include "shell/common/gin_helper/object_template_builder.h"
#include "shell/common/node_includes.h"
#include "shell/common/skia_util.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "third_party/skia/include/core/SkImageInfo.h"
#include "third_party/skia/include/core/SkPixelRef.h"
#include "ui/base/layout.h"
#include "ui/base/webui/web_ui_util.h"
#include "ui/gfx/codec/jpeg_codec.h"
#include "ui/gfx/codec/png_codec.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/image/image_skia.h"
#include "ui/gfx/image/image_skia_operations.h"
#include "ui/gfx/image/image_util.h"

#if defined(OS_WIN)
#include "base/win/scoped_gdi_object.h"
#include "shell/common/asar/archive.h"
#include "ui/gfx/icon_util.h"
#endif

namespace electron {

namespace api {

namespace {

// Get the scale factor from options object at the first argument
float GetScaleFactorFromOptions(gin::Arguments* args) {
  float scale_factor = 1.0f;
  gin_helper::Dictionary options;
  if (args->GetNext(&options))
    options.Get("scaleFactor", &scale_factor);
  return scale_factor;
}

base::FilePath NormalizePath(const base::FilePath& path) {
  if (!path.ReferencesParent()) {
    return path;
  }

  base::ThreadRestrictions::ScopedAllowIO allow_blocking;
  base::FilePath absolute_path = MakeAbsoluteFilePath(path);
  // MakeAbsoluteFilePath returns an empty path on failures so use original path
  if (absolute_path.empty()) {
    return path;
  } else {
    return absolute_path;
  }
}

#if defined(OS_MAC)
bool IsTemplateFilename(const base::FilePath& path) {
  return (base::MatchPattern(path.value(), "*Template.*") ||
          base::MatchPattern(path.value(), "*Template@*x.*"));
}
#endif

#if defined(OS_WIN)
base::win::ScopedHICON ReadICOFromPath(int size, const base::FilePath& path) {
  // If file is in asar archive, we extract it to a temp file so LoadImage can
  // load it.
  base::FilePath asar_path, relative_path;
  base::FilePath image_path(path);
  if (asar::GetAsarArchivePath(image_path, &asar_path, &relative_path)) {
    std::shared_ptr<asar::Archive> archive =
        asar::GetOrCreateAsarArchive(asar_path);
    if (archive)
      archive->CopyFileOut(relative_path, &image_path);
  }

  // Load the icon from file.
  return base::win::ScopedHICON(
      static_cast<HICON>(LoadImage(NULL, image_path.value().c_str(), IMAGE_ICON,
                                   size, size, LR_LOADFROMFILE)));
}
#endif

}  // namespace

NativeImage::NativeImage(v8::Isolate* isolate, const gfx::Image& image)
    : image_(image), isolate_(isolate) {
  if (image_.HasRepresentation(gfx::Image::kImageRepSkia)) {
    auto* const image_skia = image_.ToImageSkia();
    if (!image_skia->isNull()) {
      isolate_->AdjustAmountOfExternalAllocatedMemory(
          image_skia->bitmap()->computeByteSize());
    }
  }
}

#if defined(OS_WIN)
NativeImage::NativeImage(v8::Isolate* isolate, const base::FilePath& hicon_path)
    : hicon_path_(hicon_path), isolate_(isolate) {
  // Use the 256x256 icon as fallback icon.
  gfx::ImageSkia image_skia;
  electron::util::ReadImageSkiaFromICO(&image_skia, GetHICON(256));
  image_ = gfx::Image(image_skia);
  if (image_.HasRepresentation(gfx::Image::kImageRepSkia)) {
    if (!image_skia.isNull()) {
      isolate_->AdjustAmountOfExternalAllocatedMemory(
          image_.ToImageSkia()->bitmap()->computeByteSize());
    }
  }
}
#endif

NativeImage::~NativeImage() {
  if (image_.HasRepresentation(gfx::Image::kImageRepSkia)) {
    auto* const image_skia = image_.ToImageSkia();
    if (!image_skia->isNull()) {
      isolate_->AdjustAmountOfExternalAllocatedMemory(
          -static_cast<int64_t>(image_skia->bitmap()->computeByteSize()));
    }
  }
}

// static
bool NativeImage::TryConvertNativeImage(v8::Isolate* isolate,
                                        v8::Local<v8::Value> image,
                                        NativeImage** native_image,
                                        OnConvertError on_error) {
  std::string error_message;

  base::FilePath icon_path;
  if (gin::ConvertFromV8(isolate, image, &icon_path)) {
    *native_image = NativeImage::CreateFromPath(isolate, icon_path).get();
    if ((*native_image)->image().IsEmpty()) {
#if defined(OS_WIN)
      const auto img_path = base::WideToUTF8(icon_path.value());
#else
      const auto img_path = icon_path.value();
#endif
      error_message = "Failed to load image from path '" + img_path + "'";
    }
  } else {
    if (!gin::ConvertFromV8(isolate, image, native_image)) {
      error_message = "Argument must be a file path or a NativeImage";
    }
  }
{'fraction_non_alphanumeric': 0.13677998943103753, 'fraction_numerical': 0.004667958428747578, 'mean_word_length': 4.396863117870723, 'pattern_counts': {'":': 0, '<': 18, '<?xml version=': 0, '>': 46, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 8, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4883172', 'n_tokens_mistral': 3858, 'n_tokens_neox': 3812, 'n_words': 712}