Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "ui/base/clipboard/clipboard_util_win.h" | 5 #include "ui/base/clipboard/clipboard_util_win.h" |
| 6 | 6 |
| 7 #include <shellapi.h> | 7 #include <shellapi.h> |
| 8 #include <shlwapi.h> | 8 #include <shlwapi.h> |
| 9 #include <wininet.h> // For INTERNET_MAX_URL_LENGTH. | 9 #include <wininet.h> // For INTERNET_MAX_URL_LENGTH. |
| 10 | 10 |
| 11 #include "base/basictypes.h" | 11 #include "base/basictypes.h" |
| 12 #include "base/logging.h" | 12 #include "base/logging.h" |
| 13 #include "base/strings/string_util.h" | 13 #include "base/strings/string_util.h" |
| 14 #include "base/strings/stringprintf.h" | 14 #include "base/strings/stringprintf.h" |
| 15 #include "base/strings/sys_string_conversions.h" | |
| 15 #include "base/strings/utf_string_conversions.h" | 16 #include "base/strings/utf_string_conversions.h" |
| 16 #include "base/win/scoped_hglobal.h" | 17 #include "base/win/scoped_hglobal.h" |
| 18 #include "net/base/filename_util.h" | |
| 17 #include "ui/base/clipboard/clipboard.h" | 19 #include "ui/base/clipboard/clipboard.h" |
| 18 #include "ui/base/clipboard/custom_data_helper.h" | 20 #include "ui/base/clipboard/custom_data_helper.h" |
| 21 #include "url/gurl.h" | |
| 19 | 22 |
| 20 namespace ui { | 23 namespace ui { |
| 21 | 24 |
| 22 namespace { | 25 namespace { |
| 23 | 26 |
| 24 bool HasData(IDataObject* data_object, const Clipboard::FormatType& format) { | 27 bool HasData(IDataObject* data_object, const Clipboard::FormatType& format) { |
| 25 FORMATETC format_etc = format.ToFormatEtc(); | 28 FORMATETC format_etc = format.ToFormatEtc(); |
| 26 return SUCCEEDED(data_object->QueryGetData(&format_etc)); | 29 return SUCCEEDED(data_object->QueryGetData(&format_etc)); |
| 27 } | 30 } |
| 28 | 31 |
| 29 bool GetData(IDataObject* data_object, | 32 bool GetData(IDataObject* data_object, |
| 30 const Clipboard::FormatType& format, | 33 const Clipboard::FormatType& format, |
| 31 STGMEDIUM* medium) { | 34 STGMEDIUM* medium) { |
| 32 FORMATETC format_etc = format.ToFormatEtc(); | 35 FORMATETC format_etc = format.ToFormatEtc(); |
| 33 return SUCCEEDED(data_object->GetData(&format_etc, medium)); | 36 return SUCCEEDED(data_object->GetData(&format_etc, medium)); |
| 34 } | 37 } |
| 35 | 38 |
| 36 bool GetUrlFromHDrop(IDataObject* data_object, | 39 bool GetUrlFromHDrop(IDataObject* data_object, |
| 37 base::string16* url, | 40 GURL* url, |
| 38 base::string16* title) { | 41 base::string16* title) { |
| 39 DCHECK(data_object && url && title); | 42 DCHECK(data_object && url && title); |
| 40 | 43 |
| 41 STGMEDIUM medium; | 44 STGMEDIUM medium; |
| 42 if (!GetData(data_object, Clipboard::GetCFHDropFormatType(), &medium)) | 45 if (!GetData(data_object, Clipboard::GetCFHDropFormatType(), &medium)) |
| 43 return false; | 46 return false; |
| 44 | 47 |
| 45 HDROP hdrop = static_cast<HDROP>(GlobalLock(medium.hGlobal)); | 48 HDROP hdrop = static_cast<HDROP>(GlobalLock(medium.hGlobal)); |
| 46 | 49 |
| 47 if (!hdrop) | 50 if (!hdrop) |
| 48 return false; | 51 return false; |
| 49 | 52 |
| 50 bool success = false; | 53 bool success = false; |
| 51 wchar_t filename[MAX_PATH]; | 54 wchar_t filename[MAX_PATH]; |
| 52 if (DragQueryFileW(hdrop, 0, filename, arraysize(filename))) { | 55 if (DragQueryFileW(hdrop, 0, filename, arraysize(filename))) { |
| 53 wchar_t url_buffer[INTERNET_MAX_URL_LENGTH]; | 56 wchar_t url_buffer[INTERNET_MAX_URL_LENGTH]; |
| 54 if (0 == _wcsicmp(PathFindExtensionW(filename), L".url") && | 57 if (0 == _wcsicmp(PathFindExtensionW(filename), L".url") && |
| 55 GetPrivateProfileStringW(L"InternetShortcut", L"url", 0, url_buffer, | 58 GetPrivateProfileStringW(L"InternetShortcut", L"url", 0, url_buffer, |
| 56 arraysize(url_buffer), filename)) { | 59 arraysize(url_buffer), filename)) { |
| 57 url->assign(url_buffer); | 60 *url = GURL(url_buffer); |
| 58 PathRemoveExtension(filename); | 61 PathRemoveExtension(filename); |
| 59 title->assign(PathFindFileName(filename)); | 62 title->assign(PathFindFileName(filename)); |
| 60 success = true; | 63 success = url->is_valid(); |
| 61 } | 64 } |
| 62 } | 65 } |
| 63 | 66 |
| 64 DragFinish(hdrop); | 67 DragFinish(hdrop); |
| 65 GlobalUnlock(medium.hGlobal); | 68 GlobalUnlock(medium.hGlobal); |
| 66 // We don't need to call ReleaseStgMedium here because as far as I can tell, | 69 // We don't need to call ReleaseStgMedium here because as far as I can tell, |
| 67 // DragFinish frees the hGlobal for us. | 70 // DragFinish frees the hGlobal for us. |
| 68 return success; | 71 return success; |
| 69 } | 72 } |
| 70 | 73 |
| 71 void SplitUrlAndTitle(const base::string16& str, | 74 void SplitUrlAndTitle(const base::string16& str, |
| 72 base::string16* url, | 75 GURL* url, |
| 73 base::string16* title) { | 76 base::string16* title) { |
| 74 DCHECK(url && title); | 77 DCHECK(url && title); |
| 75 size_t newline_pos = str.find('\n'); | 78 size_t newline_pos = str.find('\n'); |
| 76 if (newline_pos != base::string16::npos) { | 79 if (newline_pos != base::string16::npos) { |
| 77 url->assign(str, 0, newline_pos); | 80 *url = GURL(base::string16(str, 0, newline_pos)); |
| 78 title->assign(str, newline_pos + 1, base::string16::npos); | 81 title->assign(str, newline_pos + 1, base::string16::npos); |
| 79 } else { | 82 } else { |
| 80 url->assign(str); | 83 *url = GURL(str); |
| 81 title->assign(str); | 84 title->assign(str); |
| 82 } | 85 } |
| 83 } | 86 } |
| 84 | 87 |
| 85 bool GetFileUrl(IDataObject* data_object, base::string16* url, | |
| 86 base::string16* title) { | |
| 87 STGMEDIUM store; | |
| 88 if (GetData(data_object, Clipboard::GetFilenameWFormatType(), &store)) { | |
| 89 bool success = false; | |
| 90 { | |
| 91 // filename using unicode | |
| 92 base::win::ScopedHGlobal<wchar_t> data(store.hGlobal); | |
| 93 if (data.get() && data.get()[0] && | |
| 94 (PathFileExists(data.get()) || PathIsUNC(data.get()))) { | |
| 95 wchar_t file_url[INTERNET_MAX_URL_LENGTH]; | |
| 96 DWORD file_url_len = arraysize(file_url); | |
| 97 if (SUCCEEDED(::UrlCreateFromPathW(data.get(), file_url, &file_url_len, | |
| 98 0))) { | |
| 99 url->assign(file_url); | |
| 100 title->assign(file_url); | |
| 101 success = true; | |
| 102 } | |
| 103 } | |
| 104 } | |
| 105 ReleaseStgMedium(&store); | |
| 106 if (success) | |
| 107 return true; | |
| 108 } | |
| 109 | |
| 110 if (GetData(data_object, Clipboard::GetFilenameFormatType(), &store)) { | |
| 111 bool success = false; | |
| 112 { | |
| 113 // filename using ascii | |
| 114 base::win::ScopedHGlobal<char> data(store.hGlobal); | |
| 115 if (data.get() && data.get()[0] && (PathFileExistsA(data.get()) || | |
| 116 PathIsUNCA(data.get()))) { | |
| 117 char file_url[INTERNET_MAX_URL_LENGTH]; | |
| 118 DWORD file_url_len = arraysize(file_url); | |
| 119 if (SUCCEEDED(::UrlCreateFromPathA(data.get(), file_url, &file_url_len, | |
| 120 0))) { | |
| 121 url->assign(base::UTF8ToWide(file_url)); | |
| 122 title->assign(*url); | |
| 123 success = true; | |
| 124 } | |
| 125 } | |
| 126 } | |
| 127 ReleaseStgMedium(&store); | |
| 128 if (success) | |
| 129 return true; | |
| 130 } | |
| 131 return false; | |
| 132 } | |
| 133 | |
| 134 } // namespace | 88 } // namespace |
| 135 | 89 |
| 136 bool ClipboardUtil::HasUrl(IDataObject* data_object, bool convert_filenames) { | 90 bool ClipboardUtil::HasUrl(IDataObject* data_object, bool convert_filenames) { |
| 137 DCHECK(data_object); | 91 DCHECK(data_object); |
| 138 return HasData(data_object, Clipboard::GetMozUrlFormatType()) || | 92 return HasData(data_object, Clipboard::GetMozUrlFormatType()) || |
| 139 HasData(data_object, Clipboard::GetUrlWFormatType()) || | 93 HasData(data_object, Clipboard::GetUrlWFormatType()) || |
| 140 HasData(data_object, Clipboard::GetUrlFormatType()) || | 94 HasData(data_object, Clipboard::GetUrlFormatType()) || |
| 141 (convert_filenames && ( | 95 (convert_filenames && HasFilenames(data_object)); |
| 142 HasData(data_object, Clipboard::GetFilenameWFormatType()) || | |
| 143 HasData(data_object, Clipboard::GetFilenameFormatType()))); | |
| 144 } | 96 } |
| 145 | 97 |
| 146 bool ClipboardUtil::HasFilenames(IDataObject* data_object) { | 98 bool ClipboardUtil::HasFilenames(IDataObject* data_object) { |
| 147 DCHECK(data_object); | 99 DCHECK(data_object); |
| 148 return HasData(data_object, Clipboard::GetCFHDropFormatType()); | 100 return HasData(data_object, Clipboard::GetCFHDropFormatType()) || |
| 101 HasData(data_object, Clipboard::GetFilenameWFormatType()) || | |
| 102 HasData(data_object, Clipboard::GetFilenameFormatType()); | |
| 149 } | 103 } |
| 150 | 104 |
| 151 bool ClipboardUtil::HasFileContents(IDataObject* data_object) { | 105 bool ClipboardUtil::HasFileContents(IDataObject* data_object) { |
| 152 DCHECK(data_object); | 106 DCHECK(data_object); |
| 153 return HasData(data_object, Clipboard::GetFileContentZeroFormatType()); | 107 return HasData(data_object, Clipboard::GetFileContentZeroFormatType()); |
| 154 } | 108 } |
| 155 | 109 |
| 156 bool ClipboardUtil::HasHtml(IDataObject* data_object) { | 110 bool ClipboardUtil::HasHtml(IDataObject* data_object) { |
| 157 DCHECK(data_object); | 111 DCHECK(data_object); |
| 158 return HasData(data_object, Clipboard::GetHtmlFormatType()) || | 112 return HasData(data_object, Clipboard::GetHtmlFormatType()) || |
| 159 HasData(data_object, Clipboard::GetTextHtmlFormatType()); | 113 HasData(data_object, Clipboard::GetTextHtmlFormatType()); |
| 160 } | 114 } |
| 161 | 115 |
| 162 bool ClipboardUtil::HasPlainText(IDataObject* data_object) { | 116 bool ClipboardUtil::HasPlainText(IDataObject* data_object) { |
| 163 DCHECK(data_object); | 117 DCHECK(data_object); |
| 164 return HasData(data_object, Clipboard::GetPlainTextWFormatType()) || | 118 return HasData(data_object, Clipboard::GetPlainTextWFormatType()) || |
| 165 HasData(data_object, Clipboard::GetPlainTextFormatType()); | 119 HasData(data_object, Clipboard::GetPlainTextFormatType()); |
| 166 } | 120 } |
| 167 | 121 |
| 168 bool ClipboardUtil::GetUrl(IDataObject* data_object, | 122 bool ClipboardUtil::GetUrl(IDataObject* data_object, |
| 169 base::string16* url, base::string16* title, bool convert_filenames) { | 123 GURL* url, |
| 124 base::string16* title, | |
| 125 bool convert_filenames) { | |
| 170 DCHECK(data_object && url && title); | 126 DCHECK(data_object && url && title); |
| 171 if (!HasUrl(data_object, convert_filenames)) | 127 if (!HasUrl(data_object, convert_filenames)) |
| 172 return false; | 128 return false; |
| 173 | 129 |
| 174 // Try to extract a URL from |data_object| in a variety of formats. | 130 // Try to extract a URL from |data_object| in a variety of formats. |
| 175 STGMEDIUM store; | 131 STGMEDIUM store; |
| 176 if (GetUrlFromHDrop(data_object, url, title)) | 132 if (GetUrlFromHDrop(data_object, url, title)) |
| 177 return true; | 133 return true; |
| 178 | 134 |
| 179 if (GetData(data_object, Clipboard::GetMozUrlFormatType(), &store) || | 135 if (GetData(data_object, Clipboard::GetMozUrlFormatType(), &store) || |
| 180 GetData(data_object, Clipboard::GetUrlWFormatType(), &store)) { | 136 GetData(data_object, Clipboard::GetUrlWFormatType(), &store)) { |
| 181 { | 137 { |
| 182 // Mozilla URL format or unicode URL | 138 // Mozilla URL format or unicode URL |
| 183 base::win::ScopedHGlobal<wchar_t> data(store.hGlobal); | 139 base::win::ScopedHGlobal<wchar_t> data(store.hGlobal); |
| 184 SplitUrlAndTitle(data.get(), url, title); | 140 SplitUrlAndTitle(data.get(), url, title); |
| 185 } | 141 } |
| 186 ReleaseStgMedium(&store); | 142 ReleaseStgMedium(&store); |
| 187 return true; | 143 return url->is_valid(); |
|
sky
2014/07/10 22:44:33
Under what circumstance would url not be valid her
dcheng
2014/07/11 22:32:58
Other apps may have written a malformed URL to the
| |
| 188 } | 144 } |
| 189 | 145 |
| 190 if (GetData(data_object, Clipboard::GetUrlFormatType(), &store)) { | 146 if (GetData(data_object, Clipboard::GetUrlFormatType(), &store)) { |
| 191 { | 147 { |
| 192 // URL using ascii | 148 // URL using ascii |
| 193 base::win::ScopedHGlobal<char> data(store.hGlobal); | 149 base::win::ScopedHGlobal<char> data(store.hGlobal); |
| 194 SplitUrlAndTitle(base::UTF8ToWide(data.get()), url, title); | 150 SplitUrlAndTitle(base::UTF8ToWide(data.get()), url, title); |
| 195 } | 151 } |
| 196 ReleaseStgMedium(&store); | 152 ReleaseStgMedium(&store); |
| 197 return true; | 153 return url->is_valid(); |
| 198 } | 154 } |
| 199 | 155 |
| 200 if (convert_filenames) { | 156 if (convert_filenames) { |
| 201 return GetFileUrl(data_object, url, title); | 157 std::vector<base::string16> filenames; |
| 202 } else { | 158 if (!GetFilenames(data_object, &filenames)) |
| 203 return false; | 159 return false; |
| 160 DCHECK_GT(filenames.size(), 0U); | |
| 161 *url = net::FilePathToFileURL(base::FilePath(filenames[0])); | |
| 162 return url->is_valid(); | |
| 204 } | 163 } |
| 164 | |
| 165 return false; | |
| 205 } | 166 } |
| 206 | 167 |
| 207 bool ClipboardUtil::GetFilenames(IDataObject* data_object, | 168 bool ClipboardUtil::GetFilenames(IDataObject* data_object, |
| 208 std::vector<base::string16>* filenames) { | 169 std::vector<base::string16>* filenames) { |
| 209 DCHECK(data_object && filenames); | 170 DCHECK(data_object && filenames); |
| 210 if (!HasFilenames(data_object)) | 171 if (!HasFilenames(data_object)) |
| 211 return false; | 172 return false; |
| 212 | 173 |
| 213 STGMEDIUM medium; | 174 STGMEDIUM medium; |
| 214 if (!GetData(data_object, Clipboard::GetCFHDropFormatType(), &medium)) | 175 if (GetData(data_object, Clipboard::GetCFHDropFormatType(), &medium)) { |
| 215 return false; | 176 HDROP hdrop = static_cast<HDROP>(GlobalLock(medium.hGlobal)); |
| 177 if (!hdrop) | |
| 178 return false; | |
| 216 | 179 |
| 217 HDROP hdrop = static_cast<HDROP>(GlobalLock(medium.hGlobal)); | 180 const int kMaxFilenameLen = 4096; |
| 218 if (!hdrop) | 181 const unsigned num_files = DragQueryFileW(hdrop, 0xffffffff, 0, 0); |
| 219 return false; | 182 for (unsigned int i = 0; i < num_files; ++i) { |
| 183 wchar_t filename[kMaxFilenameLen]; | |
| 184 if (!DragQueryFileW(hdrop, i, filename, kMaxFilenameLen)) | |
| 185 continue; | |
| 186 filenames->push_back(filename); | |
| 187 } | |
| 220 | 188 |
| 221 const int kMaxFilenameLen = 4096; | 189 DragFinish(hdrop); |
| 222 const unsigned num_files = DragQueryFileW(hdrop, 0xffffffff, 0, 0); | 190 GlobalUnlock(medium.hGlobal); |
|
sky
2014/07/10 22:44:34
I don't like having to worry if GlobalUnlock is ba
dcheng
2014/07/11 22:32:58
So it turns out ScopedHGlobal is poorly suited for
dcheng
2014/07/11 22:45:30
Actually I just forced everyone who wants a T* to
| |
| 223 for (unsigned int i = 0; i < num_files; ++i) { | 191 // We don't need to call ReleaseStgMedium here because as far as I can tell, |
| 224 wchar_t filename[kMaxFilenameLen]; | 192 // DragFinish frees the hGlobal for us. |
| 225 if (!DragQueryFileW(hdrop, i, filename, kMaxFilenameLen)) | 193 return true; |
| 226 continue; | |
| 227 filenames->push_back(filename); | |
| 228 } | 194 } |
| 229 | 195 |
| 230 DragFinish(hdrop); | 196 if (GetData(data_object, Clipboard::GetFilenameWFormatType(), &medium)) { |
| 231 GlobalUnlock(medium.hGlobal); | 197 { |
| 232 // We don't need to call ReleaseStgMedium here because as far as I can tell, | 198 // filename using unicode |
| 233 // DragFinish frees the hGlobal for us. | 199 base::win::ScopedHGlobal<wchar_t> data(medium.hGlobal); |
| 234 return true; | 200 if (data.get() && data.get()[0]) |
| 201 filenames->push_back(data.get()); | |
| 202 } | |
| 203 ReleaseStgMedium(&medium); | |
| 204 return true; | |
| 205 } | |
| 206 | |
| 207 if (GetData(data_object, Clipboard::GetFilenameFormatType(), &medium)) { | |
| 208 { | |
| 209 // filename using ascii | |
| 210 base::win::ScopedHGlobal<char> data(medium.hGlobal); | |
| 211 if (data.get() && data.get()[0]) | |
| 212 filenames->push_back(base::SysNativeMBToWide(data.get())); | |
| 213 } | |
| 214 ReleaseStgMedium(&medium); | |
| 215 return true; | |
| 216 } | |
| 217 | |
| 218 return false; | |
| 235 } | 219 } |
| 236 | 220 |
| 237 bool ClipboardUtil::GetPlainText(IDataObject* data_object, | 221 bool ClipboardUtil::GetPlainText(IDataObject* data_object, |
| 238 base::string16* plain_text) { | 222 base::string16* plain_text) { |
| 239 DCHECK(data_object && plain_text); | 223 DCHECK(data_object && plain_text); |
| 240 if (!HasPlainText(data_object)) | 224 if (!HasPlainText(data_object)) |
| 241 return false; | 225 return false; |
| 242 | 226 |
| 243 STGMEDIUM store; | 227 STGMEDIUM store; |
| 244 if (GetData(data_object, Clipboard::GetPlainTextWFormatType(), &store)) { | 228 if (GetData(data_object, Clipboard::GetPlainTextWFormatType(), &store)) { |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 256 // ascii text | 240 // ascii text |
| 257 base::win::ScopedHGlobal<char> data(store.hGlobal); | 241 base::win::ScopedHGlobal<char> data(store.hGlobal); |
| 258 plain_text->assign(base::UTF8ToWide(data.get())); | 242 plain_text->assign(base::UTF8ToWide(data.get())); |
| 259 } | 243 } |
| 260 ReleaseStgMedium(&store); | 244 ReleaseStgMedium(&store); |
| 261 return true; | 245 return true; |
| 262 } | 246 } |
| 263 | 247 |
| 264 // If a file is dropped on the window, it does not provide either of the | 248 // 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. | 249 // plain text formats, so here we try to forcibly get a url. |
| 250 GURL url; | |
| 266 base::string16 title; | 251 base::string16 title; |
| 267 return GetUrl(data_object, plain_text, &title, false); | 252 if (GetUrl(data_object, &url, &title, false)) { |
| 253 *plain_text = base::UTF8ToUTF16(url.spec()); | |
| 254 return true; | |
| 255 } | |
| 256 return false; | |
| 268 } | 257 } |
| 269 | 258 |
| 270 bool ClipboardUtil::GetHtml(IDataObject* data_object, | 259 bool ClipboardUtil::GetHtml(IDataObject* data_object, |
| 271 base::string16* html, std::string* base_url) { | 260 base::string16* html, std::string* base_url) { |
| 272 DCHECK(data_object && html && base_url); | 261 DCHECK(data_object && html && base_url); |
| 273 | 262 |
| 274 STGMEDIUM store; | 263 STGMEDIUM store; |
| 275 if (HasData(data_object, Clipboard::GetHtmlFormatType()) && | 264 if (HasData(data_object, Clipboard::GetHtmlFormatType()) && |
| 276 GetData(data_object, Clipboard::GetHtmlFormatType(), &store)) { | 265 GetData(data_object, Clipboard::GetHtmlFormatType(), &store)) { |
| 277 { | 266 { |
| (...skipping 225 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 503 end_fragment_start + end_fragment_str.length())); | 492 end_fragment_start + end_fragment_str.length())); |
| 504 } | 493 } |
| 505 } else { | 494 } else { |
| 506 *fragment_start = cf_html.find('>', tag_start) + 1; | 495 *fragment_start = cf_html.find('>', tag_start) + 1; |
| 507 size_t tag_end = cf_html.rfind("<!--EndFragment", std::string::npos); | 496 size_t tag_end = cf_html.rfind("<!--EndFragment", std::string::npos); |
| 508 *fragment_end = cf_html.rfind('<', tag_end); | 497 *fragment_end = cf_html.rfind('<', tag_end); |
| 509 } | 498 } |
| 510 } | 499 } |
| 511 | 500 |
| 512 } // namespace ui | 501 } // namespace ui |
| OLD | NEW |