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
5#include "components/bookmarks/browser/bookmark_node_data.h"
6
7#include <string>
8
9#include "base/basictypes.h"
10#include "base/pickle.h"
11#include "base/strings/string_util.h"
12#include "base/strings/utf_string_conversions.h"
13#include "components/bookmarks/browser/bookmark_utils.h"
14#include "ui/base/clipboard/scoped_clipboard_writer.h"
15
16namespace bookmarks {
17
18const char* BookmarkNodeData::kClipboardFormatString =
19    "chromium/x-bookmark-entries";
20
21BookmarkNodeData::Element::Element() : is_url(false), id_(0) {
22}
23
24BookmarkNodeData::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 (int i = 0; i < node->child_count(); ++i)
34    children.push_back(Element(node->GetChild(i)));
35}
36
37BookmarkNodeData::Element::~Element() {
38}
39
40void BookmarkNodeData::Element::WriteToPickle(Pickle* pickle) const {
41  pickle->WriteBool(is_url);
42  pickle->WriteString(url.spec());
43  pickle->WriteString16(title);
44  pickle->WriteInt64(id_);
45  pickle->WriteUInt64(meta_info_map.size());
46  for (BookmarkNode::MetaInfoMap::const_iterator it = meta_info_map.begin();
47      it != meta_info_map.end(); ++it) {
48    pickle->WriteString(it->first);
49    pickle->WriteString(it->second);
50  }
51  if (!is_url) {
52    pickle->WriteUInt64(children.size());
53    for (std::vector<Element>::const_iterator i = children.begin();
54         i != children.end(); ++i) {
55      i->WriteToPickle(pickle);
56    }
57  }
58}
59
60bool BookmarkNodeData::Element::ReadFromPickle(Pickle* pickle,
61                                               PickleIterator* iterator) {
62  std::string url_spec;
63  if (!pickle->ReadBool(iterator, &is_url) ||
64      !pickle->ReadString(iterator, &url_spec) ||
65      !pickle->ReadString16(iterator, &title) ||
66      !pickle->ReadInt64(iterator, &id_)) {
67    return false;
68  }
69  url = GURL(url_spec);
70  date_added = base::Time();
71  date_folder_modified = base::Time();
72  meta_info_map.clear();
73  uint64 meta_field_count;
74  if (!pickle->ReadUInt64(iterator, &meta_field_count))
75    return false;
76  for (uint64 i = 0; i < meta_field_count; ++i) {
77    std::string key;
78    std::string value;
79    if (!pickle->ReadString(iterator, &key) ||
80        !pickle->ReadString(iterator, &value)) {
81      return false;
82    }
83    meta_info_map[key] = value;
84  }
85  children.clear();
86  if (!is_url) {
87    uint64 children_count;
88    if (!pickle->ReadUInt64(iterator, &children_count))
89      return false;
90    children.reserve(children_count);
91    for (uint64 i = 0; i < children_count; ++i) {
92      children.push_back(Element());
93      if (!children.back().ReadFromPickle(pickle, iterator))
94        return false;
95    }
96  }
97  return true;
98}
99
100// BookmarkNodeData -----------------------------------------------------------
101
102BookmarkNodeData::BookmarkNodeData() {
103}
104
105BookmarkNodeData::BookmarkNodeData(const BookmarkNode* node) {
106  elements.push_back(Element(node));
107}
108
109BookmarkNodeData::BookmarkNodeData(
110    const std::vector<const BookmarkNode*>& nodes) {
111  ReadFromVector(nodes);
112}
113
114BookmarkNodeData::~BookmarkNodeData() {
115}
116
117#if !defined(OS_MACOSX)
118// static
119bool BookmarkNodeData::ClipboardContainsBookmarks() {
120  return ui::Clipboard::GetForCurrentThread()->IsFormatAvailable(
121      ui::Clipboard::GetFormatType(kClipboardFormatString),
122      ui::CLIPBOARD_TYPE_COPY_PASTE);
123}
124#endif
125
126bool BookmarkNodeData::ReadFromVector(
127    const std::vector<const BookmarkNode*>& nodes) {
128  Clear();
129
130  if (nodes.empty())
131    return false;
132
133  for (size_t i = 0; i < nodes.size(); ++i)
134    elements.push_back(Element(nodes[i]));
135
136  return true;
137}
138
139bool BookmarkNodeData::ReadFromTuple(const GURL& url,
140                                     const base::string16& title) {
141  Clear();
142
143  if (!url.is_valid())
144    return false;
145
146  Element element;
147  element.title = title;
148  element.url = url;
149  element.is_url = true;
150
151  elements.push_back(element);
152
153  return true;
154}
155
156#if !defined(OS_MACOSX)
157void BookmarkNodeData::WriteToClipboard(ui::ClipboardType clipboard_type) {
158  DCHECK(clipboard_type == ui::CLIPBOARD_TYPE_COPY_PASTE ||
159         clipboard_type == ui::CLIPBOARD_TYPE_SELECTION);
160  ui::ScopedClipboardWriter scw(clipboard_type);
161
162  // If there is only one element and it is a URL, write the URL to the
163  // clipboard.
164  if (elements.size() == 1 && elements[0].is_url) {
165    const base::string16& title = elements[0].title;
166    const std::string url = elements[0].url.spec();
167
168    scw.WriteBookmark(title, url);
169
170    // Don't call scw.WriteHyperlink() here, since some rich text editors will
171    // change fonts when such data is pasted in; besides, most such editors
172    // auto-linkify at some point anyway.
173
174    // Also write the URL to the clipboard as text so that it can be pasted
175    // into text fields. We use WriteText instead of WriteURL because we don't
176    // want to clobber the X clipboard when the user copies out of the omnibox
177    // on Linux (on Windows and Mac, there is no difference between these
178    // functions).
179    scw.WriteText(base::UTF8ToUTF16(url));
180  }
181
182  Pickle pickle;
183  WriteToPickle(base::FilePath(), &pickle);
184  scw.WritePickledData(pickle,
185                       ui::Clipboard::GetFormatType(kClipboardFormatString));
186}
187
188bool BookmarkNodeData::ReadFromClipboard(ui::ClipboardType type) {
189  DCHECK_EQ(type, ui::CLIPBOARD_TYPE_COPY_PASTE);
190  std::string data;
191  ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread();
192  clipboard->ReadData(ui::Clipboard::GetFormatType(kClipboardFormatString),
193                      &data);
194
195  if (!data.empty()) {
196    Pickle pickle(data.data(), static_cast<int>(data.size()));
197    if (ReadFromPickle(&pickle))
198      return true;
199  }
200
201  base::string16 title;
202  std::string url;
203  clipboard->ReadBookmark(&title, &url);
204  if (!url.empty()) {
205    Element element;
206    element.is_url = true;
207    element.url = GURL(url);
208    element.title = title;
209
210    elements.clear();
211    elements.push_back(element);
212    return true;
213  }
214
215  return false;
216}
217#endif
218
219void BookmarkNodeData::WriteToPickle(const base::FilePath& profile_path,
220                                     Pickle* pickle) const {
221  profile_path.WriteToPickle(pickle);
222  pickle->WriteUInt64(elements.size());
223
224  for (size_t i = 0; i < elements.size(); ++i)
225    elements[i].WriteToPickle(pickle);
226}
227
228bool BookmarkNodeData::ReadFromPickle(Pickle* pickle) {
229  PickleIterator data_iterator(*pickle);
230  uint64 element_count;
231  if (profile_path_.ReadFromPickle(&data_iterator) &&
232      pickle->ReadUInt64(&data_iterator, &element_count)) {
233    std::vector<Element> tmp_elements;
234    tmp_elements.resize(element_count);
235    for (uint64 i = 0; i < element_count; ++i) {
236      if (!tmp_elements[i].ReadFromPickle(pickle, &data_iterator)) {
237        return false;
238      }
239    }
240    elements.swap(tmp_elements);
241  }
242
243  return true;
244}
245
246std::vector<const BookmarkNode*> BookmarkNodeData::GetNodes(
247    BookmarkModel* model,
248    const base::FilePath& profile_path) const {
249  std::vector<const BookmarkNode*> nodes;
250
251  if (!IsFromProfilePath(profile_path))
252    return nodes;
253
254  for (size_t i = 0; i < elements.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
265const BookmarkNode* BookmarkNodeData::GetFirstNode(
266    BookmarkModel* model,
267    const base::FilePath& profile_path) const {
268  std::vector<const BookmarkNode*> nodes = GetNodes(model, profile_path);
269  return nodes.size() == 1 ? nodes[0] : NULL;
270}
271
272void BookmarkNodeData::Clear() {
273  profile_path_.clear();
274  elements.clear();
275}
276
277void BookmarkNodeData::SetOriginatingProfilePath(
278    const base::FilePath& profile_path) {
279  DCHECK(profile_path_.empty());
280  profile_path_ = profile_path;
281}
282
283bool BookmarkNodeData::IsFromProfilePath(
284    const base::FilePath& profile_path) const {
285  // An empty path means the data is not associated with any profile.
286  return !profile_path_.empty() && profile_path_ == profile_path;
287}
288
289}  // namespace bookmarks
290