From 9acfc04a6928371104b37c22df7769cab5340805 Mon Sep 17 00:00:00 2001 From: mstr2 <43553916+mstr2@users.noreply.github.com> Date: Mon, 21 Oct 2024 17:14:21 +0200 Subject: [PATCH] start transitions only when not in initial CSS state --- .../java/com/sun/javafx/scene/NodeHelper.java | 5 ++ .../javafx/css/StyleableBooleanProperty.java | 3 +- .../javafx/css/StyleableDoubleProperty.java | 5 +- .../javafx/css/StyleableFloatProperty.java | 3 +- .../javafx/css/StyleableIntegerProperty.java | 3 +- .../javafx/css/StyleableLongProperty.java | 5 +- .../javafx/css/StyleableObjectProperty.java | 10 +-- .../javafx/css/StyleableStringProperty.java | 5 +- .../src/main/java/javafx/scene/Node.java | 32 ++++++++ .../src/main/java/javafx/scene/Scene.java | 23 ++++++ .../StyleableProperty_transition_Test.java | 11 ++- .../scene/Node_transitionEvent_Test.java | 11 ++- .../javafx/scene/Node_transition_Test.java | 80 ++++++++++++++++++- 13 files changed, 170 insertions(+), 26 deletions(-) diff --git a/modules/javafx.graphics/src/main/java/com/sun/javafx/scene/NodeHelper.java b/modules/javafx.graphics/src/main/java/com/sun/javafx/scene/NodeHelper.java index 41b1fd62244..14a19fe4401 100644 --- a/modules/javafx.graphics/src/main/java/com/sun/javafx/scene/NodeHelper.java +++ b/modules/javafx.graphics/src/main/java/com/sun/javafx/scene/NodeHelper.java @@ -286,6 +286,10 @@ public static void reapplyCSS(Node node) { nodeAccessor.reapplyCSS(node); } + public static boolean isInitialCssState(Node node) { + return nodeAccessor.isInitialCssState(node); + } + public static void recalculateRelativeSizeProperties(Node node, Font fontForRelativeSizes) { nodeAccessor.recalculateRelativeSizeProperties(node, fontForRelativeSizes); } @@ -391,6 +395,7 @@ boolean doComputeIntersects(Node node, PickRay pickRay, void setLabeledBy(Node node, Node labeledBy); Accessible getAccessible(Node node); void reapplyCSS(Node node); + boolean isInitialCssState(Node node); void recalculateRelativeSizeProperties(Node node, Font fontForRelativeSizes); boolean isTreeVisible(Node node); BooleanExpression treeVisibleProperty(Node node); diff --git a/modules/javafx.graphics/src/main/java/javafx/css/StyleableBooleanProperty.java b/modules/javafx.graphics/src/main/java/javafx/css/StyleableBooleanProperty.java index 899ea6724fa..033d7767b35 100644 --- a/modules/javafx.graphics/src/main/java/javafx/css/StyleableBooleanProperty.java +++ b/modules/javafx.graphics/src/main/java/javafx/css/StyleableBooleanProperty.java @@ -70,7 +70,8 @@ public StyleableBooleanProperty(boolean initialValue) { /** {@inheritDoc} */ @Override public void applyStyle(StyleOrigin origin, Boolean v) { - TransitionDefinition transition = this.origin != null && getBean() instanceof Node node ? + // If the value is applied for the first time, we don't start a transition. + TransitionDefinition transition = getBean() instanceof Node node && !NodeHelper.isInitialCssState(node) ? NodeHelper.findTransitionDefinition(node, getCssMetaData()) : null; boolean newValue = v != null && v; diff --git a/modules/javafx.graphics/src/main/java/javafx/css/StyleableDoubleProperty.java b/modules/javafx.graphics/src/main/java/javafx/css/StyleableDoubleProperty.java index 7efd036b873..21346891891 100644 --- a/modules/javafx.graphics/src/main/java/javafx/css/StyleableDoubleProperty.java +++ b/modules/javafx.graphics/src/main/java/javafx/css/StyleableDoubleProperty.java @@ -70,9 +70,8 @@ public StyleableDoubleProperty(double initialValue) { /** {@inheritDoc} */ @Override public void applyStyle(StyleOrigin origin, Number v) { - // If this.origin == null, we're setting the value for the first time. - // No transition should be started in this case. - TransitionDefinition transition = this.origin != null && getBean() instanceof Node node ? + // If the value is applied for the first time, we don't start a transition. + TransitionDefinition transition = getBean() instanceof Node node && !NodeHelper.isInitialCssState(node) ? NodeHelper.findTransitionDefinition(node, getCssMetaData()) : null; double newValue = v != null ? v.doubleValue() : 0; diff --git a/modules/javafx.graphics/src/main/java/javafx/css/StyleableFloatProperty.java b/modules/javafx.graphics/src/main/java/javafx/css/StyleableFloatProperty.java index a8f829ae9fb..e6261fd71a4 100644 --- a/modules/javafx.graphics/src/main/java/javafx/css/StyleableFloatProperty.java +++ b/modules/javafx.graphics/src/main/java/javafx/css/StyleableFloatProperty.java @@ -70,7 +70,8 @@ public StyleableFloatProperty(float initialValue) { /** {@inheritDoc} */ @Override public void applyStyle(StyleOrigin origin, Number v) { - TransitionDefinition transition = this.origin != null && getBean() instanceof Node node ? + // If the value is applied for the first time, we don't start a transition. + TransitionDefinition transition = getBean() instanceof Node node && !NodeHelper.isInitialCssState(node) ? NodeHelper.findTransitionDefinition(node, getCssMetaData()) : null; float newValue = v != null ? v.floatValue() : 0; diff --git a/modules/javafx.graphics/src/main/java/javafx/css/StyleableIntegerProperty.java b/modules/javafx.graphics/src/main/java/javafx/css/StyleableIntegerProperty.java index ca932d8a6bf..453b18e3694 100644 --- a/modules/javafx.graphics/src/main/java/javafx/css/StyleableIntegerProperty.java +++ b/modules/javafx.graphics/src/main/java/javafx/css/StyleableIntegerProperty.java @@ -70,7 +70,8 @@ public StyleableIntegerProperty(int initialValue) { /** {@inheritDoc} */ @Override public void applyStyle(StyleOrigin origin, Number v) { - TransitionDefinition transition = this.origin != null && getBean() instanceof Node node ? + // If the value is applied for the first time, we don't start a transition. + TransitionDefinition transition = getBean() instanceof Node node && !NodeHelper.isInitialCssState(node) ? NodeHelper.findTransitionDefinition(node, getCssMetaData()) : null; int newValue = v != null ? v.intValue() : 0; diff --git a/modules/javafx.graphics/src/main/java/javafx/css/StyleableLongProperty.java b/modules/javafx.graphics/src/main/java/javafx/css/StyleableLongProperty.java index 18b09fa332f..b977e6c5ea1 100644 --- a/modules/javafx.graphics/src/main/java/javafx/css/StyleableLongProperty.java +++ b/modules/javafx.graphics/src/main/java/javafx/css/StyleableLongProperty.java @@ -71,9 +71,8 @@ public StyleableLongProperty(long initialValue) { /** {@inheritDoc} */ @Override public void applyStyle(StyleOrigin origin, Number v) { - // If this.origin == null, we're setting the value for the first time. - // No transition should be started in this case. - TransitionDefinition transition = this.origin != null && getBean() instanceof Node node ? + // If the value is applied for the first time, we don't start a transition. + TransitionDefinition transition = getBean() instanceof Node node && !NodeHelper.isInitialCssState(node) ? NodeHelper.findTransitionDefinition(node, getCssMetaData()) : null; long newValue = v != null ? v.longValue() : 0; diff --git a/modules/javafx.graphics/src/main/java/javafx/css/StyleableObjectProperty.java b/modules/javafx.graphics/src/main/java/javafx/css/StyleableObjectProperty.java index 1f6e44538cf..156627125be 100644 --- a/modules/javafx.graphics/src/main/java/javafx/css/StyleableObjectProperty.java +++ b/modules/javafx.graphics/src/main/java/javafx/css/StyleableObjectProperty.java @@ -103,10 +103,9 @@ public void applyStyle(StyleOrigin origin, T newValue) { * @param metadata the CSS metadata of the value */ private void applyValue(T oldValue, T newValue, CssMetaData metadata) { - // If this.origin == null, we're setting the value for the first time. - // No transition should be started in this case. + // If the value is applied for the first time, we don't start a transition. TransitionDefinition transition = - this.origin != null && getBean() instanceof Node node ? + getBean() instanceof Node node && !NodeHelper.isInitialCssState(node) ? NodeHelper.findTransitionDefinition(node, metadata) : null; // We only start a new transition if the new target value is different from the target @@ -146,10 +145,9 @@ private void applyValue(T oldValue, T newValue, CssMetaData metadata, SubPropertyConverter converter) { - // If this.origin == null, we're setting the value for the first time. - // No transition should be started in this case. + // If the value is applied for the first time, we don't start a transition. Map, TransitionDefinition> transitions = - this.origin != null && getBean() instanceof Node node ? + getBean() instanceof Node node && !NodeHelper.isInitialCssState(node) ? NodeHelper.findTransitionDefinitions(node, metadata) : null; List> subMetadata = metadata.getSubProperties(); diff --git a/modules/javafx.graphics/src/main/java/javafx/css/StyleableStringProperty.java b/modules/javafx.graphics/src/main/java/javafx/css/StyleableStringProperty.java index afaebdd83e7..a74ba8a332e 100644 --- a/modules/javafx.graphics/src/main/java/javafx/css/StyleableStringProperty.java +++ b/modules/javafx.graphics/src/main/java/javafx/css/StyleableStringProperty.java @@ -71,10 +71,9 @@ public StyleableStringProperty(String initialValue) { /** {@inheritDoc} */ @Override public void applyStyle(StyleOrigin origin, String newValue) { - // If this.origin == null, we're setting the value for the first time. - // No transition should be started in this case. + // If the value is applied for the first time, we don't start a transition. TransitionDefinition transition = - this.origin != null && getBean() instanceof Node node ? + getBean() instanceof Node node && !NodeHelper.isInitialCssState(node) ? NodeHelper.findTransitionDefinition(node, getCssMetaData()) : null; if (transition == null) { diff --git a/modules/javafx.graphics/src/main/java/javafx/scene/Node.java b/modules/javafx.graphics/src/main/java/javafx/scene/Node.java index 00064b6a6fe..5473907c774 100644 --- a/modules/javafx.graphics/src/main/java/javafx/scene/Node.java +++ b/modules/javafx.graphics/src/main/java/javafx/scene/Node.java @@ -595,6 +595,11 @@ public void reapplyCSS(Node node) { node.reapplyCSS(); } + @Override + public boolean isInitialCssState(Node node) { + return node.initialCssState; + } + @Override public void recalculateRelativeSizeProperties(Node node, Font fontForRelativeSizes) { node.recalculateRelativeSizeProperties(fontForRelativeSizes); @@ -1010,6 +1015,8 @@ protected void invalidated() { } updateDisabled(); computeDerivedDepthTest(); + resetInitialCssStateFlag(); + final Parent newParent = get(); // Update the focus bits before calling reapplyCss(), as the focus bits can affect CSS styling. @@ -1104,8 +1111,14 @@ private void invalidatedScenes(Scene oldScene, SubScene oldSubScene) { getClip().setScenes(newScene, newSubScene); } if (sceneChanged) { + if (oldScene != null) { + oldScene.unregisterClearInitialCssStageFlag(this); + } + if (newScene == null) { completeTransitionTimers(); + } else { + resetInitialCssStateFlag(); } updateCanReceiveFocus(); if (isFocusTraversable()) { @@ -10066,6 +10079,25 @@ private void doProcessCSS() { } } + /** + * A node is considered to be in its initial CSS state if it wasn't shown in a scene graph before. + * This flag is cleared after CSS processing was completed in a Scene pulse event. Note that manual + * calls to {@link #applyCss()} or similar methods will not clear this flag, since we consider all + * CSS processing before the Scene pulse to be part of the node's initial state. + */ + private boolean initialCssState = true; + + private void resetInitialCssStateFlag() { + initialCssState = true; + Scene scene = getScene(); + if (scene != null) { + scene.registerClearInitialCssStateFlag(this); + } + } + + void clearInitialCssStateFlag() { + initialCssState = false; + } /** * A StyleHelper for this node. diff --git a/modules/javafx.graphics/src/main/java/javafx/scene/Scene.java b/modules/javafx.graphics/src/main/java/javafx/scene/Scene.java index d9f2a0acd9a..eaf6bc0d521 100644 --- a/modules/javafx.graphics/src/main/java/javafx/scene/Scene.java +++ b/modules/javafx.graphics/src/main/java/javafx/scene/Scene.java @@ -589,6 +589,29 @@ private void doCSSPass() { sceneRoot.clearDirty(com.sun.javafx.scene.DirtyBits.NODE_CSS); sceneRoot.processCSS(); } + + if (!clearInitialCssStateNodes.isEmpty()) { + for (var node : clearInitialCssStateNodes) { + node.clearInitialCssStateFlag(); + } + + clearInitialCssStateNodes.clear(); + } + } + + /** + * A list of nodes that have expressed their interest to be notified when the next CSS pass + * is completed. Nodes will use this event to determine whether they are in their initial + * CSS state (see {@link Node#initialCssState}. + */ + private final Set clearInitialCssStateNodes = Collections.newSetFromMap(new IdentityHashMap<>()); + + void registerClearInitialCssStateFlag(Node node) { + clearInitialCssStateNodes.add(node); + } + + void unregisterClearInitialCssStageFlag(Node node) { + clearInitialCssStateNodes.remove(node); } void doLayoutPass() { diff --git a/modules/javafx.graphics/src/test/java/test/javafx/css/StyleableProperty_transition_Test.java b/modules/javafx.graphics/src/test/java/test/javafx/css/StyleableProperty_transition_Test.java index 36c35e399e8..8c49d828162 100644 --- a/modules/javafx.graphics/src/test/java/test/javafx/css/StyleableProperty_transition_Test.java +++ b/modules/javafx.graphics/src/test/java/test/javafx/css/StyleableProperty_transition_Test.java @@ -198,7 +198,6 @@ void setup() { scene = new Scene(new Group()); stage = new Stage(); stage.setScene(scene); - stage.show(); } @AfterEach @@ -217,6 +216,7 @@ void testRedundantTransitionIsDiscarded(TestRun testRun) { testRun.property.applyStyle(StyleOrigin.USER, testRun.defaultValue); var mediator1 = getFieldValue(testRun.property, testRun.fieldName); assertNull(mediator1); + stage.show(); // Start the transition. This adds it to the list of running transitions. testRun.property.applyStyle(StyleOrigin.USER, testRun.newValue); @@ -240,6 +240,7 @@ void testReversingTransitionIsNotDiscarded(TestRun testRun) { testRun.property.applyStyle(StyleOrigin.USER, testRun.defaultValue); var mediator1 = getFieldValue(testRun.property, testRun.fieldName); assertNull(mediator1); + stage.show(); // Start the transition. This adds it to the list of running transitions. testRun.property.applyStyle(StyleOrigin.USER, testRun.newValue); @@ -271,6 +272,7 @@ void testExistingTransitionOfComponentTransitionableIsPreserved() { // Setting a value for the first time doesn't start a transition. setAnimationTime(0); property.applyStyle(StyleOrigin.USER, border1); + stage.show(); // Start the transition and capture a copy of the sub-property mediator list. // -fx-background-color will transition from RED to GREEN @@ -301,6 +303,7 @@ void testIntegerTransitionsInRealNumberSpace() { // Setting a value for the first time doesn't start a transition. setAnimationTime(0); property.applyStyle(StyleOrigin.USER, 0); + stage.show(); // Start the transition and sample the outputs. property.applyStyle(StyleOrigin.USER, 2); @@ -324,6 +327,7 @@ void testLongTransitionsInRealNumberSpace() { // Setting a value for the first time doesn't start a transition. setAnimationTime(0); property.applyStyle(StyleOrigin.USER, 0); + stage.show(); // Start the transition and sample the outputs. property.applyStyle(StyleOrigin.USER, 2); @@ -347,6 +351,7 @@ void testBooleanTransitionsAsDiscrete() { // Setting a value for the first time doesn't start a transition. setAnimationTime(0); property.applyStyle(StyleOrigin.USER, false); + stage.show(); // Start the transition and sample the outputs. property.applyStyle(StyleOrigin.USER, true); @@ -364,6 +369,7 @@ void testStringTransitionsAsDiscrete() { // Setting a value for the first time doesn't start a transition. setAnimationTime(0); property.applyStyle(StyleOrigin.USER, "foo"); + stage.show(); // Start the transition and sample the outputs. property.applyStyle(StyleOrigin.USER, "bar"); @@ -403,6 +409,7 @@ enum Fruit { APPLE, ORANGE } // Setting a value for the first time doesn't start a transition. setAnimationTime(0); property.applyStyle(StyleOrigin.USER, Fruit.APPLE); + stage.show(); // Start the transition and sample the outputs. property.applyStyle(StyleOrigin.USER, Fruit.ORANGE); @@ -427,6 +434,7 @@ void testNullObjectTransitionsAsDiscrete_withInterpolatableValue() { // Setting a value for the first time doesn't start a transition. setAnimationTime(0); property.applyStyle(StyleOrigin.USER, Color.RED); + stage.show(); // Start the transition and sample the outputs. property.applyStyle(StyleOrigin.USER, null); @@ -451,6 +459,7 @@ void testNullObjectTransitionsAsDiscrete_withComponentTransitionableValue() { // Setting a value for the first time doesn't start a transition. setAnimationTime(0); property.applyStyle(StyleOrigin.USER, Background.fill(Color.RED)); + stage.show(); // Start the transition and sample the outputs. property.applyStyle(StyleOrigin.USER, null); diff --git a/modules/javafx.graphics/src/test/java/test/javafx/scene/Node_transitionEvent_Test.java b/modules/javafx.graphics/src/test/java/test/javafx/scene/Node_transitionEvent_Test.java index 56d879cc1c4..235ca72463e 100644 --- a/modules/javafx.graphics/src/test/java/test/javafx/scene/Node_transitionEvent_Test.java +++ b/modules/javafx.graphics/src/test/java/test/javafx/scene/Node_transitionEvent_Test.java @@ -65,7 +65,6 @@ public void startup() { scene = new Scene(new Group(node)); stage = new Stage(); stage.setScene(scene); - stage.show(); } @AfterEach @@ -89,7 +88,7 @@ public void testRegularPlayback() { toolkit.setCurrentTime(0); scene.getStylesheets().add(url); node.getStyleClass().add("testClass"); - node.applyCss(); + stage.show(); List trace = new ArrayList<>(); node.addEventHandler(TransitionEvent.ANY, trace::add); @@ -132,7 +131,7 @@ public void testPlaybackIsElidedWhenDurationIsZero() { toolkit.setCurrentTime(0); scene.getStylesheets().add(url); node.getStyleClass().add("testClass"); - node.applyCss(); + stage.show(); List trace = new ArrayList<>(); node.addEventHandler(TransitionEvent.ANY, trace::add); @@ -161,7 +160,7 @@ public void testInterruptedPlayback() { toolkit.setCurrentTime(0); scene.getStylesheets().add(url); node.getStyleClass().add("testClass"); - node.applyCss(); + stage.show(); List trace = new ArrayList<>(); node.addEventHandler(TransitionEvent.ANY, trace::add); @@ -199,7 +198,7 @@ public void testInterruptedPlaybackWithNegativeDelay() { toolkit.setCurrentTime(0); scene.getStylesheets().add(url); node.getStyleClass().add("testClass"); - node.applyCss(); + stage.show(); List trace = new ArrayList<>(); node.addEventHandler(TransitionEvent.ANY, trace::add); @@ -237,7 +236,7 @@ public void testInterruptedPlaybackDuringDelayPhase() { toolkit.setCurrentTime(0); scene.getStylesheets().add(url); node.getStyleClass().add("testClass"); - node.applyCss(); + stage.show(); List trace = new ArrayList<>(); node.addEventHandler(TransitionEvent.ANY, trace::add); diff --git a/modules/javafx.graphics/src/test/java/test/javafx/scene/Node_transition_Test.java b/modules/javafx.graphics/src/test/java/test/javafx/scene/Node_transition_Test.java index da4df862b34..aa69a147c87 100644 --- a/modules/javafx.graphics/src/test/java/test/javafx/scene/Node_transition_Test.java +++ b/modules/javafx.graphics/src/test/java/test/javafx/scene/Node_transition_Test.java @@ -67,7 +67,6 @@ public void startup() { scene = new Scene(new Group(node)); stage = new Stage(); stage.setScene(scene); - stage.show(); } @AfterEach @@ -188,6 +187,10 @@ public void testRunningTimersAreTrackedInNode() { Map timers = NodeShim.getTransitionTimers(node); assertNull(timers); + // Showing the stage causes the first Scene CSS pass, after which the node is + // eligible for CSS transitions. + stage.show(); + // The hover state starts the timer. node.pseudoClassStateChanged(PseudoClass.getPseudoClass("hover"), true); node.applyCss(); @@ -213,6 +216,10 @@ public void testRunningTimerIsCompletedWhenNodeIsRemovedFromSceneGraph() { node.applyCss(); assertNull(NodeShim.getTransitionTimers(node)); + // Showing the stage causes the first Scene CSS pass, after which the node is + // eligible for CSS transitions. + stage.show(); + // The hover state starts the timer. node.pseudoClassStateChanged(PseudoClass.getPseudoClass("hover"), true); node.applyCss(); @@ -238,6 +245,10 @@ public void testRunningTimerIsCompletedWhenNodeBecomesInvisible() { node.applyCss(); assertNull(NodeShim.getTransitionTimers(node)); + // Showing the stage causes the first Scene CSS pass, after which the node is + // eligible for CSS transitions. + stage.show(); + // The hover state starts the timer. node.pseudoClassStateChanged(PseudoClass.getPseudoClass("hover"), true); node.applyCss(); @@ -251,4 +262,71 @@ public void testRunningTimerIsCompletedWhenNodeBecomesInvisible() { assertEquals(1, node.getOpacity(), 0.001); } + @Test + public void testTransitionIsStartedWhenInitialPropertyIsNotSpecifiedInStylesheet() { + String url = "data:text/css;base64," + Base64.getUrlEncoder().encodeToString(""" + .testClass { transition: all 1s; } + .testClass:hover { -fx-opacity: 0.5; } + """.getBytes(StandardCharsets.UTF_8)); + + scene.getStylesheets().add(url); + node.getStyleClass().add("testClass"); + node.applyCss(); + assertNull(NodeShim.getTransitionTimers(node)); + + stage.show(); + node.pseudoClassStateChanged(PseudoClass.getPseudoClass("hover"), true); + toolkit.firePulse(); + assertEquals(1, NodeShim.getTransitionTimers(node).size()); + } + + @Test + public void testTransitionIsOnlyStartedAfterSceneHasProcessedCSS() { + String url = "data:text/css;base64," + Base64.getUrlEncoder().encodeToString(""" + .testClass { -fx-opacity: 0; transition: all 1s; } + .testClass:hover { -fx-opacity: 1; } + """.getBytes(StandardCharsets.UTF_8)); + + scene.getStylesheets().add(url); + node.getStyleClass().add("testClass"); + node.applyCss(); + assertNull(NodeShim.getTransitionTimers(node)); + + // Even though we apply CSS here, no transition is started because the Scene has not + // processed CSS in response to a pulse event yet. + node.pseudoClassStateChanged(PseudoClass.getPseudoClass("hover"), true); + node.applyCss(); + assertNull(NodeShim.getTransitionTimers(node)); + + // This will fire the first pulse event, which also does not start the transition + // because the initial CSS pass only establishes the initial CSS state for the node. + node.pseudoClassStateChanged(PseudoClass.getPseudoClass("hover"), false); + stage.show(); + assertNull(NodeShim.getTransitionTimers(node)); + + // This will fire the second pulse event, which will start the transition. + node.pseudoClassStateChanged(PseudoClass.getPseudoClass("hover"), true); + toolkit.firePulse(); + assertEquals(1, NodeShim.getTransitionTimers(node).size()); + } + + @Test + public void testInitialCssStateFlagIsSetWhenAddedToOrRemovedFromScene() { + stage.show(); + + // The initial CSS pass is already done, so the initialCssState flag is cleared. + assertFalse(NodeHelper.isInitialCssState(node)); + + // Removing the node from the scene resets the initialCssState flag. + ((Group)scene.getRoot()).getChildren().remove(0); + assertTrue(NodeHelper.isInitialCssState(node)); + + // The initialCssState flag is set on a newly-created node. + var node2 = new Rectangle(); + assertTrue(NodeHelper.isInitialCssState(node2)); + + // The flag is also set when the new node is added to the scene. + ((Group)scene.getRoot()).getChildren().add(node2); + assertTrue(NodeHelper.isInitialCssState(node)); + } }