910e62b5创建于 1月15日历史提交
// Copyright 2013 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/base/x/selection_utils.h"

#include <stdint.h>

#include <set>
#include <string>
#include <string_view>
#include <vector>

#include "base/containers/contains.h"
#include "base/containers/span.h"
#include "base/i18n/icu_string_conversions.h"
#include "base/memory/ref_counted_memory.h"
#include "base/memory/scoped_refptr.h"
#include "base/notreached.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/strings/string_view_util.h"
#include "base/strings/utf_string_conversions.h"
#include "ui/base/clipboard/clipboard_constants.h"
#include "ui/gfx/x/atom_cache.h"

namespace ui {

std::vector<x11::Atom> GetTextAtomsFrom() {
  return {x11::GetAtom(kMimeTypeLinuxUtf8String),
          x11::GetAtom(kMimeTypeLinuxString), x11::GetAtom(kMimeTypeLinuxText),
          x11::GetAtom(kMimeTypePlainText),
          x11::GetAtom(kMimeTypeUtf8PlainText)};
}

std::vector<x11::Atom> GetURLAtomsFrom() {
  return {x11::GetAtom(kMimeTypeUriList), x11::GetAtom(kMimeTypeMozillaUrl)};
}

std::vector<x11::Atom> GetURIListAtomsFrom() {
  return {x11::GetAtom(kMimeTypeUriList)};
}

void GetAtomIntersection(const std::vector<x11::Atom>& desired,
                         const std::vector<x11::Atom>& offered,
                         std::vector<x11::Atom>* output) {
  for (const auto& desired_atom : desired) {
    if (base::Contains(offered, desired_atom))
      output->push_back(desired_atom);
  }
}

void AddString16ToVector(std::u16string_view str,
                         std::vector<unsigned char>* bytes) {
  auto span = base::as_byte_span(str);
  bytes->insert(bytes->end(), span.begin(), span.end());
}

std::vector<std::string> ParseURIList(const SelectionData& data) {
  // uri-lists are newline separated file lists in URL encoding.
  std::string unparsed;
  data.AssignTo(&unparsed);
  return base::SplitString(unparsed, "\n", base::KEEP_WHITESPACE,
                           base::SPLIT_WANT_NONEMPTY);
}

std::string RefCountedMemoryToString(
    const scoped_refptr<base::RefCountedMemory>& memory) {
  CHECK(memory.get());

  return std::string(base::as_string_view(*memory));
}

std::u16string RefCountedMemoryToString16(
    const scoped_refptr<base::RefCountedMemory>& memory) {
  CHECK(memory.get());

  auto in_bytes = base::span(*memory);
  std::u16string out;
  out.resize(memory->size() / 2u);
  base::as_writable_byte_span(out).copy_from(in_bytes);
  return out;
}

///////////////////////////////////////////////////////////////////////////////

SelectionFormatMap::SelectionFormatMap() = default;

SelectionFormatMap::SelectionFormatMap(const SelectionFormatMap& other) =
    default;

SelectionFormatMap::~SelectionFormatMap() = default;

void SelectionFormatMap::Insert(
    x11::Atom atom,
    const scoped_refptr<base::RefCountedMemory>& item) {
  data_.erase(atom);
  data_.emplace(atom, item);
}

ui::SelectionData SelectionFormatMap::GetFirstOf(
    const std::vector<x11::Atom>& requested_types) const {
  for (const auto& requested_type : requested_types) {
    auto data_it = data_.find(requested_type);
    if (data_it != data_.end()) {
      return SelectionData(data_it->first, data_it->second);
    }
  }

  return SelectionData();
}

ui::SelectionData SelectionFormatMap::Get(x11::Atom requested_type) const {
  auto data_it = data_.find(requested_type);
  if (data_it != data_.end()) {
    return SelectionData(data_it->first, data_it->second);
  }

  return SelectionData();
}

std::vector<x11::Atom> SelectionFormatMap::GetTypes() const {
  std::vector<x11::Atom> atoms;
  for (const auto& datum : data_)
    atoms.push_back(datum.first);

  return atoms;
}

///////////////////////////////////////////////////////////////////////////////

SelectionData::SelectionData() : type_(x11::Atom::None) {}

SelectionData::SelectionData(
    x11::Atom type,
    const scoped_refptr<base::RefCountedMemory>& memory)
    : type_(type), memory_(memory) {}

SelectionData::SelectionData(const SelectionData& rhs) = default;

SelectionData::~SelectionData() = default;

SelectionData& SelectionData::operator=(const SelectionData& rhs) {
  type_ = rhs.type_;
  memory_ = rhs.memory_;
  // TODO(erg): In some future where we have to support multiple X Displays,
  // the following will also need to deal with the display.
  return *this;
}

bool SelectionData::IsValid() const {
  return type_ != x11::Atom::None;
}

x11::Atom SelectionData::GetType() const {
  return type_;
}

base::span<const unsigned char> SelectionData::GetSpan() const {
  return memory_ ? *memory_ : base::span<const unsigned char>();
}

std::string SelectionData::GetText() const {
  if (type_ == x11::GetAtom(kMimeTypeLinuxUtf8String) ||
      type_ == x11::GetAtom(kMimeTypeLinuxText) ||
      type_ == x11::GetAtom(kMimeTypeUtf8PlainText)) {
    return RefCountedMemoryToString(memory_);
  } else {
    // BTW, I looked at COMPOUND_TEXT, and there's no way we're going to
    // support that. Yuck.
    CHECK(type_ == x11::GetAtom(kMimeTypeLinuxString) ||
          type_ == x11::GetAtom(kMimeTypePlainText));
    std::string result;
    base::ConvertToUtf8AndNormalize(RefCountedMemoryToString(memory_),
                                    base::kCodepageLatin1, &result);
    return result;
  }
}

std::u16string SelectionData::GetHtml() const {
  std::u16string markup;

  CHECK_EQ(type_, x11::GetAtom(kMimeTypeHtml));
  base::span<const unsigned char> span = GetSpan();

  // If the data starts with U+FEFF, i.e., Byte Order Mark, assume it is
  // UTF-16, otherwise assume UTF-8.
  UNSAFE_TODO({
    if (span.size() >= 2 &&
        reinterpret_cast<const char16_t*>(span.data())[0] == u'\uFEFF') {
      markup.assign(reinterpret_cast<const char16_t*>(span.data()) + 1,
                    (span.size() / 2) - 1);
    } else {
      markup = base::UTF8ToUTF16(base::as_string_view(span));
    }
  });

  // If there is a terminating NULL, drop it.
  if (!markup.empty() && markup.back() == '\0') {
    markup.pop_back();
  }

  return markup;
}

void SelectionData::AssignTo(std::string* result) const {
  *result = RefCountedMemoryToString(memory_);
}

void SelectionData::AssignTo(std::u16string* result) const {
  *result = RefCountedMemoryToString16(memory_);
}

scoped_refptr<base::RefCountedBytes> SelectionData::TakeBytes() {
  if (!memory_.get()) {
    return nullptr;
  }
  auto* memory = memory_.release();
  return base::MakeRefCounted<base::RefCountedBytes>(*memory);
}

}  // namespace ui