Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(142)

Side by Side Diff: base/clipboard_util.cc

Issue 260003: Move the clipboard stuff out of base and into app/clipboard. I renamed... (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 11 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « base/clipboard_util.h ('k') | base/clipboard_win.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2006-2008 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 "base/clipboard_util.h"
6
7 #include <shellapi.h>
8 #include <shlwapi.h>
9 #include <wininet.h>
10
11 #include "base/basictypes.h"
12 #include "base/logging.h"
13 #include "base/scoped_handle.h"
14 #include "base/string_util.h"
15
16 namespace {
17
18 bool GetUrlFromHDrop(IDataObject* data_object, std::wstring* url,
19 std::wstring* title) {
20 DCHECK(data_object && url && title);
21
22 STGMEDIUM medium;
23 if (FAILED(data_object->GetData(ClipboardUtil::GetCFHDropFormat(), &medium)))
24 return false;
25
26 HDROP hdrop = static_cast<HDROP>(GlobalLock(medium.hGlobal));
27
28 if (!hdrop)
29 return false;
30
31 bool success = false;
32 wchar_t filename[MAX_PATH];
33 if (DragQueryFileW(hdrop, 0, filename, arraysize(filename))) {
34 wchar_t url_buffer[INTERNET_MAX_URL_LENGTH];
35 if (0 == _wcsicmp(PathFindExtensionW(filename), L".url") &&
36 GetPrivateProfileStringW(L"InternetShortcut", L"url", 0, url_buffer,
37 arraysize(url_buffer), filename)) {
38 *url = url_buffer;
39 PathRemoveExtension(filename);
40 title->assign(PathFindFileName(filename));
41 success = true;
42 }
43 }
44
45 DragFinish(hdrop);
46 GlobalUnlock(medium.hGlobal);
47 // We don't need to call ReleaseStgMedium here because as far as I can tell,
48 // DragFinish frees the hGlobal for us.
49 return success;
50 }
51
52 bool SplitUrlAndTitle(const std::wstring& str, std::wstring* url,
53 std::wstring* title) {
54 DCHECK(url && title);
55 size_t newline_pos = str.find('\n');
56 bool success = false;
57 if (newline_pos != std::string::npos) {
58 *url = str.substr(0, newline_pos);
59 title->assign(str.substr(newline_pos + 1));
60 success = true;
61 } else {
62 *url = str;
63 title->assign(str);
64 success = true;
65 }
66 return success;
67 }
68
69 } // namespace
70
71
72 FORMATETC* ClipboardUtil::GetUrlFormat() {
73 static UINT cf = RegisterClipboardFormat(CFSTR_INETURLA);
74 static FORMATETC format = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
75 return &format;
76 }
77
78 FORMATETC* ClipboardUtil::GetUrlWFormat() {
79 static UINT cf = RegisterClipboardFormat(CFSTR_INETURLW);
80 static FORMATETC format = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
81 return &format;
82 }
83
84 FORMATETC* ClipboardUtil::GetMozUrlFormat() {
85 // The format is "URL\nTitle"
86 static UINT cf = RegisterClipboardFormat(L"text/x-moz-url");
87 static FORMATETC format = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
88 return &format;
89 }
90
91 FORMATETC* ClipboardUtil::GetPlainTextFormat() {
92 // We don't need to register this format since it's a built in format.
93 static FORMATETC format = {CF_TEXT, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
94 return &format;
95 }
96
97 FORMATETC* ClipboardUtil::GetPlainTextWFormat() {
98 // We don't need to register this format since it's a built in format.
99 static FORMATETC format = {CF_UNICODETEXT, 0, DVASPECT_CONTENT, -1,
100 TYMED_HGLOBAL};
101 return &format;
102 }
103
104 FORMATETC* ClipboardUtil::GetFilenameWFormat() {
105 static UINT cf = RegisterClipboardFormat(CFSTR_FILENAMEW);
106 static FORMATETC format = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
107 return &format;
108 }
109
110 FORMATETC* ClipboardUtil::GetFilenameFormat() {
111 static UINT cf = RegisterClipboardFormat(CFSTR_FILENAMEA);
112 static FORMATETC format = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
113 return &format;
114 }
115
116 FORMATETC* ClipboardUtil::GetHtmlFormat() {
117 static UINT cf = RegisterClipboardFormat(L"HTML Format");
118 static FORMATETC format = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
119 return &format;
120 }
121
122 FORMATETC* ClipboardUtil::GetTextHtmlFormat() {
123 static UINT cf = RegisterClipboardFormat(L"text/html");
124 static FORMATETC format = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
125 return &format;
126 }
127
128 FORMATETC* ClipboardUtil::GetCFHDropFormat() {
129 // We don't need to register this format since it's a built in format.
130 static FORMATETC format = {CF_HDROP, 0, DVASPECT_CONTENT, -1,
131 TYMED_HGLOBAL};
132 return &format;
133 }
134
135 FORMATETC* ClipboardUtil::GetFileDescriptorFormat() {
136 static UINT cf = RegisterClipboardFormat(CFSTR_FILEDESCRIPTOR);
137 static FORMATETC format = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
138 return &format;
139 }
140
141 FORMATETC* ClipboardUtil::GetFileContentFormatZero() {
142 static UINT cf = RegisterClipboardFormat(CFSTR_FILECONTENTS);
143 static FORMATETC format = {cf, 0, DVASPECT_CONTENT, 0, TYMED_HGLOBAL};
144 return &format;
145 }
146
147 FORMATETC* ClipboardUtil::GetWebKitSmartPasteFormat() {
148 static UINT cf = RegisterClipboardFormat(L"WebKit Smart Paste Format");
149 static FORMATETC format = {cf, 0, DVASPECT_CONTENT, 0, TYMED_HGLOBAL};
150 return &format;
151 }
152
153
154 bool ClipboardUtil::HasUrl(IDataObject* data_object) {
155 DCHECK(data_object);
156 return SUCCEEDED(data_object->QueryGetData(GetMozUrlFormat())) ||
157 SUCCEEDED(data_object->QueryGetData(GetUrlWFormat())) ||
158 SUCCEEDED(data_object->QueryGetData(GetUrlFormat())) ||
159 SUCCEEDED(data_object->QueryGetData(GetFilenameWFormat())) ||
160 SUCCEEDED(data_object->QueryGetData(GetFilenameFormat()));
161 }
162
163 bool ClipboardUtil::HasFilenames(IDataObject* data_object) {
164 DCHECK(data_object);
165 return SUCCEEDED(data_object->QueryGetData(GetCFHDropFormat()));
166 }
167
168 bool ClipboardUtil::HasFileContents(IDataObject* data_object) {
169 DCHECK(data_object);
170 return SUCCEEDED(data_object->QueryGetData(GetFileContentFormatZero()));
171 }
172
173 bool ClipboardUtil::HasHtml(IDataObject* data_object) {
174 DCHECK(data_object);
175 return SUCCEEDED(data_object->QueryGetData(GetHtmlFormat())) ||
176 SUCCEEDED(data_object->QueryGetData(GetTextHtmlFormat()));
177 }
178
179 bool ClipboardUtil::HasPlainText(IDataObject* data_object) {
180 DCHECK(data_object);
181 return SUCCEEDED(data_object->QueryGetData(GetPlainTextWFormat())) ||
182 SUCCEEDED(data_object->QueryGetData(GetPlainTextFormat()));
183 }
184
185
186 bool ClipboardUtil::GetUrl(IDataObject* data_object,
187 std::wstring* url, std::wstring* title) {
188 DCHECK(data_object && url && title);
189 if (!HasUrl(data_object))
190 return false;
191
192 // Try to extract a URL from |data_object| in a variety of formats.
193 STGMEDIUM store;
194 if (GetUrlFromHDrop(data_object, url, title)) {
195 return true;
196 }
197
198 if (SUCCEEDED(data_object->GetData(GetMozUrlFormat(), &store)) ||
199 SUCCEEDED(data_object->GetData(GetUrlWFormat(), &store))) {
200 // Mozilla URL format or unicode URL
201 ScopedHGlobal<wchar_t> data(store.hGlobal);
202 bool success = SplitUrlAndTitle(data.get(), url, title);
203 ReleaseStgMedium(&store);
204 if (success)
205 return true;
206 }
207
208 if (SUCCEEDED(data_object->GetData(GetUrlFormat(), &store))) {
209 // URL using ascii
210 ScopedHGlobal<char> data(store.hGlobal);
211 bool success = SplitUrlAndTitle(UTF8ToWide(data.get()), url, title);
212 ReleaseStgMedium(&store);
213 if (success)
214 return true;
215 }
216
217 if (SUCCEEDED(data_object->GetData(GetFilenameWFormat(), &store))) {
218 // filename using unicode
219 ScopedHGlobal<wchar_t> data(store.hGlobal);
220 bool success = false;
221 if (data.get() && data.get()[0] && (PathFileExists(data.get()) ||
222 PathIsUNC(data.get()))) {
223 wchar_t file_url[INTERNET_MAX_URL_LENGTH];
224 DWORD file_url_len = sizeof(file_url) / sizeof(file_url[0]);
225 if (SUCCEEDED(::UrlCreateFromPathW(data.get(), file_url, &file_url_len,
226 0))) {
227 *url = file_url;
228 title->assign(file_url);
229 success = true;
230 }
231 }
232 ReleaseStgMedium(&store);
233 if (success)
234 return true;
235 }
236
237 if (SUCCEEDED(data_object->GetData(GetFilenameFormat(), &store))) {
238 // filename using ascii
239 ScopedHGlobal<char> data(store.hGlobal);
240 bool success = false;
241 if (data.get() && data.get()[0] && (PathFileExistsA(data.get()) ||
242 PathIsUNCA(data.get()))) {
243 char file_url[INTERNET_MAX_URL_LENGTH];
244 DWORD file_url_len = sizeof(file_url) / sizeof(file_url[0]);
245 if (SUCCEEDED(::UrlCreateFromPathA(data.get(),
246 file_url,
247 &file_url_len,
248 0))) {
249 *url = UTF8ToWide(file_url);
250 title->assign(*url);
251 success = true;
252 }
253 }
254 ReleaseStgMedium(&store);
255 if (success)
256 return true;
257 }
258
259 return false;
260 }
261
262 bool ClipboardUtil::GetFilenames(IDataObject* data_object,
263 std::vector<std::wstring>* filenames) {
264 DCHECK(data_object && filenames);
265 if (!HasFilenames(data_object))
266 return false;
267
268 STGMEDIUM medium;
269 if (FAILED(data_object->GetData(GetCFHDropFormat(), &medium)))
270 return false;
271
272 HDROP hdrop = static_cast<HDROP>(GlobalLock(medium.hGlobal));
273 if (!hdrop)
274 return false;
275
276 const int kMaxFilenameLen = 4096;
277 const unsigned num_files = DragQueryFileW(hdrop, 0xffffffff, 0, 0);
278 for (unsigned int i = 0; i < num_files; ++i) {
279 wchar_t filename[kMaxFilenameLen];
280 if (!DragQueryFileW(hdrop, i, filename, kMaxFilenameLen))
281 continue;
282 filenames->push_back(filename);
283 }
284
285 DragFinish(hdrop);
286 GlobalUnlock(medium.hGlobal);
287 // We don't need to call ReleaseStgMedium here because as far as I can tell,
288 // DragFinish frees the hGlobal for us.
289 return true;
290 }
291
292 bool ClipboardUtil::GetPlainText(IDataObject* data_object,
293 std::wstring* plain_text) {
294 DCHECK(data_object && plain_text);
295 if (!HasPlainText(data_object))
296 return false;
297
298 STGMEDIUM store;
299 bool success = false;
300 if (SUCCEEDED(data_object->GetData(GetPlainTextWFormat(), &store))) {
301 // Unicode text
302 ScopedHGlobal<wchar_t> data(store.hGlobal);
303 plain_text->assign(data.get());
304 ReleaseStgMedium(&store);
305 success = true;
306 } else if (SUCCEEDED(data_object->GetData(GetPlainTextFormat(), &store))) {
307 // ascii text
308 ScopedHGlobal<char> data(store.hGlobal);
309 plain_text->assign(UTF8ToWide(data.get()));
310 ReleaseStgMedium(&store);
311 success = true;
312 } else {
313 // If a file is dropped on the window, it does not provide either of the
314 // plain text formats, so here we try to forcibly get a url.
315 std::wstring title;
316 success = GetUrl(data_object, plain_text, &title);
317 }
318
319 return success;
320 }
321
322 bool ClipboardUtil::GetHtml(IDataObject* data_object,
323 std::wstring* html, std::string* base_url) {
324 DCHECK(data_object && html && base_url);
325
326 if (SUCCEEDED(data_object->QueryGetData(GetHtmlFormat()))) {
327 STGMEDIUM store;
328 if (SUCCEEDED(data_object->GetData(GetHtmlFormat(), &store))) {
329 // MS CF html
330 ScopedHGlobal<char> data(store.hGlobal);
331
332 std::string html_utf8;
333 CFHtmlToHtml(std::string(data.get(), data.Size()), &html_utf8, base_url);
334 html->assign(UTF8ToWide(html_utf8));
335
336 ReleaseStgMedium(&store);
337 return true;
338 }
339 }
340
341 if (FAILED(data_object->QueryGetData(GetTextHtmlFormat())))
342 return false;
343
344 STGMEDIUM store;
345 if (FAILED(data_object->GetData(GetTextHtmlFormat(), &store)))
346 return false;
347
348 // text/html
349 ScopedHGlobal<wchar_t> data(store.hGlobal);
350 html->assign(data.get());
351 ReleaseStgMedium(&store);
352 return true;
353 }
354
355 bool ClipboardUtil::GetFileContents(IDataObject* data_object,
356 std::wstring* filename, std::string* file_contents) {
357 DCHECK(data_object && filename && file_contents);
358 bool has_data =
359 SUCCEEDED(data_object->QueryGetData(GetFileContentFormatZero())) ||
360 SUCCEEDED(data_object->QueryGetData(GetFileDescriptorFormat()));
361
362 if (!has_data)
363 return false;
364
365 STGMEDIUM content;
366 // The call to GetData can be very slow depending on what is in
367 // |data_object|.
368 if (SUCCEEDED(data_object->GetData(GetFileContentFormatZero(), &content))) {
369 if (TYMED_HGLOBAL == content.tymed) {
370 ScopedHGlobal<char> data(content.hGlobal);
371 file_contents->assign(data.get(), data.Size());
372 }
373 ReleaseStgMedium(&content);
374 }
375
376 STGMEDIUM description;
377 if (SUCCEEDED(data_object->GetData(GetFileDescriptorFormat(),
378 &description))) {
379 ScopedHGlobal<FILEGROUPDESCRIPTOR> fgd(description.hGlobal);
380 // We expect there to be at least one file in here.
381 DCHECK_GE(fgd->cItems, 1);
382 filename->assign(fgd->fgd[0].cFileName);
383 ReleaseStgMedium(&description);
384 }
385 return true;
386 }
387
388 // HtmlToCFHtml and CFHtmlToHtml are based on similar methods in
389 // WebCore/platform/win/ClipboardUtilitiesWin.cpp.
390 /*
391 * Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
392 *
393 * Redistribution and use in source and binary forms, with or without
394 * modification, are permitted provided that the following conditions
395 * are met:
396 * 1. Redistributions of source code must retain the above copyright
397 * notice, this list of conditions and the following disclaimer.
398 * 2. Redistributions in binary form must reproduce the above copyright
399 * notice, this list of conditions and the following disclaimer in the
400 * documentation and/or other materials provided with the distribution.
401 *
402 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
403 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
404 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
405 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
406 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
407 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
408 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
409 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
410 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
411 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
412 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
413 */
414
415 // Helper method for converting from text/html to MS CF_HTML.
416 // Documentation for the CF_HTML format is available at
417 // http://msdn.microsoft.com/en-us/library/aa767917(VS.85).aspx
418 std::string ClipboardUtil::HtmlToCFHtml(const std::string& html,
419 const std::string& base_url) {
420 if (html.empty())
421 return std::string();
422
423 #define MAX_DIGITS 10
424 #define MAKE_NUMBER_FORMAT_1(digits) MAKE_NUMBER_FORMAT_2(digits)
425 #define MAKE_NUMBER_FORMAT_2(digits) "%0" #digits "u"
426 #define NUMBER_FORMAT MAKE_NUMBER_FORMAT_1(MAX_DIGITS)
427
428 static const char* header = "Version:0.9\r\n"
429 "StartHTML:" NUMBER_FORMAT "\r\n"
430 "EndHTML:" NUMBER_FORMAT "\r\n"
431 "StartFragment:" NUMBER_FORMAT "\r\n"
432 "EndFragment:" NUMBER_FORMAT "\r\n";
433 static const char* source_url_prefix = "SourceURL:";
434
435 static const char* start_markup =
436 "<html>\r\n<body>\r\n<!--StartFragment-->\r\n";
437 static const char* end_markup =
438 "\r\n<!--EndFragment-->\r\n</body>\r\n</html>";
439
440 // Calculate offsets
441 size_t start_html_offset = strlen(header) - strlen(NUMBER_FORMAT) * 4 +
442 MAX_DIGITS * 4;
443 if (!base_url.empty()) {
444 start_html_offset += strlen(source_url_prefix) +
445 base_url.length() + 2; // Add 2 for \r\n.
446 }
447 size_t start_fragment_offset = start_html_offset + strlen(start_markup);
448 size_t end_fragment_offset = start_fragment_offset + html.length();
449 size_t end_html_offset = end_fragment_offset + strlen(end_markup);
450
451 std::string result = StringPrintf(header, start_html_offset,
452 end_html_offset, start_fragment_offset, end_fragment_offset);
453 if (!base_url.empty()) {
454 result.append(source_url_prefix);
455 result.append(base_url);
456 result.append("\r\n");
457 }
458 result.append(start_markup);
459 result.append(html);
460 result.append(end_markup);
461
462 #undef MAX_DIGITS
463 #undef MAKE_NUMBER_FORMAT_1
464 #undef MAKE_NUMBER_FORMAT_2
465 #undef NUMBER_FORMAT
466
467 return result;
468 }
469
470 // Helper method for converting from MS CF_HTML to text/html.
471 void ClipboardUtil::CFHtmlToHtml(const std::string& cf_html,
472 std::string* html,
473 std::string* base_url) {
474 // Obtain base_url if present.
475 if (base_url) {
476 static std::string src_url_str("SourceURL:");
477 size_t line_start = cf_html.find(src_url_str);
478 if (line_start != std::string::npos) {
479 size_t src_end = cf_html.find("\n", line_start);
480 size_t src_start = line_start + src_url_str.length();
481 if (src_end != std::string::npos && src_start != std::string::npos) {
482 *base_url = cf_html.substr(src_start, src_end - src_start);
483 TrimWhitespace(*base_url, TRIM_ALL, base_url);
484 }
485 }
486 }
487
488 // Find the markup between "<!--StartFragment -->" and "<!--EndFragment-->".
489 if (html) {
490 std::string cf_html_lower = StringToLowerASCII(cf_html);
491 size_t markup_start = cf_html_lower.find("<html", 0);
492 size_t tag_start = cf_html.find("StartFragment", markup_start);
493 size_t fragment_start = cf_html.find('>', tag_start) + 1;
494 size_t tag_end = cf_html.rfind("EndFragment", std::string::npos);
495 size_t fragment_end = cf_html.rfind('<', tag_end);
496 if (fragment_start != std::string::npos &&
497 fragment_end != std::string::npos) {
498 *html = cf_html.substr(fragment_start, fragment_end - fragment_start);
499 TrimWhitespace(*html, TRIM_ALL, html);
500 }
501 }
502 }
OLDNEW
« no previous file with comments | « base/clipboard_util.h ('k') | base/clipboard_win.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698