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