chromium/ui/display/manager/json_converter.cc

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

#include "ui/display/manager/json_converter.h"

#include <memory>
#include <string>

#include "base/logging.h"
#include "base/strings/string_number_conversions.h"
#include "base/values.h"
#include "ui/display/display_layout.h"

namespace display {

namespace {

// Persistent key names
const char kDefaultUnifiedKey[] = "default_unified";
const char kPrimaryIdKey[] = "primary-id";
const char kDisplayPlacementKey[] = "display_placement";

// DisplayPlacement key names
const char kPositionKey[] = "position";
const char kOffsetKey[] = "offset";
const char kDisplayPlacementDisplayIdKey[] = "display_id";
const char kDisplayPlacementParentDisplayIdKey[] = "parent_display_id";

bool AddLegacyValuesFromValue(const base::Value::Dict& dict,
                              DisplayLayout* layout) {
  std::optional<int> optional_offset = dict.FindInt(kOffsetKey);
  if (optional_offset) {
    DisplayPlacement::Position position;
    const std::string* position_str = dict.FindString(kPositionKey);
    if (!position_str)
      return false;
    DisplayPlacement::StringToPosition(*position_str, &position);
    layout->placement_list.emplace_back(position, *optional_offset);
  }
  return true;
}

// Returns true if
//     The key is missing - output is left unchanged
//     The key matches the type - output is updated to the dict.
bool UpdateFromDict(const base::Value::Dict& dict,
                    const std::string& field_name,
                    bool* output) {
  const base::Value* field = dict.Find(field_name);
  if (!field) {
    LOG(WARNING) << "Missing field: " << field_name;
    return true;
  }

  std::optional<bool> field_value = field->GetIfBool();
  if (!field_value)
    return false;

  *output = *field_value;
  return true;
}

// Returns true if
//     The key is missing - output is left unchanged
//     The key matches the type - output is updated to the dict.
bool UpdateFromDict(const base::Value::Dict& dict,
                    const std::string& field_name,
                    int* output) {
  const base::Value* field = dict.Find(field_name);
  if (!field) {
    LOG(WARNING) << "Missing field: " << field_name;
    return true;
  }

  std::optional<int> field_value = field->GetIfInt();
  if (!field_value)
    return false;

  *output = *field_value;
  return true;
}

// Returns true if
//     The key is missing - output is left unchanged
//     The key matches the type - output is updated to the dict.
bool UpdateFromDict(const base::Value::Dict& dict,
                    const std::string& field_name,
                    DisplayPlacement::Position* output) {
  const base::Value* field = dict.Find(field_name);
  if (!field) {
    LOG(WARNING) << "Missing field: " << field_name;
    return true;
  }

  const std::string* field_value = field->GetIfString();
  if (!field_value)
    return false;

  return field_value->empty()
             ? true
             : DisplayPlacement::StringToPosition(*field_value, output);
}

// Returns true if
//     The key is missing - output is left unchanged
//     The key matches the type - output is updated to the dict.
bool UpdateFromDict(const base::Value::Dict& dict,
                    const std::string& field_name,
                    int64_t* output) {
  const base::Value* field = dict.Find(field_name);
  if (!field) {
    LOG(WARNING) << "Missing field: " << field_name;
    return true;
  }

  const std::string* field_value = field->GetIfString();
  if (!field_value)
    return false;

  return field_value->empty() ? true
                              : base::StringToInt64(*field_value, output);
}

// Returns true if
//     The key is missing - output is left unchanged
//     The key matches the type - output is updated to the dict.
bool UpdateFromDict(const base::Value::Dict& dict,
                    const std::string& field_name,
                    std::vector<DisplayPlacement>* output) {
  const base::Value* field = dict.Find(field_name);
  if (!field) {
    LOG(WARNING) << "Missing field: " << field_name;
    return true;
  }

  if (!field->is_list())
    return false;

  const base::Value::List& list = field->GetList();
  output->reserve(list.size());

  for (const base::Value& list_item : list) {
    if (!list_item.is_dict())
      return false;

    DisplayPlacement item;
    const base::Value::Dict& item_dict = list_item.GetDict();
    if (!UpdateFromDict(item_dict, kOffsetKey, &item.offset) ||
        !UpdateFromDict(item_dict, kPositionKey, &item.position) ||
        !UpdateFromDict(item_dict, kDisplayPlacementDisplayIdKey,
                        &item.display_id) ||
        !UpdateFromDict(item_dict, kDisplayPlacementParentDisplayIdKey,
                        &item.parent_display_id)) {
      return false;
    }

    output->push_back(item);
  }
  return true;
}

}  // namespace

bool JsonToDisplayLayout(const base::Value& value, DisplayLayout* layout) {
  if (!value.is_dict())
    return false;
  return JsonToDisplayLayout(value.GetDict(), layout);
}

bool JsonToDisplayLayout(const base::Value::Dict& dict, DisplayLayout* layout) {
  layout->placement_list.clear();

  if (!UpdateFromDict(dict, kDefaultUnifiedKey, &layout->default_unified) ||
      !UpdateFromDict(dict, kPrimaryIdKey, &layout->primary_id)) {
    return false;
  }

  UpdateFromDict(dict, kDisplayPlacementKey, &layout->placement_list);
  if (layout->placement_list.size() != 0u)
    return true;

  // For compatibility with old format.
  return AddLegacyValuesFromValue(dict, layout);
}

void DisplayLayoutToJson(const DisplayLayout& layout, base::Value::Dict& dict) {
  dict.Set(kDefaultUnifiedKey, layout.default_unified);
  dict.Set(kPrimaryIdKey, base::NumberToString(layout.primary_id));

  base::Value::List placement_list;
  for (const auto& placement : layout.placement_list) {
    base::Value::Dict placement_value;
    placement_value.Set(kPositionKey,
                        DisplayPlacement::PositionToString(placement.position));
    placement_value.Set(kOffsetKey, placement.offset);
    placement_value.Set(kDisplayPlacementDisplayIdKey,
                        base::NumberToString(placement.display_id));
    placement_value.Set(kDisplayPlacementParentDisplayIdKey,
                        base::NumberToString(placement.parent_display_id));
    placement_list.Append(std::move(placement_value));
  }
  dict.Set(kDisplayPlacementKey, std::move(placement_list));
}

}  // namespace display