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 45namespace WebCore { 46 47static UINT HTMLClipboardFormat = 0; 48static UINT BookmarkClipboardFormat = 0; 49static UINT WebSmartPasteFormat = 0; 50 51static 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 79Pasteboard* Pasteboard::generalPasteboard() 80{ 81 static Pasteboard* pasteboard = new Pasteboard; 82 return pasteboard; 83} 84 85Pasteboard::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 103void Pasteboard::clear() 104{ 105 if (::OpenClipboard(m_owner)) { 106 ::EmptyClipboard(); 107 ::CloseClipboard(); 108 } 109} 110 111void 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 148void 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 163void 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 203void 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 246bool Pasteboard::canSmartReplace() 247{ 248 return ::IsClipboardFormatAvailable(WebSmartPasteFormat); 249} 250 251String 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 280PassRefPtr<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