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

Side by Side Diff: ui/base/clipboard/clipboard_util_win.cc

Issue 380553002: Add a unit test that filenames aren't unintentionally converted to URLs. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Fix Windows Created 6 years, 5 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
OLDNEW
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
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
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
OLDNEW
« no previous file with comments | « ui/base/clipboard/clipboard_util_win.h ('k') | ui/base/dragdrop/os_exchange_data_provider_win.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698