OLD | NEW |
| (Empty) |
1 /* | |
2 * Copyright (C) 2007 Apple Inc. All rights reserved. | |
3 * | |
4 * Redistribution and use in source and binary forms, with or without | |
5 * modification, are permitted provided that the following conditions | |
6 * are met: | |
7 * 1. Redistributions of source code must retain the above copyright | |
8 * notice, this list of conditions and the following disclaimer. | |
9 * 2. Redistributions in binary form must reproduce the above copyright | |
10 * notice, this list of conditions and the following disclaimer in the | |
11 * documentation and/or other materials provided with the distribution. | |
12 * | |
13 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY | |
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | |
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR | |
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | |
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
24 */ | |
25 | |
26 #include "config.h" | |
27 #include "ClipboardUtilitiesWin.h" | |
28 | |
29 #include "KURL.h" | |
30 #include "CString.h" | |
31 #include "DocumentFragment.h" | |
32 #include "markup.h" | |
33 #include "PlatformString.h" | |
34 #include "TextEncoding.h" | |
35 #include <shlobj.h> | |
36 #include <shlwapi.h> | |
37 #include <shellapi.h> | |
38 #include <wininet.h> // for INTERNET_MAX_URL_LENGTH | |
39 | |
40 #include "base/clipboard_util.h" | |
41 | |
42 namespace WebCore { | |
43 | |
44 FORMATETC* cfHDropFormat() | |
45 { | |
46 static FORMATETC urlFormat = {CF_HDROP, 0, DVASPECT_CONTENT, -1, TYMED_HGLOB
AL}; | |
47 return &urlFormat; | |
48 } | |
49 | |
50 //Firefox text/html | |
51 static FORMATETC* texthtmlFormat() | |
52 { | |
53 static UINT cf = RegisterClipboardFormat(L"text/html"); | |
54 static FORMATETC texthtmlFormat = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBA
L}; | |
55 return &texthtmlFormat; | |
56 } | |
57 | |
58 HGLOBAL createGlobalData(const KURL& url, const String& title) | |
59 { | |
60 String mutableURL(url.string()); | |
61 String mutableTitle(title); | |
62 SIZE_T size = mutableURL.length() + mutableTitle.length() + 2; // +1 for "\
n" and +1 for null terminator | |
63 HGLOBAL cbData = ::GlobalAlloc(GPTR, size * sizeof(UChar)); | |
64 | |
65 if (cbData) { | |
66 PWSTR buffer = (PWSTR)::GlobalLock(cbData); | |
67 swprintf_s(buffer, size, L"%s\n%s", mutableURL.charactersWithNullTermina
tion(), mutableTitle.isNull() ? L"" : mutableTitle.charactersWithNullTermination
()); | |
68 ::GlobalUnlock(cbData); | |
69 } | |
70 return cbData; | |
71 } | |
72 | |
73 HGLOBAL createGlobalData(const String& str) | |
74 { | |
75 HGLOBAL globalData = ::GlobalAlloc(GPTR, (str.length() + 1) * sizeof(UChar))
; | |
76 if (!globalData) | |
77 return 0; | |
78 UChar* buffer = static_cast<UChar*>(::GlobalLock(globalData)); | |
79 memcpy(buffer, str.characters(), str.length() * sizeof(UChar)); | |
80 buffer[str.length()] = 0; | |
81 ::GlobalUnlock(globalData); | |
82 return globalData; | |
83 } | |
84 | |
85 HGLOBAL createGlobalData(const Vector<char>& vector) | |
86 { | |
87 HGLOBAL globalData = ::GlobalAlloc(GPTR, vector.size() + 1); | |
88 if (!globalData) | |
89 return 0; | |
90 char* buffer = static_cast<char*>(::GlobalLock(globalData)); | |
91 memcpy(buffer, vector.data(), vector.size()); | |
92 buffer[vector.size()] = 0; | |
93 ::GlobalUnlock(globalData); | |
94 return globalData; | |
95 } | |
96 | |
97 static void append(Vector<char>& vector, const char* string) | |
98 { | |
99 vector.append(string, strlen(string)); | |
100 } | |
101 | |
102 static void append(Vector<char>& vector, const CString& string) | |
103 { | |
104 vector.append(string.data(), string.length()); | |
105 } | |
106 | |
107 // Documentation for the CF_HTML format is available at http://msdn.microsoft.co
m/workshop/networking/clipboard/htmlclipboard.asp | |
108 void markupToCF_HTML(const String& markup, const String& srcURL, Vector<char>& r
esult) | |
109 { | |
110 if (markup.isEmpty()) | |
111 return; | |
112 | |
113 #define MAX_DIGITS 10 | |
114 #define MAKE_NUMBER_FORMAT_1(digits) MAKE_NUMBER_FORMAT_2(digits) | |
115 #define MAKE_NUMBER_FORMAT_2(digits) "%0" #digits "u" | |
116 #define NUMBER_FORMAT MAKE_NUMBER_FORMAT_1(MAX_DIGITS) | |
117 | |
118 const char* header = "Version:0.9\n" | |
119 "StartHTML:" NUMBER_FORMAT "\n" | |
120 "EndHTML:" NUMBER_FORMAT "\n" | |
121 "StartFragment:" NUMBER_FORMAT "\n" | |
122 "EndFragment:" NUMBER_FORMAT "\n"; | |
123 const char* sourceURLPrefix = "SourceURL:"; | |
124 | |
125 const char* startMarkup = "<HTML>\n<BODY>\n<!--StartFragment-->\n"; | |
126 const char* endMarkup = "\n<!--EndFragment-->\n</BODY>\n</HTML>"; | |
127 | |
128 CString sourceURLUTF8 = srcURL == blankURL() ? "" : srcURL.utf8(); | |
129 CString markupUTF8 = markup.utf8(); | |
130 | |
131 // calculate offsets | |
132 unsigned startHTMLOffset = strlen(header) - strlen(NUMBER_FORMAT) * 4 + MAX_
DIGITS * 4; | |
133 if (sourceURLUTF8.length()) | |
134 startHTMLOffset += strlen(sourceURLPrefix) + sourceURLUTF8.length() + 1; | |
135 unsigned startFragmentOffset = startHTMLOffset + strlen(startMarkup); | |
136 unsigned endFragmentOffset = startFragmentOffset + markupUTF8.length(); | |
137 unsigned endHTMLOffset = endFragmentOffset + strlen(endMarkup); | |
138 | |
139 append(result, String::format(header, startHTMLOffset, endHTMLOffset, startF
ragmentOffset, endFragmentOffset).utf8()); | |
140 if (sourceURLUTF8.length()) { | |
141 append(result, sourceURLPrefix); | |
142 append(result, sourceURLUTF8); | |
143 result.append('\n'); | |
144 } | |
145 append(result, startMarkup); | |
146 append(result, markupUTF8); | |
147 append(result, endMarkup); | |
148 | |
149 #undef MAX_DIGITS | |
150 #undef MAKE_NUMBER_FORMAT_1 | |
151 #undef MAKE_NUMBER_FORMAT_2 | |
152 #undef NUMBER_FORMAT | |
153 } | |
154 | |
155 String urlToMarkup(const KURL& url, const String& title) | |
156 { | |
157 String markup("<a href=\""); | |
158 markup.append(url.string()); | |
159 markup.append("\">"); | |
160 markup.append(title); | |
161 markup.append("</a>"); | |
162 return markup; | |
163 } | |
164 | |
165 String urlToImageMarkup(const KURL& url, const String& altStr) { | |
166 String markup("<img src=\""); | |
167 markup.append(url.string()); | |
168 markup.append("\""); | |
169 if (!altStr.isEmpty()) { | |
170 markup.append(" alt=\""); | |
171 // TODO(dglazkov): escape string | |
172 markup.append(altStr); | |
173 markup.append("\""); | |
174 } | |
175 markup.append("/>"); | |
176 return markup; | |
177 } | |
178 | |
179 void replaceNewlinesWithWindowsStyleNewlines(String& str) | |
180 { | |
181 static const UChar Newline = '\n'; | |
182 static const char* const WindowsNewline("\r\n"); | |
183 str.replace(Newline, WindowsNewline); | |
184 } | |
185 | |
186 void replaceNBSPWithSpace(String& str) | |
187 { | |
188 static const UChar NonBreakingSpaceCharacter = 0xA0; | |
189 static const UChar SpaceCharacter = ' '; | |
190 str.replace(NonBreakingSpaceCharacter, SpaceCharacter); | |
191 } | |
192 | |
193 FORMATETC* urlWFormat() | |
194 { | |
195 static UINT cf = RegisterClipboardFormat(L"UniformResourceLocatorW"); | |
196 static FORMATETC urlFormat = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; | |
197 return &urlFormat; | |
198 } | |
199 | |
200 FORMATETC* urlFormat() | |
201 { | |
202 static UINT cf = RegisterClipboardFormat(L"UniformResourceLocator"); | |
203 static FORMATETC urlFormat = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; | |
204 return &urlFormat; | |
205 } | |
206 | |
207 FORMATETC* plainTextFormat() | |
208 { | |
209 static FORMATETC textFormat = {CF_TEXT, 0, DVASPECT_CONTENT, -1, TYMED_HGLOB
AL}; | |
210 return &textFormat; | |
211 } | |
212 | |
213 FORMATETC* plainTextWFormat() | |
214 { | |
215 static FORMATETC textFormat = {CF_UNICODETEXT, 0, DVASPECT_CONTENT, -1, TYME
D_HGLOBAL}; | |
216 return &textFormat; | |
217 } | |
218 | |
219 FORMATETC* filenameWFormat() | |
220 { | |
221 static UINT cf = RegisterClipboardFormat(L"FileNameW"); | |
222 static FORMATETC urlFormat = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; | |
223 return &urlFormat; | |
224 } | |
225 | |
226 FORMATETC* filenameFormat() | |
227 { | |
228 static UINT cf = RegisterClipboardFormat(L"FileName"); | |
229 static FORMATETC urlFormat = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; | |
230 return &urlFormat; | |
231 } | |
232 | |
233 //MSIE HTML Format | |
234 FORMATETC* htmlFormat() | |
235 { | |
236 static UINT cf = RegisterClipboardFormat(L"HTML Format"); | |
237 static FORMATETC htmlFormat = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; | |
238 return &htmlFormat; | |
239 } | |
240 | |
241 FORMATETC* smartPasteFormat() | |
242 { | |
243 static UINT cf = RegisterClipboardFormat(L"WebKit Smart Paste Format"); | |
244 static FORMATETC htmlFormat = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; | |
245 return &htmlFormat; | |
246 } | |
247 | |
248 PassRefPtr<DocumentFragment> fragmentFromFilenames(Document*, const IDataObject*
) | |
249 { | |
250 //FIXME: We should be able to create fragments from files | |
251 return 0; | |
252 } | |
253 | |
254 bool containsFilenames(const IDataObject*) | |
255 { | |
256 //FIXME: We'll want to update this once we can produce fragments from files | |
257 return false; | |
258 } | |
259 | |
260 //Convert a String containing CF_HTML formatted text to a DocumentFragment | |
261 PassRefPtr<DocumentFragment> fragmentFromCF_HTML(Document* doc, const String& cf
_html) | |
262 { | |
263 // obtain baseURL if present | |
264 String srcURLStr("sourceURL:"); | |
265 String srcURL; | |
266 unsigned lineStart = cf_html.find(srcURLStr, 0, false); | |
267 if (lineStart != -1) { | |
268 unsigned srcEnd = cf_html.find("\n", lineStart, false); | |
269 unsigned srcStart = lineStart+srcURLStr.length(); | |
270 String rawSrcURL = cf_html.substring(srcStart, srcEnd-srcStart); | |
271 replaceNBSPWithSpace(rawSrcURL); | |
272 srcURL = rawSrcURL.stripWhiteSpace(); | |
273 } | |
274 | |
275 // find the markup between "<!--StartFragment -->" and "<!--EndFragment -->"
, accounting for browser quirks | |
276 unsigned markupStart = cf_html.find("<html", 0, false); | |
277 unsigned tagStart = cf_html.find("startfragment", markupStart, false); | |
278 unsigned fragmentStart = cf_html.find('>', tagStart) + 1; | |
279 unsigned tagEnd = cf_html.find("endfragment", fragmentStart, false); | |
280 unsigned fragmentEnd = cf_html.reverseFind('<', tagEnd); | |
281 String markup = cf_html.substring(fragmentStart, fragmentEnd - fragmentStart
).stripWhiteSpace(); | |
282 | |
283 return createFragmentFromMarkup(doc, markup, srcURL); | |
284 } | |
285 | |
286 | |
287 PassRefPtr<DocumentFragment> fragmentFromHTML(Document* doc, IDataObject* data) | |
288 { | |
289 if (!doc || !data) | |
290 return 0; | |
291 | |
292 STGMEDIUM store; | |
293 String html; | |
294 String srcURL; | |
295 if (SUCCEEDED(data->GetData(ClipboardUtil::GetHtmlFormat(), &store))) { | |
296 //MS HTML Format parsing | |
297 char* data = (char*)GlobalLock(store.hGlobal); | |
298 SIZE_T dataSize = ::GlobalSize(store.hGlobal); | |
299 String cf_html(UTF8Encoding().decode(data, dataSize)); | |
300 GlobalUnlock(store.hGlobal); | |
301 ReleaseStgMedium(&store); | |
302 if (PassRefPtr<DocumentFragment> fragment = fragmentFromCF_HTML(doc, cf_
html)) | |
303 return fragment; | |
304 } | |
305 if (SUCCEEDED(data->GetData(ClipboardUtil::GetTextHtmlFormat(), &store))) { | |
306 //raw html | |
307 UChar* data = (UChar*)GlobalLock(store.hGlobal); | |
308 html = String(data); | |
309 GlobalUnlock(store.hGlobal); | |
310 ReleaseStgMedium(&store); | |
311 return createFragmentFromMarkup(doc, html, srcURL); | |
312 } | |
313 | |
314 return 0; | |
315 } | |
316 | |
317 bool containsHTML(IDataObject* data) | |
318 { | |
319 return SUCCEEDED(data->QueryGetData(ClipboardUtil::GetTextHtmlFormat())) || | |
320 SUCCEEDED(data->QueryGetData(ClipboardUtil::GetHtmlFormat())); | |
321 } | |
322 | |
323 } // namespace WebCore | |
OLD | NEW |