1 // Copyright (c) 2012 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/strings/string_util.h" 12 #include "base/strings/utf_string_conversions.h" 13 #include "chrome/browser/bookmarks/bookmark_model.h" 14 #include "chrome/browser/bookmarks/bookmark_model_factory.h" 15 #include "chrome/browser/profiles/profile.h" 16 #include "ui/base/clipboard/scoped_clipboard_writer.h" 17 18 const char* BookmarkNodeData::kClipboardFormatString = 19 "chromium/x-bookmark-entries"; 20 21 BookmarkNodeData::Element::Element() : is_url(false), id_(0) { 22 } 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 (int i = 0; i < node->child_count(); ++i) 34 children.push_back(Element(node->GetChild(i))); 35 } 36 37 BookmarkNodeData::Element::~Element() { 38 } 39 40 void 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 60 bool 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 102 BookmarkNodeData::BookmarkNodeData() { 103 } 104 105 BookmarkNodeData::BookmarkNodeData(const BookmarkNode* node) { 106 elements.push_back(Element(node)); 107 } 108 109 BookmarkNodeData::BookmarkNodeData( 110 const std::vector<const BookmarkNode*>& nodes) { 111 ReadFromVector(nodes); 112 } 113 114 BookmarkNodeData::~BookmarkNodeData() { 115 } 116 117 #if !defined(OS_MACOSX) 118 // static 119 bool BookmarkNodeData::ClipboardContainsBookmarks() { 120 return ui::Clipboard::GetForCurrentThread()->IsFormatAvailable( 121 ui::Clipboard::GetFormatType(kClipboardFormatString), 122 ui::CLIPBOARD_TYPE_COPY_PASTE); 123 } 124 #endif 125 126 bool 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 139 bool BookmarkNodeData::ReadFromTuple(const GURL& url, const base::string16& title) { 140 Clear(); 141 142 if (!url.is_valid()) 143 return false; 144 145 Element element; 146 element.title = title; 147 element.url = url; 148 element.is_url = true; 149 150 elements.push_back(element); 151 152 return true; 153 } 154 155 #if !defined(OS_MACOSX) 156 void BookmarkNodeData::WriteToClipboard(ui::ClipboardType type) { 157 DCHECK_EQ(type, ui::CLIPBOARD_TYPE_COPY_PASTE); 158 ui::ScopedClipboardWriter scw(ui::Clipboard::GetForCurrentThread(), 159 ui::CLIPBOARD_TYPE_COPY_PASTE); 160 161 // If there is only one element and it is a URL, write the URL to the 162 // clipboard. 163 if (elements.size() == 1 && elements[0].is_url) { 164 const base::string16& title = elements[0].title; 165 const std::string url = elements[0].url.spec(); 166 167 scw.WriteBookmark(title, url); 168 169 // Don't call scw.WriteHyperlink() here, since some rich text editors will 170 // change fonts when such data is pasted in; besides, most such editors 171 // auto-linkify at some point anyway. 172 173 // Also write the URL to the clipboard as text so that it can be pasted 174 // into text fields. We use WriteText instead of WriteURL because we don't 175 // want to clobber the X clipboard when the user copies out of the omnibox 176 // on Linux (on Windows and Mac, there is no difference between these 177 // functions). 178 scw.WriteText(UTF8ToUTF16(url)); 179 } 180 181 Pickle pickle; 182 WriteToPickle(NULL, &pickle); 183 scw.WritePickledData(pickle, 184 ui::Clipboard::GetFormatType(kClipboardFormatString)); 185 } 186 187 bool BookmarkNodeData::ReadFromClipboard(ui::ClipboardType type) { 188 DCHECK_EQ(type, ui::CLIPBOARD_TYPE_COPY_PASTE); 189 std::string data; 190 ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread(); 191 clipboard->ReadData(ui::Clipboard::GetFormatType(kClipboardFormatString), 192 &data); 193 194 if (!data.empty()) { 195 Pickle pickle(data.data(), data.size()); 196 if (ReadFromPickle(&pickle)) 197 return true; 198 } 199 200 base::string16 title; 201 std::string url; 202 clipboard->ReadBookmark(&title, &url); 203 if (!url.empty()) { 204 Element element; 205 element.is_url = true; 206 element.url = GURL(url); 207 element.title = title; 208 209 elements.clear(); 210 elements.push_back(element); 211 return true; 212 } 213 214 return false; 215 } 216 #endif 217 218 void BookmarkNodeData::WriteToPickle(Profile* profile, Pickle* pickle) const { 219 base::FilePath path = profile ? profile->GetPath() : base::FilePath(); 220 path.WriteToPickle(pickle); 221 pickle->WriteUInt64(elements.size()); 222 223 for (size_t i = 0; i < elements.size(); ++i) 224 elements[i].WriteToPickle(pickle); 225 } 226 227 bool BookmarkNodeData::ReadFromPickle(Pickle* pickle) { 228 PickleIterator data_iterator(*pickle); 229 uint64 element_count; 230 if (profile_path_.ReadFromPickle(&data_iterator) && 231 pickle->ReadUInt64(&data_iterator, &element_count)) { 232 std::vector<Element> tmp_elements; 233 tmp_elements.resize(element_count); 234 for (uint64 i = 0; i < element_count; ++i) { 235 if (!tmp_elements[i].ReadFromPickle(pickle, &data_iterator)) { 236 return false; 237 } 238 } 239 elements.swap(tmp_elements); 240 } 241 242 return true; 243 } 244 245 std::vector<const BookmarkNode*> BookmarkNodeData::GetNodes( 246 Profile* profile) const { 247 std::vector<const BookmarkNode*> nodes; 248 249 if (!IsFromProfile(profile)) 250 return nodes; 251 252 for (size_t i = 0; i < elements.size(); ++i) { 253 const BookmarkNode* node = BookmarkModelFactory::GetForProfile( 254 profile)->GetNodeByID(elements[i].id_); 255 if (!node) { 256 nodes.clear(); 257 return nodes; 258 } 259 nodes.push_back(node); 260 } 261 return nodes; 262 } 263 264 const BookmarkNode* BookmarkNodeData::GetFirstNode(Profile* profile) const { 265 std::vector<const BookmarkNode*> nodes = GetNodes(profile); 266 return nodes.size() == 1 ? nodes[0] : NULL; 267 } 268 269 void BookmarkNodeData::Clear() { 270 profile_path_.clear(); 271 elements.clear(); 272 } 273 274 void BookmarkNodeData::SetOriginatingProfile(Profile* profile) { 275 DCHECK(profile_path_.empty()); 276 277 if (profile) 278 profile_path_ = profile->GetPath(); 279 } 280 281 bool BookmarkNodeData::IsFromProfile(Profile* profile) const { 282 // An empty path means the data is not associated with any profile. 283 return !profile_path_.empty() && profile_path_ == profile->GetPath(); 284 } 285