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