1 /*
2  * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 #include "awt_Clipboard.h"
27 #include "awt_DataTransferer.h"
28 #include "awt_Toolkit.h"
29 #include <shlobj.h>
30 #include <sun_awt_windows_WClipboard.h>
31 
32 
33 /************************************************************************
34  * AwtClipboard fields
35  */
36 
37 jmethodID AwtClipboard::lostSelectionOwnershipMID;
38 jobject AwtClipboard::theCurrentClipboard;
39 
40 /* This flag is set while we call EmptyClipboard to indicate to
41    WM_DESTROYCLIPBOARD handler that we are not losing ownership */
42 BOOL AwtClipboard::isGettingOwnership = FALSE;
43 
44 volatile jmethodID AwtClipboard::handleContentsChangedMID;
45 volatile BOOL AwtClipboard::skipInitialWmDrawClipboardMsg = TRUE;
46 volatile BOOL AwtClipboard::isClipboardViewerRegistered = FALSE;
47 volatile HWND AwtClipboard::hwndNextViewer = NULL;
48 
49 #define GALLOCFLG (GMEM_DDESHARE | GMEM_MOVEABLE | GMEM_ZEROINIT)
50 
51 /************************************************************************
52  * AwtClipboard methods
53  */
54 
LostOwnership(JNIEnv * env)55 void AwtClipboard::LostOwnership(JNIEnv *env) {
56     if (theCurrentClipboard != NULL) {
57         env->CallVoidMethod(theCurrentClipboard, lostSelectionOwnershipMID);
58         DASSERT(!safe_ExceptionOccurred(env));
59     }
60 }
61 
WmChangeCbChain(WPARAM wParam,LPARAM lParam)62 void AwtClipboard::WmChangeCbChain(WPARAM wParam, LPARAM lParam) {
63     if ((HWND)wParam == hwndNextViewer) {
64         hwndNextViewer = (HWND)lParam;
65     } else if (hwndNextViewer != NULL) {
66         ::SendMessage(hwndNextViewer, WM_CHANGECBCHAIN, wParam, lParam);
67     }
68 }
69 
WmDrawClipboard(JNIEnv * env,WPARAM wParam,LPARAM lParam)70 void AwtClipboard::WmDrawClipboard(JNIEnv *env, WPARAM wParam, LPARAM lParam) {
71     if (skipInitialWmDrawClipboardMsg) {
72         // skipping the first contents change notification as it comes
73         // immediately after registering the clipboard viewer window
74         // and it is not caused by an actual contents change.
75         skipInitialWmDrawClipboardMsg = FALSE;
76         return;
77     }
78     if (theCurrentClipboard != NULL) {
79         env->CallVoidMethod(theCurrentClipboard, handleContentsChangedMID);
80         DASSERT(!safe_ExceptionOccurred(env));
81     }
82     ::SendMessage(hwndNextViewer, WM_DRAWCLIPBOARD, wParam, lParam);
83 }
84 
RegisterClipboardViewer(JNIEnv * env,jobject jclipboard)85 void AwtClipboard::RegisterClipboardViewer(JNIEnv *env, jobject jclipboard) {
86     if (isClipboardViewerRegistered) {
87         return;
88     }
89 
90     if (theCurrentClipboard == NULL) {
91         theCurrentClipboard = env->NewGlobalRef(jclipboard);
92     }
93 
94     jclass cls = env->GetObjectClass(jclipboard);
95     AwtClipboard::handleContentsChangedMID =
96             env->GetMethodID(cls, "handleContentsChanged", "()V");
97     DASSERT(AwtClipboard::handleContentsChangedMID != NULL);
98 
99     hwndNextViewer = ::SetClipboardViewer(AwtToolkit::GetInstance().GetHWnd());
100     isClipboardViewerRegistered = TRUE;
101 }
102 
UnregisterClipboardViewer(JNIEnv * env)103 void AwtClipboard::UnregisterClipboardViewer(JNIEnv *env) {
104     TRY;
105 
106     if (isClipboardViewerRegistered) {
107         ::ChangeClipboardChain(AwtToolkit::GetInstance().GetHWnd(), AwtClipboard::hwndNextViewer);
108         AwtClipboard::hwndNextViewer = NULL;
109         isClipboardViewerRegistered = FALSE;
110         skipInitialWmDrawClipboardMsg = TRUE;
111     }
112 
113     CATCH_BAD_ALLOC;
114 }
115 
116 extern "C" {
117 
awt_clipboard_uninitialize(JNIEnv * env)118 void awt_clipboard_uninitialize(JNIEnv *env) {
119     AwtClipboard::UnregisterClipboardViewer(env);
120     env->DeleteGlobalRef(AwtClipboard::theCurrentClipboard);
121     AwtClipboard::theCurrentClipboard = NULL;
122 }
123 
124 /************************************************************************
125  * WClipboard native methods
126  */
127 
128 /*
129  * Class:     sun_awt_windows_WClipboard
130  * Method:    init
131  * Signature: ()V
132  */
133 JNIEXPORT void JNICALL
Java_sun_awt_windows_WClipboard_init(JNIEnv * env,jclass cls)134 Java_sun_awt_windows_WClipboard_init(JNIEnv *env, jclass cls)
135 {
136     TRY;
137 
138     AwtClipboard::lostSelectionOwnershipMID =
139         env->GetMethodID(cls, "lostSelectionOwnershipImpl", "()V");
140     DASSERT(AwtClipboard::lostSelectionOwnershipMID != NULL);
141 
142     CATCH_BAD_ALLOC;
143 }
144 
145 /*
146  * Class:     sun_awt_windows_WClipboard
147  * Method:    openClipboard
148  * Signature: (Lsun/awt/windows/WClipboard;)V
149  */
150 JNIEXPORT void JNICALL
Java_sun_awt_windows_WClipboard_openClipboard(JNIEnv * env,jobject self,jobject newOwner)151 Java_sun_awt_windows_WClipboard_openClipboard(JNIEnv *env, jobject self,
152                                               jobject newOwner)
153 {
154     TRY;
155 
156     DASSERT(::GetOpenClipboardWindow() != AwtToolkit::GetInstance().GetHWnd());
157 
158     if (!::OpenClipboard(AwtToolkit::GetInstance().GetHWnd())) {
159         JNU_ThrowByName(env, "java/lang/IllegalStateException",
160                         "cannot open system clipboard");
161         return;
162     }
163     if (newOwner != NULL) {
164         AwtClipboard::GetOwnership();
165         if (AwtClipboard::theCurrentClipboard != NULL) {
166             env->DeleteGlobalRef(AwtClipboard::theCurrentClipboard);
167         }
168         AwtClipboard::theCurrentClipboard = env->NewGlobalRef(newOwner);
169     }
170 
171     CATCH_BAD_ALLOC;
172 }
173 
174 /*
175  * Class:     sun_awt_windows_WClipboard
176  * Method:    closeClipboard
177  * Signature: ()V
178  */
179 JNIEXPORT void JNICALL
Java_sun_awt_windows_WClipboard_closeClipboard(JNIEnv * env,jobject self)180 Java_sun_awt_windows_WClipboard_closeClipboard(JNIEnv *env, jobject self)
181 {
182     TRY;
183 
184     if (::GetOpenClipboardWindow() == AwtToolkit::GetInstance().GetHWnd()) {
185         VERIFY(::CloseClipboard());
186     }
187 
188     CATCH_BAD_ALLOC;
189 }
190 
191 /*
192  * Class:     sun_awt_windows_WClipboard
193  * Method:    registerClipboardViewer
194  * Signature: ()V
195  */
196 JNIEXPORT void JNICALL
Java_sun_awt_windows_WClipboard_registerClipboardViewer(JNIEnv * env,jobject self)197 Java_sun_awt_windows_WClipboard_registerClipboardViewer(JNIEnv *env, jobject self)
198 {
199     TRY;
200 
201     AwtClipboard::RegisterClipboardViewer(env, self);
202 
203     CATCH_BAD_ALLOC;
204 }
205 
206 /*
207  * Class:     sun_awt_windows_WClipboard
208  * Method:    publishClipboardData
209  * Signature: (J[B)V
210  */
211 JNIEXPORT void JNICALL
Java_sun_awt_windows_WClipboard_publishClipboardData(JNIEnv * env,jobject self,jlong format,jbyteArray bytes)212 Java_sun_awt_windows_WClipboard_publishClipboardData(JNIEnv *env,
213                                                      jobject self,
214                                                      jlong format,
215                                                      jbyteArray bytes)
216 {
217     TRY;
218 
219     DASSERT(::GetOpenClipboardWindow() == AwtToolkit::GetInstance().GetHWnd());
220 
221     if (bytes == NULL) {
222         return;
223     }
224 
225     jint nBytes = env->GetArrayLength(bytes);
226 
227     if (format == CF_ENHMETAFILE) {
228         LPBYTE lpbEmfBuffer =
229             (LPBYTE)env->GetPrimitiveArrayCritical(bytes, NULL);
230         if (lpbEmfBuffer == NULL) {
231             env->PopLocalFrame(NULL);
232             throw std::bad_alloc();
233         }
234 
235         HENHMETAFILE hemf = ::SetEnhMetaFileBits(nBytes, lpbEmfBuffer);
236 
237         env->ReleasePrimitiveArrayCritical(bytes, (LPVOID)lpbEmfBuffer,
238                                            JNI_ABORT);
239 
240         if (hemf != NULL) {
241             VERIFY(::SetClipboardData((UINT)format, hemf));
242         }
243         return;
244     } else if (format == CF_METAFILEPICT) {
245         LPBYTE lpbMfpBuffer =
246             (LPBYTE)env->GetPrimitiveArrayCritical(bytes, NULL);
247         if (lpbMfpBuffer == NULL) {
248             env->PopLocalFrame(NULL);
249             throw std::bad_alloc();
250         }
251 
252         HMETAFILE hmf = ::SetMetaFileBitsEx(nBytes - sizeof(METAFILEPICT),
253                                          lpbMfpBuffer + sizeof(METAFILEPICT));
254         if (hmf == NULL) {
255             env->ReleasePrimitiveArrayCritical(bytes, (LPVOID)lpbMfpBuffer, JNI_ABORT);
256             env->PopLocalFrame(NULL);
257             return;
258         }
259 
260         LPMETAFILEPICT lpMfpOld = (LPMETAFILEPICT)lpbMfpBuffer;
261 
262         HMETAFILEPICT hmfp = ::GlobalAlloc(GALLOCFLG, sizeof(METAFILEPICT));
263         if (hmfp == NULL) {
264             VERIFY(::DeleteMetaFile(hmf));
265             env->ReleasePrimitiveArrayCritical(bytes, (LPVOID)lpbMfpBuffer, JNI_ABORT);
266             env->PopLocalFrame(NULL);
267             throw std::bad_alloc();
268         }
269 
270         LPMETAFILEPICT lpMfp = (LPMETAFILEPICT)::GlobalLock(hmfp);
271         lpMfp->mm = lpMfpOld->mm;
272         lpMfp->xExt = lpMfpOld->xExt;
273         lpMfp->yExt = lpMfpOld->yExt;
274         lpMfp->hMF = hmf;
275         ::GlobalUnlock(hmfp);
276 
277         env->ReleasePrimitiveArrayCritical(bytes, (LPVOID)lpbMfpBuffer, JNI_ABORT);
278 
279         VERIFY(::SetClipboardData((UINT)format, hmfp));
280 
281         return;
282     }
283 
284     // We have to prepend the DROPFILES structure here because WDataTransferer
285     // doesn't.
286     HGLOBAL hglobal = ::GlobalAlloc(GALLOCFLG, nBytes + ((format == CF_HDROP)
287                                                             ? sizeof(DROPFILES)
288                                                             : 0));
289     if (hglobal == NULL) {
290         throw std::bad_alloc();
291     }
292     char *dataout = (char *)::GlobalLock(hglobal);
293 
294     if (format == CF_HDROP) {
295         DROPFILES *dropfiles = (DROPFILES *)dataout;
296         dropfiles->pFiles = sizeof(DROPFILES);
297         dropfiles->fWide = TRUE; // we publish only Unicode
298         dataout += sizeof(DROPFILES);
299     }
300 
301     env->GetByteArrayRegion(bytes, 0, nBytes, (jbyte *)dataout);
302     ::GlobalUnlock(hglobal);
303 
304     VERIFY(::SetClipboardData((UINT)format, hglobal));
305 
306     CATCH_BAD_ALLOC;
307 }
308 
309 /*
310  * Class:     sun_awt_windows_WClipboard
311  * Method:    getClipboardFormats
312  * Signature: ()[J
313  */
314 JNIEXPORT jlongArray JNICALL
Java_sun_awt_windows_WClipboard_getClipboardFormats(JNIEnv * env,jobject self)315 Java_sun_awt_windows_WClipboard_getClipboardFormats
316     (JNIEnv *env, jobject self)
317 {
318     TRY;
319 
320     DASSERT(::GetOpenClipboardWindow() == AwtToolkit::GetInstance().GetHWnd());
321 
322     jsize nFormats = ::CountClipboardFormats();
323     jlongArray formats = env->NewLongArray(nFormats);
324     if (formats == NULL) {
325         throw std::bad_alloc();
326     }
327     if (nFormats == 0) {
328         return formats;
329     }
330     jboolean isCopy;
331     jlong *lFormats = env->GetLongArrayElements(formats, &isCopy),
332         *saveFormats = lFormats;
333     UINT num = 0;
334 
335     for (jsize i = 0; i < nFormats; i++, lFormats++) {
336         *lFormats = num = ::EnumClipboardFormats(num);
337     }
338 
339     env->ReleaseLongArrayElements(formats, saveFormats, 0);
340 
341     return formats;
342 
343     CATCH_BAD_ALLOC_RET(NULL);
344 }
345 
346 /*
347  * Class:     sun_awt_windows_WClipboard
348  * Method:    getClipboardData
349  * Signature: (J)[B
350  */
351 JNIEXPORT jbyteArray JNICALL
Java_sun_awt_windows_WClipboard_getClipboardData(JNIEnv * env,jobject self,jlong format)352 Java_sun_awt_windows_WClipboard_getClipboardData
353     (JNIEnv *env, jobject self, jlong format)
354 {
355     TRY;
356 
357     DASSERT(::GetOpenClipboardWindow() == AwtToolkit::GetInstance().GetHWnd());
358 
359     HANDLE handle = ::GetClipboardData((UINT)format);
360     if (handle == NULL) {
361         JNU_ThrowIOException(env, "system clipboard data unavailable");
362         return NULL;
363     }
364 
365     jbyteArray bytes = NULL;
366     jbyteArray paletteData = NULL;
367 
368     switch (format) {
369     case CF_ENHMETAFILE:
370     case CF_METAFILEPICT: {
371         HENHMETAFILE hemf = NULL;
372 
373         if (format == CF_METAFILEPICT) {
374             HMETAFILEPICT hMetaFilePict = (HMETAFILEPICT)handle;
375             LPMETAFILEPICT lpMetaFilePict =
376                 (LPMETAFILEPICT)::GlobalLock(hMetaFilePict);
377             UINT uSize = ::GetMetaFileBitsEx(lpMetaFilePict->hMF, 0, NULL);
378             DASSERT(uSize != 0);
379 
380             try {
381                 LPBYTE lpMfBits = (LPBYTE)safe_Malloc(uSize);
382                 VERIFY(::GetMetaFileBitsEx(lpMetaFilePict->hMF, uSize,
383                                            lpMfBits) == uSize);
384                 hemf = ::SetWinMetaFileBits(uSize, lpMfBits, NULL,
385                                             lpMetaFilePict);
386                 free(lpMfBits);
387                 if (hemf == NULL) {
388                     ::GlobalUnlock(hMetaFilePict);
389                     JNU_ThrowIOException(env, "failed to get system clipboard data");
390                     return NULL;
391                 }
392             } catch (...) {
393                 ::GlobalUnlock(hMetaFilePict);
394                 throw;
395             }
396             ::GlobalUnlock(hMetaFilePict);
397         } else {
398             hemf = (HENHMETAFILE)handle;
399         }
400 
401         UINT uEmfSize = ::GetEnhMetaFileBits(hemf, 0, NULL);
402         if (uEmfSize == 0) {
403             JNU_ThrowIOException(env, "cannot retrieve metafile bits");
404             return NULL;
405         }
406 
407         bytes = env->NewByteArray(uEmfSize);
408         if (bytes == NULL) {
409             throw std::bad_alloc();
410         }
411 
412         LPBYTE lpbEmfBuffer =
413             (LPBYTE)env->GetPrimitiveArrayCritical(bytes, NULL);
414         if (lpbEmfBuffer == NULL) {
415             env->DeleteLocalRef(bytes);
416             throw std::bad_alloc();
417         }
418         VERIFY(::GetEnhMetaFileBits(hemf, uEmfSize, lpbEmfBuffer) == uEmfSize);
419         env->ReleasePrimitiveArrayCritical(bytes, lpbEmfBuffer, 0);
420 
421         paletteData =
422             AwtDataTransferer::GetPaletteBytes(hemf, OBJ_ENHMETAFILE, FALSE);
423         break;
424     }
425     case CF_LOCALE: {
426         LCID *lcid = (LCID *)::GlobalLock(handle);
427         if (lcid == NULL) {
428             JNU_ThrowIOException(env, "invalid LCID");
429             return NULL;
430         }
431         try {
432             bytes = AwtDataTransferer::LCIDToTextEncoding(env, *lcid);
433         } catch (...) {
434             ::GlobalUnlock(handle);
435             throw;
436         }
437         ::GlobalUnlock(handle);
438         break;
439     }
440     default: {
441         ::SetLastError(0); // clear error
442         // Warning C4244.
443         // Cast SIZE_T (__int64 on 64-bit/unsigned int on 32-bit)
444         // to jsize (long).
445         SIZE_T globalSize = ::GlobalSize(handle);
446         jsize size = (globalSize <= INT_MAX) ? (jsize)globalSize : INT_MAX;
447         if (::GetLastError() != 0) {
448             JNU_ThrowIOException(env, "invalid global memory block handle");
449             return NULL;
450         }
451 
452         bytes = env->NewByteArray(size);
453         if (bytes == NULL) {
454             throw std::bad_alloc();
455         }
456 
457         if (size != 0) {
458             LPVOID data = ::GlobalLock(handle);
459             env->SetByteArrayRegion(bytes, 0, size, (jbyte *)data);
460             ::GlobalUnlock(handle);
461         }
462         break;
463     }
464     }
465 
466     switch (format) {
467     case CF_ENHMETAFILE:
468     case CF_METAFILEPICT:
469     case CF_DIB: {
470         if (JNU_IsNull(env, paletteData)) {
471             HPALETTE hPalette = (HPALETTE)::GetClipboardData(CF_PALETTE);
472             paletteData =
473                 AwtDataTransferer::GetPaletteBytes(hPalette, OBJ_PAL, TRUE);
474         }
475         DASSERT(!JNU_IsNull(env, paletteData) &&
476                 !JNU_IsNull(env, bytes));
477 
478         jbyteArray concat =
479             (jbyteArray)AwtDataTransferer::ConcatData(env, paletteData, bytes);
480 
481         if (!JNU_IsNull(env, safe_ExceptionOccurred(env))) {
482             env->ExceptionDescribe();
483             env->ExceptionClear();
484             env->DeleteLocalRef(bytes);
485             env->DeleteLocalRef(paletteData);
486             return NULL;
487         }
488 
489         env->DeleteLocalRef(bytes);
490         env->DeleteLocalRef(paletteData);
491         bytes = concat;
492         break;
493     }
494     }
495 
496     return bytes;
497 
498     CATCH_BAD_ALLOC_RET(NULL);
499 }
500 
501 } /* extern "C" */
502