1 /* 2 * Copyright (c) 2010, 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 com.sun.javafx.application; 27 28 import static com.sun.javafx.FXPermissions.CREATE_TRANSPARENT_WINDOW_PERMISSION; 29 import com.sun.javafx.PlatformUtil; 30 import com.sun.javafx.css.StyleManager; 31 import com.sun.javafx.runtime.SystemProperties; 32 import com.sun.javafx.tk.TKListener; 33 import com.sun.javafx.tk.TKStage; 34 import com.sun.javafx.tk.Toolkit; 35 import com.sun.javafx.util.ModuleHelper; 36 37 import java.lang.reflect.InvocationTargetException; 38 import java.lang.reflect.Method; 39 import java.security.AccessControlContext; 40 import java.security.AccessController; 41 import java.security.PrivilegedAction; 42 import java.util.ArrayList; 43 import java.util.HashMap; 44 import java.util.List; 45 import java.util.Map; 46 import java.util.Optional; 47 import java.util.Set; 48 import java.util.concurrent.CopyOnWriteArraySet; 49 import java.util.concurrent.CountDownLatch; 50 import java.util.concurrent.atomic.AtomicBoolean; 51 import java.util.concurrent.atomic.AtomicInteger; 52 import java.util.function.Predicate; 53 54 import javafx.application.Application; 55 import javafx.application.ConditionalFeature; 56 import javafx.beans.property.BooleanProperty; 57 import javafx.beans.property.SimpleBooleanProperty; 58 import javafx.scene.Scene; 59 import javafx.util.FXPermission; 60 61 public class PlatformImpl { 62 63 private static AtomicBoolean initialized = new AtomicBoolean(false); 64 private static AtomicBoolean platformExit = new AtomicBoolean(false); 65 private static AtomicBoolean toolkitExit = new AtomicBoolean(false); 66 private static CountDownLatch startupLatch = new CountDownLatch(1); 67 private static AtomicBoolean listenersRegistered = new AtomicBoolean(false); 68 private static TKListener toolkitListener = null; 69 private static volatile boolean implicitExit = true; 70 private static boolean taskbarApplication = true; 71 private static boolean contextual2DNavigation; 72 private static AtomicInteger pendingRunnables = new AtomicInteger(0); 73 private static AtomicInteger numWindows = new AtomicInteger(0); 74 private static volatile boolean firstWindowShown = false; 75 private static volatile boolean lastWindowClosed = false; 76 private static AtomicBoolean reallyIdle = new AtomicBoolean(false); 77 private static Set<FinishListener> finishListeners = 78 new CopyOnWriteArraySet<FinishListener>(); 79 private final static Object runLaterLock = new Object(); 80 private static Boolean isGraphicsSupported; 81 private static Boolean isControlsSupported; 82 private static Boolean isMediaSupported; 83 private static Boolean isWebSupported; 84 private static Boolean isSWTSupported; 85 private static Boolean isSwingSupported; 86 private static Boolean isFXMLSupported; 87 private static Boolean hasTwoLevelFocus; 88 private static Boolean hasVirtualKeyboard; 89 private static Boolean hasTouch; 90 private static Boolean hasMultiTouch; 91 private static Boolean hasPointer; 92 private static boolean isThreadMerged = false; 93 private static String applicationType = ""; 94 private static BooleanProperty accessibilityActive = new SimpleBooleanProperty(); 95 96 // Internal permission used by FXCanvas (SWT interop) 97 private static final FXPermission FXCANVAS_PERMISSION = 98 new FXPermission("accessFXCanvasInternals"); 99 100 /** 101 * Set a flag indicating whether this application should show up in the 102 * task bar. The default value is true. 103 * 104 * @param taskbarApplication the new value of this attribute 105 */ 106 public static void setTaskbarApplication(boolean taskbarApplication) { 107 PlatformImpl.taskbarApplication = taskbarApplication; 108 } 109 110 /** 111 * Returns the current value of the taskBarApplication flag. 112 * 113 * @return the current state of the flag. 114 */ 115 public static boolean isTaskbarApplication() { 116 return taskbarApplication; 117 } 118 119 /** 120 * Sets the name of the this application based on the Application class. 121 * This method is called by the launcher or by the deploy code, and is not 122 * called from the FX Application Thread, so we need to do it in a runLater. 123 * We do not need to wait for the result since it will complete before the 124 * Application start() method is called regardless. 125 * 126 * @param appClass the Application class. 127 */ 128 public static void setApplicationName(final Class appClass) { 129 runLater(() -> com.sun.glass.ui.Application.GetApplication().setName(appClass.getName())); 130 } 131 132 /** 133 * Return whether or not focus navigation between controls is context- 134 * sensitive. 135 * @return true if the context-sensitive algorithm for focus navigation is 136 * used 137 */ 138 public static boolean isContextual2DNavigation() { 139 return contextual2DNavigation; 140 } 141 142 /** 143 * This method is invoked typically on the main thread. At this point, 144 * the JavaFX Application Thread has not been started. Any attempt 145 * to call startup more than once results in all subsequent calls turning into 146 * nothing more than a runLater call with the provided Runnable being called. 147 * @param r 148 */ 149 public static void startup(final Runnable r) { 150 startup(r, false); 151 } 152 153 /** 154 * This method is invoked typically on the main thread. At this point, 155 * the JavaFX Application Thread has not been started. If preventDuplicateCalls 156 * is true, calling this method multiple times will result in an 157 * IllegalStateException. If it is false, calling this method multiple times 158 * will result in all subsequent calls turning into 159 * nothing more than a runLater call with the provided Runnable being called. 160 * @param r 161 * @param preventDuplicateCalls 162 */ 163 public static void startup(final Runnable r, boolean preventDuplicateCalls) { 164 165 // NOTE: if we ever support re-launching an application and/or 166 // launching a second application in the same VM/classloader 167 // this will need to be changed. 168 if (platformExit.get()) { 169 throw new IllegalStateException("Platform.exit has been called"); 170 } 171 172 if (initialized.getAndSet(true)) { 173 if (preventDuplicateCalls) { 174 throw new IllegalStateException("Toolkit already initialized"); 175 } 176 177 // If we've already initialized, just put the runnable on the queue. 178 runLater(r); 179 return; 180 } 181 182 AccessController.doPrivileged((PrivilegedAction<Void>) () -> { 183 applicationType = System.getProperty("com.sun.javafx.application.type"); 184 if (applicationType == null) applicationType = ""; 185 186 contextual2DNavigation = Boolean.getBoolean( 187 "com.sun.javafx.isContextual2DNavigation"); 188 String s = System.getProperty("com.sun.javafx.twoLevelFocus"); 189 if (s != null) { 190 hasTwoLevelFocus = Boolean.valueOf(s); 191 } 192 s = System.getProperty("com.sun.javafx.virtualKeyboard"); 193 if (s != null) { 194 if (s.equalsIgnoreCase("none")) { 195 hasVirtualKeyboard = false; 196 } else if (s.equalsIgnoreCase("javafx")) { 197 hasVirtualKeyboard = true; 198 } else if (s.equalsIgnoreCase("native")) { 199 hasVirtualKeyboard = true; 200 } 201 } 202 s = System.getProperty("com.sun.javafx.touch"); 203 if (s != null) { 204 hasTouch = Boolean.valueOf(s); 205 } 206 s = System.getProperty("com.sun.javafx.multiTouch"); 207 if (s != null) { 208 hasMultiTouch = Boolean.valueOf(s); 209 } 210 s = System.getProperty("com.sun.javafx.pointer"); 211 if (s != null) { 212 hasPointer = Boolean.valueOf(s); 213 } 214 s = System.getProperty("javafx.embed.singleThread"); 215 if (s != null) { 216 isThreadMerged = Boolean.valueOf(s); 217 } 218 return null; 219 }); 220 221 System.err.println("PlatformImpl::startup : applicationType = " + applicationType); 222 if ("FXCanvas".equals(applicationType)) { 223 initFXCanvas(); 224 } 225 226 if (!taskbarApplication) { 227 AccessController.doPrivileged((PrivilegedAction<Void>) () -> { 228 System.setProperty("glass.taskbarApplication", "false"); 229 return null; 230 }); 231 } 232 233 // Create Toolkit listener and register it with the Toolkit. 234 // Call notifyFinishListeners when we get notified. 235 toolkitListener = new TKListener() { 236 @Override public void changedTopLevelWindows(List<TKStage> windows) { 237 numWindows.set(windows.size()); 238 checkIdle(); 239 } 240 241 @Override 242 public void exitedLastNestedLoop() { 243 checkIdle(); 244 } 245 }; 246 Toolkit.getToolkit().addTkListener(toolkitListener); 247 248 Toolkit.getToolkit().startup(() -> { 249 startupLatch.countDown(); 250 r.run(); 251 }); 252 253 //Initialize the thread merging mechanism 254 if (isThreadMerged) { 255 installFwEventQueue(); 256 } 257 } 258 259 // Pass certain system properties to glass via the device details Map 260 private static void initDeviceDetailsFXCanvas() { 261 // Read the javafx.embed.eventProc system property and store 262 // it in an entry in the glass Application device details map 263 final String eventProcProperty = "javafx.embed.eventProc"; 264 final long eventProc = AccessController.doPrivileged((PrivilegedAction<Long>) () -> 265 Long.getLong(eventProcProperty, 0)); 266 System.err.println("PlatformImpl.initSWT: " 267 + eventProcProperty + " = " + eventProc); 268 if (eventProc != 0L) { 269 // Set the value for the javafx.embed.eventProc 270 // key in the glass Application map 271 Map map = com.sun.glass.ui.Application.getDeviceDetails(); 272 if (map == null) { 273 map = new HashMap(); 274 com.sun.glass.ui.Application.setDeviceDetails(map); 275 } 276 if (map.get(eventProcProperty) == null) { 277 map.put(eventProcProperty, eventProc); 278 } 279 } 280 } 281 282 // Add the necessary qualified exports to the calling module 283 private static void addExportsToFXCanvas(Class<?> fxCanvasClass) { 284 final String[] swtNeededPackages = { 285 "com.sun.glass.ui", 286 "com.sun.javafx.cursor", 287 "com.sun.javafx.embed", 288 "com.sun.javafx.stage" 289 }; 290 291 System.err.println("addExportsToFXCanvas: class = " + fxCanvasClass); 292 Object thisModule = ModuleHelper.getModule(PlatformImpl.class); 293 Object javafxSwtModule = ModuleHelper.getModule(fxCanvasClass); 294 for (String pkg : swtNeededPackages) { 295 System.err.println("add export of " + pkg + " from " + thisModule + " to " + javafxSwtModule); 296 ModuleHelper.addExports(thisModule, pkg, javafxSwtModule); 297 } 298 } 299 300 // FXCanvas-specific initialization 301 private static void initFXCanvas() { 302 // Verify that we have the appropriate permission 303 final SecurityManager sm = System.getSecurityManager(); 304 if (sm != null) { 305 try { 306 sm.checkPermission(FXCANVAS_PERMISSION); 307 } catch (SecurityException ex) { 308 System.err.println("FXCanvas: no permission to access JavaFX internals"); 309 ex.printStackTrace(); 310 return; 311 } 312 } 313 314 // Find the calling class, ignoring any stack frames from FX application classes 315 Predicate<StackWalker.StackFrame> classFilter = f -> 316 !f.getClassName().startsWith("javafx.application.") 317 && !f.getClassName().startsWith("com.sun.javafx.application."); 318 319 final StackWalker walker = AccessController.doPrivileged((PrivilegedAction<StackWalker>) () -> 320 StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE)); 321 Optional<StackWalker.StackFrame> frame = walker.walk( 322 s -> s.filter(classFilter).findFirst()); 323 System.err.println("frame = " + frame); 324 325 if (frame.isPresent()) { 326 Class<?> caller = frame.get().getDeclaringClass(); 327 System.err.println("callerClassName = " + caller); 328 329 // Verify that the caller is javafx.embed.swt.FXCanvas 330 if ("javafx.embed.swt.FXCanvas".equals(caller.getName())) { 331 initDeviceDetailsFXCanvas(); 332 addExportsToFXCanvas(caller); 333 } 334 } 335 } 336 337 private static void installFwEventQueue() { 338 invokeSwingFXUtilsMethod("installFwEventQueue"); 339 } 340 341 private static void removeFwEventQueue() { 342 invokeSwingFXUtilsMethod("removeFwEventQueue"); 343 } 344 345 private static void invokeSwingFXUtilsMethod(final String methodName) { 346 //Use reflection in case we are running compact profile 347 try { 348 Class swingFXUtilsClass = Class.forName("javafx.embed.swing.SwingFXUtils"); 349 Method installFwEventQueue = swingFXUtilsClass.getDeclaredMethod(methodName); 350 AccessController.doPrivileged((PrivilegedAction<Void>) () -> { 351 installFwEventQueue.setAccessible(true); 352 return null; 353 }); 354 355 waitForStart(); 356 installFwEventQueue.invoke(null); 357 358 } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException e) { 359 throw new RuntimeException("Property javafx.embed.singleThread is not supported"); 360 } catch (InvocationTargetException e) { 361 throw new RuntimeException(e); 362 } 363 } 364 365 private static void waitForStart() { 366 // If the startup runnable has not yet been called, then wait it. 367 // Note that we check the count before calling await() to avoid 368 // the try/catch which is unnecessary after startup. 369 if (startupLatch.getCount() > 0) { 370 try { 371 startupLatch.await(); 372 } catch (InterruptedException ex) { 373 ex.printStackTrace(); 374 } 375 } 376 } 377 378 public static boolean isFxApplicationThread() { 379 return Toolkit.getToolkit().isFxUserThread(); 380 } 381 382 public static void runLater(final Runnable r) { 383 runLater(r, false); 384 } 385 386 private static void runLater(final Runnable r, boolean exiting) { 387 if (!initialized.get()) { 388 throw new IllegalStateException("Toolkit not initialized"); 389 } 390 391 pendingRunnables.incrementAndGet(); 392 waitForStart(); 393 394 if (SystemProperties.isDebug()) { 395 Toolkit.getToolkit().pauseCurrentThread(); 396 } 397 398 synchronized (runLaterLock) { 399 if (!exiting && toolkitExit.get()) { 400 // Don't schedule a runnable after we have exited the toolkit 401 pendingRunnables.decrementAndGet(); 402 return; 403 } 404 405 final AccessControlContext acc = AccessController.getContext(); 406 // Don't catch exceptions, they are handled by Toolkit.defer() 407 Toolkit.getToolkit().defer(() -> { 408 try { 409 AccessController.doPrivileged((PrivilegedAction<Void>) () -> { 410 r.run(); 411 return null; 412 }, acc); 413 } finally { 414 pendingRunnables.decrementAndGet(); 415 checkIdle(); 416 } 417 }); 418 } 419 } 420 421 public static void runAndWait(final Runnable r) { 422 runAndWait(r, false); 423 } 424 425 private static void runAndWait(final Runnable r, boolean exiting) { 426 if (SystemProperties.isDebug()) { 427 Toolkit.getToolkit().pauseCurrentThread(); 428 } 429 430 if (isFxApplicationThread()) { 431 try { 432 r.run(); 433 } catch (Throwable t) { 434 System.err.println("Exception in runnable"); 435 t.printStackTrace(); 436 } 437 } else { 438 final CountDownLatch doneLatch = new CountDownLatch(1); 439 runLater(() -> { 440 try { 441 r.run(); 442 } finally { 443 doneLatch.countDown(); 444 } 445 }, exiting); 446 447 if (!exiting && toolkitExit.get()) { 448 throw new IllegalStateException("Toolkit has exited"); 449 } 450 451 try { 452 doneLatch.await(); 453 } catch (InterruptedException ex) { 454 ex.printStackTrace(); 455 } 456 } 457 } 458 459 public static void setImplicitExit(boolean implicitExit) { 460 PlatformImpl.implicitExit = implicitExit; 461 checkIdle(); 462 } 463 464 public static boolean isImplicitExit() { 465 return implicitExit; 466 } 467 468 public static void addListener(FinishListener l) { 469 listenersRegistered.set(true); 470 finishListeners.add(l); 471 } 472 473 public static void removeListener(FinishListener l) { 474 finishListeners.remove(l); 475 listenersRegistered.set(!finishListeners.isEmpty()); 476 if (!listenersRegistered.get()) { 477 checkIdle(); 478 } 479 } 480 481 private static void notifyFinishListeners(boolean exitCalled) { 482 // Notify listeners if any are registered, else exit directly 483 if (listenersRegistered.get()) { 484 for (FinishListener l : finishListeners) { 485 if (exitCalled) { 486 l.exitCalled(); 487 } else { 488 l.idle(implicitExit); 489 } 490 } 491 } else if (implicitExit || platformExit.get()) { 492 tkExit(); 493 } 494 } 495 496 // Check for idle, meaning the last top-level window has been closed and 497 // there are no pending Runnables waiting to be run. 498 private static void checkIdle() { 499 // If we aren't initialized yet, then this method is a no-op. 500 if (!initialized.get()) { 501 return; 502 } 503 504 if (!isFxApplicationThread()) { 505 // Add a dummy runnable to the runLater queue, which will then call 506 // checkIdle() on the FX application thread. 507 runLater(() -> { 508 }); 509 return; 510 } 511 512 boolean doNotify = false; 513 514 synchronized (PlatformImpl.class) { 515 int numWin = numWindows.get(); 516 if (numWin > 0) { 517 firstWindowShown = true; 518 lastWindowClosed = false; 519 reallyIdle.set(false); 520 } else if (numWin == 0 && firstWindowShown) { 521 lastWindowClosed = true; 522 } 523 524 // In case there is an event in process, allow for it to show 525 // another window. If no new window is shown before all pending 526 // runnables (including this one) are done and there is no running 527 // nested loops, then we will shutdown. 528 if (lastWindowClosed && pendingRunnables.get() == 0 529 && (toolkitExit.get() || !Toolkit.getToolkit().isNestedLoopRunning())) { 530 // System.err.println("Last window closed and no pending runnables"); 531 if (reallyIdle.getAndSet(true)) { 532 // System.err.println("Really idle now"); 533 doNotify = true; 534 lastWindowClosed = false; 535 } else { 536 // System.err.println("Queuing up a dummy idle check runnable"); 537 runLater(() -> { 538 // System.err.println("Dummy runnable"); 539 }); 540 } 541 } 542 } 543 544 if (doNotify) { 545 notifyFinishListeners(false); 546 } 547 } 548 549 // package scope method for testing 550 private static final CountDownLatch platformExitLatch = new CountDownLatch(1); 551 static CountDownLatch test_getPlatformExitLatch() { 552 return platformExitLatch; 553 } 554 555 public static void tkExit() { 556 if (toolkitExit.getAndSet(true)) { 557 return; 558 } 559 560 if (initialized.get()) { 561 // Always call toolkit exit on FX app thread 562 // System.err.println("PlatformImpl.tkExit: scheduling Toolkit.exit"); 563 PlatformImpl.runAndWait(() -> { 564 // System.err.println("PlatformImpl.tkExit: calling Toolkit.exit"); 565 Toolkit.getToolkit().exit(); 566 }, true); 567 568 if (isThreadMerged) { 569 removeFwEventQueue(); 570 } 571 572 Toolkit.getToolkit().removeTkListener(toolkitListener); 573 toolkitListener = null; 574 platformExitLatch.countDown(); 575 } 576 } 577 578 public static BooleanProperty accessibilityActiveProperty() { 579 return accessibilityActive; 580 } 581 582 public static void exit() { 583 platformExit.set(true); 584 notifyFinishListeners(true); 585 } 586 587 private static Boolean checkForClass(String classname) { 588 try { 589 Class.forName(classname, false, PlatformImpl.class.getClassLoader()); 590 return Boolean.TRUE; 591 } catch (ClassNotFoundException cnfe) { 592 return Boolean.FALSE; 593 } 594 } 595 596 public static boolean isSupported(ConditionalFeature feature) { 597 final boolean supported = isSupportedImpl(feature); 598 if (supported && (feature == ConditionalFeature.TRANSPARENT_WINDOW)) { 599 // some features require the application to have the corresponding 600 // permissions, if the application doesn't have them, the platform 601 // will behave as if the feature wasn't supported 602 final SecurityManager securityManager = 603 System.getSecurityManager(); 604 if (securityManager != null) { 605 try { 606 securityManager.checkPermission(CREATE_TRANSPARENT_WINDOW_PERMISSION); 607 } catch (final SecurityException e) { 608 return false; 609 } 610 } 611 612 return true; 613 } 614 615 return supported; 616 } 617 618 public static interface FinishListener { 619 public void idle(boolean implicitExit); 620 public void exitCalled(); 621 } 622 623 /** 624 * Set the platform user agent stylesheet to the default. 625 */ 626 public static void setDefaultPlatformUserAgentStylesheet() { 627 setPlatformUserAgentStylesheet(Application.STYLESHEET_MODENA); 628 } 629 630 private static boolean isModena = false; 631 private static boolean isCaspian = false; 632 633 /** 634 * Current Platform User Agent Stylesheet is Modena. 635 * 636 * Note: Please think hard before using this as we really want to avoid special cases in the platform for specific 637 * themes. This was added to allow tempory work arounds in the platform for bugs. 638 * 639 * @return true if using modena stylesheet 640 */ 641 public static boolean isModena() { 642 return isModena; 643 } 644 645 /** 646 * Current Platform User Agent Stylesheet is Caspian. 647 * 648 * Note: Please think hard before using this as we really want to avoid special cases in the platform for specific 649 * themes. This was added to allow tempory work arounds in the platform for bugs. 650 * 651 * @return true if using caspian stylesheet 652 */ 653 public static boolean isCaspian() { 654 return isCaspian; 655 } 656 657 /** 658 * Set the platform user agent stylesheet to the given URL. This method has special handling for platform theme 659 * name constants. 660 */ 661 public static void setPlatformUserAgentStylesheet(final String stylesheetUrl) { 662 if (isFxApplicationThread()) { 663 _setPlatformUserAgentStylesheet(stylesheetUrl); 664 } else { 665 runLater(() -> _setPlatformUserAgentStylesheet(stylesheetUrl)); 666 } 667 } 668 669 private static String accessibilityTheme; 670 public static boolean setAccessibilityTheme(String platformTheme) { 671 672 if (accessibilityTheme != null) { 673 StyleManager.getInstance().removeUserAgentStylesheet(accessibilityTheme); 674 accessibilityTheme = null; 675 } 676 677 _setAccessibilityTheme(platformTheme); 678 679 if (accessibilityTheme != null) { 680 StyleManager.getInstance().addUserAgentStylesheet(accessibilityTheme); 681 return true; 682 } 683 return false; 684 685 } 686 687 private static void _setAccessibilityTheme(String platformTheme) { 688 689 // check to see if there is an override to enable a high-contrast theme 690 final String userTheme = AccessController.doPrivileged( 691 (PrivilegedAction<String>) () -> System.getProperty("com.sun.javafx.highContrastTheme")); 692 693 if (isCaspian()) { 694 if (platformTheme != null || userTheme != null) { 695 // caspian has only one high contrast theme, use it regardless of the user or platform theme. 696 accessibilityTheme = "com/sun/javafx/scene/control/skin/caspian/highcontrast.css"; 697 } 698 } else if (isModena()) { 699 // User-defined property takes precedence 700 if (userTheme != null) { 701 switch (userTheme.toUpperCase()) { 702 case "BLACKONWHITE": 703 accessibilityTheme = "com/sun/javafx/scene/control/skin/modena/blackOnWhite.css"; 704 break; 705 case "WHITEONBLACK": 706 accessibilityTheme = "com/sun/javafx/scene/control/skin/modena/whiteOnBlack.css"; 707 break; 708 case "YELLOWONBLACK": 709 accessibilityTheme = "com/sun/javafx/scene/control/skin/modena/yellowOnBlack.css"; 710 break; 711 default: 712 } 713 } else { 714 if (platformTheme != null) { 715 // The following names are Platform specific (Windows 7 and 8) 716 switch (platformTheme) { 717 case "High Contrast White": 718 accessibilityTheme = "com/sun/javafx/scene/control/skin/modena/blackOnWhite.css"; 719 break; 720 case "High Contrast Black": 721 accessibilityTheme = "com/sun/javafx/scene/control/skin/modena/whiteOnBlack.css"; 722 break; 723 case "High Contrast #1": 724 case "High Contrast #2": //TODO #2 should be green on black 725 accessibilityTheme = "com/sun/javafx/scene/control/skin/modena/yellowOnBlack.css"; 726 break; 727 default: 728 } 729 } 730 } 731 } 732 } 733 734 private static void _setPlatformUserAgentStylesheet(String stylesheetUrl) { 735 isModena = isCaspian = false; 736 // check for command line override 737 final String overrideStylesheetUrl = AccessController.doPrivileged( 738 (PrivilegedAction<String>) () -> System.getProperty("javafx.userAgentStylesheetUrl")); 739 740 if (overrideStylesheetUrl != null) { 741 stylesheetUrl = overrideStylesheetUrl; 742 } 743 744 final List<String> uaStylesheets = new ArrayList<>(); 745 746 // check for named theme constants for modena and caspian 747 if (Application.STYLESHEET_CASPIAN.equalsIgnoreCase(stylesheetUrl)) { 748 isCaspian = true; 749 750 uaStylesheets.add("com/sun/javafx/scene/control/skin/caspian/caspian.css"); 751 752 if (isSupported(ConditionalFeature.INPUT_TOUCH)) { 753 uaStylesheets.add("com/sun/javafx/scene/control/skin/caspian/embedded.css"); 754 if (com.sun.javafx.util.Utils.isQVGAScreen()) { 755 uaStylesheets.add("com/sun/javafx/scene/control/skin/caspian/embedded-qvga.css"); 756 } 757 if (PlatformUtil.isAndroid()) { 758 uaStylesheets.add("com/sun/javafx/scene/control/skin/caspian/android.css"); 759 } 760 } 761 762 if (isSupported(ConditionalFeature.TWO_LEVEL_FOCUS)) { 763 uaStylesheets.add("com/sun/javafx/scene/control/skin/caspian/two-level-focus.css"); 764 } 765 766 if (isSupported(ConditionalFeature.VIRTUAL_KEYBOARD)) { 767 uaStylesheets.add("com/sun/javafx/scene/control/skin/caspian/fxvk.css"); 768 } 769 770 if (!isSupported(ConditionalFeature.TRANSPARENT_WINDOW)) { 771 uaStylesheets.add("com/sun/javafx/scene/control/skin/caspian/caspian-no-transparency.css"); 772 } 773 774 } else if (Application.STYLESHEET_MODENA.equalsIgnoreCase(stylesheetUrl)) { 775 isModena = true; 776 777 uaStylesheets.add("com/sun/javafx/scene/control/skin/modena/modena.css"); 778 779 if (isSupported(ConditionalFeature.INPUT_TOUCH)) { 780 uaStylesheets.add("com/sun/javafx/scene/control/skin/modena/touch.css"); 781 } 782 // when running on embedded add a extra stylesheet to tune performance of modena theme 783 if (PlatformUtil.isEmbedded()) { 784 uaStylesheets.add("com/sun/javafx/scene/control/skin/modena/modena-embedded-performance.css"); 785 } 786 if (PlatformUtil.isAndroid()) { 787 uaStylesheets.add("com/sun/javafx/scene/control/skin/modena/android.css"); 788 } 789 790 if (isSupported(ConditionalFeature.TWO_LEVEL_FOCUS)) { 791 uaStylesheets.add("com/sun/javafx/scene/control/skin/modena/two-level-focus.css"); 792 } 793 794 if (isSupported(ConditionalFeature.VIRTUAL_KEYBOARD)) { 795 uaStylesheets.add("com/sun/javafx/scene/control/skin/caspian/fxvk.css"); 796 } 797 798 if (!isSupported(ConditionalFeature.TRANSPARENT_WINDOW)) { 799 uaStylesheets.add("com/sun/javafx/scene/control/skin/modena/modena-no-transparency.css"); 800 } 801 802 } else { 803 uaStylesheets.add(stylesheetUrl); 804 } 805 806 // Ensure that accessibility starts right 807 _setAccessibilityTheme(Toolkit.getToolkit().getThemeName()); 808 if (accessibilityTheme != null) { 809 uaStylesheets.add(accessibilityTheme); 810 } 811 812 AccessController.doPrivileged((PrivilegedAction) () -> { 813 StyleManager.getInstance().setUserAgentStylesheets(uaStylesheets); 814 return null; 815 }); 816 817 } 818 819 public static void addNoTransparencyStylesheetToScene(final Scene scene) { 820 if (PlatformImpl.isCaspian()) { 821 AccessController.doPrivileged((PrivilegedAction) () -> { 822 StyleManager.getInstance().addUserAgentStylesheet(scene, 823 "com/sun/javafx/scene/control/skin/caspian/caspian-no-transparency.css"); 824 return null; 825 }); 826 } else if (PlatformImpl.isModena()) { 827 AccessController.doPrivileged((PrivilegedAction) () -> { 828 StyleManager.getInstance().addUserAgentStylesheet(scene, 829 "com/sun/javafx/scene/control/skin/modena/modena-no-transparency.css"); 830 return null; 831 }); 832 } 833 } 834 835 private static boolean isSupportedImpl(ConditionalFeature feature) { 836 switch (feature) { 837 case GRAPHICS: 838 if (isGraphicsSupported == null) { 839 isGraphicsSupported = checkForClass("javafx.stage.Stage"); 840 } 841 return isGraphicsSupported; 842 case CONTROLS: 843 if (isControlsSupported == null) { 844 isControlsSupported = checkForClass( 845 "javafx.scene.control.Control"); 846 } 847 return isControlsSupported; 848 case MEDIA: 849 if (isMediaSupported == null) { 850 isMediaSupported = checkForClass( 851 "javafx.scene.media.MediaView"); 852 if (isMediaSupported && PlatformUtil.isEmbedded()) { 853 AccessController.doPrivileged((PrivilegedAction<Void>) () -> { 854 String s = System.getProperty( 855 "com.sun.javafx.experimental.embedded.media", 856 "false"); 857 isMediaSupported = Boolean.valueOf(s); 858 return null; 859 860 }); 861 } 862 } 863 return isMediaSupported; 864 case WEB: 865 if (isWebSupported == null) { 866 isWebSupported = checkForClass("javafx.scene.web.WebView"); 867 if (isWebSupported && PlatformUtil.isEmbedded()) { 868 AccessController.doPrivileged((PrivilegedAction<Void>) () -> { 869 String s = System.getProperty( 870 "com.sun.javafx.experimental.embedded.web", 871 "false"); 872 isWebSupported = Boolean.valueOf(s); 873 return null; 874 875 }); 876 } 877 } 878 return isWebSupported; 879 case SWT: 880 if (isSWTSupported == null) { 881 isSWTSupported = checkForClass("javafx.embed.swt.FXCanvas"); 882 } 883 return isSWTSupported; 884 case SWING: 885 if (isSwingSupported == null) { 886 isSwingSupported = 887 // check for JComponent first, it may not be present 888 checkForClass("javax.swing.JComponent") && 889 checkForClass("javafx.embed.swing.JFXPanel"); 890 } 891 return isSwingSupported; 892 case FXML: 893 if (isFXMLSupported == null) { 894 isFXMLSupported = checkForClass("javafx.fxml.FXMLLoader") 895 && checkForClass("javax.xml.stream.XMLInputFactory"); 896 } 897 return isFXMLSupported; 898 case TWO_LEVEL_FOCUS: 899 if (hasTwoLevelFocus == null) { 900 return Toolkit.getToolkit().isSupported(feature); 901 } 902 return hasTwoLevelFocus; 903 case VIRTUAL_KEYBOARD: 904 if (hasVirtualKeyboard == null) { 905 return Toolkit.getToolkit().isSupported(feature); 906 } 907 return hasVirtualKeyboard; 908 case INPUT_TOUCH: 909 if (hasTouch == null) { 910 return Toolkit.getToolkit().isSupported(feature); 911 } 912 return hasTouch; 913 case INPUT_MULTITOUCH: 914 if (hasMultiTouch == null) { 915 return Toolkit.getToolkit().isSupported(feature); 916 } 917 return hasMultiTouch; 918 case INPUT_POINTER: 919 if (hasPointer == null) { 920 return Toolkit.getToolkit().isSupported(feature); 921 } 922 return hasPointer; 923 default: 924 return Toolkit.getToolkit().isSupported(feature); 925 } 926 } 927 }