text: // Copyright 2014 The Chromium Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  #include "content/child/blink_platform_impl.h"
  
  #include <math.h>
  
  #include <memory>
  #include <vector>
  
  #include "base/bind.h"
  #include "base/files/file_path.h"
  #include "base/location.h"
  #include "base/memory/singleton.h"
  #include "base/metrics/user_metrics_action.h"
  #include "base/rand_util.h"
  #include "base/run_loop.h"
  #include "base/sequence_checker.h"
  #include "base/single_thread_task_runner.h"
  #include "base/stl_util.h"
  #include "base/strings/string_number_conversions.h"
  #include "base/strings/string_util.h"
  #include "base/strings/utf_string_conversions.h"
  #include "base/synchronization/lock.h"
  #include "base/system/sys_info.h"
  #include "base/threading/thread_task_runner_handle.h"
  #include "base/time/time.h"
  #include "base/trace_event/memory_allocator_dump_guid.h"
  #include "base/trace_event/memory_dump_manager.h"
  #include "base/trace_event/trace_event.h"
  #include "build/build_config.h"
  #include "content/app/resources/grit/content_resources.h"
  #include "content/child/child_thread_impl.h"
  #include "content/common/appcache_interfaces.h"
  #include "content/common/child_process.mojom.h"
  #include "content/common/service_worker/service_worker_utils.h"
  #include "content/public/common/content_client.h"
  #include "content/public/common/content_features.h"
  #include "content/public/common/content_switches.h"
  #include "content/public/common/url_utils.h"
  #include "mojo/public/cpp/bindings/shared_remote.h"
  #include "net/base/net_errors.h"
  #include "services/network/public/cpp/features.h"
  #include "third_party/blink/public/common/thread_safe_browser_interface_broker_proxy.h"
  #include "third_party/blink/public/platform/web_data.h"
  #include "third_party/blink/public/platform/web_security_origin.h"
  #include "third_party/blink/public/platform/web_string.h"
  #include "third_party/blink/public/platform/web_url.h"
  #include "third_party/blink/public/resources/grit/blink_image_resources.h"
  #include "third_party/blink/public/resources/grit/blink_resources.h"
  #include "third_party/blink/public/strings/grit/blink_strings.h"
  #include "third_party/zlib/google/compression_utils.h"
  #include "ui/base/layout.h"
  #include "ui/base/ui_base_features.h"
  #include "ui/events/gestures/blink/web_gesture_curve_impl.h"
  
  #if defined(OS_ANDROID)
  #include "content/child/webthemeengine_impl_android.h"
  #else
  #include "content/child/webthemeengine_impl_default.h"
  #endif
  
  #if defined(OS_MAC)
  #include "content/child/webthemeengine_impl_mac.h"
  #endif
  
  using blink::WebData;
  using blink::WebString;
  using blink::WebThemeEngine;
  using blink::WebURL;
  using blink::WebURLError;
  
  namespace content {
  
  namespace {
  
  std::unique_ptr<blink::WebThemeEngine> GetWebThemeEngine() {
  #if defined(OS_ANDROID)
    return std::make_unique<WebThemeEngineAndroid>();
  #elif defined(OS_MAC)
    return std::make_unique<WebThemeEngineMac>();
  #else
    return std::make_unique<WebThemeEngineDefault>();
  #endif
  }
  
  // This must match third_party/WebKit/public/blink_resources.grd.
  struct DataResource {
    const char* name;
    int id;
    ui::ScaleFactor scale_factor;
  };
  
  class NestedMessageLoopRunnerImpl
      : public blink::Platform::NestedMessageLoopRunner {
   public:
    NestedMessageLoopRunnerImpl() = default;
  
    ~NestedMessageLoopRunnerImpl() override {
      DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
    }
  
    void Run() override {
      DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
      base::RunLoop* const previous_run_loop = run_loop_;
      base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
      run_loop_ = &run_loop;
      run_loop.Run();
      run_loop_ = previous_run_loop;
    }
  
    void QuitNow() override {
      DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
      DCHECK(run_loop_);
      run_loop_->Quit();
    }
  
   private:
    base::RunLoop* run_loop_ = nullptr;
  
    SEQUENCE_CHECKER(sequence_checker_);
  };
  
  mojo::SharedRemote<mojom::ChildProcessHost> GetChildProcessHost() {
    auto* thread = ChildThreadImpl::current();
    if (thread)
      return thread->child_process_host();
    return {};
  }
  
  // An implementation of BrowserInterfaceBroker which forwards to the
  // ChildProcessHost interface. This lives on the IO thread.
  class ThreadSafeBrowserInterfaceBrokerProxyImpl
      : public blink::ThreadSafeBrowserInterfaceBrokerProxy {
   public:
    ThreadSafeBrowserInterfaceBrokerProxyImpl()
        : process_host_(GetChildProcessHost()) {}
  
    // blink::ThreadSafeBrowserInterfaceBrokerProxy implementation:
    void GetInterfaceImpl(mojo::GenericPendingReceiver receiver) override {
      if (process_host_)
        process_host_->BindHostReceiver(std::move(receiver));
    }
  
   private:
    ~ThreadSafeBrowserInterfaceBrokerProxyImpl() override = default;
  
    const mojo::SharedRemote<mojom::ChildProcessHost> process_host_;
  
    DISALLOW_COPY_AND_ASSIGN(ThreadSafeBrowserInterfaceBrokerProxyImpl);
  };
  
  }  // namespace
  
  // TODO(skyostil): Ensure that we always have an active task runner when
  // constructing the platform.
  BlinkPlatformImpl::BlinkPlatformImpl() : BlinkPlatformImpl(nullptr) {}
  
  BlinkPlatformImpl::BlinkPlatformImpl(
      scoped_refptr<base::SingleThreadTaskRunner> io_thread_task_runner)
      : io_thread_task_runner_(std::move(io_thread_task_runner)),
        browser_interface_broker_proxy_(
            base::MakeRefCounted<ThreadSafeBrowserInterfaceBrokerProxyImpl>()),
        native_theme_engine_(GetWebThemeEngine()) {}
  
  BlinkPlatformImpl::~BlinkPlatformImpl() = default;
  
  void BlinkPlatformImpl::RecordAction(const blink::UserMetricsAction& name) {
    if (ChildThread* child_thread = ChildThread::Get())
      child_thread->RecordComputedAction(name.Action());
  }
  
  WebData BlinkPlatformImpl::GetDataResource(int resource_id,
                                             ui::ScaleFactor scale_factor) {
    base::StringPiece resource =
        GetContentClient()->GetDataResource(resource_id, scale_factor);
    return WebData(resource.data(), resource.size());
  }
  
  WebData BlinkPlatformImpl::UncompressDataResource(int resource_id) {
    base::StringPiece resource =
        GetContentClient()->GetDataResource(resource_id, ui::SCALE_FACTOR_NONE);
    if (resource.empty())
      return WebData(resource.data(), resource.size());
    std::string uncompressed;
    CHECK(compression::GzipUncompress(resource.as_string(), &uncompressed));
    return WebData(uncompressed.data(), uncompressed.size());
  }
  
  WebString BlinkPlatformImpl::QueryLocalizedString(int resource_id) {
    if (resource_id < 0)
      return WebString();
    return WebString::FromUTF16(
        GetContentClient()->GetLocalizedString(resource_id));
  }
  
  WebString BlinkPlatformImpl::QueryLocalizedString(int resource_id,
                                                    const WebString& value) {
    if (resource_id < 0)
      return WebString();
  
    std::u16string format_string =
        GetContentClient()->GetLocalizedString(resource_id);
  
    // If the ContentClient returned an empty string, e.g. because it's using the
    // default implementation of ContentClient::GetLocalizedString, return an
    // empty string instead of crashing with a failed DCHECK in
    // base::ReplaceStringPlaceholders below. This is useful for tests that don't
    // specialize a full ContentClient, since this way they can behave as though
    // there isn't a defined |resource_id| for the |name| instead of crashing
    // outright.
    if (format_string.empty())
      return WebString();
  
    return WebString::FromUTF16(
        base::ReplaceStringPlaceholders(format_string, value.Utf16(), nullptr));
  }
  
  WebString BlinkPlatformImpl::QueryLocalizedString(int resource_id,
                                                    const WebString& value1,
                                                    const WebString& value2) {
    if (resource_id < 0)
      return WebString();
    std::vector<std::u16string> values;
    values.reserve(2);
    values.push_back(value1.Utf16());
    values.push_back(value2.Utf16());
    return WebString::FromUTF16(base::ReplaceStringPlaceholders(
        GetContentClient()->GetLocalizedString(resource_id), values, nullptr));
  }
  
  blink::WebCrypto* BlinkPlatformImpl::Crypto() {
    return &web_crypto_;
  }
  
  blink::ThreadSafeBrowserInterfaceBrokerProxy*
  BlinkPlatformImpl::GetBrowserInterfaceBroker() {
    return browser_interface_broker_proxy_.get();
  }
  
  WebThemeEngine* BlinkPlatformImpl::ThemeEngine() {
    return native_theme_engine_.get();
  }
  
  bool BlinkPlatformImpl::IsURLSupportedForAppCache(const blink::WebURL& url) {
    return IsSchemeSupportedForAppCache(url);
  }
  
  bool BlinkPlatformImpl::IsURLSavableForSavableResource(
      const blink::WebURL& url) {
    return IsSavableURL(url);
  }
  
  size_t BlinkPlatformImpl::MaxDecodedImageBytes() {
    const int kMB = 1024 * 1024;
    const int kMaxNumberOfBytesPerPixel = 4;
  #if defined(OS_ANDROID)
    if (base::SysInfo::IsLowEndDevice()) {
      // Limit image decoded size to 3M pixels on low end devices.
      // 4 is maximum number of bytes per pixel.
      return 3 * kMB * kMaxNumberOfBytesPerPixel;
    }
    // For other devices, limit decoded image size based on the amount of physical
    // memory.
    // In some cases all physical memory is not accessible by Chromium, as it can
    // be reserved for direct use by certain hardware. Thus, we set the limit so
    // that 1.6GB of reported physical memory on a 2GB device is enough to set the
    // limit at 16M pixels, which is a desirable value since 4K*4K is a relatively
    // common texture size.
    return base::SysInfo::AmountOfPhysicalMemory() / 25;
  #else
    size_t max_decoded_image_byte_limit = kNoDecodedImageByteLimit;
    base::CommandLine& command_line = *base::CommandLine::ForCurrentProcess();
    if (command_line.HasSwitch(switches::kMaxDecodedImageSizeMb)) {
      if (base::StringToSizeT(
              command_line.GetSwitchValueASCII(switches::kMaxDecodedImageSizeMb),
              &max_decoded_image_byte_limit)) {
        max_decoded_image_byte_limit *= kMB * kMaxNumberOfBytesPerPixel;
      }
    }
    return max_decoded_image_byte_limit;
  #endif
  }
  
  bool BlinkPlatformImpl::IsLowEndDevice() {
    // This value is static for performance because calculating it is non-trivial.
    static bool is_low_end_device = base::SysInfo::IsLowEndDevice();
    return is_low_end_device;
  }
  
  scoped_refptr<base::SingleThreadTaskRunner> BlinkPlatformImpl::GetIOTaskRunner()
      const {
    return io_thread_task_runner_;
  }
  
  std::unique_ptr<blink::Platform::NestedMessageLoopRunner>
  BlinkPlatformImpl::CreateNestedMessageLoopRunner() const {
    return std::make_unique<NestedMessageLoopRunnerImpl>();
  }
  
  }  // namespace content
  