Merge remote-tracking branch 'origin/develop' into develop
diff --git a/frameworks/projects/framework/src/mx/managers/LayoutManager.as b/frameworks/projects/framework/src/mx/managers/LayoutManager.as
index feebeff..d070203 100644
--- a/frameworks/projects/framework/src/mx/managers/LayoutManager.as
+++ b/frameworks/projects/framework/src/mx/managers/LayoutManager.as
@@ -846,11 +846,14 @@
var obj:ILayoutManagerClient = ILayoutManagerClient(updateCompleteQueue.removeLargest());
while (obj)
{
- if (!obj.initialized && obj.processedDescriptors)
- obj.initialized = true;
- if (obj.hasEventListener(FlexEvent.UPDATE_COMPLETE))
- obj.dispatchEvent(new FlexEvent(FlexEvent.UPDATE_COMPLETE));
- obj.updateCompletePendingFlag = false;
+ if(obj.nestLevel)
+ {
+ if (!obj.initialized && obj.processedDescriptors)
+ obj.initialized = true;
+ if (obj.hasEventListener(FlexEvent.UPDATE_COMPLETE))
+ obj.dispatchEvent(new FlexEvent(FlexEvent.UPDATE_COMPLETE));
+ obj.updateCompletePendingFlag = false;
+ }
obj = ILayoutManagerClient(updateCompleteQueue.removeLargest());
}
@@ -1094,12 +1097,15 @@
obj = ILayoutManagerClient(updateCompleteQueue.removeLargestChild(target));
while (obj)
{
- if (!obj.initialized)
- obj.initialized = true;
-
- if (obj.hasEventListener(FlexEvent.UPDATE_COMPLETE))
- obj.dispatchEvent(new FlexEvent(FlexEvent.UPDATE_COMPLETE));
- obj.updateCompletePendingFlag = false;
+ if(obj.nestLevel)
+ {
+ if (!obj.initialized)
+ obj.initialized = true;
+
+ if (obj.hasEventListener(FlexEvent.UPDATE_COMPLETE))
+ obj.dispatchEvent(new FlexEvent(FlexEvent.UPDATE_COMPLETE));
+ obj.updateCompletePendingFlag = false;
+ }
obj = ILayoutManagerClient(updateCompleteQueue.removeLargestChild(target));
}
}
diff --git a/frameworks/projects/framework/tests/mx/managers/LayoutManager_FLEX_35321_Tests.as b/frameworks/projects/framework/tests/mx/managers/LayoutManager_FLEX_35321_Tests.as
index 47d5f14..9b0b38c 100644
--- a/frameworks/projects/framework/tests/mx/managers/LayoutManager_FLEX_35321_Tests.as
+++ b/frameworks/projects/framework/tests/mx/managers/LayoutManager_FLEX_35321_Tests.as
@@ -6,7 +6,10 @@
import mx.core.mx_internal;
import mx.events.FlexEvent;
- import org.flexunit.asserts.assertFalse;
+ import org.flexunit.assertThat;
+
+ import org.flexunit.asserts.assertEquals;
+ import org.flexunit.asserts.assertNotNull;
import org.flexunit.asserts.assertNull;
import org.flexunit.async.Async;
import org.fluint.uiImpersonation.UIImpersonator;
@@ -18,63 +21,177 @@
private static var noEnterFramesRemaining:int = NaN;
private static const _finishNotifier:EventDispatcher = new EventDispatcher();
- private var _objectWhichIsRemovedOnSizeValidation:SomeComponent;
- private var _creationCompleteCalled:Boolean;
+ private var _objectWhichIsRemovedAtValidation:SomeComponent;
+ private var _creationCompleteCalls:int;
[Before]
public function setUp():void
{
- _creationCompleteCalled = false;
- _objectWhichIsRemovedOnSizeValidation = new SomeComponent();
- UIImpersonator.addChild(_objectWhichIsRemovedOnSizeValidation);
+ _creationCompleteCalls = 0;
+ _objectWhichIsRemovedAtValidation = new SomeComponent();
+ _objectWhichIsRemovedAtValidation.addEventListener(FlexEvent.CREATION_COMPLETE, onCreationComplete);
+ UIImpersonator.addElement(_objectWhichIsRemovedAtValidation);
}
[After]
public function tearDown():void
{
UIImpersonator.removeAllChildren();
- _objectWhichIsRemovedOnSizeValidation = null;
+ _objectWhichIsRemovedAtValidation = null;
}
+
+ //--------------------------------------------------------------------------
+ //
+ // Test method
+ //
+ //--------------------------------------------------------------------------
+
[Test]
public function test_object_removed_from_stage_via_code_is_not_initialized():void
{
//given
UIComponentGlobals.mx_internal::layoutManager.usePhasedInstantiation = false;
- _objectWhichIsRemovedOnSizeValidation.addEventListener(FlexEvent.CREATION_COMPLETE, onCreationComplete);
+ _objectWhichIsRemovedAtValidation.removeFromStageOnValidateProperties = true;
//when
- _objectWhichIsRemovedOnSizeValidation.validateNow();
+ _objectWhichIsRemovedAtValidation.invalidateProperties();
+ _objectWhichIsRemovedAtValidation.invalidateSize();
+ _objectWhichIsRemovedAtValidation.invalidateDisplayList();
+ _objectWhichIsRemovedAtValidation.validateNow();
//then
- assertNull("The object was actually not removed from stage. Huh?", _objectWhichIsRemovedOnSizeValidation.parent);
- assertFalse("Yep, this is the bug. Why call initialized=true on an object that's not on stage?", _creationCompleteCalled);
+ then_assert_not_initialized();
+ assert_validation_count(1, 0, 0);
}
+
+
+ //--------------------------------------------------------------------------
+ //
+ // Test method
+ //
+ //--------------------------------------------------------------------------
+
[Test(async, timeout=500)]
public function test_object_removed_from_stage_via_user_action_is_not_initialized():void
{
//given
UIComponentGlobals.mx_internal::layoutManager.usePhasedInstantiation = true;
- _objectWhichIsRemovedOnSizeValidation.addEventListener(FlexEvent.CREATION_COMPLETE, onCreationComplete);
+ _objectWhichIsRemovedAtValidation.removeFromStageOnValidateProperties = false;
//when
- _objectWhichIsRemovedOnSizeValidation.validateNow();
- _objectWhichIsRemovedOnSizeValidation.pretendUserAskedForComponentRemoval();
+ _objectWhichIsRemovedAtValidation.invalidateDisplayList();
+ _objectWhichIsRemovedAtValidation.invalidateProperties();
+ _objectWhichIsRemovedAtValidation.invalidateSize();
- //then wait 2 frames
- noEnterFramesRemaining = 2;
+ //then wait 1 frame
+ noEnterFramesRemaining = 1;
UIImpersonator.testDisplay.addEventListener(Event.ENTER_FRAME, onEnterFrame);
- Async.handleEvent(this, _finishNotifier, Event.COMPLETE, then_assert, 300);
+ Async.handleEvent(this, _finishNotifier, Event.COMPLETE, then_remove_from_stage_via_callLater, 300, {nextStep:then_assert_not_initialized_but_partially_validated, afterNumFrames:3});
}
- private function then_assert(event:Event, passThroughData:Object):void
+
+
+
+ //--------------------------------------------------------------------------
+ //
+ // Test method
+ //
+ //--------------------------------------------------------------------------
+
+ [Test(async, timeout=750)]
+ public function test_object_removed_from_stage_then_readded_is_initialized_once():void
+ {
+ //given
+ UIComponentGlobals.mx_internal::layoutManager.usePhasedInstantiation = true;
+ _objectWhichIsRemovedAtValidation.removeFromStageOnValidateProperties = false;
+
+ //when
+ _objectWhichIsRemovedAtValidation.invalidateDisplayList();
+ _objectWhichIsRemovedAtValidation.invalidateProperties();
+ _objectWhichIsRemovedAtValidation.invalidateSize();
+
+ //then wait 1 frame
+ noEnterFramesRemaining = 1;
+ UIImpersonator.testDisplay.addEventListener(Event.ENTER_FRAME, onEnterFrame);
+ Async.handleEvent(this, _finishNotifier, Event.COMPLETE, then_remove_from_stage_via_callLater, 300, {nextStep:then_readd_object, afterNumFrames:1});
+ }
+
+ private function then_readd_object(event:Event, passThroughData:Object):void
{
//then
- assertNull("The object was actually not removed from stage. Huh?", _objectWhichIsRemovedOnSizeValidation.parent);
- assertFalse("Yep, this is the bug. Why call initialized=true on an object that's not on stage?", _creationCompleteCalled);
+ assertNull("The object was actually not removed from stage. Huh?", _objectWhichIsRemovedAtValidation.parent);
+
+ //when
+ UIImpersonator.addElement(_objectWhichIsRemovedAtValidation);
+
+ //then wait 3 frames, to make sure validation is done
+ noEnterFramesRemaining = 3;
+ UIImpersonator.testDisplay.addEventListener(Event.ENTER_FRAME, onEnterFrame);
+ Async.handleEvent(this, _finishNotifier, Event.COMPLETE, then_assert_one_initialization_only, 400);
}
+ private function then_assert_one_initialization_only(event:Event, passThroughData:Object):void
+ {
+ //then
+ assertNotNull("The object should be on stage...", _objectWhichIsRemovedAtValidation.parent);
+ assertThat("If it's on stage, the nestLevel should be positive", _objectWhichIsRemovedAtValidation.nestLevel > 0);
+ assertEquals("When validation is interrupted half-way it should be complete once the object is re-added to stage", 1, _creationCompleteCalls);
+ assert_validation_count(2, 2, 1);
+ }
+
+
+ //--------------------------------------------------------------------------
+ //
+ // Shared test methods
+ //
+ //--------------------------------------------------------------------------
+
+ private function then_remove_from_stage_via_callLater(event:Event, passThroughData:Object):void
+ {
+ //then
+ assertEquals("The first validation step should have completed by now", 1, _objectWhichIsRemovedAtValidation.numValidatePropertiesCalls);
+ assertEquals("But not validateSize()", 0, _objectWhichIsRemovedAtValidation.numValidateSizeCalls);
+ assertEquals("Nor validateDisplayList()", 0, _objectWhichIsRemovedAtValidation.numValidateDisplayListCalls);
+
+ //given
+ const whereToGoNext:Function = passThroughData.nextStep as Function;
+ const afterHowManyFrames:int = passThroughData.afterNumFrames as int;
+
+ //when
+ _objectWhichIsRemovedAtValidation.pretendUserAskedForComponentRemovalInNextFrame();
+
+ //then wait
+ noEnterFramesRemaining = afterHowManyFrames;
+ UIImpersonator.testDisplay.addEventListener(Event.ENTER_FRAME, onEnterFrame);
+ Async.handleEvent(this, _finishNotifier, Event.COMPLETE, whereToGoNext, 300);
+ }
+
+ private function then_assert_not_initialized(event:Event = null, passThroughData:Object = null):void
+ {
+ //then
+ assertNull("The object was actually not removed from stage. Huh?", _objectWhichIsRemovedAtValidation.parent);
+ assertEquals("Yep, this is the bug. Why call initialized=true on an object that's not on stage?", 0, _creationCompleteCalls);
+ }
+
+ private function then_assert_not_initialized_but_partially_validated(event:Event = null, passThroughData:Object = null):void
+ {
+ //then
+ then_assert_not_initialized(event, passThroughData);
+ assert_validation_count(1, 1, 0);
+ }
+
+ private function assert_validation_count(numPropertiesValidations:int = 1, numSizeValidations:int = 1, numDisplayListValidations:int = 1):void
+ {
+ //then
+ assertEquals("Properties should have been validated", numPropertiesValidations, _objectWhichIsRemovedAtValidation.numValidatePropertiesCalls);
+ assertEquals("Size should have been validated", numSizeValidations, _objectWhichIsRemovedAtValidation.numValidateSizeCalls);
+ assertEquals("Display list should have been validated", numDisplayListValidations, _objectWhichIsRemovedAtValidation.numValidateDisplayListCalls);
+ }
+
+
+
private static function onEnterFrame(event:Event):void
{
if(!--noEnterFramesRemaining)
@@ -86,25 +203,39 @@
private function onCreationComplete(event:FlexEvent):void
{
- _creationCompleteCalled = true;
+ _creationCompleteCalls++;
}
}
}
-import flash.events.TimerEvent;
-import flash.utils.Timer;
-
import mx.core.IVisualElementContainer;
import mx.core.UIComponent;
class SomeComponent extends UIComponent
{
- private var _timer:Timer = new Timer(1, 1);
+ private var _removeFromStageOnValidateProperties:Boolean = false;
+ private var _numValidatePropertiesCalls:int = 0;
+ private var _numValidateSizeCalls:int = 0;
+ private var _numValidateDisplayListCalls:int = 0;
+
+ override public function validateProperties():void
+ {
+ super.validateProperties();
+ _numValidatePropertiesCalls++;
+ if(_removeFromStageOnValidateProperties)
+ removeFromStage();
+ }
override public function validateSize(recursive:Boolean = false):void
{
super.validateSize(recursive);
- removeFromStage();
+ _numValidateSizeCalls++;
+ }
+
+ override public function validateDisplayList():void
+ {
+ super.validateDisplayList();
+ _numValidateDisplayListCalls++;
}
private function removeFromStage():void
@@ -118,8 +249,28 @@
}
}
- public function pretendUserAskedForComponentRemoval():void
+ public function pretendUserAskedForComponentRemovalInNextFrame():void
{
callLater(removeFromStage);
}
+
+ public function set removeFromStageOnValidateProperties(value:Boolean):void
+ {
+ _removeFromStageOnValidateProperties = value;
+ }
+
+ public function get numValidateDisplayListCalls():int
+ {
+ return _numValidateDisplayListCalls;
+ }
+
+ public function get numValidateSizeCalls():int
+ {
+ return _numValidateSizeCalls;
+ }
+
+ public function get numValidatePropertiesCalls():int
+ {
+ return _numValidatePropertiesCalls;
+ }
}
\ No newline at end of file