chromium/ui/base/resource/data_pack_with_resource_sharing_lacros.h

// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef UI_BASE_RESOURCE_DATA_PACK_WITH_RESOURCE_SHARING_LACROS_H_
#define UI_BASE_RESOURCE_DATA_PACK_WITH_RESOURCE_SHARING_LACROS_H_

#include <map>
#include <memory>
#include <optional>
#include <string_view>
#include <vector>

#include "base/component_export.h"
#include "base/files/file_path.h"
#include "base/memory/raw_ptr.h"
#include "ui/base/resource/data_pack.h"
#include "ui/base/resource/resource_handle.h"
#include "ui/base/resource/resource_scale_factor.h"
#include "ui/base/resource/scoped_file_writer.h"

namespace ui {

// This class is used in lacros to reduce memory usage. The majority
// of the resources that are needed by lacros-chrome ship with ash-chrome.
// To avoid memory mapping similar (large) files (resulting in increased memory
// usage), this class shares resources with ash-chrome. Sharing is
// done by determining which resources are identical and maintaining a
// mapping file that maps from lacros-chrome resource id to ash-chrome
// resource id. Resources that are unique to lacros-chrome are placed
// in a fallback file.
//
// The mapping file is recreated as necessary.
class COMPONENT_EXPORT(UI_DATA_PACK) DataPackWithResourceSharing
    : public ResourceHandle {
 public:
  explicit DataPackWithResourceSharing(
      ResourceScaleFactor resource_scale_factor);

  DataPackWithResourceSharing(const DataPackWithResourceSharing&) = delete;
  DataPackWithResourceSharing& operator=(const DataPackWithResourceSharing&) =
      delete;

  ~DataPackWithResourceSharing() override;

  // Data of Lacros version and timestamp.
  struct LacrosVersionData {
    // The size of version components is 4. Version of Lacros. Each element
    // corredponds to major, minor, build and patch of CHROME_VERSION.
    static constexpr size_t kVersionComponentsSize = 4;

    // Version of Lacros.
    uint32_t version[kVersionComponentsSize];

    // Last updated time of lacros-chrome.
    int64_t timestamp;
  };

  // Maps resource id in Lacros resources to Ash resources.
  // Data assigned to `lacros_resource_id` and `ash_resource_id` must be same.
  struct Mapping {
    Mapping(uint16_t lacros_resource_id, uint16_t ash_resource_id);

    // Comparison function used for sorting.
    static int CompareById(const void* void_key, const void* void_entry);

    // `lacros_resource_id` is applicable in Lacros resources.
    uint16_t lacros_resource_id;
    // `ash_resource_id` is applicable in Ash resources `ash_data_pack_`.
    uint16_t ash_resource_id;
  };

  // Loads a pack file from `shared_resource_path` with ash resources
  // `ash_path`. Each resources for Lacros are included in eitehr of `ash_path`
  // or `shared_resource_path`.
  // Lacros resources are mpped to Ash resources if duplicated. The mapping tble
  // can be obtained from `shared_resource_path`. As for Lacros resources which
  // are not included in Ash resources, add them to `shared_resource_path` as
  // fallback resources.
  // `shared_resource_path` is expected to be already generated from lacros
  // resources and ash resources. If the file was not successfully generated,
  // `is_valid` in `shared_resource_path` is set to 0.
  bool LoadFromPathWithAshResource(const base::FilePath& shared_resource_path,
                                   const base::FilePath& ash_path);

  // ResourceHandle implementation:
  bool HasResource(uint16_t resource_id) const override;
  std::optional<std::string_view> GetStringPiece(
      uint16_t resource_id) const override;
  base::RefCountedStaticMemory* GetStaticMemory(
      uint16_t resource_id) const override;
  TextEncodingType GetTextEncodingType() const override;
  ResourceScaleFactor GetResourceScaleFactor() const override;
#if DCHECK_IS_ON()
  // Checks to see if any resource in this DataPack already exists in the list
  // of resources.
  void CheckForDuplicateResources(
      const std::vector<std::unique_ptr<ResourceHandle>>& handles) override;
#endif

  // If either of |shared_resource_path| is not valid or doesn't exit, generate
  // them from |ash_path| and |lacros_path|.
  static bool MaybeGenerateFallbackAndMapping(
      const base::FilePath& ash_path,
      const base::FilePath& lacros_path,
      const base::FilePath& shared_resource_path,
      ResourceScaleFactor resource_scale_factor);

  // Should only be used for unit-testing, as this is an implementation detail.
  size_t GetMappingTableSizeForTesting() const { return mapping_count_; }
  const Mapping* GetMappingByMappingTableIndexForTesting(size_t index) const {
    return &mapping_table_[index];
  }
  // Generate file with specific data. Shared resource file is expected to be
  // created only by MaybeGenerateFallbackAdnMapping, so DO NOT use this except
  // for unit-testing.
  static bool WriteSharedResourceFileForTesting(
      const base::FilePath& path,
      std::vector<Mapping> mapping = std::vector<Mapping>(),
      std::vector<uint16_t> resource_ids = std::vector<uint16_t>(),
      std::map<uint16_t, uint16_t> aliases = std::map<uint16_t, uint16_t>(),
      std::map<uint16_t, std::string_view> fallback_resources =
          std::map<uint16_t, std::string_view>());

 private:
  // Loads mapping_table_ from mapping file.
  bool LoadMappingTable(const base::FilePath& path);
  // Returns mapped resource ID if |resource_id| is in |mapping_table_|.
  // Return null if not.
  const std::optional<uint16_t> LookupMappingTable(uint16_t resource_id) const;

  // Check the shared resource `path` version is valid. If Lacros version used
  // to generate `path` is not the same with the current Lacros, return false.
  // We consider the versions are the same iff
  // 1. Lacros chrome version is same.
  // 2. Last updated time of Lacros file is same.
  // Here, we check not only chrome version but also the time since there might
  // be a change in Lacros which affects resource file. This is mostly used by
  // developers.
  static bool IsSharedResourceValid(const base::FilePath& path);

  // Writes a version and its updated time of Lacros used to generate file.
  static bool WriteLacrosVersion(ScopedFileWriter& file);
  // Writes a mapping table `mapping` to `file`.
  static bool WriteMappingTable(std::vector<Mapping> mapping,
                                ScopedFileWriter& file);
  // Writes a fallback resource table, alias table and string data to `file`.
  // `margin_to_skip` represents the size of the margin in bytes before
  // resouce_table information starts.
  static bool WriteFallbackResources(
      std::vector<uint16_t> resource_ids,
      std::map<uint16_t, uint16_t> aliases,
      std::map<uint16_t, std::string_view> fallback_resources,
      size_t margin_to_skip,
      ScopedFileWriter& file);
  // Close and delete temp shared resource file used for generating.
  // This should be called when failing during generating a shared resource.
  static void OnFailedToGenerate(ScopedFileWriter& file,
                                 const base::FilePath& shared_resource_path);

  std::unique_ptr<DataPack::DataSource> data_source_;

  // Each Mapping maps lacros resource id to ash resource id if the same data
  // exists in ash resources .pak.
  // Lacros resource id registered in `mapping_table_` as a key should not be
  // included in fallback_data_pack_.
  raw_ptr<const Mapping, AllowPtrArithmetic> mapping_table_;
  size_t mapping_count_ = 0;

  // Stores DataPacks of fallback resources and ash resources for each.
  std::unique_ptr<DataPack> fallback_data_pack_;
  std::unique_ptr<DataPack> ash_data_pack_;

  // The scale of the image in this resource pack relative to images in the 1x
  // resource pak.
  ResourceScaleFactor resource_scale_factor_;
};

}  // namespace ui

#endif  // UI_BASE_RESOURCE_DATA_PACK_WITH_RESOURCE_SHARING_LACROS_H_