1 /* 2 * Copyright (C) 2006, 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 "Pasteboard.h" 28 29 #include "BitmapInfo.h" 30 #include "CString.h" 31 #include "ClipboardUtilitiesWin.h" 32 #include "Document.h" 33 #include "DocumentFragment.h" 34 #include "Element.h" 35 #include "Frame.h" 36 #include "HitTestResult.h" 37 #include "Image.h" 38 #include "KURL.h" 39 #include "Page.h" 40 #include "Range.h" 41 #include "RenderImage.h" 42 #include "TextEncoding.h" 43 #include "markup.h" 44 45 namespace WebCore { 46 47 static UINT HTMLClipboardFormat = 0; 48 static UINT BookmarkClipboardFormat = 0; 49 static UINT WebSmartPasteFormat = 0; 50 51 static LRESULT CALLBACK PasteboardOwnerWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) 52 { 53 LRESULT lresult = 0; 54 LONG_PTR longPtr = GetWindowLongPtr(hWnd, 0); 55 56 switch(message) { 57 case WM_RENDERFORMAT: 58 // This message comes when SetClipboardData was sent a null data handle 59 // and now it's come time to put the data on the clipboard. 60 break; 61 case WM_RENDERALLFORMATS: 62 // This message comes when SetClipboardData was sent a null data handle 63 // and now this application is about to quit, so it must put data on 64 // the clipboard before it exits. 65 break; 66 case WM_DRAWCLIPBOARD: 67 break; 68 case WM_DESTROY: 69 break; 70 case WM_CHANGECBCHAIN: 71 break; 72 default: 73 lresult = DefWindowProc(hWnd, message, wParam, lParam); 74 break; 75 } 76 return lresult; 77 } 78 79 Pasteboard* Pasteboard::generalPasteboard() 80 { 81 static Pasteboard* pasteboard = new Pasteboard; 82 return pasteboard; 83 } 84 85 Pasteboard::Pasteboard() 86 { 87 // make a dummy HWND to be the Windows clipboard's owner 88 WNDCLASSEX wcex = {0}; 89 wcex.cbSize = sizeof(WNDCLASSEX); 90 wcex.lpfnWndProc = PasteboardOwnerWndProc; 91 wcex.hInstance = Page::instanceHandle(); 92 wcex.lpszClassName = L"PasteboardOwnerWindowClass"; 93 ::RegisterClassEx(&wcex); 94 95 m_owner = ::CreateWindow(L"PasteboardOwnerWindowClass", L"PasteboardOwnerWindow", 0, 0, 0, 0, 0, 96 HWND_MESSAGE, 0, 0, 0); 97 98 HTMLClipboardFormat = ::RegisterClipboardFormat(L"HTML Format"); 99 BookmarkClipboardFormat = ::RegisterClipboardFormat(L"UniformResourceLocatorW"); 100 WebSmartPasteFormat = ::RegisterClipboardFormat(L"WebKit Smart Paste Format"); 101 } 102 103 void Pasteboard::clear() 104 { 105 if (::OpenClipboard(m_owner)) { 106 ::EmptyClipboard(); 107 ::CloseClipboard(); 108 } 109 } 110 111 void Pasteboard::writeSelection(Range* selectedRange, bool canSmartCopyOrDelete, Frame* frame) 112 { 113 clear(); 114 115 // Put CF_HTML format on the pasteboard 116 if (::OpenClipboard(m_owner)) { 117 ExceptionCode ec = 0; 118 Vector<char> data; 119 markupToCF_HTML(createMarkup(selectedRange, 0, AnnotateForInterchange), 120 selectedRange->startContainer(ec)->document()->url().string(), data); 121 HGLOBAL cbData = createGlobalData(data); 122 if (!::SetClipboardData(HTMLClipboardFormat, cbData)) 123 ::GlobalFree(cbData); 124 ::CloseClipboard(); 125 } 126 127 // Put plain string on the pasteboard. CF_UNICODETEXT covers CF_TEXT as well 128 String str = frame->selectedText(); 129 replaceNewlinesWithWindowsStyleNewlines(str); 130 replaceNBSPWithSpace(str); 131 if (::OpenClipboard(m_owner)) { 132 HGLOBAL cbData = createGlobalData(str); 133 if (!::SetClipboardData(CF_UNICODETEXT, cbData)) 134 ::GlobalFree(cbData); 135 ::CloseClipboard(); 136 } 137 138 // enable smart-replacing later on by putting dummy data on the pasteboard 139 if (canSmartCopyOrDelete) { 140 if (::OpenClipboard(m_owner)) { 141 ::SetClipboardData(WebSmartPasteFormat, NULL); 142 ::CloseClipboard(); 143 } 144 145 } 146 } 147 148 void Pasteboard::writePlainText(const String& text) 149 { 150 clear(); 151 152 // Put plain string on the pasteboard. CF_UNICODETEXT covers CF_TEXT as well 153 String str = text; 154 replaceNewlinesWithWindowsStyleNewlines(str); 155 if (::OpenClipboard(m_owner)) { 156 HGLOBAL cbData = createGlobalData(str); 157 if (!::SetClipboardData(CF_UNICODETEXT, cbData)) 158 ::GlobalFree(cbData); 159 ::CloseClipboard(); 160 } 161 } 162 163 void Pasteboard::writeURL(const KURL& url, const String& titleStr, Frame* frame) 164 { 165 ASSERT(!url.isEmpty()); 166 167 clear(); 168 169 String title(titleStr); 170 if (title.isEmpty()) { 171 title = url.lastPathComponent(); 172 if (title.isEmpty()) 173 title = url.host(); 174 } 175 176 // write to clipboard in format com.apple.safari.bookmarkdata to be able to paste into the bookmarks view with appropriate title 177 if (::OpenClipboard(m_owner)) { 178 HGLOBAL cbData = createGlobalData(url, title); 179 if (!::SetClipboardData(BookmarkClipboardFormat, cbData)) 180 ::GlobalFree(cbData); 181 ::CloseClipboard(); 182 } 183 184 // write to clipboard in format CF_HTML to be able to paste into contenteditable areas as a link 185 if (::OpenClipboard(m_owner)) { 186 Vector<char> data; 187 markupToCF_HTML(urlToMarkup(url, title), "", data); 188 HGLOBAL cbData = createGlobalData(data); 189 if (!::SetClipboardData(HTMLClipboardFormat, cbData)) 190 ::GlobalFree(cbData); 191 ::CloseClipboard(); 192 } 193 194 // bare-bones CF_UNICODETEXT support 195 if (::OpenClipboard(m_owner)) { 196 HGLOBAL cbData = createGlobalData(url.string()); 197 if (!::SetClipboardData(CF_UNICODETEXT, cbData)) 198 ::GlobalFree(cbData); 199 ::CloseClipboard(); 200 } 201 } 202 203 void Pasteboard::writeImage(Node* node, const KURL&, const String&) 204 { 205 ASSERT(node && node->renderer() && node->renderer()->isImage()); 206 RenderImage* renderer = toRenderImage(node->renderer()); 207 CachedImage* cachedImage = renderer->cachedImage(); 208 ASSERT(cachedImage); 209 Image* image = cachedImage->image(); 210 ASSERT(image); 211 212 clear(); 213 214 HDC dc = GetDC(0); 215 HDC compatibleDC = CreateCompatibleDC(0); 216 HDC sourceDC = CreateCompatibleDC(0); 217 HBITMAP resultBitmap = CreateCompatibleBitmap(dc, image->width(), image->height()); 218 HBITMAP oldBitmap = (HBITMAP)SelectObject(compatibleDC, resultBitmap); 219 220 BitmapInfo bmInfo = BitmapInfo::create(image->size()); 221 222 HBITMAP coreBitmap = CreateDIBSection(dc, &bmInfo, DIB_RGB_COLORS, 0, 0, 0); 223 HBITMAP oldSource = (HBITMAP)SelectObject(sourceDC, coreBitmap); 224 image->getHBITMAP(coreBitmap); 225 226 BLENDFUNCTION bf = {AC_SRC_OVER, 0, 255, AC_SRC_ALPHA}; 227 AlphaBlend(compatibleDC, 0, 0, image->width(), image->height(), 228 sourceDC, 0, 0, image->width(), image->height(), bf); 229 230 SelectObject(compatibleDC, oldBitmap); 231 SelectObject(sourceDC, oldSource); 232 233 DeleteObject(oldBitmap); 234 DeleteObject(oldSource); 235 DeleteObject(coreBitmap); 236 ReleaseDC(0, dc); 237 DeleteDC(compatibleDC); 238 DeleteDC(sourceDC); 239 240 if (::OpenClipboard(m_owner)) { 241 ::SetClipboardData(CF_BITMAP, resultBitmap); 242 ::CloseClipboard(); 243 } 244 } 245 246 bool Pasteboard::canSmartReplace() 247 { 248 return ::IsClipboardFormatAvailable(WebSmartPasteFormat); 249 } 250 251 String Pasteboard::plainText(Frame* frame) 252 { 253 if (::IsClipboardFormatAvailable(CF_UNICODETEXT) && ::OpenClipboard(m_owner)) { 254 HANDLE cbData = ::GetClipboardData(CF_UNICODETEXT); 255 if (cbData) { 256 UChar* buffer = (UChar*)::GlobalLock(cbData); 257 String fromClipboard(buffer); 258 ::GlobalUnlock(cbData); 259 ::CloseClipboard(); 260 return fromClipboard; 261 } else 262 ::CloseClipboard(); 263 } 264 265 if (::IsClipboardFormatAvailable(CF_TEXT) && ::OpenClipboard(m_owner)) { 266 HANDLE cbData = ::GetClipboardData(CF_TEXT); 267 if (cbData) { 268 char* buffer = (char*)::GlobalLock(cbData); 269 String fromClipboard(buffer); 270 ::GlobalUnlock(cbData); 271 ::CloseClipboard(); 272 return fromClipboard; 273 } else 274 ::CloseClipboard(); 275 } 276 277 return String(); 278 } 279 280 PassRefPtr<DocumentFragment> Pasteboard::documentFragment(Frame* frame, PassRefPtr<Range> context, bool allowPlainText, bool& chosePlainText) 281 { 282 chosePlainText = false; 283 284 if (::IsClipboardFormatAvailable(HTMLClipboardFormat) && ::OpenClipboard(m_owner)) { 285 // get data off of clipboard 286 HANDLE cbData = ::GetClipboardData(HTMLClipboardFormat); 287 if (cbData) { 288 SIZE_T dataSize = ::GlobalSize(cbData); 289 String cf_html(UTF8Encoding().decode((char*)::GlobalLock(cbData), dataSize)); 290 ::GlobalUnlock(cbData); 291 ::CloseClipboard(); 292 293 PassRefPtr<DocumentFragment> fragment = fragmentFromCF_HTML(frame->document(), cf_html); 294 if (fragment) 295 return fragment; 296 } else 297 ::CloseClipboard(); 298 } 299 300 if (allowPlainText && ::IsClipboardFormatAvailable(CF_UNICODETEXT)) { 301 chosePlainText = true; 302 if (::OpenClipboard(m_owner)) { 303 HANDLE cbData = ::GetClipboardData(CF_UNICODETEXT); 304 if (cbData) { 305 UChar* buffer = (UChar*)GlobalLock(cbData); 306 String str(buffer); 307 ::GlobalUnlock( cbData ); 308 ::CloseClipboard(); 309 RefPtr<DocumentFragment> fragment = createFragmentFromText(context.get(), str); 310 if (fragment) 311 return fragment.release(); 312 } else 313 ::CloseClipboard(); 314 } 315 } 316 317 if (allowPlainText && ::IsClipboardFormatAvailable(CF_TEXT)) { 318 chosePlainText = true; 319 if (::OpenClipboard(m_owner)) { 320 HANDLE cbData = ::GetClipboardData(CF_TEXT); 321 if (cbData) { 322 char* buffer = (char*)GlobalLock(cbData); 323 String str(buffer); 324 ::GlobalUnlock( cbData ); 325 ::CloseClipboard(); 326 RefPtr<DocumentFragment> fragment = createFragmentFromText(context.get(), str); 327 if (fragment) 328 return fragment.release(); 329 } else 330 ::CloseClipboard(); 331 } 332 } 333 334 return 0; 335 } 336 337 } // namespace WebCore 338