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 16 const char* BookmarkNodeData::kClipboardFormatString = 17 "chromium/x-bookmark-entries"; 18 19 BookmarkNodeData::Element::Element() : is_url(false), id_(0) { 20 } 21 22 BookmarkNodeData::Element::Element(const BookmarkNode* node) 23 : is_url(node->is_url()), 24 url(node->url()), 25 title(node->GetTitle()), 26 date_added(node->date_added()), 27 date_folder_modified(node->date_folder_modified()), 28 id_(node->id()) { 29 if (node->GetMetaInfoMap()) 30 meta_info_map = *node->GetMetaInfoMap(); 31 for (int i = 0; i < node->child_count(); ++i) 32 children.push_back(Element(node->GetChild(i))); 33 } 34 35 BookmarkNodeData::Element::~Element() { 36 } 37 38 void BookmarkNodeData::Element::WriteToPickle(Pickle* pickle) const { 39 pickle->WriteBool(is_url); 40 pickle->WriteString(url.spec()); 41 pickle->WriteString16(title); 42 pickle->WriteInt64(id_); 43 pickle->WriteUInt64(meta_info_map.size()); 44 for (BookmarkNode::MetaInfoMap::const_iterator it = meta_info_map.begin(); 45 it != meta_info_map.end(); ++it) { 46 pickle->WriteString(it->first); 47 pickle->WriteString(it->second); 48 } 49 if (!is_url) { 50 pickle->WriteUInt64(children.size()); 51 for (std::vector<Element>::const_iterator i = children.begin(); 52 i != children.end(); ++i) { 53 i->WriteToPickle(pickle); 54 } 55 } 56 } 57 58 bool BookmarkNodeData::Element::ReadFromPickle(Pickle* pickle, 59 PickleIterator* iterator) { 60 std::string url_spec; 61 if (!pickle->ReadBool(iterator, &is_url) || 62 !pickle->ReadString(iterator, &url_spec) || 63 !pickle->ReadString16(iterator, &title) || 64 !pickle->ReadInt64(iterator, &id_)) { 65 return false; 66 } 67 url = GURL(url_spec); 68 date_added = base::Time(); 69 date_folder_modified = base::Time(); 70 meta_info_map.clear(); 71 uint64 meta_field_count; 72 if (!pickle->ReadUInt64(iterator, &meta_field_count)) 73 return false; 74 for (uint64 i = 0; i < meta_field_count; ++i) { 75 std::string key; 76 std::string value; 77 if (!pickle->ReadString(iterator, &key) || 78 !pickle->ReadString(iterator, &value)) { 79 return false; 80 } 81 meta_info_map[key] = value; 82 } 83 children.clear(); 84 if (!is_url) { 85 uint64 children_count; 86 if (!pickle->ReadUInt64(iterator, &children_count)) 87 return false; 88 children.reserve(children_count); 89 for (uint64 i = 0; i < children_count; ++i) { 90 children.push_back(Element()); 91 if (!children.back().ReadFromPickle(pickle, iterator)) 92 return false; 93 } 94 } 95 return true; 96 } 97 98 // BookmarkNodeData ----------------------------------------------------------- 99 100 BookmarkNodeData::BookmarkNodeData() { 101 } 102 103 BookmarkNodeData::BookmarkNodeData(const BookmarkNode* node) { 104 elements.push_back(Element(node)); 105 } 106 107 BookmarkNodeData::BookmarkNodeData( 108 const std::vector<const BookmarkNode*>& nodes) { 109 ReadFromVector(nodes); 110 } 111 112 BookmarkNodeData::~BookmarkNodeData() { 113 } 114 115 #if !defined(OS_MACOSX) 116 // static 117 bool BookmarkNodeData::ClipboardContainsBookmarks() { 118 return ui::Clipboard::GetForCurrentThread()->IsFormatAvailable( 119 ui::Clipboard::GetFormatType(kClipboardFormatString), 120 ui::CLIPBOARD_TYPE_COPY_PASTE); 121 } 122 #endif 123 124 bool BookmarkNodeData::ReadFromVector( 125 const std::vector<const BookmarkNode*>& nodes) { 126 Clear(); 127 128 if (nodes.empty()) 129 return false; 130 131 for (size_t i = 0; i < nodes.size(); ++i) 132 elements.push_back(Element(nodes[i])); 133 134 return true; 135 } 136 137 bool BookmarkNodeData::ReadFromTuple(const GURL& url, 138 const base::string16& title) { 139 Clear(); 140 141 if (!url.is_valid()) 142 return false; 143 144 Element element; 145 element.title = title; 146 element.url = url; 147 element.is_url = true; 148 149 elements.push_back(element); 150 151 return true; 152 } 153 154 #if !defined(OS_MACOSX) 155 void BookmarkNodeData::WriteToClipboard(ui::ClipboardType clipboard_type) { 156 DCHECK(clipboard_type == ui::CLIPBOARD_TYPE_COPY_PASTE || 157 clipboard_type == ui::CLIPBOARD_TYPE_SELECTION); 158 ui::ScopedClipboardWriter scw(ui::Clipboard::GetForCurrentThread(), 159 clipboard_type); 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(base::UTF8ToUTF16(url)); 179 } 180 181 Pickle pickle; 182 WriteToPickle(base::FilePath(), &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(), static_cast<int>(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(const base::FilePath& profile_path, 219 Pickle* pickle) const { 220 profile_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 BookmarkModel* model, 247 const base::FilePath& profile_path) const { 248 std::vector<const BookmarkNode*> nodes; 249 250 if (!IsFromProfilePath(profile_path)) 251 return nodes; 252 253 for (size_t i = 0; i < elements.size(); ++i) { 254 const BookmarkNode* node = GetBookmarkNodeByID(model, 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( 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] : NULL; 269 } 270 271 void BookmarkNodeData::Clear() { 272 profile_path_.clear(); 273 elements.clear(); 274 } 275 276 void BookmarkNodeData::SetOriginatingProfilePath( 277 const base::FilePath& profile_path) { 278 DCHECK(profile_path_.empty()); 279 profile_path_ = profile_path; 280 } 281 282 bool BookmarkNodeData::IsFromProfilePath( 283 const base::FilePath& profile_path) const { 284 // An empty path means the data is not associated with any profile. 285 return !profile_path_.empty() && profile_path_ == profile_path; 286 } 287