blob: 5902b4389fc70c00fc444a45b192ace6363b99f4 [file] [log] [blame]
license.botf003cfe2008-08-24 09:55:55 +09001// 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.
initial.commit3f4a7322008-07-27 06:49:38 +09004
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
16namespace {
17
18bool 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
52bool 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
72FORMATETC* 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
78FORMATETC* 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
84FORMATETC* 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
91FORMATETC* 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
97FORMATETC* 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
104FORMATETC* 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
110FORMATETC* ClipboardUtil::GetFilenameFormat()
111{
112 static UINT cf = RegisterClipboardFormat(CFSTR_FILENAMEA);
113 static FORMATETC format = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
114 return &format;
115}
116
117FORMATETC* ClipboardUtil::GetHtmlFormat() {
118 static UINT cf = RegisterClipboardFormat(L"HTML Format");
119 static FORMATETC format = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
120 return &format;
121}
122
123FORMATETC* ClipboardUtil::GetTextHtmlFormat() {
124 static UINT cf = RegisterClipboardFormat(L"text/html");
125 static FORMATETC format = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
126 return &format;
127}
128
129FORMATETC* ClipboardUtil::GetCFHDropFormat() {
130 // We don't need to register this format since it's a built in format.
131 static FORMATETC format = {CF_HDROP, 0, DVASPECT_CONTENT, -1,
132 TYMED_HGLOBAL};
133 return &format;
134}
135
136FORMATETC* ClipboardUtil::GetFileDescriptorFormat() {
137 static UINT cf = RegisterClipboardFormat(CFSTR_FILEDESCRIPTOR);
138 static FORMATETC format = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
139 return &format;
140}
141
142FORMATETC* ClipboardUtil::GetFileContentFormatZero() {
143 static UINT cf = RegisterClipboardFormat(CFSTR_FILECONTENTS);
144 static FORMATETC format = {cf, 0, DVASPECT_CONTENT, 0, TYMED_HGLOBAL};
145 return &format;
146}
147
148FORMATETC* ClipboardUtil::GetWebKitSmartPasteFormat() {
149 static UINT cf = RegisterClipboardFormat(L"WebKit Smart Paste Format");
150 static FORMATETC format = {cf, 0, DVASPECT_CONTENT, 0, TYMED_HGLOBAL};
151 return &format;
152}
153
154
155bool ClipboardUtil::HasUrl(IDataObject* data_object) {
156 DCHECK(data_object);
157 return SUCCEEDED(data_object->QueryGetData(GetMozUrlFormat())) ||
158 SUCCEEDED(data_object->QueryGetData(GetUrlWFormat())) ||
159 SUCCEEDED(data_object->QueryGetData(GetUrlFormat())) ||
160 SUCCEEDED(data_object->QueryGetData(GetFilenameWFormat())) ||
161 SUCCEEDED(data_object->QueryGetData(GetFilenameFormat()));
162}
163
164bool ClipboardUtil::HasFilenames(IDataObject* data_object) {
165 DCHECK(data_object);
166 return SUCCEEDED(data_object->QueryGetData(GetCFHDropFormat()));
167}
168
169bool ClipboardUtil::HasPlainText(IDataObject* data_object) {
170 DCHECK(data_object);
171 return SUCCEEDED(data_object->QueryGetData(GetPlainTextWFormat())) ||
172 SUCCEEDED(data_object->QueryGetData(GetPlainTextFormat()));
173}
174
175
176bool ClipboardUtil::GetUrl(IDataObject* data_object,
177 std::wstring* url, std::wstring* title) {
178 DCHECK(data_object && url && title);
179 if (!HasUrl(data_object))
180 return false;
181
182 // Try to extract a URL from |data_object| in a variety of formats.
183 STGMEDIUM store;
184 if (GetUrlFromHDrop(data_object, url, title)) {
185 return true;
186 }
187
188 if (SUCCEEDED(data_object->GetData(GetMozUrlFormat(), &store)) ||
189 SUCCEEDED(data_object->GetData(GetUrlWFormat(), &store))) {
190 // Mozilla URL format or unicode URL
191 ScopedHGlobal<wchar_t> data(store.hGlobal);
192 bool success = SplitUrlAndTitle(data.get(), url, title);
193 ReleaseStgMedium(&store);
194 if (success)
195 return true;
196 }
197
198 if (SUCCEEDED(data_object->GetData(GetUrlFormat(), &store))) {
199 // URL using ascii
200 ScopedHGlobal<char> data(store.hGlobal);
201 bool success = SplitUrlAndTitle(UTF8ToWide(data.get()), url, title);
202 ReleaseStgMedium(&store);
203 if (success)
204 return true;
205 }
206
207 if (SUCCEEDED(data_object->GetData(GetFilenameWFormat(), &store))) {
208 // filename using unicode
209 ScopedHGlobal<wchar_t> data(store.hGlobal);
210 bool success = false;
211 if (data.get() && data.get()[0] && (PathFileExists(data.get()) ||
212 PathIsUNC(data.get()))) {
213 wchar_t file_url[INTERNET_MAX_URL_LENGTH];
214 DWORD file_url_len = sizeof(file_url) / sizeof(file_url[0]);
215 if (SUCCEEDED(::UrlCreateFromPathW(data.get(), file_url, &file_url_len,
216 0))) {
217 *url = file_url;
218 title->assign(file_url);
219 success = true;
220 }
221 }
222 ReleaseStgMedium(&store);
223 if (success)
224 return true;
225 }
226
227 if (SUCCEEDED(data_object->GetData(GetFilenameFormat(), &store))) {
228 // filename using ascii
229 ScopedHGlobal<char> data(store.hGlobal);
230 bool success = false;
231 if (data.get() && data.get()[0] && (PathFileExistsA(data.get()) ||
232 PathIsUNCA(data.get()))) {
233 char file_url[INTERNET_MAX_URL_LENGTH];
234 DWORD file_url_len = sizeof(file_url) / sizeof(file_url[0]);
235 if (SUCCEEDED(::UrlCreateFromPathA(data.get(), file_url, &file_url_len, 0))) {
236 *url = UTF8ToWide(file_url);
237 title->assign(*url);
238 success = true;
239 }
240 }
241 ReleaseStgMedium(&store);
242 if (success)
243 return true;
244 }
245
246 return false;
247}
248
249bool ClipboardUtil::GetFilenames(IDataObject* data_object,
250 std::vector<std::wstring>* filenames) {
251 DCHECK(data_object && filenames);
252 if (!HasFilenames(data_object))
253 return false;
254
255 STGMEDIUM medium;
256 if (FAILED(data_object->GetData(GetCFHDropFormat(), &medium)))
257 return false;
258
259 HDROP hdrop = static_cast<HDROP>(GlobalLock(medium.hGlobal));
260 if (!hdrop)
261 return false;
262
263 const int kMaxFilenameLen = 4096;
264 const unsigned num_files = DragQueryFileW(hdrop, 0xffffffff, 0, 0);
265 for (unsigned int i = 0; i < num_files; ++i) {
266 wchar_t filename[kMaxFilenameLen];
267 if (!DragQueryFileW(hdrop, i, filename, kMaxFilenameLen))
268 continue;
269 filenames->push_back(filename);
270 }
271
272 DragFinish(hdrop);
273 GlobalUnlock(medium.hGlobal);
274 // We don't need to call ReleaseStgMedium here because as far as I can tell,
275 // DragFinish frees the hGlobal for us.
276 return true;
277}
278
279bool ClipboardUtil::GetPlainText(IDataObject* data_object,
280 std::wstring* plain_text) {
281 DCHECK(data_object && plain_text);
282 if (!HasPlainText(data_object))
283 return false;
284
285 STGMEDIUM store;
286 bool success = false;
287 if (SUCCEEDED(data_object->GetData(GetPlainTextWFormat(), &store))) {
288 // Unicode text
289 ScopedHGlobal<wchar_t> data(store.hGlobal);
290 plain_text->assign(data.get());
291 ReleaseStgMedium(&store);
292 success = true;
293 } else if (SUCCEEDED(data_object->GetData(GetPlainTextFormat(), &store))) {
294 // ascii text
295 ScopedHGlobal<char> data(store.hGlobal);
296 plain_text->assign(UTF8ToWide(data.get()));
297 ReleaseStgMedium(&store);
298 success = true;
299 } else {
300 //If a file is dropped on the window, it does not provide either of the
301 //plain text formats, so here we try to forcibly get a url.
302 std::wstring title;
303 success = GetUrl(data_object, plain_text, &title);
304 }
305
306 return success;
307}
308
309bool ClipboardUtil::GetCFHtml(IDataObject* data_object,
310 std::wstring* cf_html) {
311 DCHECK(data_object && cf_html);
312 if (FAILED(data_object->QueryGetData(GetHtmlFormat())))
313 return false;
314
315 STGMEDIUM store;
316 if (FAILED(data_object->GetData(GetHtmlFormat(), &store)))
317 return false;
318
319 // MS CF html
320 ScopedHGlobal<char> data(store.hGlobal);
321 cf_html->assign(UTF8ToWide(std::string(data.get(), data.Size())));
322 ReleaseStgMedium(&store);
323 return true;
324}
325
326bool ClipboardUtil::GetTextHtml(IDataObject* data_object,
327 std::wstring* text_html) {
328 DCHECK(data_object && text_html);
329 if (FAILED(data_object->QueryGetData(GetTextHtmlFormat())))
330 return false;
331
332 STGMEDIUM store;
333 if (FAILED(data_object->GetData(GetTextHtmlFormat(), &store)))
334 return false;
335
336 // raw html
337 ScopedHGlobal<wchar_t> data(store.hGlobal);
338 text_html->assign(data.get());
339 ReleaseStgMedium(&store);
340 return true;
341}
342
343bool ClipboardUtil::GetFileContents(IDataObject* data_object,
344 std::wstring* filename, std::string* file_contents) {
345 DCHECK(data_object && filename && file_contents);
346 bool has_data =
347 SUCCEEDED(data_object->QueryGetData(GetFileContentFormatZero())) ||
348 SUCCEEDED(data_object->QueryGetData(GetFileDescriptorFormat()));
349
350 if (!has_data)
351 return false;
352
353 STGMEDIUM content;
354 // The call to GetData can be very slow depending on what is in
355 // |data_object|.
356 if (SUCCEEDED(data_object->GetData(GetFileContentFormatZero(), &content))) {
357 if (TYMED_HGLOBAL == content.tymed) {
358 ScopedHGlobal<char> data(content.hGlobal);
359 // The size includes the trailing NULL byte. We don't want it.
360 file_contents->assign(data.get(), data.Size() - 1);
361 }
362 ReleaseStgMedium(&content);
363 }
364
365 STGMEDIUM description;
366 if (SUCCEEDED(data_object->GetData(GetFileDescriptorFormat(),
367 &description))) {
368 ScopedHGlobal<FILEGROUPDESCRIPTOR> fgd(description.hGlobal);
369 // We expect there to be at least one file in here.
370 DCHECK(fgd->cItems >= 1);
371 filename->assign(fgd->fgd[0].cFileName);
372 ReleaseStgMedium(&description);
373 }
374 return true;
375}
license.botf003cfe2008-08-24 09:55:55 +0900376