1 // Copyright (c) 2011 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 "chrome/browser/bookmarks/bookmark_node_data.h"
6
7 #include <string>
8
9 #include "base/basictypes.h"
10 #include "base/pickle.h"
11 #include "base/string_util.h"
12 #include "base/utf_string_conversions.h"
13 #include "chrome/browser/bookmarks/bookmark_model.h"
14 #include "chrome/browser/profiles/profile.h"
15 #include "chrome/common/url_constants.h"
16 #include "net/base/escape.h"
17 #include "ui/base/clipboard/scoped_clipboard_writer.h"
18
19 #if defined(OS_MACOSX)
20 #include "chrome/browser/bookmarks/bookmark_pasteboard_helper_mac.h"
21 #else
22 #include "chrome/browser/browser_process.h"
23 #endif
24
25 const char* BookmarkNodeData::kClipboardFormatString =
26 "chromium/x-bookmark-entries";
27
Element()28 BookmarkNodeData::Element::Element() : is_url(false), id_(0) {
29 }
30
Element(const BookmarkNode * node)31 BookmarkNodeData::Element::Element(const BookmarkNode* node)
32 : is_url(node->is_url()),
33 url(node->GetURL()),
34 title(node->GetTitle()),
35 id_(node->id()) {
36 for (int i = 0; i < node->child_count(); ++i)
37 children.push_back(Element(node->GetChild(i)));
38 }
39
~Element()40 BookmarkNodeData::Element::~Element() {
41 }
42
WriteToPickle(Pickle * pickle) const43 void BookmarkNodeData::Element::WriteToPickle(Pickle* pickle) const {
44 pickle->WriteBool(is_url);
45 pickle->WriteString(url.spec());
46 pickle->WriteString16(title);
47 pickle->WriteInt64(id_);
48 if (!is_url) {
49 pickle->WriteSize(children.size());
50 for (std::vector<Element>::const_iterator i = children.begin();
51 i != children.end(); ++i) {
52 i->WriteToPickle(pickle);
53 }
54 }
55 }
56
ReadFromPickle(Pickle * pickle,void ** iterator)57 bool BookmarkNodeData::Element::ReadFromPickle(Pickle* pickle,
58 void** iterator) {
59 std::string url_spec;
60 if (!pickle->ReadBool(iterator, &is_url) ||
61 !pickle->ReadString(iterator, &url_spec) ||
62 !pickle->ReadString16(iterator, &title) ||
63 !pickle->ReadInt64(iterator, &id_)) {
64 return false;
65 }
66 url = GURL(url_spec);
67 children.clear();
68 if (!is_url) {
69 size_t children_count;
70 if (!pickle->ReadSize(iterator, &children_count))
71 return false;
72 children.resize(children_count);
73 for (std::vector<Element>::iterator i = children.begin();
74 i != children.end(); ++i) {
75 if (!i->ReadFromPickle(pickle, iterator))
76 return false;
77 }
78 }
79 return true;
80 }
81
82 #if defined(TOOLKIT_VIEWS)
83 // static
GetBookmarkCustomFormat()84 ui::OSExchangeData::CustomFormat BookmarkNodeData::GetBookmarkCustomFormat() {
85 static ui::OSExchangeData::CustomFormat format;
86 static bool format_valid = false;
87
88 if (!format_valid) {
89 format_valid = true;
90 format = ui::OSExchangeData::RegisterCustomFormat(
91 BookmarkNodeData::kClipboardFormatString);
92 }
93 return format;
94 }
95 #endif
96
BookmarkNodeData()97 BookmarkNodeData::BookmarkNodeData() {
98 }
99
BookmarkNodeData(const BookmarkNode * node)100 BookmarkNodeData::BookmarkNodeData(const BookmarkNode* node) {
101 elements.push_back(Element(node));
102 }
103
BookmarkNodeData(const std::vector<const BookmarkNode * > & nodes)104 BookmarkNodeData::BookmarkNodeData(
105 const std::vector<const BookmarkNode*>& nodes) {
106 ReadFromVector(nodes);
107 }
108
~BookmarkNodeData()109 BookmarkNodeData::~BookmarkNodeData() {
110 }
111
ReadFromVector(const std::vector<const BookmarkNode * > & nodes)112 bool BookmarkNodeData::ReadFromVector(
113 const std::vector<const BookmarkNode*>& nodes) {
114 Clear();
115
116 if (nodes.empty())
117 return false;
118
119 for (size_t i = 0; i < nodes.size(); ++i)
120 elements.push_back(Element(nodes[i]));
121
122 return true;
123 }
124
ReadFromTuple(const GURL & url,const string16 & title)125 bool BookmarkNodeData::ReadFromTuple(const GURL& url, const string16& title) {
126 Clear();
127
128 if (!url.is_valid())
129 return false;
130
131 Element element;
132 element.title = title;
133 element.url = url;
134 element.is_url = true;
135
136 elements.push_back(element);
137
138 return true;
139 }
140
141 #if !defined(OS_MACOSX)
WriteToClipboard(Profile * profile) const142 void BookmarkNodeData::WriteToClipboard(Profile* profile) const {
143 ui::ScopedClipboardWriter scw(g_browser_process->clipboard());
144
145 // If there is only one element and it is a URL, write the URL to the
146 // clipboard.
147 if (elements.size() == 1 && elements[0].is_url) {
148 const string16& title = elements[0].title;
149 const std::string url = elements[0].url.spec();
150
151 scw.WriteBookmark(title, url);
152 scw.WriteHyperlink(EscapeForHTML(title), url);
153
154 // Also write the URL to the clipboard as text so that it can be pasted
155 // into text fields. We use WriteText instead of WriteURL because we don't
156 // want to clobber the X clipboard when the user copies out of the omnibox
157 // on Linux (on Windows and Mac, there is no difference between these
158 // functions).
159 scw.WriteText(UTF8ToUTF16(url));
160 }
161
162 Pickle pickle;
163 WriteToPickle(profile, &pickle);
164 scw.WritePickledData(pickle, kClipboardFormatString);
165 }
166
ReadFromClipboard()167 bool BookmarkNodeData::ReadFromClipboard() {
168 std::string data;
169 ui::Clipboard* clipboard = g_browser_process->clipboard();
170 clipboard->ReadData(kClipboardFormatString, &data);
171
172 if (!data.empty()) {
173 Pickle pickle(data.data(), data.size());
174 if (ReadFromPickle(&pickle))
175 return true;
176 }
177
178 string16 title;
179 std::string url;
180 clipboard->ReadBookmark(&title, &url);
181 if (!url.empty()) {
182 Element element;
183 element.is_url = true;
184 element.url = GURL(url);
185 element.title = title;
186
187 elements.clear();
188 elements.push_back(element);
189 return true;
190 }
191
192 return false;
193 }
194
ClipboardContainsBookmarks()195 bool BookmarkNodeData::ClipboardContainsBookmarks() {
196 return g_browser_process->clipboard()->IsFormatAvailableByString(
197 BookmarkNodeData::kClipboardFormatString, ui::Clipboard::BUFFER_STANDARD);
198 }
199 #else
WriteToClipboard(Profile * profile) const200 void BookmarkNodeData::WriteToClipboard(Profile* profile) const {
201 bookmark_pasteboard_helper_mac::WriteToClipboard(elements, profile_path_);
202 }
203
ReadFromClipboard()204 bool BookmarkNodeData::ReadFromClipboard() {
205 return bookmark_pasteboard_helper_mac::ReadFromClipboard(elements,
206 &profile_path_);
207 }
208
ReadFromDragClipboard()209 bool BookmarkNodeData::ReadFromDragClipboard() {
210 return bookmark_pasteboard_helper_mac::ReadFromDragClipboard(elements,
211 &profile_path_);
212 }
213
ClipboardContainsBookmarks()214 bool BookmarkNodeData::ClipboardContainsBookmarks() {
215 return bookmark_pasteboard_helper_mac::ClipboardContainsBookmarks();
216 }
217 #endif // !defined(OS_MACOSX)
218
219 #if defined(TOOLKIT_VIEWS)
Write(Profile * profile,ui::OSExchangeData * data) const220 void BookmarkNodeData::Write(Profile* profile, ui::OSExchangeData* data) const {
221 DCHECK(data);
222
223 // If there is only one element and it is a URL, write the URL to the
224 // clipboard.
225 if (elements.size() == 1 && elements[0].is_url) {
226 if (elements[0].url.SchemeIs(chrome::kJavaScriptScheme)) {
227 data->SetString(UTF8ToUTF16(elements[0].url.spec()));
228 } else {
229 data->SetURL(elements[0].url, elements[0].title);
230 }
231 }
232
233 Pickle data_pickle;
234 WriteToPickle(profile, &data_pickle);
235
236 data->SetPickledData(GetBookmarkCustomFormat(), data_pickle);
237 }
238
Read(const ui::OSExchangeData & data)239 bool BookmarkNodeData::Read(const ui::OSExchangeData& data) {
240 elements.clear();
241
242 profile_path_.clear();
243
244 if (data.HasCustomFormat(GetBookmarkCustomFormat())) {
245 Pickle drag_data_pickle;
246 if (data.GetPickledData(GetBookmarkCustomFormat(), &drag_data_pickle)) {
247 if (!ReadFromPickle(&drag_data_pickle))
248 return false;
249 }
250 } else {
251 // See if there is a URL on the clipboard.
252 Element element;
253 GURL url;
254 string16 title;
255 if (data.GetURLAndTitle(&url, &title))
256 ReadFromTuple(url, title);
257 }
258
259 return is_valid();
260 }
261 #endif
262
WriteToPickle(Profile * profile,Pickle * pickle) const263 void BookmarkNodeData::WriteToPickle(Profile* profile, Pickle* pickle) const {
264 FilePath path = profile ? profile->GetPath() : FilePath();
265 FilePath::WriteStringTypeToPickle(pickle, path.value());
266 pickle->WriteSize(elements.size());
267
268 for (size_t i = 0; i < elements.size(); ++i)
269 elements[i].WriteToPickle(pickle);
270 }
271
ReadFromPickle(Pickle * pickle)272 bool BookmarkNodeData::ReadFromPickle(Pickle* pickle) {
273 void* data_iterator = NULL;
274 size_t element_count;
275 if (FilePath::ReadStringTypeFromPickle(pickle, &data_iterator,
276 &profile_path_) &&
277 pickle->ReadSize(&data_iterator, &element_count)) {
278 std::vector<Element> tmp_elements;
279 tmp_elements.resize(element_count);
280 for (size_t i = 0; i < element_count; ++i) {
281 if (!tmp_elements[i].ReadFromPickle(pickle, &data_iterator)) {
282 return false;
283 }
284 }
285 elements.swap(tmp_elements);
286 }
287
288 return true;
289 }
290
GetNodes(Profile * profile) const291 std::vector<const BookmarkNode*> BookmarkNodeData::GetNodes(
292 Profile* profile) const {
293 std::vector<const BookmarkNode*> nodes;
294
295 if (!IsFromProfile(profile))
296 return nodes;
297
298 for (size_t i = 0; i < elements.size(); ++i) {
299 const BookmarkNode* node =
300 profile->GetBookmarkModel()->GetNodeByID(elements[i].id_);
301 if (!node) {
302 nodes.clear();
303 return nodes;
304 }
305 nodes.push_back(node);
306 }
307 return nodes;
308 }
309
GetFirstNode(Profile * profile) const310 const BookmarkNode* BookmarkNodeData::GetFirstNode(Profile* profile) const {
311 std::vector<const BookmarkNode*> nodes = GetNodes(profile);
312 return nodes.size() == 1 ? nodes[0] : NULL;
313 }
314
Clear()315 void BookmarkNodeData::Clear() {
316 profile_path_.clear();
317 elements.clear();
318 }
319
SetOriginatingProfile(Profile * profile)320 void BookmarkNodeData::SetOriginatingProfile(Profile* profile) {
321 DCHECK(profile_path_.empty());
322
323 if (profile)
324 profile_path_ = profile->GetPath().value();
325 }
326
IsFromProfile(Profile * profile) const327 bool BookmarkNodeData::IsFromProfile(Profile* profile) const {
328 // An empty path means the data is not associated with any profile.
329 return !profile_path_.empty() && profile_path_ == profile->GetPath().value();
330 }
331