bookmark_node_data.cc 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. // Copyright 2014 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/bookmarks/browser/bookmark_node_data.h"
  5. #include <algorithm>
  6. #include <string>
  7. #include "base/logging.h"
  8. #include "base/numerics/safe_conversions.h"
  9. #include "base/pickle.h"
  10. #include "base/strings/utf_string_conversions.h"
  11. #include "build/build_config.h"
  12. #include "components/bookmarks/browser/bookmark_utils.h"
  13. #include "ui/base/clipboard/scoped_clipboard_writer.h"
  14. namespace bookmarks {
  15. #if !BUILDFLAG(IS_APPLE)
  16. namespace {
  17. constexpr size_t kMaxVectorPreallocateSize = 10000;
  18. } // namespace
  19. const char BookmarkNodeData::kClipboardFormatString[] =
  20. "chromium/x-bookmark-entries";
  21. #endif
  22. BookmarkNodeData::Element::Element() : is_url(false), id_(0) {
  23. }
  24. BookmarkNodeData::Element::Element(const BookmarkNode* node)
  25. : is_url(node->is_url()),
  26. url(node->url()),
  27. title(node->GetTitle()),
  28. date_added(node->date_added()),
  29. date_folder_modified(node->date_folder_modified()),
  30. id_(node->id()) {
  31. if (node->GetMetaInfoMap())
  32. meta_info_map = *node->GetMetaInfoMap();
  33. for (const auto& child : node->children())
  34. children.push_back(Element(child.get()));
  35. }
  36. BookmarkNodeData::Element::Element(const Element& other) = default;
  37. BookmarkNodeData::Element::~Element() {
  38. }
  39. #if !BUILDFLAG(IS_APPLE)
  40. void BookmarkNodeData::Element::WriteToPickle(base::Pickle* pickle) const {
  41. pickle->WriteBool(is_url);
  42. pickle->WriteString(url.spec());
  43. pickle->WriteString16(title);
  44. pickle->WriteInt64(id_);
  45. pickle->WriteUInt32(static_cast<uint32_t>(meta_info_map.size()));
  46. for (auto it = meta_info_map.begin(); it != meta_info_map.end(); ++it) {
  47. pickle->WriteString(it->first);
  48. pickle->WriteString(it->second);
  49. }
  50. if (!is_url) {
  51. pickle->WriteUInt32(static_cast<uint32_t>(children.size()));
  52. for (auto i = children.begin(); i != children.end(); ++i) {
  53. i->WriteToPickle(pickle);
  54. }
  55. }
  56. }
  57. bool BookmarkNodeData::Element::ReadFromPickle(base::PickleIterator* iterator) {
  58. std::string url_spec;
  59. if (!iterator->ReadBool(&is_url) ||
  60. !iterator->ReadString(&url_spec) ||
  61. !iterator->ReadString16(&title) ||
  62. !iterator->ReadInt64(&id_)) {
  63. return false;
  64. }
  65. url = GURL(url_spec);
  66. date_added = base::Time();
  67. date_folder_modified = base::Time();
  68. meta_info_map.clear();
  69. uint32_t meta_field_count;
  70. if (!iterator->ReadUInt32(&meta_field_count))
  71. return false;
  72. for (size_t i = 0; i < meta_field_count; ++i) {
  73. std::string key;
  74. std::string value;
  75. if (!iterator->ReadString(&key) ||
  76. !iterator->ReadString(&value)) {
  77. return false;
  78. }
  79. meta_info_map[key] = value;
  80. }
  81. children.clear();
  82. if (!is_url) {
  83. uint32_t children_count_tmp;
  84. if (!iterator->ReadUInt32(&children_count_tmp))
  85. return false;
  86. if (!base::IsValueInRangeForNumericType<size_t>(children_count_tmp)) {
  87. LOG(WARNING) << "children_count failed bounds check";
  88. return false;
  89. }
  90. // Note: do not preallocate the children vector. A pickle could be
  91. // constructed to contain N nested Elements. By continually recursing on
  92. // this ReadFromPickle function, the fast-fail logic is subverted. Each
  93. // child would claim it contains M more children. The first (and only) child
  94. // provided would claim M more children. We would allocate N * M Elements
  95. // along the way, while only receiving N Elements.
  96. const size_t children_count =
  97. base::checked_cast<size_t>(children_count_tmp);
  98. for (size_t i = 0; i < children_count; ++i) {
  99. children.emplace_back();
  100. if (!children.back().ReadFromPickle(iterator))
  101. return false;
  102. }
  103. }
  104. return true;
  105. }
  106. #endif
  107. // BookmarkNodeData -----------------------------------------------------------
  108. BookmarkNodeData::BookmarkNodeData() {
  109. }
  110. BookmarkNodeData::BookmarkNodeData(const BookmarkNodeData& other) = default;
  111. BookmarkNodeData::BookmarkNodeData(const BookmarkNode* node) {
  112. elements.push_back(Element(node));
  113. }
  114. BookmarkNodeData::BookmarkNodeData(
  115. const std::vector<const BookmarkNode*>& nodes) {
  116. ReadFromVector(nodes);
  117. }
  118. BookmarkNodeData::~BookmarkNodeData() {
  119. }
  120. #if !BUILDFLAG(IS_APPLE)
  121. // static
  122. bool BookmarkNodeData::ClipboardContainsBookmarks() {
  123. ui::DataTransferEndpoint data_dst = ui::DataTransferEndpoint(
  124. ui::EndpointType::kDefault, /*notify_if_restricted=*/false);
  125. return ui::Clipboard::GetForCurrentThread()->IsFormatAvailable(
  126. ui::ClipboardFormatType::GetType(kClipboardFormatString),
  127. ui::ClipboardBuffer::kCopyPaste, &data_dst);
  128. }
  129. #endif
  130. bool BookmarkNodeData::ReadFromVector(
  131. const std::vector<const BookmarkNode*>& nodes) {
  132. Clear();
  133. if (nodes.empty())
  134. return false;
  135. for (size_t i = 0; i < nodes.size(); ++i)
  136. elements.push_back(Element(nodes[i]));
  137. return true;
  138. }
  139. bool BookmarkNodeData::ReadFromTuple(const GURL& url,
  140. const std::u16string& title) {
  141. Clear();
  142. if (!url.is_valid())
  143. return false;
  144. Element element;
  145. element.title = title;
  146. element.url = url;
  147. element.is_url = true;
  148. elements.push_back(element);
  149. return true;
  150. }
  151. #if !BUILDFLAG(IS_APPLE)
  152. void BookmarkNodeData::WriteToClipboard() {
  153. ui::ScopedClipboardWriter scw(ui::ClipboardBuffer::kCopyPaste);
  154. #if BUILDFLAG(IS_WIN)
  155. const std::u16string kEOL(u"\r\n");
  156. #else
  157. const std::u16string kEOL = u"\n";
  158. #endif
  159. // If there is only one element and it is a URL, write the URL to the
  160. // clipboard.
  161. if (has_single_url()) {
  162. const std::u16string& title = elements[0].title;
  163. const std::string url = elements[0].url.spec();
  164. scw.WriteBookmark(title, url);
  165. scw.WriteHyperlink(title, url);
  166. scw.WriteText(base::UTF8ToUTF16(url));
  167. } else {
  168. // We have either more than one URL, a folder, or a combination of URLs
  169. // and folders.
  170. std::u16string text;
  171. for (size_t i = 0; i < size(); i++) {
  172. text += i == 0 ? u"" : kEOL;
  173. if (!elements[i].is_url) {
  174. // Then it's a folder. Only copy the name of the folder.
  175. const std::u16string title = elements[i].title;
  176. text += title;
  177. } else {
  178. const std::u16string url = base::UTF8ToUTF16(elements[i].url.spec());
  179. text += url;
  180. }
  181. }
  182. scw.WriteText(text);
  183. }
  184. base::Pickle pickle;
  185. WriteToPickle(base::FilePath(), &pickle);
  186. scw.WritePickledData(
  187. pickle, ui::ClipboardFormatType::GetType(kClipboardFormatString));
  188. }
  189. bool BookmarkNodeData::ReadFromClipboard(ui::ClipboardBuffer buffer) {
  190. DCHECK_EQ(buffer, ui::ClipboardBuffer::kCopyPaste);
  191. std::string data;
  192. ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread();
  193. clipboard->ReadData(ui::ClipboardFormatType::GetType(kClipboardFormatString),
  194. /* data_dst = */ nullptr, &data);
  195. if (!data.empty()) {
  196. base::Pickle pickle(data.data(), static_cast<int>(data.size()));
  197. if (ReadFromPickle(&pickle))
  198. return true;
  199. }
  200. std::u16string title;
  201. std::string url;
  202. clipboard->ReadBookmark(/* data_dst = */ nullptr, &title, &url);
  203. if (!url.empty()) {
  204. Element element;
  205. element.is_url = true;
  206. element.url = GURL(url);
  207. element.title = title;
  208. elements.clear();
  209. elements.push_back(element);
  210. return true;
  211. }
  212. return false;
  213. }
  214. void BookmarkNodeData::WriteToPickle(const base::FilePath& profile_path,
  215. base::Pickle* pickle) const {
  216. profile_path.WriteToPickle(pickle);
  217. pickle->WriteUInt32(static_cast<uint32_t>(size()));
  218. for (size_t i = 0; i < size(); ++i)
  219. elements[i].WriteToPickle(pickle);
  220. }
  221. bool BookmarkNodeData::ReadFromPickle(base::Pickle* pickle) {
  222. base::PickleIterator data_iterator(*pickle);
  223. uint32_t element_count_tmp;
  224. if (profile_path_.ReadFromPickle(&data_iterator) &&
  225. data_iterator.ReadUInt32(&element_count_tmp)) {
  226. if (!base::IsValueInRangeForNumericType<size_t>(element_count_tmp)) {
  227. LOG(WARNING) << "element_count failed bounds check";
  228. return false;
  229. }
  230. const size_t element_count = base::checked_cast<size_t>(element_count_tmp);
  231. // Restrict vector preallocation to prevent OOM crashes on invalid or
  232. // malicious pickles.
  233. if (element_count > kMaxVectorPreallocateSize)
  234. LOG(WARNING) << "element_count exceeds kMaxVectorPreallocateSize";
  235. std::vector<Element> tmp_elements;
  236. tmp_elements.reserve(std::min(element_count, kMaxVectorPreallocateSize));
  237. for (size_t i = 0; i < element_count; ++i) {
  238. tmp_elements.emplace_back();
  239. if (!tmp_elements.back().ReadFromPickle(&data_iterator)) {
  240. return false;
  241. }
  242. }
  243. elements.swap(tmp_elements);
  244. }
  245. return true;
  246. }
  247. #endif // BUILDFLAG(IS_APPLE)
  248. std::vector<const BookmarkNode*> BookmarkNodeData::GetNodes(
  249. BookmarkModel* model,
  250. const base::FilePath& profile_path) const {
  251. std::vector<const BookmarkNode*> nodes;
  252. if (!IsFromProfilePath(profile_path))
  253. return nodes;
  254. for (size_t i = 0; i < size(); ++i) {
  255. const BookmarkNode* node = GetBookmarkNodeByID(model, elements[i].id_);
  256. if (!node) {
  257. nodes.clear();
  258. return nodes;
  259. }
  260. nodes.push_back(node);
  261. }
  262. return nodes;
  263. }
  264. const BookmarkNode* BookmarkNodeData::GetFirstNode(
  265. BookmarkModel* model,
  266. const base::FilePath& profile_path) const {
  267. std::vector<const BookmarkNode*> nodes = GetNodes(model, profile_path);
  268. return nodes.size() == 1 ? nodes[0] : nullptr;
  269. }
  270. void BookmarkNodeData::Clear() {
  271. profile_path_.clear();
  272. elements.clear();
  273. }
  274. void BookmarkNodeData::SetOriginatingProfilePath(
  275. const base::FilePath& profile_path) {
  276. DCHECK(profile_path_.empty());
  277. profile_path_ = profile_path;
  278. }
  279. bool BookmarkNodeData::IsFromProfilePath(
  280. const base::FilePath& profile_path) const {
  281. // An empty path means the data is not associated with any profile.
  282. return !profile_path_.empty() && profile_path_ == profile_path;
  283. }
  284. } // namespace bookmarks