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