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