1 // Copyright (c) 2013 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 "ui/base/x/selection_utils.h" 6 7 #include <set> 8 9 #include "base/i18n/icu_string_conversions.h" 10 #include "base/logging.h" 11 #include "base/strings/string_util.h" 12 #include "base/strings/utf_string_conversions.h" 13 #include "ui/base/clipboard/clipboard.h" 14 #include "ui/base/x/x11_util.h" 15 #include "ui/gfx/x/x11_atom_cache.h" 16 17 namespace ui { 18 19 const char kMimeTypeMozillaURL[] = "text/x-moz-url"; 20 const char kString[] = "STRING"; 21 const char kText[] = "TEXT"; 22 const char kTextPlain[] = "text/plain"; 23 const char kTextPlainUtf8[] = "text/plain;charset=utf-8"; 24 const char kUtf8String[] = "UTF8_STRING"; 25 26 const char* kSelectionDataAtoms[] = { 27 Clipboard::kMimeTypeHTML, 28 kString, 29 kText, 30 kTextPlain, 31 kTextPlainUtf8, 32 kUtf8String, 33 NULL 34 }; 35 36 std::vector< ::Atom> GetTextAtomsFrom(const X11AtomCache* atom_cache) { 37 std::vector< ::Atom> atoms; 38 atoms.push_back(atom_cache->GetAtom(kUtf8String)); 39 atoms.push_back(atom_cache->GetAtom(kString)); 40 atoms.push_back(atom_cache->GetAtom(kText)); 41 atoms.push_back(atom_cache->GetAtom(kTextPlain)); 42 atoms.push_back(atom_cache->GetAtom(kTextPlainUtf8)); 43 return atoms; 44 } 45 46 std::vector< ::Atom> GetURLAtomsFrom(const X11AtomCache* atom_cache) { 47 std::vector< ::Atom> atoms; 48 atoms.push_back(atom_cache->GetAtom(Clipboard::kMimeTypeURIList)); 49 atoms.push_back(atom_cache->GetAtom(kMimeTypeMozillaURL)); 50 return atoms; 51 } 52 53 std::vector< ::Atom> GetURIListAtomsFrom(const X11AtomCache* atom_cache) { 54 std::vector< ::Atom> atoms; 55 atoms.push_back(atom_cache->GetAtom(Clipboard::kMimeTypeURIList)); 56 return atoms; 57 } 58 59 void GetAtomIntersection(const std::vector< ::Atom>& desired, 60 const std::vector< ::Atom>& offered, 61 std::vector< ::Atom>* output) { 62 for (std::vector< ::Atom>::const_iterator it = desired.begin(); 63 it != desired.end(); ++it) { 64 std::vector< ::Atom>::const_iterator jt = 65 std::find(offered.begin(), offered.end(), *it); 66 if (jt != offered.end()) 67 output->push_back(*it); 68 } 69 } 70 71 void AddString16ToVector(const base::string16& str, 72 std::vector<unsigned char>* bytes) { 73 const unsigned char* front = 74 reinterpret_cast<const unsigned char*>(str.data()); 75 bytes->insert(bytes->end(), front, front + (str.size() * 2)); 76 } 77 78 std::vector<std::string> ParseURIList(const SelectionData& data) { 79 // uri-lists are newline separated file lists in URL encoding. 80 std::string unparsed; 81 data.AssignTo(&unparsed); 82 83 std::vector<std::string> tokens; 84 Tokenize(unparsed, "\n", &tokens); 85 return tokens; 86 } 87 88 std::string RefCountedMemoryToString( 89 const scoped_refptr<base::RefCountedMemory>& memory) { 90 if (!memory.get()) { 91 NOTREACHED(); 92 return std::string(); 93 } 94 95 size_t size = memory->size(); 96 if (!size) 97 return std::string(); 98 99 const unsigned char* front = memory->front(); 100 return std::string(reinterpret_cast<const char*>(front), size); 101 } 102 103 base::string16 RefCountedMemoryToString16( 104 const scoped_refptr<base::RefCountedMemory>& memory) { 105 if (!memory.get()) { 106 NOTREACHED(); 107 return base::string16(); 108 } 109 110 size_t size = memory->size(); 111 if (!size) 112 return base::string16(); 113 114 const unsigned char* front = memory->front(); 115 return base::string16(reinterpret_cast<const base::char16*>(front), size / 2); 116 } 117 118 /////////////////////////////////////////////////////////////////////////////// 119 120 SelectionFormatMap::SelectionFormatMap() {} 121 122 SelectionFormatMap::~SelectionFormatMap() {} 123 124 void SelectionFormatMap::Insert( 125 ::Atom atom, 126 const scoped_refptr<base::RefCountedMemory>& item) { 127 data_.erase(atom); 128 data_.insert(std::make_pair(atom, item)); 129 } 130 131 ui::SelectionData SelectionFormatMap::GetFirstOf( 132 const std::vector< ::Atom>& requested_types) const { 133 for (std::vector< ::Atom>::const_iterator it = requested_types.begin(); 134 it != requested_types.end(); ++it) { 135 const_iterator data_it = data_.find(*it); 136 if (data_it != data_.end()) { 137 return SelectionData(data_it->first, data_it->second); 138 } 139 } 140 141 return SelectionData(); 142 } 143 144 std::vector< ::Atom> SelectionFormatMap::GetTypes() const { 145 std::vector< ::Atom> atoms; 146 for (const_iterator it = data_.begin(); it != data_.end(); ++it) 147 atoms.push_back(it->first); 148 149 return atoms; 150 } 151 152 /////////////////////////////////////////////////////////////////////////////// 153 154 SelectionData::SelectionData() 155 : type_(None), 156 atom_cache_(gfx::GetXDisplay(), kSelectionDataAtoms) { 157 } 158 159 SelectionData::SelectionData( 160 ::Atom type, 161 const scoped_refptr<base::RefCountedMemory>& memory) 162 : type_(type), 163 memory_(memory), 164 atom_cache_(gfx::GetXDisplay(), kSelectionDataAtoms) { 165 } 166 167 SelectionData::SelectionData(const SelectionData& rhs) 168 : type_(rhs.type_), 169 memory_(rhs.memory_), 170 atom_cache_(gfx::GetXDisplay(), kSelectionDataAtoms) { 171 } 172 173 SelectionData::~SelectionData() {} 174 175 SelectionData& SelectionData::operator=(const SelectionData& rhs) { 176 type_ = rhs.type_; 177 memory_ = rhs.memory_; 178 // TODO(erg): In some future where we have to support multiple X Displays, 179 // the following will also need to deal with the display. 180 return *this; 181 } 182 183 bool SelectionData::IsValid() const { 184 return type_ != None; 185 } 186 187 ::Atom SelectionData::GetType() const { 188 return type_; 189 } 190 191 const unsigned char* SelectionData::GetData() const { 192 return memory_.get() ? memory_->front() : NULL; 193 } 194 195 size_t SelectionData::GetSize() const { 196 return memory_.get() ? memory_->size() : 0; 197 } 198 199 std::string SelectionData::GetText() const { 200 if (type_ == atom_cache_.GetAtom(kUtf8String) || 201 type_ == atom_cache_.GetAtom(kText) || 202 type_ == atom_cache_.GetAtom(kTextPlainUtf8)) { 203 return RefCountedMemoryToString(memory_); 204 } else if (type_ == atom_cache_.GetAtom(kString) || 205 type_ == atom_cache_.GetAtom(kTextPlain)) { 206 std::string result; 207 base::ConvertToUtf8AndNormalize(RefCountedMemoryToString(memory_), 208 base::kCodepageLatin1, 209 &result); 210 return result; 211 } else { 212 // BTW, I looked at COMPOUND_TEXT, and there's no way we're going to 213 // support that. Yuck. 214 NOTREACHED(); 215 return std::string(); 216 } 217 } 218 219 base::string16 SelectionData::GetHtml() const { 220 base::string16 markup; 221 222 if (type_ == atom_cache_.GetAtom(Clipboard::kMimeTypeHTML)) { 223 const unsigned char* data = GetData(); 224 size_t size = GetSize(); 225 226 // If the data starts with 0xFEFF, i.e., Byte Order Mark, assume it is 227 // UTF-16, otherwise assume UTF-8. 228 if (size >= 2 && 229 reinterpret_cast<const uint16_t*>(data)[0] == 0xFEFF) { 230 markup.assign(reinterpret_cast<const uint16_t*>(data) + 1, 231 (size / 2) - 1); 232 } else { 233 base::UTF8ToUTF16(reinterpret_cast<const char*>(data), size, &markup); 234 } 235 236 // If there is a terminating NULL, drop it. 237 if (!markup.empty() && markup.at(markup.length() - 1) == '\0') 238 markup.resize(markup.length() - 1); 239 240 return markup; 241 } else { 242 NOTREACHED(); 243 return markup; 244 } 245 } 246 247 void SelectionData::AssignTo(std::string* result) const { 248 *result = RefCountedMemoryToString(memory_); 249 } 250 251 void SelectionData::AssignTo(base::string16* result) const { 252 *result = RefCountedMemoryToString16(memory_); 253 } 254 255 } // namespace ui 256