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