1 /*
2  * Copyright (c) 2013, 2016, 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 package sun.lwawt.macosx;
27 
28 import sun.awt.AWTAccessor;
29 import sun.awt.IconInfo;
30 import sun.java2d.SunGraphics2D;
31 import sun.java2d.SurfaceData;
32 import sun.java2d.opengl.CGLLayer;
33 import sun.lwawt.LWWindowPeer;
34 import sun.lwawt.PlatformEventNotifier;
35 import sun.lwawt.SecurityWarningWindow;
36 
37 import java.awt.*;
38 import java.awt.event.MouseEvent;
39 import java.awt.geom.Point2D;
40 import java.lang.ref.WeakReference;
41 
42 public final class CWarningWindow extends CPlatformWindow
43         implements SecurityWarningWindow, PlatformEventNotifier {
44 
45     private static class Lock {}
46     private final Lock lock = new Lock();
47 
48     private static final int SHOWING_DELAY = 300;
49     private static final int HIDING_DELAY = 2000;
50 
51     private Rectangle bounds = new Rectangle();
52     private final WeakReference<LWWindowPeer> ownerPeer;
53     private final Window ownerWindow;
54 
55     /**
56      * Animation stage.
57      */
58     private volatile int currentIcon = 0;
59 
60     /* -1 - uninitialized.
61      * 0 - 16x16
62      * 1 - 24x24
63      * 2 - 32x32
64      * 3 - 48x48
65      */
66     private int currentSize = -1;
67     private static IconInfo[][] icons;
getSecurityIconInfo(int size, int num)68     private static IconInfo getSecurityIconInfo(int size, int num) {
69         synchronized (CWarningWindow.class) {
70             if (icons == null) {
71                 icons = new IconInfo[4][3];
72                 icons[0][0] = new IconInfo(sun.awt.AWTIcon32_security_icon_bw16_png.security_icon_bw16_png);
73                 icons[0][1] = new IconInfo(sun.awt.AWTIcon32_security_icon_interim16_png.security_icon_interim16_png);
74                 icons[0][2] = new IconInfo(sun.awt.AWTIcon32_security_icon_yellow16_png.security_icon_yellow16_png);
75                 icons[1][0] = new IconInfo(sun.awt.AWTIcon32_security_icon_bw24_png.security_icon_bw24_png);
76                 icons[1][1] = new IconInfo(sun.awt.AWTIcon32_security_icon_interim24_png.security_icon_interim24_png);
77                 icons[1][2] = new IconInfo(sun.awt.AWTIcon32_security_icon_yellow24_png.security_icon_yellow24_png);
78                 icons[2][0] = new IconInfo(sun.awt.AWTIcon32_security_icon_bw32_png.security_icon_bw32_png);
79                 icons[2][1] = new IconInfo(sun.awt.AWTIcon32_security_icon_interim32_png.security_icon_interim32_png);
80                 icons[2][2] = new IconInfo(sun.awt.AWTIcon32_security_icon_yellow32_png.security_icon_yellow32_png);
81                 icons[3][0] = new IconInfo(sun.awt.AWTIcon32_security_icon_bw48_png.security_icon_bw48_png);
82                 icons[3][1] = new IconInfo(sun.awt.AWTIcon32_security_icon_interim48_png.security_icon_interim48_png);
83                 icons[3][2] = new IconInfo(sun.awt.AWTIcon32_security_icon_yellow48_png.security_icon_yellow48_png);
84             }
85         }
86         final int sizeIndex = size % icons.length;
87         return icons[sizeIndex][num % icons[sizeIndex].length];
88     }
89 
CWarningWindow(final Window _ownerWindow, final LWWindowPeer _ownerPeer)90     public CWarningWindow(final Window _ownerWindow, final LWWindowPeer _ownerPeer) {
91         super();
92 
93         this.ownerPeer = new WeakReference<>(_ownerPeer);
94         this.ownerWindow = _ownerWindow;
95 
96         initialize(null, null, _ownerPeer.getPlatformWindow());
97 
98         setOpaque(false);
99 
100         String warningString = ownerWindow.getWarningString();
101         if (warningString != null) {
102             contentView.setToolTip(ownerWindow.getWarningString());
103         }
104 
105         updateIconSize();
106     }
107 
108     /**
109      * @param x,y,w,h coordinates of the untrusted window
110      */
reposition(int x, int y, int w, int h)111     public void reposition(int x, int y, int w, int h) {
112         final Point2D point = AWTAccessor.getWindowAccessor().
113                 calculateSecurityWarningPosition(ownerWindow, x, y, w, h);
114         setBounds((int)point.getX(), (int)point.getY(), getWidth(), getHeight());
115     }
116 
setVisible(boolean visible, boolean doSchedule)117     public void setVisible(boolean visible, boolean doSchedule) {
118         synchronized (taskLock) {
119             cancelTasks();
120 
121             if (visible) {
122                 if (isVisible()) {
123                     currentIcon = 0;
124                 } else {
125                     currentIcon = 2;
126                 }
127 
128                 showHideTask = new ShowingTask();
129                 LWCToolkit.performOnMainThreadAfterDelay(showHideTask, 50);
130             } else {
131                 if (!isVisible()) {
132                     return;
133                 }
134 
135                 showHideTask = new HidingTask();
136                 if (doSchedule) {
137                     LWCToolkit.performOnMainThreadAfterDelay(showHideTask, HIDING_DELAY);
138                 } else {
139                     LWCToolkit.performOnMainThreadAfterDelay(showHideTask, 50);
140                 }
141             }
142         }
143     }
144 
145     @Override
notifyIconify(boolean iconify)146     public void notifyIconify(boolean iconify) {
147     }
148 
149     @Override
notifyZoom(boolean isZoomed)150     public void notifyZoom(boolean isZoomed) {
151     }
152 
153     @Override
notifyExpose(final Rectangle r)154     public void notifyExpose(final Rectangle r) {
155         repaint();
156     }
157 
158     @Override
notifyReshape(int x, int y, int w, int h)159     public void notifyReshape(int x, int y, int w, int h) {
160     }
161 
162     @Override
notifyUpdateCursor()163     public void notifyUpdateCursor() {
164     }
165 
166     @Override
notifyActivation(boolean activation, LWWindowPeer opposite)167     public void notifyActivation(boolean activation, LWWindowPeer opposite) {
168     }
169 
170     @Override
notifyNCMouseDown()171     public void notifyNCMouseDown() {
172     }
173 
174     @Override
notifyMouseEvent(int id, long when, int button, int x, int y, int absX, int absY, int modifiers, int clickCount, boolean popupTrigger, byte[] bdata)175     public void notifyMouseEvent(int id, long when, int button, int x, int y,
176                                  int absX, int absY, int modifiers,
177                                  int clickCount, boolean popupTrigger,
178                                  byte[] bdata) {
179         LWWindowPeer peer = ownerPeer.get();
180         if (id == MouseEvent.MOUSE_EXITED) {
181             if (peer != null) {
182                 peer.updateSecurityWarningVisibility();
183             }
184         } else if(id == MouseEvent.MOUSE_ENTERED) {
185             if (peer != null) {
186                 peer.updateSecurityWarningVisibility();
187             }
188         }
189     }
190 
getBounds()191     public Rectangle getBounds() {
192         synchronized (lock) {
193             return bounds.getBounds();
194         }
195     }
196 
197     @Override
isVisible()198     public boolean isVisible() {
199         synchronized (lock) {
200             return visible;
201         }
202     }
203 
204     @Override
setVisible(boolean visible)205     public void setVisible(boolean visible) {
206         synchronized (lock) {
207             execute(ptr -> {
208                 // Actually show or hide the window
209                 if (visible) {
210                     CWrapper.NSWindow.orderFront(ptr);
211                 } else {
212                     CWrapper.NSWindow.orderOut(ptr);
213                 }
214             });
215 
216             this.visible = visible;
217 
218             // Manage parent-child relationship when showing
219             if (visible) {
220                 // Order myself above my parent
221                 if (owner != null && owner.isVisible()) {
222                     owner.execute(ownerPtr -> {
223                         execute(ptr -> {
224                             CWrapper.NSWindow.orderWindow(ptr,
225                                                           CWrapper.NSWindow.NSWindowAbove,
226                                                           ownerPtr);
227                         });
228                     });
229 
230                     // do not allow security warning to be obscured by other windows
231                     applyWindowLevel(ownerWindow);
232                 }
233             }
234         }
235     }
236 
237     @Override
notifyMouseWheelEvent(long when, int x, int y, int absX, int absY, int modifiers, int scrollType, int scrollAmount, int wheelRotation, double preciseWheelRotation, byte[] bdata)238     public void notifyMouseWheelEvent(long when, int x, int y, int absX,
239                                       int absY, int modifiers, int scrollType,
240                                       int scrollAmount, int wheelRotation,
241                                       double preciseWheelRotation,
242                                       byte[] bdata) {
243     }
244 
245     @Override
notifyKeyEvent(int id, long when, int modifiers, int keyCode, char keyChar, int keyLocation)246     public void notifyKeyEvent(int id, long when, int modifiers, int keyCode,
247                                char keyChar, int keyLocation) {
248     }
249 
getInitialStyleBits()250     protected int getInitialStyleBits() {
251         int styleBits = 0;
252         CPlatformWindow.SET(styleBits, CPlatformWindow.UTILITY, true);
253         return styleBits;
254     }
255 
deliverMoveResizeEvent(int x, int y, int width, int height, boolean byUser)256     protected void deliverMoveResizeEvent(int x, int y, int width, int height,
257                                           boolean byUser) {
258 
259         boolean isResize;
260         synchronized (lock) {
261             isResize = (bounds.width != width || bounds.height != height);
262             bounds = new Rectangle(x, y, width, height);
263         }
264 
265         if (isResize) {
266             replaceSurface();
267         }
268 
269         super.deliverMoveResizeEvent(x, y, width, height, byUser);
270     }
271 
createPlatformResponder()272     protected CPlatformResponder createPlatformResponder() {
273         return new CPlatformResponder(this, false);
274     }
275 
createContentView()276     protected CPlatformView createContentView() {
277         return new CPlatformView() {
278             public GraphicsConfiguration getGraphicsConfiguration() {
279                 LWWindowPeer peer = ownerPeer.get();
280                 return peer.getGraphicsConfiguration();
281             }
282 
283             public Rectangle getBounds() {
284                 return CWarningWindow.this.getBounds();
285             }
286 
287             public CGLLayer createCGLayer() {
288                 return new CGLLayer(null) {
289                     public Rectangle getBounds() {
290                         return CWarningWindow.this.getBounds();
291                     }
292 
293                     public GraphicsConfiguration getGraphicsConfiguration() {
294                         LWWindowPeer peer = ownerPeer.get();
295                         return peer.getGraphicsConfiguration();
296                     }
297 
298                     public boolean isOpaque() {
299                         return false;
300                     }
301                 };
302             }
303         };
304     }
305 
306     @Override
307     public void dispose() {
308         cancelTasks();
309         SurfaceData surfaceData = contentView.getSurfaceData();
310         if (surfaceData != null) {
311             surfaceData.invalidate();
312         }
313         super.dispose();
314     }
315 
316     private void cancelTasks() {
317         synchronized (taskLock) {
318             if (showHideTask != null) {
319                 showHideTask.cancel();
320                 showHideTask = null;
321             }
322         }
323     }
324 
325     private void updateIconSize() {
326         int newSize = -1;
327 
328         if (ownerWindow != null) {
329             Insets insets = ownerWindow.getInsets();
330             int max = Math.max(insets.top, Math.max(insets.bottom,
331                     Math.max(insets.left, insets.right)));
332             if (max < 24) {
333                 newSize = 0;
334             } else if (max < 32) {
335                 newSize = 1;
336             } else if (max < 48) {
337                 newSize = 2;
338             } else {
339                 newSize = 3;
340             }
341         }
342         // Make sure we have a valid size
343         if (newSize == -1) {
344             newSize = 0;
345         }
346 
347         synchronized (lock) {
348             if (newSize != currentSize) {
349                 currentSize = newSize;
350                 IconInfo ico = getSecurityIconInfo(currentSize, 0);
351                 AWTAccessor.getWindowAccessor().setSecurityWarningSize(
352                     ownerWindow, ico.getWidth(), ico.getHeight());
353             }
354         }
355     }
356 
357     private Graphics getGraphics() {
358         SurfaceData sd = contentView.getSurfaceData();
359         if (ownerWindow == null || sd == null) {
360             return null;
361         }
362 
363         return new SunGraphics2D(sd, SystemColor.windowText, SystemColor.window,
364                                  ownerWindow.getFont());
365     }
366 
367 
368     private void repaint() {
369         final Graphics g = getGraphics();
370         if (g != null) {
371             try {
372                 ((Graphics2D) g).setComposite(AlphaComposite.Src);
373                 g.drawImage(getSecurityIconInfo().getImage(), 0, 0, null);
374             } finally {
375                 g.dispose();
376             }
377         }
378     }
379 
380     private void replaceSurface() {
381         SurfaceData oldData = contentView.getSurfaceData();
382 
383         replaceSurfaceData();
384 
385         if (oldData != null && oldData != contentView.getSurfaceData()) {
386             oldData.flush();
387         }
388     }
389 
390     private int getWidth() {
391         return getSecurityIconInfo().getWidth();
392     }
393 
394     private int getHeight() {
395         return getSecurityIconInfo().getHeight();
396     }
397 
398     private IconInfo getSecurityIconInfo() {
399         return getSecurityIconInfo(currentSize, currentIcon);
400     }
401 
402     private final Lock taskLock = new Lock();
403     private CancelableRunnable showHideTask;
404 
405     private abstract static class CancelableRunnable implements Runnable {
406         private volatile boolean perform = true;
407 
408         public final void cancel() {
409             perform = false;
410         }
411 
412         @Override
413         public final void run() {
414             if (perform) {
415                 perform();
416             }
417         }
418 
419         public abstract void perform();
420     }
421 
422     private class HidingTask extends CancelableRunnable {
423         @Override
424         public void perform() {
425             synchronized (lock) {
426                 setVisible(false);
427             }
428 
429             synchronized (taskLock) {
430                 showHideTask = null;
431             }
432         }
433     }
434 
435     private class ShowingTask extends CancelableRunnable {
436         @Override
437         public void perform() {
438             synchronized (lock) {
439                 if (!isVisible()) {
440                     setVisible(true);
441                 }
442                 repaint();
443             }
444 
445             synchronized (taskLock) {
446                 if (currentIcon > 0) {
447                     currentIcon--;
448                     showHideTask = new ShowingTask();
449                     LWCToolkit.performOnMainThreadAfterDelay(showHideTask, SHOWING_DELAY);
450                 } else {
451                     showHideTask = null;
452                 }
453             }
454         }
455     }
456 }
457 
458