data_source.cc 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379
  1. // Copyright 2017 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "components/exo/data_source.h"
  5. #include <limits>
  6. #include "base/bind.h"
  7. #include "base/callback_helpers.h"
  8. #include "base/files/file_util.h"
  9. #include "base/i18n/character_encoding.h"
  10. #include "base/i18n/icu_string_conversions.h"
  11. #include "base/posix/eintr_wrapper.h"
  12. #include "base/strings/string_split.h"
  13. #include "base/strings/string_util.h"
  14. #include "base/task/thread_pool.h"
  15. #include "components/exo/data_source_delegate.h"
  16. #include "components/exo/data_source_observer.h"
  17. #include "components/exo/mime_utils.h"
  18. #include "net/base/mime_util.h"
  19. #include "third_party/abseil-cpp/absl/types/optional.h"
  20. #include "third_party/blink/public/common/mime_util/mime_util.h"
  21. #include "third_party/icu/source/common/unicode/ucnv.h"
  22. #include "ui/base/clipboard/clipboard_constants.h"
  23. namespace exo {
  24. namespace {
  25. constexpr char kTextPlain[] = "text/plain";
  26. constexpr char kTextRTF[] = "text/rtf";
  27. constexpr char kTextHTML[] = "text/html";
  28. constexpr char kTextUriList[] = "text/uri-list";
  29. constexpr char kApplicationOctetStream[] = "application/octet-stream";
  30. constexpr char kWebCustomData[] = "chromium/x-web-custom-data";
  31. constexpr char kDataTransferEndpoint[] = "chromium/x-data-transfer-endpoint";
  32. constexpr char kUtfPrefix[] = "UTF";
  33. constexpr char kEncoding16[] = "16";
  34. constexpr char kEncodingASCII[] = "ASCII";
  35. constexpr char kUTF16Unspecified[] = "UTF-16";
  36. constexpr char kUTF16LittleEndian[] = "UTF-16LE";
  37. constexpr char kUTF16BigEndian[] = "UTF-16BE";
  38. constexpr uint8_t kByteOrderMark[] = {0xFE, 0xFF};
  39. constexpr int kByteOrderMarkSize = sizeof(kByteOrderMark);
  40. constexpr char kImageBitmap[] = "image/bmp";
  41. constexpr char kImagePNG[] = "image/png";
  42. constexpr char kImageAPNG[] = "image/apng";
  43. absl::optional<std::vector<uint8_t>> ReadDataOnWorkerThread(base::ScopedFD fd) {
  44. constexpr size_t kChunkSize = 1024;
  45. std::vector<uint8_t> bytes;
  46. while (true) {
  47. uint8_t chunk[kChunkSize];
  48. ssize_t bytes_read = HANDLE_EINTR(read(fd.get(), chunk, kChunkSize));
  49. if (bytes_read > 0) {
  50. bytes.insert(bytes.end(), chunk, chunk + bytes_read);
  51. continue;
  52. }
  53. if (!bytes_read)
  54. return bytes;
  55. if (bytes_read < 0) {
  56. PLOG(ERROR) << "Failed to read selection data from clipboard";
  57. return absl::nullopt;
  58. }
  59. }
  60. }
  61. // Map a named character set to an integer ranking, lower is better. This is an
  62. // implementation detail of DataSource::GetPreferredMimeTypes and should not be
  63. // considered to have any greater meaning. In particular, these are not expected
  64. // to remain stable over time.
  65. int GetCharsetRank(const std::string& charset_input) {
  66. std::string charset = base::ToUpperASCII(charset_input);
  67. // Prefer UTF-16 over all other encodings, because that's what the clipboard
  68. // interface takes as input; then other unicode encodings; then any non-ASCII
  69. // encoding, because most or all such encodings are super-sets of ASCII;
  70. // finally, only use ASCII if nothing else is available.
  71. if (base::StartsWith(charset, kUtfPrefix, base::CompareCase::SENSITIVE)) {
  72. if (charset.find(kEncoding16) != std::string::npos)
  73. return 0;
  74. return 1;
  75. } else if (charset.find(kEncodingASCII) == std::string::npos) {
  76. return 2;
  77. }
  78. return 3;
  79. }
  80. // Map an image MIME type to an integer ranking, lower is better. This is an
  81. // implementation detail of DataSource::GetPreferredMimeTypes and should not be
  82. // considered to have any greater meaning. In particular, these are not expected
  83. // to remain stable over time.
  84. int GetImageTypeRank(const std::string& mime_type) {
  85. // Prefer PNG most of all because this format preserves the alpha channel and
  86. // is lossless, followed by BMP for being lossless and fast to decode (but
  87. // doesn't preserve alpha), followed by everything else.
  88. if (net::MatchesMimeType(std::string(kImagePNG), mime_type) ||
  89. net::MatchesMimeType(std::string(kImageAPNG), mime_type))
  90. return 0;
  91. if (net::MatchesMimeType(std::string(kImageBitmap), mime_type))
  92. return 1;
  93. return 2;
  94. }
  95. std::u16string CodepageToUTF16(const std::vector<uint8_t>& data,
  96. const std::string& charset_input) {
  97. std::u16string output;
  98. base::StringPiece piece(reinterpret_cast<const char*>(data.data()),
  99. data.size());
  100. const char* charset = charset_input.c_str();
  101. // Despite claims in the documentation to the contrary, the ICU UTF-16
  102. // converter does not automatically detect and interpret the byte order
  103. // mark. Therefore, we must do this ourselves.
  104. if (!ucnv_compareNames(charset, kUTF16Unspecified) &&
  105. data.size() >= kByteOrderMarkSize) {
  106. if (static_cast<uint8_t>(piece.data()[0]) == kByteOrderMark[0] &&
  107. static_cast<uint8_t>(piece.data()[1]) == kByteOrderMark[1]) {
  108. // BOM is in big endian format. Consume the BOM so it doesn't get
  109. // interpreted as a character.
  110. piece.remove_prefix(2);
  111. charset = kUTF16BigEndian;
  112. } else if (static_cast<uint8_t>(piece.data()[0]) == kByteOrderMark[1] &&
  113. static_cast<uint8_t>(piece.data()[1]) == kByteOrderMark[0]) {
  114. // BOM is in little endian format. Consume the BOM so it doesn't get
  115. // interpreted as a character.
  116. piece.remove_prefix(2);
  117. charset = kUTF16LittleEndian;
  118. }
  119. }
  120. base::CodepageToUTF16(
  121. piece, charset, base::OnStringConversionError::Type::SUBSTITUTE, &output);
  122. return output;
  123. }
  124. // Returns name parameter in application/octet-stream;name=<...>, or empty
  125. // string if parsing fails.
  126. std::string GetApplicationOctetStreamName(const std::string& mime_type) {
  127. base::StringPairs params;
  128. if (net::MatchesMimeType(std::string(kApplicationOctetStream), mime_type) &&
  129. net::ParseMimeType(mime_type, nullptr, &params)) {
  130. for (const auto& kv : params) {
  131. if (kv.first == "name")
  132. return kv.second;
  133. }
  134. }
  135. return std::string();
  136. }
  137. } // namespace
  138. ScopedDataSource::ScopedDataSource(DataSource* data_source,
  139. DataSourceObserver* observer)
  140. : data_source_(data_source), observer_(observer) {
  141. data_source_->AddObserver(observer_);
  142. }
  143. ScopedDataSource::~ScopedDataSource() {
  144. data_source_->RemoveObserver(observer_);
  145. }
  146. DataSource::DataSource(DataSourceDelegate* delegate)
  147. : delegate_(delegate), finished_(false) {}
  148. DataSource::~DataSource() {
  149. delegate_->OnDataSourceDestroying(this);
  150. for (DataSourceObserver& observer : observers_) {
  151. observer.OnDataSourceDestroying(this);
  152. }
  153. }
  154. void DataSource::AddObserver(DataSourceObserver* observer) {
  155. observers_.AddObserver(observer);
  156. }
  157. void DataSource::RemoveObserver(DataSourceObserver* observer) {
  158. observers_.RemoveObserver(observer);
  159. }
  160. void DataSource::Offer(const std::string& mime_type) {
  161. mime_types_.insert(mime_type);
  162. }
  163. void DataSource::SetActions(const base::flat_set<DndAction>& dnd_actions) {
  164. dnd_actions_ = dnd_actions;
  165. }
  166. void DataSource::Target(const absl::optional<std::string>& mime_type) {
  167. delegate_->OnTarget(mime_type);
  168. }
  169. void DataSource::Action(DndAction action) {
  170. delegate_->OnAction(action);
  171. }
  172. void DataSource::DndDropPerformed() {
  173. delegate_->OnDndDropPerformed();
  174. }
  175. void DataSource::Cancelled() {
  176. finished_ = true;
  177. read_data_weak_ptr_factory_.InvalidateWeakPtrs();
  178. delegate_->OnCancelled();
  179. }
  180. void DataSource::DndFinished() {
  181. finished_ = true;
  182. read_data_weak_ptr_factory_.InvalidateWeakPtrs();
  183. delegate_->OnDndFinished();
  184. }
  185. void DataSource::ReadDataForTesting(const std::string& mime_type,
  186. ReadDataCallback callback) {
  187. ReadData(mime_type, std::move(callback), base::DoNothing());
  188. }
  189. void DataSource::ReadData(const std::string& mime_type,
  190. ReadDataCallback callback,
  191. base::OnceClosure failure_callback) {
  192. // This DataSource does not contain the requested MIME type.
  193. if (mime_type.empty() || !mime_types_.count(mime_type) || finished_) {
  194. std::move(failure_callback).Run();
  195. return;
  196. }
  197. base::ScopedFD read_fd;
  198. base::ScopedFD write_fd;
  199. PCHECK(base::CreatePipe(&read_fd, &write_fd));
  200. delegate_->OnSend(mime_type, std::move(write_fd));
  201. base::ThreadPool::PostTaskAndReplyWithResult(
  202. FROM_HERE,
  203. {base::MayBlock(), base::TaskPriority::USER_BLOCKING,
  204. base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
  205. base::BindOnce(&ReadDataOnWorkerThread, std::move(read_fd)),
  206. base::BindOnce(
  207. &DataSource::OnDataRead, read_data_weak_ptr_factory_.GetWeakPtr(),
  208. std::move(callback), mime_type, std::move(failure_callback)));
  209. }
  210. void DataSource::OnDataRead(ReadDataCallback callback,
  211. const std::string& mime_type,
  212. base::OnceClosure failure_callback,
  213. const absl::optional<std::vector<uint8_t>>& data) {
  214. if (!data) {
  215. std::move(failure_callback).Run();
  216. return;
  217. }
  218. std::move(callback).Run(mime_type, *data);
  219. }
  220. void DataSource::ReadDataTransferEndpoint(
  221. ReadTextDataCallback dte_reader,
  222. base::RepeatingClosure failure_callback) {
  223. ReadData(kDataTransferEndpoint,
  224. base::BindOnce(&DataSource::OnTextRead,
  225. read_data_weak_ptr_factory_.GetWeakPtr(),
  226. std::move(dte_reader)),
  227. failure_callback);
  228. }
  229. void DataSource::GetDataForPreferredMimeTypes(
  230. ReadTextDataCallback text_reader,
  231. ReadDataCallback rtf_reader,
  232. ReadTextDataCallback html_reader,
  233. ReadDataCallback image_reader,
  234. ReadDataCallback filenames_reader,
  235. ReadFileContentsDataCallback file_contents_reader,
  236. ReadDataCallback web_custom_data_reader,
  237. base::RepeatingClosure failure_callback) {
  238. std::string text_mime;
  239. std::string rtf_mime;
  240. std::string html_mime;
  241. std::string image_mime;
  242. std::string filenames_mime;
  243. std::string file_contents_mime;
  244. std::string web_custom_data_mime;
  245. int text_rank = std::numeric_limits<int>::max();
  246. int html_rank = std::numeric_limits<int>::max();
  247. int image_rank = std::numeric_limits<int>::max();
  248. for (auto mime_type : mime_types_) {
  249. if (net::MatchesMimeType(std::string(kTextPlain), mime_type) ||
  250. mime_type == ui::kMimeTypeLinuxUtf8String) {
  251. if (text_reader.is_null())
  252. continue;
  253. std::string charset;
  254. charset = GetCharset(mime_type);
  255. int new_rank = GetCharsetRank(charset);
  256. if (new_rank < text_rank) {
  257. text_mime = mime_type;
  258. text_rank = new_rank;
  259. }
  260. } else if (net::MatchesMimeType(std::string(kTextRTF), mime_type)) {
  261. if (rtf_reader.is_null())
  262. continue;
  263. // The RTF MIME type will never have a character set because it only uses
  264. // 7-bit bytes and stores character set information internally.
  265. rtf_mime = mime_type;
  266. } else if (net::MatchesMimeType(std::string(kTextHTML), mime_type)) {
  267. if (html_reader.is_null())
  268. continue;
  269. auto charset = GetCharset(mime_type);
  270. int new_rank = GetCharsetRank(charset);
  271. if (new_rank < html_rank) {
  272. html_mime = mime_type;
  273. html_rank = new_rank;
  274. }
  275. } else if (blink::IsSupportedImageMimeType(mime_type)) {
  276. if (image_reader.is_null())
  277. continue;
  278. int new_rank = GetImageTypeRank(mime_type);
  279. if (new_rank < image_rank) {
  280. image_mime = mime_type;
  281. image_rank = new_rank;
  282. }
  283. } else if (net::MatchesMimeType(std::string(kTextUriList), mime_type)) {
  284. if (filenames_reader.is_null())
  285. continue;
  286. filenames_mime = mime_type;
  287. } else if (!GetApplicationOctetStreamName(mime_type).empty()) {
  288. file_contents_mime = mime_type;
  289. } else if (net::MatchesMimeType(std::string(kWebCustomData), mime_type)) {
  290. web_custom_data_mime = mime_type;
  291. }
  292. }
  293. ReadData(text_mime,
  294. base::BindOnce(&DataSource::OnTextRead,
  295. read_data_weak_ptr_factory_.GetWeakPtr(),
  296. std::move(text_reader)),
  297. failure_callback);
  298. ReadData(rtf_mime, std::move(rtf_reader), failure_callback);
  299. ReadData(html_mime,
  300. base::BindOnce(&DataSource::OnTextRead,
  301. read_data_weak_ptr_factory_.GetWeakPtr(),
  302. std::move(html_reader)),
  303. failure_callback);
  304. ReadData(image_mime, std::move(image_reader), failure_callback);
  305. ReadData(filenames_mime, std::move(filenames_reader), failure_callback);
  306. ReadData(file_contents_mime,
  307. base::BindOnce(&DataSource::OnFileContentsRead,
  308. read_data_weak_ptr_factory_.GetWeakPtr(),
  309. std::move(file_contents_reader)),
  310. failure_callback);
  311. ReadData(web_custom_data_mime, std::move(web_custom_data_reader),
  312. failure_callback);
  313. }
  314. void DataSource::OnTextRead(ReadTextDataCallback callback,
  315. const std::string& mime_type,
  316. const std::vector<uint8_t>& data) {
  317. std::u16string output = CodepageToUTF16(data, GetCharset(mime_type));
  318. std::move(callback).Run(mime_type, std::move(output));
  319. }
  320. void DataSource::OnFileContentsRead(ReadFileContentsDataCallback callback,
  321. const std::string& mime_type,
  322. const std::vector<uint8_t>& data) {
  323. const base::FilePath filename(GetApplicationOctetStreamName(mime_type));
  324. std::move(callback).Run(mime_type, filename, data);
  325. }
  326. bool DataSource::CanBeDataSourceForCopy(Surface* surface) const {
  327. return delegate_->CanAcceptDataEventsForSurface(surface);
  328. }
  329. } // namespace exo