| 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 |