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 |