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 28 BookmarkNodeData::Element::Element() : is_url(false), id_(0) { 29 } 30 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 40 BookmarkNodeData::Element::~Element() { 41 } 42 43 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 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 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 97 BookmarkNodeData::BookmarkNodeData() { 98 } 99 100 BookmarkNodeData::BookmarkNodeData(const BookmarkNode* node) { 101 elements.push_back(Element(node)); 102 } 103 104 BookmarkNodeData::BookmarkNodeData( 105 const std::vector<const BookmarkNode*>& nodes) { 106 ReadFromVector(nodes); 107 } 108 109 BookmarkNodeData::~BookmarkNodeData() { 110 } 111 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 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) 142 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 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 195 bool BookmarkNodeData::ClipboardContainsBookmarks() { 196 return g_browser_process->clipboard()->IsFormatAvailableByString( 197 BookmarkNodeData::kClipboardFormatString, ui::Clipboard::BUFFER_STANDARD); 198 } 199 #else 200 void BookmarkNodeData::WriteToClipboard(Profile* profile) const { 201 bookmark_pasteboard_helper_mac::WriteToClipboard(elements, profile_path_); 202 } 203 204 bool BookmarkNodeData::ReadFromClipboard() { 205 return bookmark_pasteboard_helper_mac::ReadFromClipboard(elements, 206 &profile_path_); 207 } 208 209 bool BookmarkNodeData::ReadFromDragClipboard() { 210 return bookmark_pasteboard_helper_mac::ReadFromDragClipboard(elements, 211 &profile_path_); 212 } 213 214 bool BookmarkNodeData::ClipboardContainsBookmarks() { 215 return bookmark_pasteboard_helper_mac::ClipboardContainsBookmarks(); 216 } 217 #endif // !defined(OS_MACOSX) 218 219 #if defined(TOOLKIT_VIEWS) 220 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 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 263 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 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 291 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 310 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 315 void BookmarkNodeData::Clear() { 316 profile_path_.clear(); 317 elements.clear(); 318 } 319 320 void BookmarkNodeData::SetOriginatingProfile(Profile* profile) { 321 DCHECK(profile_path_.empty()); 322 323 if (profile) 324 profile_path_ = profile->GetPath().value(); 325 } 326 327 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