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