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