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
PasteboardOwnerWndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)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
generalPasteboard()79 Pasteboard* Pasteboard::generalPasteboard()
80 {
81 static Pasteboard* pasteboard = new Pasteboard;
82 return pasteboard;
83 }
84
Pasteboard()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
clear()103 void Pasteboard::clear()
104 {
105 if (::OpenClipboard(m_owner)) {
106 ::EmptyClipboard();
107 ::CloseClipboard();
108 }
109 }
110
writeSelection(Range * selectedRange,bool canSmartCopyOrDelete,Frame * frame)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
writeURL(const KURL & url,const String & titleStr,Frame * frame)148 void Pasteboard::writeURL(const KURL& url, const String& titleStr, Frame* frame)
149 {
150 ASSERT(!url.isEmpty());
151
152 clear();
153
154 String title(titleStr);
155 if (title.isEmpty()) {
156 title = url.lastPathComponent();
157 if (title.isEmpty())
158 title = url.host();
159 }
160
161 // write to clipboard in format com.apple.safari.bookmarkdata to be able to paste into the bookmarks view with appropriate title
162 if (::OpenClipboard(m_owner)) {
163 HGLOBAL cbData = createGlobalData(url, title);
164 if (!::SetClipboardData(BookmarkClipboardFormat, cbData))
165 ::GlobalFree(cbData);
166 ::CloseClipboard();
167 }
168
169 // write to clipboard in format CF_HTML to be able to paste into contenteditable areas as a link
170 if (::OpenClipboard(m_owner)) {
171 Vector<char> data;
172 markupToCF_HTML(urlToMarkup(url, title), "", data);
173 HGLOBAL cbData = createGlobalData(data);
174 if (!::SetClipboardData(HTMLClipboardFormat, cbData))
175 ::GlobalFree(cbData);
176 ::CloseClipboard();
177 }
178
179 // bare-bones CF_UNICODETEXT support
180 if (::OpenClipboard(m_owner)) {
181 HGLOBAL cbData = createGlobalData(url.string());
182 if (!::SetClipboardData(CF_UNICODETEXT, cbData))
183 ::GlobalFree(cbData);
184 ::CloseClipboard();
185 }
186 }
187
writeImage(Node * node,const KURL &,const String &)188 void Pasteboard::writeImage(Node* node, const KURL&, const String&)
189 {
190 ASSERT(node && node->renderer() && node->renderer()->isImage());
191 RenderImage* renderer = toRenderImage(node->renderer());
192 CachedImage* cachedImage = renderer->cachedImage();
193 ASSERT(cachedImage);
194 Image* image = cachedImage->image();
195 ASSERT(image);
196
197 clear();
198
199 HDC dc = GetDC(0);
200 HDC compatibleDC = CreateCompatibleDC(0);
201 HDC sourceDC = CreateCompatibleDC(0);
202 HBITMAP resultBitmap = CreateCompatibleBitmap(dc, image->width(), image->height());
203 HBITMAP oldBitmap = (HBITMAP)SelectObject(compatibleDC, resultBitmap);
204
205 BitmapInfo bmInfo = BitmapInfo::create(image->size());
206
207 HBITMAP coreBitmap = CreateDIBSection(dc, &bmInfo, DIB_RGB_COLORS, 0, 0, 0);
208 HBITMAP oldSource = (HBITMAP)SelectObject(sourceDC, coreBitmap);
209 image->getHBITMAP(coreBitmap);
210
211 BLENDFUNCTION bf = {AC_SRC_OVER, 0, 255, AC_SRC_ALPHA};
212 AlphaBlend(compatibleDC, 0, 0, image->width(), image->height(),
213 sourceDC, 0, 0, image->width(), image->height(), bf);
214
215 SelectObject(compatibleDC, oldBitmap);
216 SelectObject(sourceDC, oldSource);
217
218 DeleteObject(oldBitmap);
219 DeleteObject(oldSource);
220 DeleteObject(coreBitmap);
221 ReleaseDC(0, dc);
222 DeleteDC(compatibleDC);
223 DeleteDC(sourceDC);
224
225 if (::OpenClipboard(m_owner)) {
226 ::SetClipboardData(CF_BITMAP, resultBitmap);
227 ::CloseClipboard();
228 }
229 }
230
canSmartReplace()231 bool Pasteboard::canSmartReplace()
232 {
233 return ::IsClipboardFormatAvailable(WebSmartPasteFormat);
234 }
235
plainText(Frame * frame)236 String Pasteboard::plainText(Frame* frame)
237 {
238 if (::IsClipboardFormatAvailable(CF_UNICODETEXT) && ::OpenClipboard(m_owner)) {
239 HANDLE cbData = ::GetClipboardData(CF_UNICODETEXT);
240 if (cbData) {
241 UChar* buffer = (UChar*)::GlobalLock(cbData);
242 String fromClipboard(buffer);
243 ::GlobalUnlock(cbData);
244 ::CloseClipboard();
245 return fromClipboard;
246 } else
247 ::CloseClipboard();
248 }
249
250 if (::IsClipboardFormatAvailable(CF_TEXT) && ::OpenClipboard(m_owner)) {
251 HANDLE cbData = ::GetClipboardData(CF_TEXT);
252 if (cbData) {
253 char* buffer = (char*)::GlobalLock(cbData);
254 String fromClipboard(buffer);
255 ::GlobalUnlock(cbData);
256 ::CloseClipboard();
257 return fromClipboard;
258 } else
259 ::CloseClipboard();
260 }
261
262 return String();
263 }
264
documentFragment(Frame * frame,PassRefPtr<Range> context,bool allowPlainText,bool & chosePlainText)265 PassRefPtr<DocumentFragment> Pasteboard::documentFragment(Frame* frame, PassRefPtr<Range> context, bool allowPlainText, bool& chosePlainText)
266 {
267 chosePlainText = false;
268
269 if (::IsClipboardFormatAvailable(HTMLClipboardFormat) && ::OpenClipboard(m_owner)) {
270 // get data off of clipboard
271 HANDLE cbData = ::GetClipboardData(HTMLClipboardFormat);
272 if (cbData) {
273 SIZE_T dataSize = ::GlobalSize(cbData);
274 String cf_html(UTF8Encoding().decode((char*)::GlobalLock(cbData), dataSize));
275 ::GlobalUnlock(cbData);
276 ::CloseClipboard();
277
278 PassRefPtr<DocumentFragment> fragment = fragmentFromCF_HTML(frame->document(), cf_html);
279 if (fragment)
280 return fragment;
281 } else
282 ::CloseClipboard();
283 }
284
285 if (allowPlainText && ::IsClipboardFormatAvailable(CF_UNICODETEXT)) {
286 chosePlainText = true;
287 if (::OpenClipboard(m_owner)) {
288 HANDLE cbData = ::GetClipboardData(CF_UNICODETEXT);
289 if (cbData) {
290 UChar* buffer = (UChar*)GlobalLock(cbData);
291 String str(buffer);
292 ::GlobalUnlock( cbData );
293 ::CloseClipboard();
294 RefPtr<DocumentFragment> fragment = createFragmentFromText(context.get(), str);
295 if (fragment)
296 return fragment.release();
297 } else
298 ::CloseClipboard();
299 }
300 }
301
302 if (allowPlainText && ::IsClipboardFormatAvailable(CF_TEXT)) {
303 chosePlainText = true;
304 if (::OpenClipboard(m_owner)) {
305 HANDLE cbData = ::GetClipboardData(CF_TEXT);
306 if (cbData) {
307 char* buffer = (char*)GlobalLock(cbData);
308 String str(buffer);
309 ::GlobalUnlock( cbData );
310 ::CloseClipboard();
311 RefPtr<DocumentFragment> fragment = createFragmentFromText(context.get(), str);
312 if (fragment)
313 return fragment.release();
314 } else
315 ::CloseClipboard();
316 }
317 }
318
319 return 0;
320 }
321
322 } // namespace WebCore
323