Remove support for specifying version on command line.
[chromium-blink-merge.git] / ui / base / clipboard / clipboard_util_win.cc
blobcc5a0ee1a9fb6f46f6504dea0e4854645927cc21
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.
5 #include "ui/base/clipboard/clipboard_util_win.h"
7 #include <shellapi.h>
8 #include <shlwapi.h>
9 #include <wininet.h> // For INTERNET_MAX_URL_LENGTH.
11 #include "base/basictypes.h"
12 #include "base/logging.h"
13 #include "base/memory/scoped_handle.h"
14 #include "base/strings/string_util.h"
15 #include "base/strings/stringprintf.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "base/win/scoped_hglobal.h"
18 #include "ui/base/clipboard/clipboard.h"
19 #include "ui/base/clipboard/custom_data_helper.h"
21 namespace ui {
23 namespace {
25 bool HasData(IDataObject* data_object, const Clipboard::FormatType& format) {
26 FORMATETC format_etc = format.ToFormatEtc();
27 return SUCCEEDED(data_object->QueryGetData(&format_etc));
30 bool GetData(IDataObject* data_object,
31 const Clipboard::FormatType& format,
32 STGMEDIUM* medium) {
33 FORMATETC format_etc = format.ToFormatEtc();
34 return SUCCEEDED(data_object->GetData(&format_etc, medium));
37 bool GetUrlFromHDrop(IDataObject* data_object,
38 base::string16* url,
39 base::string16* title) {
40 DCHECK(data_object && url && title);
42 STGMEDIUM medium;
43 if (!GetData(data_object, Clipboard::GetCFHDropFormatType(), &medium))
44 return false;
46 HDROP hdrop = static_cast<HDROP>(GlobalLock(medium.hGlobal));
48 if (!hdrop)
49 return false;
51 bool success = false;
52 wchar_t filename[MAX_PATH];
53 if (DragQueryFileW(hdrop, 0, filename, arraysize(filename))) {
54 wchar_t url_buffer[INTERNET_MAX_URL_LENGTH];
55 if (0 == _wcsicmp(PathFindExtensionW(filename), L".url") &&
56 GetPrivateProfileStringW(L"InternetShortcut", L"url", 0, url_buffer,
57 arraysize(url_buffer), filename)) {
58 url->assign(url_buffer);
59 PathRemoveExtension(filename);
60 title->assign(PathFindFileName(filename));
61 success = true;
65 DragFinish(hdrop);
66 GlobalUnlock(medium.hGlobal);
67 // We don't need to call ReleaseStgMedium here because as far as I can tell,
68 // DragFinish frees the hGlobal for us.
69 return success;
72 void SplitUrlAndTitle(const base::string16& str,
73 base::string16* url,
74 base::string16* title) {
75 DCHECK(url && title);
76 size_t newline_pos = str.find('\n');
77 if (newline_pos != base::string16::npos) {
78 url->assign(str, 0, newline_pos);
79 title->assign(str, newline_pos + 1, base::string16::npos);
80 } else {
81 url->assign(str);
82 title->assign(str);
86 bool GetFileUrl(IDataObject* data_object, base::string16* url,
87 base::string16* title) {
88 STGMEDIUM store;
89 if (GetData(data_object, Clipboard::GetFilenameWFormatType(), &store)) {
90 bool success = false;
92 // filename using unicode
93 base::win::ScopedHGlobal<wchar_t> data(store.hGlobal);
94 if (data.get() && data.get()[0] &&
95 (PathFileExists(data.get()) || PathIsUNC(data.get()))) {
96 wchar_t file_url[INTERNET_MAX_URL_LENGTH];
97 DWORD file_url_len = arraysize(file_url);
98 if (SUCCEEDED(::UrlCreateFromPathW(data.get(), file_url, &file_url_len,
99 0))) {
100 url->assign(file_url);
101 title->assign(file_url);
102 success = true;
106 ReleaseStgMedium(&store);
107 if (success)
108 return true;
111 if (GetData(data_object, Clipboard::GetFilenameFormatType(), &store)) {
112 bool success = false;
114 // filename using ascii
115 base::win::ScopedHGlobal<char> data(store.hGlobal);
116 if (data.get() && data.get()[0] && (PathFileExistsA(data.get()) ||
117 PathIsUNCA(data.get()))) {
118 char file_url[INTERNET_MAX_URL_LENGTH];
119 DWORD file_url_len = arraysize(file_url);
120 if (SUCCEEDED(::UrlCreateFromPathA(data.get(), file_url, &file_url_len,
121 0))) {
122 url->assign(base::UTF8ToWide(file_url));
123 title->assign(*url);
124 success = true;
128 ReleaseStgMedium(&store);
129 if (success)
130 return true;
132 return false;
135 } // namespace
137 bool ClipboardUtil::HasUrl(IDataObject* data_object) {
138 DCHECK(data_object);
139 return HasData(data_object, Clipboard::GetMozUrlFormatType()) ||
140 HasData(data_object, Clipboard::GetUrlWFormatType()) ||
141 HasData(data_object, Clipboard::GetUrlFormatType()) ||
142 HasData(data_object, Clipboard::GetFilenameWFormatType()) ||
143 HasData(data_object, Clipboard::GetFilenameFormatType());
146 bool ClipboardUtil::HasFilenames(IDataObject* data_object) {
147 DCHECK(data_object);
148 return HasData(data_object, Clipboard::GetCFHDropFormatType());
151 bool ClipboardUtil::HasFileContents(IDataObject* data_object) {
152 DCHECK(data_object);
153 return HasData(data_object, Clipboard::GetFileContentZeroFormatType());
156 bool ClipboardUtil::HasHtml(IDataObject* data_object) {
157 DCHECK(data_object);
158 return HasData(data_object, Clipboard::GetHtmlFormatType()) ||
159 HasData(data_object, Clipboard::GetTextHtmlFormatType());
162 bool ClipboardUtil::HasPlainText(IDataObject* data_object) {
163 DCHECK(data_object);
164 return HasData(data_object, Clipboard::GetPlainTextWFormatType()) ||
165 HasData(data_object, Clipboard::GetPlainTextFormatType());
168 bool ClipboardUtil::GetUrl(IDataObject* data_object,
169 base::string16* url, base::string16* title, bool convert_filenames) {
170 DCHECK(data_object && url && title);
171 if (!HasUrl(data_object))
172 return false;
174 // Try to extract a URL from |data_object| in a variety of formats.
175 STGMEDIUM store;
176 if (GetUrlFromHDrop(data_object, url, title))
177 return true;
179 if (GetData(data_object, Clipboard::GetMozUrlFormatType(), &store) ||
180 GetData(data_object, Clipboard::GetUrlWFormatType(), &store)) {
182 // Mozilla URL format or unicode URL
183 base::win::ScopedHGlobal<wchar_t> data(store.hGlobal);
184 SplitUrlAndTitle(data.get(), url, title);
186 ReleaseStgMedium(&store);
187 return true;
190 if (GetData(data_object, Clipboard::GetUrlFormatType(), &store)) {
192 // URL using ascii
193 base::win::ScopedHGlobal<char> data(store.hGlobal);
194 SplitUrlAndTitle(base::UTF8ToWide(data.get()), url, title);
196 ReleaseStgMedium(&store);
197 return true;
200 if (convert_filenames) {
201 return GetFileUrl(data_object, url, title);
202 } else {
203 return false;
207 bool ClipboardUtil::GetFilenames(IDataObject* data_object,
208 std::vector<base::string16>* filenames) {
209 DCHECK(data_object && filenames);
210 if (!HasFilenames(data_object))
211 return false;
213 STGMEDIUM medium;
214 if (!GetData(data_object, Clipboard::GetCFHDropFormatType(), &medium))
215 return false;
217 HDROP hdrop = static_cast<HDROP>(GlobalLock(medium.hGlobal));
218 if (!hdrop)
219 return false;
221 const int kMaxFilenameLen = 4096;
222 const unsigned num_files = DragQueryFileW(hdrop, 0xffffffff, 0, 0);
223 for (unsigned int i = 0; i < num_files; ++i) {
224 wchar_t filename[kMaxFilenameLen];
225 if (!DragQueryFileW(hdrop, i, filename, kMaxFilenameLen))
226 continue;
227 filenames->push_back(filename);
230 DragFinish(hdrop);
231 GlobalUnlock(medium.hGlobal);
232 // We don't need to call ReleaseStgMedium here because as far as I can tell,
233 // DragFinish frees the hGlobal for us.
234 return true;
237 bool ClipboardUtil::GetPlainText(IDataObject* data_object,
238 base::string16* plain_text) {
239 DCHECK(data_object && plain_text);
240 if (!HasPlainText(data_object))
241 return false;
243 STGMEDIUM store;
244 if (GetData(data_object, Clipboard::GetPlainTextWFormatType(), &store)) {
246 // Unicode text
247 base::win::ScopedHGlobal<wchar_t> data(store.hGlobal);
248 plain_text->assign(data.get());
250 ReleaseStgMedium(&store);
251 return true;
254 if (GetData(data_object, Clipboard::GetPlainTextFormatType(), &store)) {
256 // ascii text
257 base::win::ScopedHGlobal<char> data(store.hGlobal);
258 plain_text->assign(base::UTF8ToWide(data.get()));
260 ReleaseStgMedium(&store);
261 return true;
264 // If a file is dropped on the window, it does not provide either of the
265 // plain text formats, so here we try to forcibly get a url.
266 base::string16 title;
267 return GetUrl(data_object, plain_text, &title, false);
270 bool ClipboardUtil::GetHtml(IDataObject* data_object,
271 base::string16* html, std::string* base_url) {
272 DCHECK(data_object && html && base_url);
274 STGMEDIUM store;
275 if (HasData(data_object, Clipboard::GetHtmlFormatType()) &&
276 GetData(data_object, Clipboard::GetHtmlFormatType(), &store)) {
278 // MS CF html
279 base::win::ScopedHGlobal<char> data(store.hGlobal);
281 std::string html_utf8;
282 CFHtmlToHtml(std::string(data.get(), data.Size()), &html_utf8, base_url);
283 html->assign(base::UTF8ToWide(html_utf8));
285 ReleaseStgMedium(&store);
286 return true;
289 if (!HasData(data_object, Clipboard::GetTextHtmlFormatType()))
290 return false;
292 if (!GetData(data_object, Clipboard::GetTextHtmlFormatType(), &store))
293 return false;
296 // text/html
297 base::win::ScopedHGlobal<wchar_t> data(store.hGlobal);
298 html->assign(data.get());
300 ReleaseStgMedium(&store);
301 return true;
304 bool ClipboardUtil::GetFileContents(IDataObject* data_object,
305 base::string16* filename, std::string* file_contents) {
306 DCHECK(data_object && filename && file_contents);
307 if (!HasData(data_object, Clipboard::GetFileContentZeroFormatType()) &&
308 !HasData(data_object, Clipboard::GetFileDescriptorFormatType()))
309 return false;
311 STGMEDIUM content;
312 // The call to GetData can be very slow depending on what is in
313 // |data_object|.
314 if (GetData(
315 data_object, Clipboard::GetFileContentZeroFormatType(), &content)) {
316 if (TYMED_HGLOBAL == content.tymed) {
317 base::win::ScopedHGlobal<char> data(content.hGlobal);
318 file_contents->assign(data.get(), data.Size());
320 ReleaseStgMedium(&content);
323 STGMEDIUM description;
324 if (GetData(data_object,
325 Clipboard::GetFileDescriptorFormatType(),
326 &description)) {
328 base::win::ScopedHGlobal<FILEGROUPDESCRIPTOR> fgd(description.hGlobal);
329 // We expect there to be at least one file in here.
330 DCHECK_GE(fgd->cItems, 1u);
331 filename->assign(fgd->fgd[0].cFileName);
333 ReleaseStgMedium(&description);
335 return true;
338 bool ClipboardUtil::GetWebCustomData(
339 IDataObject* data_object,
340 std::map<base::string16, base::string16>* custom_data) {
341 DCHECK(data_object && custom_data);
343 if (!HasData(data_object, Clipboard::GetWebCustomDataFormatType()))
344 return false;
346 STGMEDIUM store;
347 if (GetData(data_object, Clipboard::GetWebCustomDataFormatType(), &store)) {
349 base::win::ScopedHGlobal<char> data(store.hGlobal);
350 ReadCustomDataIntoMap(data.get(), data.Size(), custom_data);
352 ReleaseStgMedium(&store);
353 return true;
355 return false;
359 // HtmlToCFHtml and CFHtmlToHtml are based on similar methods in
360 // WebCore/platform/win/ClipboardUtilitiesWin.cpp.
362 * Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
364 * Redistribution and use in source and binary forms, with or without
365 * modification, are permitted provided that the following conditions
366 * are met:
367 * 1. Redistributions of source code must retain the above copyright
368 * notice, this list of conditions and the following disclaimer.
369 * 2. Redistributions in binary form must reproduce the above copyright
370 * notice, this list of conditions and the following disclaimer in the
371 * documentation and/or other materials provided with the distribution.
373 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
374 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
375 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
376 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
377 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
378 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
379 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
380 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
381 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
382 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
383 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
386 // Helper method for converting from text/html to MS CF_HTML.
387 // Documentation for the CF_HTML format is available at
388 // http://msdn.microsoft.com/en-us/library/aa767917(VS.85).aspx
389 std::string ClipboardUtil::HtmlToCFHtml(const std::string& html,
390 const std::string& base_url) {
391 if (html.empty())
392 return std::string();
394 #define MAX_DIGITS 10
395 #define MAKE_NUMBER_FORMAT_1(digits) MAKE_NUMBER_FORMAT_2(digits)
396 #define MAKE_NUMBER_FORMAT_2(digits) "%0" #digits "u"
397 #define NUMBER_FORMAT MAKE_NUMBER_FORMAT_1(MAX_DIGITS)
399 static const char* header = "Version:0.9\r\n"
400 "StartHTML:" NUMBER_FORMAT "\r\n"
401 "EndHTML:" NUMBER_FORMAT "\r\n"
402 "StartFragment:" NUMBER_FORMAT "\r\n"
403 "EndFragment:" NUMBER_FORMAT "\r\n";
404 static const char* source_url_prefix = "SourceURL:";
406 static const char* start_markup =
407 "<html>\r\n<body>\r\n<!--StartFragment-->";
408 static const char* end_markup =
409 "<!--EndFragment-->\r\n</body>\r\n</html>";
411 // Calculate offsets
412 size_t start_html_offset = strlen(header) - strlen(NUMBER_FORMAT) * 4 +
413 MAX_DIGITS * 4;
414 if (!base_url.empty()) {
415 start_html_offset += strlen(source_url_prefix) +
416 base_url.length() + 2; // Add 2 for \r\n.
418 size_t start_fragment_offset = start_html_offset + strlen(start_markup);
419 size_t end_fragment_offset = start_fragment_offset + html.length();
420 size_t end_html_offset = end_fragment_offset + strlen(end_markup);
422 std::string result = base::StringPrintf(header,
423 start_html_offset,
424 end_html_offset,
425 start_fragment_offset,
426 end_fragment_offset);
427 if (!base_url.empty()) {
428 result.append(source_url_prefix);
429 result.append(base_url);
430 result.append("\r\n");
432 result.append(start_markup);
433 result.append(html);
434 result.append(end_markup);
436 #undef MAX_DIGITS
437 #undef MAKE_NUMBER_FORMAT_1
438 #undef MAKE_NUMBER_FORMAT_2
439 #undef NUMBER_FORMAT
441 return result;
444 // Helper method for converting from MS CF_HTML to text/html.
445 void ClipboardUtil::CFHtmlToHtml(const std::string& cf_html,
446 std::string* html,
447 std::string* base_url) {
448 size_t fragment_start = std::string::npos;
449 size_t fragment_end = std::string::npos;
451 ClipboardUtil::CFHtmlExtractMetadata(
452 cf_html, base_url, NULL, &fragment_start, &fragment_end);
454 if (html &&
455 fragment_start != std::string::npos &&
456 fragment_end != std::string::npos) {
457 *html = cf_html.substr(fragment_start, fragment_end - fragment_start);
458 TrimWhitespace(*html, TRIM_ALL, html);
462 void ClipboardUtil::CFHtmlExtractMetadata(const std::string& cf_html,
463 std::string* base_url,
464 size_t* html_start,
465 size_t* fragment_start,
466 size_t* fragment_end) {
467 // Obtain base_url if present.
468 if (base_url) {
469 static std::string src_url_str("SourceURL:");
470 size_t line_start = cf_html.find(src_url_str);
471 if (line_start != std::string::npos) {
472 size_t src_end = cf_html.find("\n", line_start);
473 size_t src_start = line_start + src_url_str.length();
474 if (src_end != std::string::npos && src_start != std::string::npos) {
475 *base_url = cf_html.substr(src_start, src_end - src_start);
476 TrimWhitespace(*base_url, TRIM_ALL, base_url);
481 // Find the markup between "<!--StartFragment-->" and "<!--EndFragment-->".
482 // If the comments cannot be found, like copying from OpenOffice Writer,
483 // we simply fall back to using StartFragment/EndFragment bytecount values
484 // to determine the fragment indexes.
485 std::string cf_html_lower = StringToLowerASCII(cf_html);
486 size_t markup_start = cf_html_lower.find("<html", 0);
487 if (html_start) {
488 *html_start = markup_start;
490 size_t tag_start = cf_html.find("<!--StartFragment", markup_start);
491 if (tag_start == std::string::npos) {
492 static std::string start_fragment_str("StartFragment:");
493 size_t start_fragment_start = cf_html.find(start_fragment_str);
494 if (start_fragment_start != std::string::npos) {
495 *fragment_start = static_cast<size_t>(atoi(cf_html.c_str() +
496 start_fragment_start + start_fragment_str.length()));
499 static std::string end_fragment_str("EndFragment:");
500 size_t end_fragment_start = cf_html.find(end_fragment_str);
501 if (end_fragment_start != std::string::npos) {
502 *fragment_end = static_cast<size_t>(atoi(cf_html.c_str() +
503 end_fragment_start + end_fragment_str.length()));
505 } else {
506 *fragment_start = cf_html.find('>', tag_start) + 1;
507 size_t tag_end = cf_html.rfind("<!--EndFragment", std::string::npos);
508 *fragment_end = cf_html.rfind('<', tag_end);
512 } // namespace ui