You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@flex.apache.org by cf...@apache.org on 2012/06/18 20:10:25 UTC
svn commit: r1351437 [4/12] - in /incubator/flex/trunk/frameworks/projects:
automation_agent/ automation_agent/bundles/ automation_agent/bundles/da_DK/
automation_agent/bundles/de_DE/ automation_agent/bundles/en_US/
automation_agent/bundles/es_ES/ auto...
Added: incubator/flex/trunk/frameworks/projects/automation_agent/src/mx/automation/AutomationManager.as
URL: http://svn.apache.org/viewvc/incubator/flex/trunk/frameworks/projects/automation_agent/src/mx/automation/AutomationManager.as?rev=1351437&view=auto
==============================================================================
--- incubator/flex/trunk/frameworks/projects/automation_agent/src/mx/automation/AutomationManager.as (added)
+++ incubator/flex/trunk/frameworks/projects/automation_agent/src/mx/automation/AutomationManager.as Mon Jun 18 18:10:20 2012
@@ -0,0 +1,5119 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////////
+
+package mx.automation
+{
+ import flash.display.DisplayObject;
+ import flash.display.DisplayObjectContainer;
+ import flash.display.Stage;
+ import flash.events.Event;
+ import flash.events.EventDispatcher;
+ import flash.events.FocusEvent;
+ import flash.events.IEventDispatcher;
+ import flash.events.KeyboardEvent;
+ import flash.events.MouseEvent;
+ import flash.geom.Point;
+ import flash.system.ApplicationDomain;
+ import flash.utils.Dictionary;
+ import flash.utils.clearTimeout;
+ import flash.utils.getDefinitionByName;
+ import flash.utils.getQualifiedClassName;
+ import flash.utils.getQualifiedSuperclassName;
+ import flash.utils.setTimeout;
+
+ import mx.automation.delegates.DragManagerAutomationImpl;
+ import mx.automation.events.AutomationAirEvent;
+ import mx.automation.events.AutomationEvent;
+ import mx.automation.events.AutomationRecordEvent;
+ import mx.automation.events.AutomationReplayEvent;
+ import mx.automation.events.EventDetails;
+ import mx.automation.events.MarshalledAutomationEvent;
+ import mx.controls.Alert;
+ import mx.core.Application;
+ import mx.core.Container;
+ import mx.core.EventPriority;
+ import mx.core.IChildList;
+ import mx.core.IDeferredInstantiationUIComponent;
+ import mx.core.IFlexModuleFactory;
+ import mx.core.IRawChildrenContainer;
+ import mx.core.ISWFBridgeProvider;
+ import mx.core.IUIComponent;
+ import mx.core.UIComponent;
+ import mx.core.mx_internal;
+ import mx.events.DragEvent;
+ import mx.events.FlexChangeEvent;
+ import mx.events.FlexEvent;
+ import mx.events.InterManagerRequest;
+ import mx.events.SandboxMouseEvent;
+ import mx.managers.IMarshalSystemManager;
+ import mx.managers.ISystemManager;
+ import mx.managers.SystemManager;
+ import mx.managers.SystemManagerProxy;
+ import mx.modules.ModuleManager;
+ import mx.resources.IResourceManager;
+ import mx.resources.ResourceManager;
+ import mx.styles.IStyleClient;
+
+ use namespace mx_internal;
+ [Mixin]
+
+[ResourceBundle("automation_agent")]
+
+/**
+ * Provides the interface for manipulating the automation hierarchy,
+ * and for recording and replaying events.
+ *
+ * @langversion 3.0
+ * @playerversion Flash 9
+ * @playerversion AIR 1.1
+ * @productversion Flex 3
+ */
+public class AutomationManager extends EventDispatcher
+ implements IAutomationManager2, IAutomationObjectHelper,
+ IAutomationMouseSimulator, IAutomationDebugTracer
+{
+ include "../core/Version.as";
+
+
+
+ //--------------------------------------------------------------------------
+ //
+ // Class constants
+ //
+ //--------------------------------------------------------------------------
+
+ /**
+ * @private
+ */
+ private static const MOUSE_CLICK_TYPES:Array = [ MouseEvent.MOUSE_OVER,
+ MouseEvent.MOUSE_DOWN,
+ MouseEvent.MOUSE_UP,
+ MouseEvent.CLICK ];
+
+ /**
+ * @private
+ */
+ private static const KEY_CLICK_TYPES:Array = [ KeyboardEvent.KEY_DOWN,
+ KeyboardEvent.KEY_UP ];
+
+ //--------------------------------------------------------------------------
+ //
+ // Class variables
+ //
+ //--------------------------------------------------------------------------
+
+ /**
+ * @private
+ * Dictionary of all app domains/systemManagers
+ */
+ private static var allSystemManagers:Dictionary = new Dictionary(true);
+
+ /**
+ * @private
+ * The highest place we can listen for events in our DOM
+ */
+ private static var _mainListenerObj:IEventDispatcher;
+
+
+ /**
+ * @private
+ * The uniqueAppID of this applicaiton as decided by the
+ * root AutomationManager
+ * This field will be the applicaiton.id for the root appliction
+ */
+ private static var _uniqueApplicationId:String;
+
+ /**
+ * @private
+ * The start point of this application in screen coordinates
+ */
+ private static var _appStartPoint:Point;
+
+ /**
+ * @private
+ * the system manager of the current applicaiton domain
+ */
+
+ private static var sm1:ISystemManager;
+ private static var _sm1MSm:IMarshalSystemManager;
+
+
+ /**
+ * @private
+ * The popup's of the current appliation domain
+ */
+
+ private static var popUpObjects:Array;
+
+ /**
+ * @private
+ * The daragProxy from the sub application
+ */
+
+ private static var currentDragProxyHolder:Array;
+
+
+ private static var allAirWindowsToIdDictionary:Dictionary = new Dictionary(true);
+ private static var allAirIdToWindowsDictionary:Dictionary = new Dictionary(true);
+ private static var allAirWindowList:Array = new Array();
+ private static var lastRegisteredWindowCount:int = 0;
+ private static const airWindowIdFixedString:String = "_AIRWindow_";
+ public static const airWindowIndicatorPropertyName:String = "isAIRWindow";
+ //--------------------------------------------------------------------------
+ //
+ // Class methods
+ //
+ //--------------------------------------------------------------------------
+
+ private static function get mainListenerObj():IEventDispatcher
+ {
+ if(!_mainListenerObj)
+ initMainListeners();
+
+ return _mainListenerObj;
+
+ }
+
+
+ private static function get sm1MSm():IMarshalSystemManager
+ {
+ if(!_sm1MSm)
+ initMainListeners();
+
+ return _sm1MSm;
+
+ }
+ /**
+ * @private
+ * Function invoked by the SystemManager. Creates AutomationManager singleton.
+ */
+ public static function init(root:DisplayObject):void
+ {
+ if(!Automation.initialized)
+ {
+
+ sm1 = root as ISystemManager;
+ var sysMgr:SystemManager = root as SystemManager;
+ var tempOj:Object = sysMgr.topLevelSystemManager.getImplementation("mx.managers::IMarshalSystemManager");
+ _sm1MSm = IMarshalSystemManager(sysMgr.topLevelSystemManager.getImplementation("mx.managers::IMarshalSystemManager") );
+ //sm1MSm = root as IMarshalSystemManager;
+ // add event listener for the new sand_box_bridge event.
+ // whenver we get the new bridge
+ //mainListenerObj = getMainListenerObject(sm1);
+
+ Automation.automationManager = new AutomationManager;
+ AutomationHelper.registerSystemManager(sm1);
+
+ }
+ }
+
+ /**
+ * @private
+ */
+ private static function isChild(parent:DisplayObject,
+ child:DisplayObject):Boolean
+ {
+ while (child != null)
+ {
+ if (parent == child)
+ return true;
+
+ child = child.parent;
+ }
+
+ return false;
+ }
+
+ /**
+ * @private
+ */
+ private static function comparePropertyValues(lhs:Object, rhs:Object):Boolean
+ {
+ //we should probably be use the DefaultPropertyCodec to transcoding help here
+ //parts coming in from the testing tool should be properly typed, but they aren't
+ //so pretty much lhs will always be a String or RegExp
+ if (lhs == null && rhs == null)
+ return true;
+
+ if ((lhs is String || lhs is Array) &&
+ lhs.length == 0 && rhs == null)
+ return true;
+
+ /* Commenting the trimming part below because XMLList is now retruning non-trimmed strings*/
+ //For strings we are trimming because otherwise, it returns false when compared with XML.toString()
+ //because it returns trimmed strings
+ /*if(rhs is String)
+ rhs = trim(rhs as String);
+ if(lhs is String)
+ lhs = trim(lhs as String);*/
+
+ if ((lhs is XML ) &&
+ (lhs as XML).toXMLString.length == 0 && rhs == null)
+ return true;
+
+ if(rhs == null)
+ {
+ if ((lhs is XMLList ) &&
+ ((lhs as XMLList).length() == 1))
+ {
+ var currentVar:XML = lhs[0];
+ if(currentVar.toXMLString().length == 0)
+ return true;
+ }
+ }
+
+ if (rhs == null)
+ return false;
+
+ if(rhs is Boolean)
+ return (rhs == Boolean(Number(lhs)));
+
+ if (lhs is Array)
+ {
+ if (!(rhs is Array))
+ return false;
+
+ if (lhs.length != rhs.length)
+ return false;
+
+ for (var no:int = 0; no < lhs.length; ++no)
+ {
+ if (!comparePropertyValues(lhs[no], rhs[no]))
+ return false;
+ }
+
+ return true;
+ }
+ else if (lhs is RegExp)
+ return lhs.test(rhs.toString());
+ else if (lhs is String)
+ return lhs == rhs.toString();
+ else
+ return lhs == rhs;
+ }
+
+ /**
+ * Applies [f] to each item in [list] by calling f(list[i])
+ * for i=0..[list].length.
+ *
+ * @langversion 3.0
+ * @playerversion Flash 9
+ * @playerversion AIR 1.1
+ * @productversion Flex 3
+ */
+ private static function map(f:Function, list:Array):void
+ {
+ for (var i:int = 0; i < list.length; i++)
+ {
+ f(list[i]);
+ }
+ }
+
+ private static function isWhitespace( ch:String ):Boolean {
+ return ch == '\r' ||
+ ch == '\n' ||
+ ch == '\f' ||
+ ch == '\t' ||
+ ch == ' ';
+ }
+
+ private static function trim(string:String):String
+ {
+ var n:int = string.length;
+ var i:int;
+ while(n>0)
+ {
+ if(isWhitespace(string.charAt(0)))
+ {
+ string = string.substring(1,n);
+ n--;
+ }
+ else
+ break;
+ }
+ n = string.length;
+ while(n>0)
+ {
+ if(isWhitespace(string.charAt(n-1)))
+ {
+ string = string.substring(0,n-1);
+ n--;
+ }
+ else
+ break;
+ }
+ return string;
+ }
+
+ /**
+ * @private
+ */
+ private function childAddedHandler(event:Event):void
+ {
+ if (!Automation.delegateDictionary)
+ return;
+
+ var object:DisplayObject = event.target as DisplayObject;
+
+ if (object && object.root && object.root is DisplayObject && !allSystemManagers[object.root])
+ allSystemManagers[object.root] = object.root;
+
+
+ var delegateCreated:Boolean = createDelegate(event.target as DisplayObject);
+ addDelegates(event.target as DisplayObject);
+
+ if(delegateCreated == false)
+ {
+ var component:IAutomationObject = event.target as IAutomationObject;
+ if(!component) // the obejct is not an IAutomationObject of the main applicaiton
+ {
+
+ if(object.parent )
+ {
+ // try to get the parents classname.
+ var className:String = getQualifiedClassName(object.parent);
+
+ // when we get Alerts which are part of the another application
+ // we cannot create the delegate here. Here we should send the details
+ // to the other application and let them handle the same.
+ if((className == "mx.managers::SystemManagerProxy")||
+ ((object.hasOwnProperty("className")&& (object["className"] == "DragProxy")))||
+ (className =="mx.managers.dragClasses::DragProxy"))
+
+ //if(className == "mx.managers::SystemManagerProxy")
+ {
+
+ var tempEventObj:MarshalledAutomationEvent = new MarshalledAutomationEvent(
+ MarshalledAutomationEvent.POPUP_HANDLER_REQUEST);
+ var tempArr:Array = new Array();
+ tempArr.push(object);
+ tempEventObj.interAppDataToSubApp = tempArr;
+ dispatchMarshalledEventToSubApplications(tempEventObj);
+ }
+ }
+ }
+
+ }
+ }
+
+
+ /**
+ * @private
+ * Given a object returns the SystemManager object which contains
+ * the applicationDomain containing the object class.
+ */
+ private static function getSWFRoot(object:DisplayObject):DisplayObject
+ {
+ var className:String = getQualifiedClassName(object);
+
+ var domain:ApplicationDomain;
+ var compClass:Class;
+ for (var p:* in allSystemManagers)
+ {
+ domain = p.loaderInfo.applicationDomain;
+ try
+ {
+ compClass = Class(domain.getDefinition(className));
+ if (object is compClass)
+ return p as DisplayObject;
+ }
+ catch(e:Error)
+ {
+ //exception means that the application domain
+ //doesn't contain the object class.
+ }
+ }
+
+ //we have failed to find the application domain in the dictionary.
+ //try the nearest systemManager instance.
+ var sm:DisplayObject = object;
+ while(sm && !(sm is ISystemManager))
+ {
+ sm = sm.parent;
+ }
+ if(sm)
+ {
+
+ domain = sm.loaderInfo.applicationDomain;
+ try
+ {
+ compClass = Class(domain.getDefinition(className));
+ if (object is compClass)
+ return sm;
+ }
+ catch(e:Error)
+ {
+ // we didnt get the current object in any of the system manager's domain
+ // and we got this exception. It is quite possible that the class
+ // is an internal class. so let us rerutn the last parent's system manager.
+ // FLEXENT-1088, 1090, we should not return the parent's sm as this will prevent
+ // the module's app domain getting tried out.
+ //return sm;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * @private
+ */
+ private static function createDelegate(obj:DisplayObject):Boolean
+ {
+ var component:IAutomationObject = obj as IAutomationObject;
+ //if(!(obj is IAutomationObject || component == null || component.automationDelegate))
+ // the above looks to be wrong as we were adding the delegate for the same object more than once
+ // so change as follows
+ if((component == null)||(component.automationDelegate))
+ {
+ return false;
+ }
+
+ var retValue:Boolean = false;
+ var appDomain:ApplicationDomain;
+ var className:String = getQualifiedClassName(obj);
+ var message:String;
+ if(!className)
+ {
+ message = "class name for the object could not be obtained " + obj.toString();
+ Automation.automationDebugTracer.traceMessage("AutomationManager","createDelegate()",message);
+ return false;
+ }
+
+ var sm:DisplayObject = getSWFRoot(obj);
+
+ if(!sm)
+ {
+ var factory:IFlexModuleFactory = ModuleManager.getAssociatedFactory(obj);
+ if (factory != null)
+ {
+ appDomain = ApplicationDomain(factory.info()["currentDomain"]);
+ }
+ else
+ {
+ message = "Factory module failure";
+ Automation.automationDebugTracer.traceMessage("AutomationManager","createDelegate()",message);
+ }
+ }
+ else
+ {
+ appDomain = (sm.loaderInfo) ? sm.loaderInfo.applicationDomain :
+ ApplicationDomain.currentDomain;
+ }
+
+ var delegateClass:Class = null;
+ var compClass:Class = null;
+ var mainComponentClass:Class = null;
+ try
+ {
+ if(appDomain)
+ {
+ compClass = appDomain.getDefinition(className) as Class;
+ mainComponentClass = compClass;
+ delegateClass = Automation.delegateDictionary[compClass] as Class;
+ }
+ else
+ {
+ message = "Failed in getting the definition or the class or getting the delegate. " +
+ "Automation will not work for this component. " + className;
+ Automation.automationDebugTracer.traceMessage("AutomationManager","createDelegate()",message);
+ }
+ }
+ catch(e:Error)
+ {
+ message = "Failed in getting the definition or the class or getting the delegate. " +
+ "Automation will not work for this component. " + className;
+ Automation.automationDebugTracer.traceMessage("AutomationManager","createDelegate()",message);
+ Automation.automationDebugTracer.traceMessage("AutomationManager","createDelegate()",e.message);
+ //return false;
+ }
+
+ if(!delegateClass && appDomain)
+ {
+ var componentClass:String = className;
+ do
+ {
+ try
+ {
+ className = getQualifiedSuperclassName(appDomain.getDefinition(className));
+ if(className)
+ {
+ compClass = appDomain.getDefinition(className) as Class;
+ delegateClass = Automation.delegateDictionary[compClass] as Class;
+ }
+ }
+ catch(e:Error)
+ {
+ Automation.automationDebugTracer.traceMessage("AutomationManager","createDelegate()",e.message);
+ break;
+ }
+ }
+ while(!delegateClass && className);
+
+ Automation.delegateDictionary[mainComponentClass] = delegateClass;
+ //trace("Added mapping for : " + componentClass);
+
+ if(!className)
+ {
+ message = "super class name for the object could not be obtained "+ componentClass;
+ Automation.automationDebugTracer.traceMessage("AutomationManager","createDelegate()",message);
+ return false;
+ }
+
+ }
+
+
+ var c:Class = delegateClass;
+ if (c)
+ {
+ try
+ {
+ var delegate:Object = new c (obj);
+ }
+ catch(e:Error)
+ {
+ Automation.automationDebugTracer.traceMessage("AutomationManager","createDelegate()",e.message);
+ message = "Delegate object couldnot be created";
+ Automation.automationDebugTracer.traceMessage("AutomationManager","createDelegate()",message);
+ }
+
+ try
+ {
+ component.automationDelegate = delegate;
+ retValue = true;
+ }
+ catch(e:Error)
+ {
+ Automation.automationDebugTracer.traceMessage("AutomationManager","createDelegate()",e.message);
+ message = "object created but delegates not set";
+ Automation.automationDebugTracer.traceMessage("AutomationManager","createDelegate()",message);
+ }
+ }
+ else{
+ message = "Unable to find definition for class : " + className;
+ Automation.automationDebugTracer.traceMessage("AutomationManager","createDelegate()",message);
+ }
+
+ return retValue;
+ }
+
+ /**
+ * @private
+ * Do a tree walk and add all children you can find.
+ */
+ private static function addDelegates(o:DisplayObject):void
+ {
+ var child:DisplayObject ;
+ var i:int;
+
+ if (o is DisplayObjectContainer)
+ {
+ var doc:DisplayObjectContainer = DisplayObjectContainer(o);
+
+ if (o is IRawChildrenContainer)
+ {
+ // trace("using view rawChildren");
+ var rawChildren:IChildList = IRawChildrenContainer(o).rawChildren;
+ // recursively visit and add children of components
+ // we don't do this for containers because we get individual
+ // adds for the individual children
+ for (i = 0; i < rawChildren.numChildren; i++)
+ {
+ try
+ {
+ child = rawChildren.getChildAt(i);
+ createDelegate(child);
+ addDelegates(child);
+ }
+ catch(error:SecurityError)
+ {
+ // Ignore this child if we can't access it
+ }
+ }
+
+ }
+ else
+ {
+ // trace("using container's children");
+ // recursively visit and add children of components
+ // we don't do this for containers because we get individual
+ // adds for the individual children
+ for (i = 0; i < doc.numChildren; i++)
+ {
+ try
+ {
+ child = doc.getChildAt(i);
+ createDelegate(child);
+ addDelegates(child);
+ }
+ catch(error:SecurityError)
+ {
+ // Ignore this child if we can't access it
+ }
+ }
+ }
+ }
+
+ // do special creation of repeater delegates as they do not
+ // get added as children of Containers
+ var container:Container;
+ var repeaters:Array;
+ var count:int;
+ if(o is Container)
+ {
+ container = o as Container;
+ repeaters = container.childRepeaters;
+ // change for https://bugs.adobe.com/jira/browse/FLEXENT-1044
+ //if(!repeaters)
+ // return;
+ count = repeaters?repeaters.length:0;
+ for(i = 0; i < count; ++i)
+ {
+ createDelegate(repeaters[i]);
+ }
+ }
+
+ // do special creation of repeater delegates as they do not
+ // get added as children of Containers
+ if(o.parent is Container)
+ {
+ container = o.parent as Container;
+ repeaters = container.childRepeaters;
+ // change for https://bugs.adobe.com/jira/browse/FLEXENT-1044
+ //if(!repeaters)
+ // return;
+ count = repeaters?repeaters.length:0;
+ for(i = 0; i < count; ++i)
+ {
+ var repeater:IAutomationObject = repeaters[i] as IAutomationObject;
+ if(repeater && !repeater.automationDelegate)
+ createDelegate(repeaters[i]);
+ }
+ }
+
+ // let us add one more level of check for the repeaters.
+ // change for https://bugs.adobe.com/jira/browse/FLEXENT-1044
+
+ if(o is UIComponent)
+ {
+ var uiComp:UIComponent = o as UIComponent;
+ repeaters = uiComp.repeaters;
+ count = repeaters? repeaters.length : 0;
+ for(i = 0; i < count; ++i)
+ {
+ var repeater1:IAutomationObject = repeaters[i] as IAutomationObject;
+ if(repeater1 && !repeater1.automationDelegate)
+ createDelegate(repeaters[i]);
+ }
+ }
+
+ }
+
+ //--------------------------------------------------------------------------
+ //
+ // Constructor
+ //
+ //--------------------------------------------------------------------------
+
+ /**
+ * @private
+ * Constructor
+ */
+ public function AutomationManager()
+ {
+ super();
+
+ //if(mainListenerObj)
+ {
+ // when the application is completed we need to add listener to the existing bridge
+ sm1.addEventListener(FlexEvent.APPLICATION_COMPLETE, applicationCompleteHandler,false,EventPriority.DEFAULT);
+ sm1.addEventListener(FlexChangeEvent.ADD_CHILD_BRIDGE , childBridgeHandler);
+
+ sm1.addEventListener(Event.ADDED, childAddedHandler, false, 0, true);
+ // FLEXENT-894 or 895 it was observerd that popupmenubutton menu popup object is
+ // creatd before application completion. So we need to listen to the event
+ // from the main app before the application completion.
+ // this event is removed lated when we listen to the after application completion
+ //mainListenerObj.addEventListener(MarshalledAutomationEvent.POPUP_HANDLER_REQUEST , popupHandlerBeforeApplicationCompletion, false, 0, true);
+ // FLEXENT-1002
+ // when the sdk changes happened with the IMarshaledSystemManager, since the mainListenerObj was not available here
+ // we moved the following line to the applicaiton completion handler. But we need this handled before the application completion
+ // the popups of the application domain is added to the sandbox root application, we can depend on the sanbox to add to the listener for this
+ // event.
+ sm1.getSandboxRoot().addEventListener(MarshalledAutomationEvent.POPUP_HANDLER_REQUEST , popupHandlerBeforeApplicationCompletion, false, 0, true);
+
+ }
+ }
+
+ //--------------------------------------------------------------------------
+ //
+ // Variables
+ //
+ //--------------------------------------------------------------------------
+
+ /**
+ * @private
+ */
+ private var lastMouseTarget:IEventDispatcher = null;
+
+ /**
+ * @private
+ */
+ private var hierarchyCacheCounter:int = 0;
+
+ /**
+ * @private
+ */
+ private var rebuildPartCache:Boolean = true;
+
+ /**
+ * @private
+ * Holds automationIDPart objects for reuse.
+ * The IDParts are cached for the duration of one recording/function call.
+ */
+ private var cachedParts:Dictionary = null;
+
+ /**
+ * @private
+ * Holds the array of automation children for a container.
+ * The children are cached for the duration of one recording/function call.
+ */
+ private var cachedChildren:Dictionary = null;
+
+ /**
+ * @private
+ */
+ private var cachedCompositor:Dictionary = null;
+
+ /**
+ * @private
+ */
+ private var cachingEvents:Boolean = false;
+
+ /**
+ * @private
+ */
+ private var cachedTargetOriginator:EventDispatcher = null;
+
+ /**
+ * @private
+ */
+ private var eventCache:Array = [];
+
+ /**
+ * @private
+ */
+ private var flushCacheTimeoutID:int = -1;
+
+ /**
+ * @private
+ */
+ private var recordedEventInCurrentMouseSequence:Boolean = false;
+
+ /**
+ * @private
+ */
+ private var inMouseSequence:Boolean = false;
+
+ /**
+ * @private
+ */
+ private var synchronization:Array = [];
+
+ /**
+ * @private
+ */
+ private var _currentMousePositions:Array = [];
+
+ /**
+ * @private
+ */
+ private var _prevMouseTargets:Array = [];
+
+ /**
+ * @private
+ * Used for accessing localized Error messages.
+ */
+ private var resourceManager:IResourceManager =
+ ResourceManager.getInstance();
+
+ //--------------------------------------------------------------------------
+ //
+ // Properties
+ //
+ //--------------------------------------------------------------------------
+
+ //----------------------------------
+ // automationEnvironment
+ //----------------------------------
+
+ /**
+ * @private
+ * Storage for the automationEnvironment property.
+ */
+ private static var _automationEnvironment:IAutomationEnvironment;
+ private static var _automationEnvironmentString:String;
+ private static var _automationEnvironmentHandlingClassName:String;
+
+ /**
+ * @private
+ */
+ public function get automationEnvironment():Object
+ {
+ //For AIR apps it is possible that environment details for main app are set
+ //after the child apps request handlers are handled. So it can be null for child apps
+ //intially. We need a way to get the environment details for AIR apps when actually needed
+ if(!_automationEnvironment) //happens only for AIR apps
+ {
+ // we will listen to the initial details from our parent.
+ var initialStatusRequest:MarshalledAutomationEvent =
+ new MarshalledAutomationEvent(MarshalledAutomationEvent.INITIAL_DETAILS_REQUEST);
+ _inInitialDetailsRequestProcessing = true;
+ dispatchToParent(initialStatusRequest);
+ }
+ return _automationEnvironment;
+ }
+
+ /**
+ * @private
+ */
+ public function set automationEnvironment(value:Object):void
+ {
+ _automationEnvironment = value as IAutomationEnvironment;
+ // we expect this method to be called only on the top root applicaiton
+ }
+
+ /**
+ * @private
+ */
+ public function get automationEnvironmentString():String
+ {
+ return _automationEnvironmentString;
+ }
+
+ /**
+ * @private
+ */
+ public function set automationEnvironmentString(value:String):void
+ {
+ _automationEnvironmentString = value;
+
+ }
+
+
+ public function getUniqueApplicationID():String
+ {
+ if (_uniqueApplicationId == null)
+ {
+ if(sm1.isTopLevelRoot() == false)
+ {
+ // we should the following approach only for the sub
+ // applicaiton in the main security domain
+ // applications across security domain cannot access the
+ // parents of the system manager
+ if(sm1.isTopLevel() && (sm1.topLevelSystemManager.getSandboxRoot() == sm1.getSandboxRoot()) )
+ {
+ // we need to get the id of the current application from the parent
+ dispatchUniqueAppIdRequestEvent();
+
+ var currentApplicationId:String = Automation.getMainApplication().className;
+ _uniqueApplicationId = currentApplicationId + "_"+ _uniqueApplicationId;
+ //trace(_uniqueApplicationId + " - "+ classNameArray.join("|"));
+
+ }
+ else
+ {
+ // we expect this loop to reach for the sub applicaiton is
+ // different security domain
+ var temp:int = 0;
+ }
+
+ }
+ else
+ {
+ if(Automation.getMainApplication().hasOwnProperty("applicationID"))// this should work for AIR app's
+ {
+ _uniqueApplicationId = Automation.getMainApplication().applicationID;
+ }
+ else
+ _uniqueApplicationId = Automation.getMainApplication().id;
+
+ if(!_uniqueApplicationId)
+ _uniqueApplicationId = AutomationHelper.getAppTitle();
+
+ }
+ }
+
+ return _uniqueApplicationId;
+ }
+
+ //This method is used only by Flex apps which are loaded from air apps
+ // to get the start point of their main air app in screen coordinates
+ public function getStartPointInScreenCoordinates(windowId:String):Point
+ {
+ var startPointRequest:MarshalledAutomationEvent =
+ new MarshalledAutomationEvent(MarshalledAutomationEvent.START_POINT_REQUEST);
+ _inStartPointRequestProcessing = true;
+ var tempArray:Array = [];
+ tempArray.push(windowId);
+ startPointRequest.interAppDataToMainApp = tempArray;
+ dispatchToParent(startPointRequest);
+ //reply handler for the above event (startPointReplyHandler) would store the
+ //start point in the variable _appStartPoint
+
+ return _appStartPoint;
+ }
+
+ private function dispatchStartPointRequestEvent(windowId:String):void
+ {
+ var startPointRequest:MarshalledAutomationEvent =
+ new MarshalledAutomationEvent(MarshalledAutomationEvent.START_POINT_REQUEST);
+ _inStartPointRequestProcessing = true;
+ var tempArray:Array = [];
+ tempArray.push(windowId);
+ startPointRequest.interAppDataToMainApp = tempArray;
+ dispatchToBridgeParent(startPointRequest);
+ }
+
+ private static function getChildIndex1(parent:DisplayObjectContainer, child:DisplayObject):int
+ {
+ try
+ {
+ return parent.getChildIndex(child);
+ }
+ catch(e:Error)
+ {
+ if (parent is IRawChildrenContainer)
+ return IRawChildrenContainer(parent).rawChildren.getChildIndex(child);
+ throw e;
+ }
+ throw new Error("FocusManager.getChildIndex failed"); // shouldn't ever get here
+ }
+
+
+ /**
+ * @private
+ */
+ public function set automationEnvironmentHandlingClassName(className:String):void
+ {
+ _automationEnvironmentHandlingClassName = className;
+ }
+
+ /**
+ * @private
+ */
+ public function get automationEnvironmentHandlingClassName():String
+ {
+ return _automationEnvironmentHandlingClassName;
+ }
+ //----------------------------------
+ // recording
+ //----------------------------------
+
+ /**
+ * @private
+ * Storage for the recording property.
+ */
+ private var _recording:Boolean = false;
+ /**
+ * @private
+ */
+ public function get recording():Boolean
+ {
+ return _recording;
+ }
+
+ //----------------------------------
+ // replaying
+ //----------------------------------
+
+ /**
+ * @private
+ * Storage for the replaying property.
+ */
+ private var _replaying:Boolean = false;
+
+ /**
+ * @private
+ */
+ public function get replaying():Boolean
+ {
+ return _replaying;
+ }
+
+ //--------------------------------------------------------------------------
+ //
+ // Methods
+ //
+ //--------------------------------------------------------------------------
+
+ /**
+ * @private
+ */
+ public function getParent(
+ obj:IAutomationObject,
+ parentToStopAt:IAutomationObject = null,
+ ignoreShowInHierarchy:Boolean = false):IAutomationObject
+ {
+ while (obj)
+ {
+ var parent:IAutomationObject;
+ if(obj is IAutomationObject)
+ parent = (obj as IAutomationObject).automationOwner as IAutomationObject;
+
+ if (!parent && (obj is IUIComponent ) &&(IUIComponent(obj).systemManager != null) &&
+ (IUIComponent(obj).systemManager.document != null) && (obj != IUIComponent(obj).systemManager.document))
+ {
+ var doc:Object = IUIComponent(obj).systemManager.document;
+ parent = (doc as IAutomationObject);
+ }
+ else
+ {
+ var parentObj:DisplayObject;
+ if(obj is IAutomationObject)
+ parentObj = (obj as IAutomationObject).automationOwner as DisplayObject;
+
+ var parentClassName:String = getQualifiedClassName(parentObj);
+ if(parentClassName == "mx.managers::SystemManagerProxy")
+ {
+ parent = (Automation.getMainApplication() as IAutomationObject);
+ }
+
+ }
+
+ if (parent && (parent == parentToStopAt ||
+ ignoreShowInHierarchy ||
+ showInHierarchy(parent)))
+ {
+ return parent;
+ }
+ else
+ {
+ obj = parent;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * @private
+ */
+ public function getChildrenFromIDPart(
+ obj:IAutomationObject,
+ part:AutomationIDPart = null,
+ ignoreShowInHierarchy:Boolean = false):Array
+ {
+ if (part == null)
+ {
+ return getChildren(obj, ignoreShowInHierarchy);
+ }
+ else
+ {
+ // important to do this check and not rely on it being checked
+ // when getChildren is eventually called by resolveIDPart because
+ // resolving always ignores the show in hierarchy and automation
+ // composite flags because resolving can be delegated to children
+ // that are not visible in the hieararchy (see comment in scoreChild)
+ if (!obj ||
+ !(obj is IAutomationObject) ||
+ !(ignoreShowInHierarchy || showInHierarchy(obj)))
+ {
+ return [];
+ }
+
+ return resolveIDPart(obj, part);
+ }
+ }
+
+ private function getApplicationChildren(obj:IAutomationObject):Array
+ {
+ var result:Array = [];
+ if ( (obj is IUIComponent) && (IUIComponent(obj).systemManager != null)&&(obj == IUIComponent(obj).systemManager.document))
+ {
+ var sm:IChildList = null;
+ if(IUIComponent(obj).systemManager is IChildList)
+ sm = IChildList(IUIComponent(obj).systemManager);
+
+ var x:DisplayObject;
+ var delegate:IAutomationObject;
+
+ var count:int = sm?sm.numChildren:0;
+ for (var i:int = 0; i < count; i++)
+ {
+ //check that the automationParent is null because
+ //popup menus will all be children of SM but only one
+ //is the root, the rest are automation children of other menus
+ x = sm.getChildAt(i);
+
+ delegate = (x as IAutomationObject);
+ if (delegate &&
+ delegate != obj &&
+ (!(delegate.automationOwner is IAutomationObject) ||
+ delegate.automationOwner == obj))
+ {
+ result.push(delegate);
+ }
+ }
+
+ var popupChildren:IChildList = null;
+
+ if(IUIComponent(obj).systemManager &&
+ (IUIComponent(obj).systemManager.popUpChildren) is IChildList )
+ popupChildren= IChildList(IUIComponent(obj).systemManager.popUpChildren);
+
+ var count1:int = popupChildren? popupChildren.numChildren:0;
+
+ for (i = 0; i < count1; i++)
+ {
+ //check that the automationParent is null because
+ //popup menus will all be children of SM but only one
+ //is the root, the rest are automation children of other menus
+ x = popupChildren.getChildAt(i);
+
+ delegate = (x as IAutomationObject);
+ if (delegate &&
+ delegate != obj &&
+ (!(delegate.automationOwner is IAutomationObject) ||
+ delegate.automationOwner == obj))
+ {
+ result.push(delegate);
+ }
+ }
+ }
+ return result;
+ }
+
+ /**
+ * @private
+ */
+ public function getChildren(obj:IAutomationObject,
+ ignoreShowInHierarchy:Boolean = false):Array
+ {
+ if (!obj ||
+ !(obj is IAutomationObject) ||
+ !(ignoreShowInHierarchy || showInHierarchy(obj)))
+ {
+ return [];
+ }
+
+ var result:Array = cachedChildren != null ? cachedChildren[obj] : null;
+
+ if (result == null)
+ {
+ result = getChildrenRecursively(obj);
+
+ if( (obj is IUIComponent) && (IUIComponent(obj).systemManager != null) && (obj == IUIComponent(obj).systemManager.document))
+ {
+ var children:Array = getApplicationChildren(obj);
+ result = result ? result.concat(children) : children;
+ }
+
+ result = result || [];
+
+ if (hierarchyCacheCounter > 0)
+ cachedChildren[obj] = result;
+ }
+
+ return result;
+ }
+
+ /**
+ * @private
+ */
+ private function getChildrenRecursively(
+ aoc:IAutomationObject):Array
+ {
+ var result:Array = null;
+ // code modified below to avoid the usage of numAutomationChildren and
+ // getAutomationChildAt in a loop
+ //var childList:Array = aoc.getAutomationChildren();
+ var childList:Array = getAutomationChildrenArray(aoc);
+ var numAutomationChildren:int = childList?childList.length:0;
+
+ //var numAutomationChildren:int = aoc.numAutomationChildren;
+ for (var i:int = 0; i < numAutomationChildren; i++)
+ {
+ //var ao:IAutomationObject = aoc.getAutomationChildAt(i);
+ var ao:IAutomationObject = childList[i] as IAutomationObject;
+ if(ao)
+ {
+ if (isAutomationComposite(ao))
+ continue;
+
+ if (! result)
+ result = [];
+ result.push(ao);
+
+ if (showInHierarchy(ao))
+ continue;
+
+ // we dont need this check as this check itself needs to
+ // calculate all its children
+ //if (ao.numAutomationChildren > 0)
+ {
+ var x:Array =
+ getChildrenRecursively(ao)
+
+ if (x && x.length)
+ result = result ? result.concat(x) : x;
+ }
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * @private
+ */
+ public function getAutomationName(obj:IAutomationObject):String
+ {
+ if (!obj)
+ return null;
+
+ var result:Object = createIDPart(obj);
+ return result.automationName;
+ }
+
+ /**
+ * @private
+ */
+ public function getAutomationClassName(obj:IAutomationObject):String
+ {
+ if (!obj)
+ return null;
+
+ if(automationEnvironment)
+ {
+ var automationClass:IAutomationClass =
+ automationEnvironment.getAutomationClassByInstance(obj);
+
+ return automationClass ? automationClass.name : null;
+ }
+ else
+ return null;
+ }
+
+ /**
+ * @private
+ */
+ public function getProperties(obj:IAutomationObject,
+ names:Array = null,
+ forVerification:Boolean = true,
+ forDescription:Boolean = true):Array
+ {
+ if (!obj)
+ return null;
+
+ try
+ {
+ incrementCacheCounter();
+
+ // in the marshalle application if the tool libraries have not
+ // handled the requriemetns all applications will not be getting the
+ // env details.
+ if(!automationEnvironment)
+ return null;
+
+ var automationClass:IAutomationClass =
+ automationEnvironment.getAutomationClassByInstance(obj);
+ var propertMap:Object = automationClass.propertyNameMap;
+ var i:int;
+ var result:Array = [];
+ if (!names)
+ {
+ var propertyDescriptors:Array =
+ automationClass.getPropertyDescriptors(obj,
+ forVerification,
+ forDescription);
+ names = [];
+ for (i = 0; i < propertyDescriptors.length; i++)
+ {
+ names[i] = propertyDescriptors[i].name;
+ }
+ }
+ var part:Object = createIDPartForSpecifiedProperties(names,obj as IAutomationObject);
+ for (i = 0; i < names.length; i++)
+ {
+ var propertyDescriptor:IAutomationPropertyDescriptor =
+ propertMap[ names[i] ];
+ var value:Object = (propertyDescriptor
+ ? getPropertyValueFromPart(part,obj, propertyDescriptor)
+ : null);
+ //don't convert to String, testing tools want it
+ //delivered in the correct type
+
+
+ result.push(value);
+ }
+
+ decrementCacheCounter();
+ }
+ catch(e:Error)
+ {
+ decrementCacheCounter();
+
+ throw e;
+ }
+
+ return result;
+ }
+
+ /**
+ * @private
+ */
+ public function getTabularData(obj:IAutomationObject):IAutomationTabularData
+ {
+ return obj.automationTabularData as IAutomationTabularData;
+ }
+
+ /**
+ * @private
+ */
+ public function replayAutomatableEvent(event:AutomationReplayEvent):Boolean
+ {
+ var re:AutomationReplayEvent = event as AutomationReplayEvent;
+
+ // check the recorded line count whether it is the max allowed limit
+ var recordedLinesCount:Number= Automation.incrementRecordedLinesCount();
+ var licencePresent:Boolean = Automation.isLicensePresent();
+ if((recordedLinesCount > Automation.recordReplayLimit ) && (licencePresent == false))
+ {
+ _replaying = false;
+ if(Automation.errorShown == false)
+ {
+ var warningMessage:String = resourceManager.getString(
+ "automation_agent", "replayLimitReached");
+
+ Alert.show( warningMessage );
+ Automation.errorShown = true;
+ }
+ return false;
+
+ }
+
+ // required to make MouseMove work
+ if (re.replayableEvent is MouseEvent ||
+ ("triggerEvent" in re.replayableEvent &&
+ re.replayableEvent["triggerEvent"] is MouseEvent))
+ {
+ var evDispatcher:IEventDispatcher = re.automationObject as IEventDispatcher;
+ var rollOver:MouseEvent = new MouseEvent(MouseEvent.ROLL_OVER, false);
+ replayMouseEventInternal(evDispatcher, rollOver);
+ var mouseOver:MouseEvent = new MouseEvent(MouseEvent.MOUSE_OVER);
+ replayMouseEventInternal(evDispatcher, mouseOver);
+ }
+
+ if (! isVisible(re.automationObject as DisplayObject))
+ {
+ var message:String = resourceManager.getString(
+ "automation_agent", "notVisible",
+ [re.automationObject.automationName]);
+ throw new AutomationError(message,
+ AutomationError.OBJECT_NOT_VISIBLE);
+ }
+
+ pushMouseSimulator(re.automationObject,
+ re.replayableEvent);
+ _replaying = true;
+ var uiObject:IAutomationObject = re.automationObject as IAutomationObject;
+ if (uiObject && !(uiObject.automationVisible && uiObject.automationEnabled))
+ {
+ re.succeeded = false;
+ }
+ else
+ {
+ re.succeeded = re.automationObject.replayAutomatableEvent(re.replayableEvent);
+ }
+ _replaying = false;
+ popMouseSimulator();
+
+ return dispatchEvent(re);
+ }
+
+ /**
+ * @private
+ *
+ */
+ // commented out the sandbox mouse events as it was causing the event overflow when we have the air window
+ // in the application. Found all the trial cases worked even without that. when we face an issue we need to
+ // analyse the WindowedSystemManager -> System Manager otherSystemManagerMouseListener sequence to analyse the
+ // event overflow reason.
+ public function beginRecording():void
+ {
+ if (!recording)
+ {
+ _recording = true;
+
+ sm1.addEventListener(AutomationRecordEvent.RECORD,
+ recordHandler, false, EventPriority.DEFAULT_HANDLER, true);
+ sm1.getSandboxRoot().addEventListener(MouseEvent.MOUSE_DOWN,
+ captureIDFromMouseDownEvent, true, 0, true);
+
+ //sm1.getSandboxRoot().addEventListener(SandboxMouseEvent.MOUSE_DOWN_SOMEWHERE,
+ // captureIDFromMouseDownEvent, true, 0, true);
+ sm1.addEventListener(KeyboardEvent.KEY_DOWN,
+ captureIDFromKeyDownEvent, true, 0, true);
+ //ideally we would listen in the bubble phase so
+ //we'd get this last and all components have had a chance
+ //to react and record events, but some components are stopping
+ //the propagation so capture first and flush events
+ //in a delayed manner
+ sm1.getSandboxRoot().addEventListener(MouseEvent.CLICK,
+ onEndMouseSequence, true, 0, true);
+ sm1.getSandboxRoot().addEventListener(MouseEvent.DOUBLE_CLICK,
+ onEndMouseSequence, true, 0, true);
+
+ //sm1.getSandboxRoot().addEventListener(SandboxMouseEvent.CLICK_SOMEWHERE,
+ // onEndMouseSequence, true, 0, true);
+ // sm1.getSandboxRoot().addEventListener(SandboxMouseEvent.DOUBLE_CLICK_SOMEWHERE,
+ // onEndMouseSequence, true, 0, true);
+
+ sm1.addEventListener(KeyboardEvent.KEY_UP,
+ onEndKeySequence, true, 0, true);
+ //Ideally we'd flush events after the last click (or double click)
+ //however the player has a bug where it doesn't always send click
+ //events (and also there can be legitimate times when a click
+ //event won't come through, souch as a mouse down, mouse move off
+ //the component then a mouse up), so do a timed flush after the
+ //mouse up (it needs to be after any click events that might occur)
+ sm1.getSandboxRoot().addEventListener(MouseEvent.MOUSE_UP,
+ onEndMouseSequence, true, 0, true);
+ //sm1.getSandboxRoot().addEventListener(SandboxMouseEvent.MOUSE_UP_SOMEWHERE,
+ //onEndMouseSequence, true, 0, true);
+ sm1.getSandboxRoot().addEventListener(FocusEvent.KEY_FOCUS_CHANGE, keyFocusChangeHandler, false, 0, true);
+
+ dispatchEvent(new AutomationEvent);
+
+ // if we are the top level automation Manager, we should have recieved this call.
+ // we need to inform other managers about this record starting
+ if(sm1.isTopLevelRoot())
+ {
+ var beginRecordMarshalledEvent:MarshalledAutomationEvent = new MarshalledAutomationEvent
+ (MarshalledAutomationEvent.BEGIN_RECORDING);
+ dispatchMarshalledEventToSubApplications(beginRecordMarshalledEvent);
+ }
+ }
+ }
+
+ /**
+ * @private
+ */
+ public function endRecording():void
+ {
+ if (recording)
+ {
+ _recording = false;
+
+ dispatchEvent(new AutomationEvent(AutomationEvent.END_RECORD));
+
+ sm1.removeEventListener(AutomationRecordEvent.RECORD,
+ recordHandler);
+ sm1.getSandboxRoot().removeEventListener(MouseEvent.MOUSE_DOWN,
+ captureIDFromMouseDownEvent, true);
+ sm1.getSandboxRoot().removeEventListener(SandboxMouseEvent.MOUSE_DOWN_SOMEWHERE,
+ captureIDFromMouseDownEvent, true);
+ sm1.removeEventListener(KeyboardEvent.KEY_DOWN,
+ captureIDFromKeyDownEvent, true);
+ sm1.getSandboxRoot().removeEventListener(MouseEvent.CLICK,
+ onEndMouseSequence, true);
+ sm1.getSandboxRoot().removeEventListener(MouseEvent.DOUBLE_CLICK,
+ onEndMouseSequence, true);
+ sm1.getSandboxRoot().removeEventListener(MouseEvent.MOUSE_UP,
+ onEndMouseSequence, true);
+ sm1.getSandboxRoot().removeEventListener(SandboxMouseEvent.CLICK_SOMEWHERE,
+ onEndMouseSequence, true);
+ sm1.getSandboxRoot().removeEventListener(SandboxMouseEvent.DOUBLE_CLICK_SOMEWHERE,
+ onEndMouseSequence, true);
+ sm1.getSandboxRoot().removeEventListener(SandboxMouseEvent.MOUSE_UP_SOMEWHERE,
+ onEndMouseSequence, true);
+ sm1.removeEventListener(KeyboardEvent.KEY_UP,
+ onEndKeySequence, true);
+
+ clearHierarchyCache();
+ clearEventCache();
+
+ recordedEventInCurrentMouseSequence = false;
+ inMouseSequence = false;
+ }
+ }
+
+ public function getElementFromPoint2(x:int, y:int,windowId:String ):IAutomationObject
+ {
+ var stage:Stage = getAIRWindow(windowId).stage;
+ return getElementFromPointOnRequiredWindow(x,y,stage);
+ }
+
+ private function getElementFromPointOnRequiredWindow(x:int, y:int, requiredStage:Stage):IAutomationObject
+ {
+ var o:Array = requiredStage.getObjectsUnderPoint(new Point(x, y));
+ for (var i:int = o.length - 1; i >= 0; i--)
+ {
+ var displayObject:DisplayObject = o[i];
+ while (displayObject != null)
+ {
+ // don't use showInHierarchy because that would prevent
+ // checkpoints on things like boxes
+ var delegate:IAutomationObject = (displayObject as IAutomationObject);
+ if (delegate &&
+ // check that it's an IAutomationObject before
+ // checking visible since some components
+ // such as stage (which aren't IAutomationObjects)
+ // will yell and shout if you call visible on them
+ displayObject.visible)
+ {
+ var obj:IAutomationObject ;
+ if(isAutomationComposite(delegate))
+ obj = getAutomationComposite(delegate);
+ else
+ obj = delegate;
+
+ return obj;
+ }
+
+ displayObject = displayObject.parent;
+ }
+ }
+ return null;
+ }
+ /**
+ * @private
+ */
+ public function getElementFromPoint(x:int, y:int):IAutomationObject
+ {
+ //use the stage, not the system manager to find elements
+ //because popups do not appear as children of the system manager
+ //and so things like alerts wouldn't be found
+ var stage:Stage = Automation.getMainApplication().stage;
+ return getElementFromPointOnRequiredWindow(x,y,stage);
+ }
+
+ /**
+ * @private
+ */
+ public function getRectangle(obj:DisplayObject):Array
+ {
+ var p:Point = new Point(0,0);
+ p = obj.localToGlobal(p);
+ // it was observed that the start points and the width and height are getting as
+ // non interger values, which makes those values as zero at the other end.
+ // so we are converting to the near int and passing.
+ return [int(p.x), int(p.y),int( p.x + obj.width), int(p.y + obj.height)];
+ }
+
+ /**
+ * @private
+ */
+ public function isVisible(obj:DisplayObject):Boolean
+ {
+ while (obj && obj != obj.root && obj != obj.stage)
+ {
+ if (!obj.visible)
+ return false;
+ obj = obj.parent;
+ }
+ return true;
+ }
+
+ /**
+ * @private
+ */
+ private function getDistanceFromOriginalEvent(event:AutomationRecordEvent):int
+ {
+ var distance:int = 0;
+ var displayObject:DisplayObject = cachedTargetOriginator as DisplayObject;
+
+ while (displayObject != null && displayObject != event.automationObject)
+ {
+ ++distance;
+ displayObject = displayObject.parent;
+ }
+
+ return displayObject == event.automationObject ? distance : int.MAX_VALUE;
+ }
+
+ /**
+ * @private
+ */
+ public function onEndKeySequence(event:KeyboardEvent):void
+ {
+/* if (flushCachedEvents() == null)
+ onFinishEventSequence();
+
+ inMouseSequence = false;
+ */ }
+
+ /**
+ * @private
+ */
+ public function onEndMouseSequence(event:Event):void
+ {
+ if (flushCacheTimeoutID != -1)
+ clearTimeout(flushCacheTimeoutID);
+
+ if(event.type == MouseEvent.MOUSE_UP || event.type == SandboxMouseEvent.MOUSE_UP_SOMEWHERE ||
+ event.type == SandboxMouseEvent.CLICK_SOMEWHERE)
+ rebuildPartCache = true;
+
+ // we're in the capture phase of mouse up sometimes,
+ // so put in the timeout in either case. so don't try
+ // to optimize by checking eventCache.length
+ flushCacheTimeoutID = setTimeout(endMouseSequence,
+ event.type == MouseEvent.MOUSE_UP ||
+ event.type == SandboxMouseEvent.MOUSE_UP_SOMEWHERE ||
+ event.type == SandboxMouseEvent.CLICK_SOMEWHERE ? 500 : 1);
+ }
+
+ /**
+ * @private
+ */
+ protected function keyFocusChangeHandler(event:FocusEvent):void
+ {
+ var focusTarget:Object = event.target;
+ // check whether the focus target is from the same application
+ // else do not record this event
+ // we want to avoid the recording of the bubbled event from other applications
+ if(focusTarget.root != sm1)
+ return;
+
+
+ var dispatcher:IAutomationObject = null;
+ if(Automation.getMainApplication() is IAutomationObject)
+ dispatcher = IAutomationObject(Automation.getMainApplication());
+
+ var ao:IAutomationObject = (focusTarget as IAutomationObject);
+ if (ao)
+ dispatcher = (getAutomationComposite(ao) || ao);
+
+ recordAutomatableEvent(dispatcher, event);
+ }
+
+ /**
+ * @private
+ */
+ private function endMouseSequence():void
+ {
+ if (flushCacheTimeoutID != -1)
+ {
+ clearTimeout(flushCacheTimeoutID);
+ flushCacheTimeoutID = -1;
+ }
+
+ if (flushCachedEvents() == null)
+ onFinishEventSequence();
+
+ inMouseSequence = false;
+ }
+
+ /**
+ * @private
+ *
+ * This is only public because the test harness needs to call this
+ * due to a bug in the player. No one should call this.
+ */
+ public function flushCachedEvents():Event
+ {
+ var event:AutomationRecordEvent;
+
+ if (eventCache.length > 0)
+ {
+ var closestEvents:Array = [];
+ var closestDistance:int = int.MAX_VALUE;
+
+ for (var i:int = 0; i < eventCache.length; ++i)
+ {
+ event = eventCache[i];
+ var distance:int = getDistanceFromOriginalEvent(event);
+
+ if (distance < closestDistance)
+ {
+ closestDistance = distance;
+ closestEvents = [];
+ closestEvents.push(event);
+ }
+ else if (distance == closestDistance)
+ {
+ closestEvents.push(event);
+ }
+ }
+
+ if (closestEvents.length > 0)
+ event = closestEvents[0];
+ }
+
+ if (event != null)
+ {
+ dispatchRecordEvent(event, true);
+
+ // at this place we record a new line hence increment the counter
+ // since this happened with a previous decrement, we need not check the
+ // boundary conditions
+ var recordedLinesCount:Number= Automation.incrementRecordedLinesCount();
+ }
+
+ return event;
+ }
+
+ /**
+ * @private
+ */
+ public function resolveIDToSingleObject(rid:AutomationID,
+ currentParent:IAutomationObject = null):IAutomationObject
+ {
+
+ var childArray:Array = resolveID(rid, currentParent);
+
+ var message:String;
+
+ if (childArray == null || childArray.length == 0)
+ {
+ message = resourceManager.getString(
+ "automation_agent", "idNotResolved", [rid.toString()]);
+ throw new AutomationError(message,
+ AutomationError.OBJECT_NOT_FOUND);
+ }
+
+ if (childArray.length > 1)
+ {
+ message = resourceManager.getString(
+ "automation_agent", "matchesMsg", [childArray.length,
+ rid.toString().replace(/\n/, ' ')]) + ":\n";
+
+ for (var i:int = 0; i < childArray.length; i++)
+ {
+ message += AutomationClass.getClassName(childArray[i]) +
+ "(" + childArray[i].automationName + ")\n";
+ }
+
+ throw new AutomationError(message,
+ AutomationError.OBJECT_NOT_UNIQUE);
+ }
+ return (childArray[0] as IAutomationObject);
+ }
+
+ private function isApplication(part:AutomationIDPart):Boolean
+ {
+ // for Air we have top level parents which are notapplications
+ // they will be of the class
+ // TBD this is just for prototype. This will not work if the user
+ // has extended the Window class
+ /*if(part["className"] == "mx.core.Window")
+ return true;*/
+
+ if(part.hasOwnProperty(AutomationManager.airWindowIndicatorPropertyName))
+ return true;
+
+ if(scoreChild((Automation.getMainApplication() as IAutomationObject), part,true) >= 0)
+ return true;
+
+ return false;
+ }
+
+ /**
+ * @private
+ */
+ public function resolveID(rid:AutomationID,
+ currentParent:IAutomationObject = null):Array
+ {
+ var part:AutomationIDPart;
+ var id:AutomationID = rid.clone();
+ var message:String;
+ if (currentParent == null)
+ {
+ //remove the application
+ part = id.removeFirst();
+ if (!isApplication(part))
+ {
+ message = resourceManager.getString(
+ "automation_agent", "rootApplication",[ id.toString()]);
+ throw new AutomationError(message,
+ AutomationError.ILLEGAL_RUNTIME_ID);
+ }
+
+ // check for the AIR window.we can get the current parent as null even for
+ // window object
+ if (part.hasOwnProperty(AutomationManager.airWindowIndicatorPropertyName))
+ {
+ // get the automationName
+ //var currentAutomationName:String = part["automationName"];
+ var currentAutomationName:String = getPassedUniqueName(part);
+ currentParent = getAIRWindow(currentAutomationName) as IAutomationObject;
+ }
+ else
+ currentParent = (Automation.getMainApplication() as IAutomationObject);
+ }
+
+ var result:Array = null;
+ var currentChildArray:Array = [currentParent];
+
+ while (true)
+ {
+ if (id.isEmpty())
+ {
+ result = currentChildArray;
+ break;
+ }
+
+ // contains part for resolving
+ part = id.removeFirst();
+
+ // child found by resolving
+ currentChildArray = currentParent.resolveAutomationIDPart(part);
+
+ //check for nothing found
+ if (currentChildArray.length == 0)
+ {
+ //null results are legal on the last node for regex searches
+ //because it just means there was no match, but not legal
+ //when still traversing the parent nodes
+ if (!id.isEmpty())
+ {
+ message = resourceManager.getString(
+ "automation_agent", "notResolved", [part.automationName,
+ part.className, currentParent.automationName]);
+ throw new AutomationError(message,
+ AutomationError.OBJECT_NOT_FOUND);
+ }
+ }
+ else
+ {
+ //check for too many parents found
+ if (currentChildArray.length > 1 && !id.isEmpty())
+ {
+ message = resourceManager.getString(
+ "automation_agent", "matchesMsg",
+ [currentChildArray.length, part.toString()]);
+ throw new AutomationError(message,
+ AutomationError.OBJECT_NOT_UNIQUE);
+ }
+
+ //check for parent = child
+ if (currentChildArray[0] == currentParent)
+ {
+ message = resourceManager.getString(
+ "automation_agent", "resolvedTo",
+ [currentParent, currentChildArray[0]]);
+ throw new AutomationError(message,
+ AutomationError.ILLEGAL_OPERATION);
+ }
+
+ //traverse into the next parent
+ if (currentChildArray[0].numAutomationChildren > 0)
+ currentParent = currentChildArray[0] ;
+ //check for nothing found
+ else if (!id.isEmpty())
+ {
+ message = resourceManager.getString(
+ "automation_agent", "idResolved",[ id.toString()]);
+ throw new AutomationError(message,
+ AutomationError.ILLEGAL_RUNTIME_ID);
+ }
+ }
+ }
+ return result;
+ }
+
+ /**
+ * @private
+ */
+ public function resolveIDPartToSingleObject(parent:IAutomationObject,
+ part:AutomationIDPart):IAutomationObject
+ {
+ var rid:AutomationID = new AutomationID();
+ rid.addFirst(part);
+
+ return resolveIDToSingleObject(rid, parent);
+ }
+
+ /**
+ * @private
+ */
+ public function resolveIDPart(parent:IAutomationObject,
+ part:AutomationIDPart):Array
+ {
+ var rid:AutomationID = new AutomationID();
+ rid.addFirst(part);
+
+ return resolveID(rid, parent);
+ }
+
+ /**
+ * @private
+ */
+ public function createID(obj:IAutomationObject,
+ relativeToParent:IAutomationObject = null):AutomationID
+ {
+ var result:AutomationID = new AutomationID();
+
+ if (obj == relativeToParent)
+ return result;
+
+ do
+ {
+ //if relativeToParent is not in the hiearchy, then we need to do a special
+ //getParent so that we don't skip this parent
+ var parent:IAutomationObject =
+ getParent(obj, relativeToParent, true);
+ // use the real parent for creating child ids
+ var part:AutomationIDPart = createIDPart(obj, parent);
+ result.addFirst(part);
+
+ // respect showInHierarchy when walking parent chain
+ obj = getParent(obj, relativeToParent);
+
+ if (obj == relativeToParent)
+ break;
+ } while (obj);
+ return result;
+ }
+
+ /**
+ * @private
+ */
+ public function createIDPart(obj:IAutomationObject,
+ parent:IAutomationObject = null):AutomationIDPart
+ {
+ if (parent == null)
+ parent = getParent(obj, null, true);
+
+ var part:AutomationIDPart = (cachedParts
+ ? cachedParts[obj] as AutomationIDPart
+ : null);
+
+ if (!part)
+ {
+ part = (parent
+ ? parent.createAutomationIDPart(obj) as AutomationIDPart
+ : helpCreateIDPart(null, obj));
+
+ if (hierarchyCacheCounter > 0)
+ cachedParts[obj] = part;
+ }
+
+ return part;
+ }
+
+ /**
+ * @private
+ */
+ public function showInHierarchy(obj:IAutomationObject):Boolean
+ {
+ return obj == null ||
+ !(obj is IAutomationObject) ||
+ (!isAutomationComposite(obj) && obj.showInAutomationHierarchy);
+ }
+
+ /**
+ * @private
+ *
+ * Helper implementation of IAutomationIDHelper. Resolves an id based
+ * on a set of properties. This should not be used, instead use
+ * resolveID, resolveIDToSingleObject, or resolveIDPart.
+ */
+ public function helpResolveIDPart(parent:IAutomationObject,
+ partObj:Object):Array
+ {
+ var part:AutomationIDPart = partObj as AutomationIDPart;
+ if(!part)
+ return [];
+ // trace("--- searching for child [" + ObjectUtil.toString(part) +
+ // "] in parent [" + parent.automationName + "]");
+
+ //Because resolving can be delegated to a child composite
+ //we need to ignore hierarchy. An example is
+ //ComboBox composites List. List will call helpResolveIDPart
+ //but will appear to have no children since it's not in the
+ //hierarchy, so pass true to getChildren to ignore showInHierarchy
+ //Note that resolving off ComboBox instead of List would
+ //not be appropriate since ComboBox may have other children (such
+ //as edit propertyName or button)
+ var children:Array = getChildren(parent, true);
+ var winners:Array = getWinners(children,part,false);
+ if(winners.length > 1)
+ winners = getWinners(children,part,true);
+ return winners;
+ }
+
+ /**
+ * @private
+ *
+ */
+ private function getWinners(children:Array, part:AutomationIDPart,forceIndexCalculation:Boolean):Array
+ {
+ //Because resolving can be delegated to a child composite
+ //we need to ignore hierarchy. An example is
+ //ComboBox composites List. List will call helpResolveIDPart
+ //but will appear to have no children since it's not in the
+ //hierarchy, so pass true to getChildren to ignore showInHierarchy
+ //Note that resolving off ComboBox instead of List would
+ //not be appropriate since ComboBox may have other children (such
+ //as edit propertyName or button)
+ var winners:Array = [];
+ var bestScore:int = -1;
+ for (var i:int = 0; i < children.length; i++)
+ {
+ var child:IAutomationObject = children[i];
+ /*
+ if (!child)
+ {
+ var message:String = resourceManager.getString(
+ "automation_agent", "nullReturned",
+ [i, parent.automationName, children.length]);
+ throw new Error(message);
+ }
+ */
+ // we are commenting out the checks above for the following reason
+ // this check stops the automation of the application if any cheild of the application
+ // is null. Initially since the automation framework was supporting only flex, all components
+ // by default will be inheriting IAutomationObject. But when the flash-flex compoenets got added
+ // to the application, it broke the automation of other flex components also because of this check.
+ // reason: flex-flash components were not implementing the IAutomationObjects and hence the components
+ // corresponding to that became null and hence the automation stopped.
+ // to avoid such a scenario we have commented out this check.
+ // and the flash-flex components are planning to be changed to implement this interface soon.
+ // till then the usage of this change will allow the users to continue with automation of other components.
+
+ var score:int = -1;
+ if (child != null)
+ {
+ score = scoreChild(child, part,forceIndexCalculation);
+ }
+
+ if (score == -1)
+ continue;
+
+ // we are not processing all possible objects to match
+ // if we got an object with all the required properties of the part.
+ if(score == int.MAX_VALUE)
+ return [child];
+
+ if (score > bestScore)
+ {
+ bestScore = score;
+ winners = [];
+ }
+
+ if (score == bestScore)
+ winners.push(child);
+ }
+
+ return winners;
+
+ }
+ /**
+ * @private
+ *
+ * Helper implementation of IAutomationIDHelper. Creates an id for
+ * a given child. This should not be used, instead use createID,
+ * or createIDPart.
+ */
+
+ public function helpCreateIDPart(parent:IAutomationObject,
+ child:IAutomationObject,
+ automationNameCallback:Function = null,
+ automationIndexCallback:Function = null):AutomationIDPart
+ {
+ var part:AutomationIDPart = new AutomationIDPart();
+ if(!automationEnvironment)
+ return part;
+
+ var automationClass:IAutomationClass =
+ automationEnvironment.getAutomationClassByInstance(child);
+ if(!automationClass)
+ return part;
+
+ var propertyDescriptors:Array =
+ automationClass.getPropertyDescriptors(child, false, true);
+
+ if(!propertyDescriptors)
+ return part;
+
+ //It doesn't matter if a property is null
+ //add it anyways, because the callee asked for it
+ //and not adding it will confuse QTP since we've
+ //told it already about the properties in the env file
+ //If this causes a problem and we need to add the if
+ //null checks back, then be sure to update QTPAdapter
+ //to not return null properties in Learn and ActiveScreen
+
+ for (var propNo:int = 0; propNo < propertyDescriptors.length; ++propNo)
+ {
+ var propertyName:String = propertyDescriptors[propNo].name;
+
+ if (propertyName == "id")
+ {
+ part.id = child is IDeferredInstantiationUIComponent
+ ? IDeferredInstantiationUIComponent(child).id
+ : null;
+ if ((part.id == null) && (parent == null))
+ {
+ //trace ("inside the helpCreateIDPart - id "+ child.automationName);
+ // currently we are in the application object.
+ // this is a temp fix till we have AIR delegates in place.
+ // we need the application iD of this component instead of the id
+ if(Automation.getMainApplication().hasOwnProperty("applicationID"))// this should work for AIR app's
+ {
+ part.id = Automation.getMainApplication().applicationID;
+ //trace ("inside the helpCreateIDPart - id "+ part.id );
+ }
+ else
+ {
+ //we are in flex app hosted from Air app
+ part.id = processAppIDFromUniqueAppID();
+ }
+ }
+ }
+ else if (propertyName == "automationName")
+ part.automationName = (automationNameCallback == null
+ ? child.automationName
+ : automationNameCallback(child));
+ else if (propertyName == "automationIndex")
+ //note that parent can be null if it's the parentApplication
+ part.automationIndex = (automationIndexCallback == null ?
+ getChildIndex(getParent(child), child)
+ : automationIndexCallback(child));
+ else if (propertyName == "className")
+ part.className = AutomationClass.getClassName(child);
+ else if (propertyName == "automationClassName")
+ part.automationClassName = getAutomationClassName(child);
+ else if (propertyName == AutomationManager.airWindowIndicatorPropertyName)
+ {
+ // we added this property to identify the airtoplevel windows
+ part.isAIRWindow = true;
+ }
+ else
+ {
+ if (propertyName in child)
+ part[propertyName] = child[propertyName];
+ else if (child is IStyleClient)
+ part[propertyName] = IStyleClient(child).getStyle(propertyName);
+ else
+ {
+ var message:String = resourceManager.getString(
+ "automation_agent", "notDefined", [propertyName, child]);
+ traceMessage("AutomationManager", "helpCreateIDPart()", message);
+ // throw new Error(message);
+ }
+ }
+ }
+
+ if ("automationName" in part && ((part.automationName == null)||(part.automationName.length == 0)))
+ part.automationName = part.automationIndex;
+
+ return part;
+ }
+
+ private function processAppIDFromUniqueAppID():String
+ {
+ var appId:String = getUniqueApplicationID();
+ var index:int = appId.lastIndexOf("_");
+ if(index != -1)
+ {
+ appId = appId.substring(index + 1, appId.length );
+ }
+ else
+ {
+ appId = null;
+ }
+ return appId;
+ }
+
+ /**
+ * @private
+ */
+ private function isClassAvailable(className:String):Boolean
+ {
+ try
+ {
+ if(getDefinitionByName(className) != null)
+ return true;
+ }
+ catch(e:Error)
+ {
+ return false;
+ }
+
+ return false;
+ }
+
+ /**
+ * Dispatch the event as a replayable event. Causes the
+ * ReplayableEventEvent with REPLAYABLE_EVENT type to be fired.
+ * However, this method will not attempt the dispatch if there are no
+ * listeners.
+ *
+ * @param eventReplayer The IEventReplayer dispatching this
+ * event since event.target may not be
+ * accurate
+ * @param event The event that represents the replayable event.
+ *
+ * @langversion 3.0
+ * @playerversion Flash 9
+ * @playerversion AIR 1.1
+ * @productversion Flex 3
+ */
+ public function recordAutomatableEvent(recorder:IAutomationObject, event:Event,
+ cacheable:Boolean = false):void
+ {
+ // this method should not get called with null event.
+ // However during some usage of the events being dispatched programmatically
+ // the trigger events may be null and the record of the same is called with the
+ // null. Eventhough this is not the expected way of usage, a check is added.
+ if (event == null)
+ return;
+
+ if (!recording)
+ return;
+
+ var message:String;
+
+ var re:AutomationRecordEvent;
+ if (event is AutomationRecordEvent)
+ re = event as AutomationRecordEvent;
+ else
+ {
+ re = new AutomationRecordEvent(AutomationRecordEvent.RECORD);
+ re.automationObject = recorder;
+ re.replayableEvent = event;
+ re.cacheable = cacheable;
+ }
+
+ //swallow orphan clicks when not following a mouseDown
+ if (re.replayableEvent.type == MouseEvent.CLICK && !inMouseSequence)
+ return;
+
+ // in the marshalled application if the tool libraries have not
+ // handled the requriemetns all applications will not be getting the
+ // env details.
+ if(!automationEnvironment)
+ return ;
+
+ var automationClass:IAutomationClass =
+ automationEnvironment.getAutomationClassByInstance(re.automationObject as IAutomationObject);
+
+ if (automationClass == null)
+ {
+ message = resourceManager.getString(
+ "automation_agent", "classNotFound",
+ [AutomationClass.getClassName(re.automationObject)]);
+ throw new Error(message);
+ }
+
+ var eventDescriptor:IAutomationEventDescriptor =
+ automationClass.getDescriptorForEvent(re.replayableEvent);
+
+ if (eventDescriptor == null)
+ {
+ message = resourceManager.getString(
+ "automation_agent", "methodNotFound",
+ [AutomationClass.getClassName(re.replayableEvent),
+ automationClass]);
+ throw new Error(message);
+ }
+
+ re.name = eventDescriptor.name;
+ re.args = eventDescriptor.record(re.automationObject, re.replayableEvent);
+
+ // check the recorded line count whether it is the max allowed limit
+ var recordedLinesCount:Number= Automation.incrementRecordedLinesCount();
+ var licencePresent:Boolean = Automation.isLicensePresent();
+ if((recordedLinesCount > Automation.recordReplayLimit ) && (licencePresent == false))
+ {
+ endRecording();
+
+ var warningMessage:String = resourceManager.getString(
+ "automation_agent", "recordLimitReached");
+
+ Alert.show( warningMessage );
+ return;
+
+ }
+
+ // if the components are part of the Popup winodws (e.g Alert and objects
+ // hosted by the popUpManager and if they belong to the non root applicaiotn
+ // they are hosted by the main application. So it looks like the events dispatched on
+ // them does not reach the appropriate application. So to handle the special case,
+ // we directly call the record Handler.
+ if(isObjectChildOfSystemManagerProxy (re.automationObject) )
+ recordHandler(re);
+
+ if(getQualifiedClassName(re.automationObject) == "mx.controls::FlexNativeMenu")
+ recordHandler(re);
+ if (re.bubbles && re.automationObject is IEventDispatcher)
+ IEventDispatcher(re.automationObject).dispatchEvent(re);
+ else
+ recordHandler(re);
+ }
+
+ /**
+ * @private
+ */
+ public function isObjectChildOfSystemManagerProxy(automationObject:IAutomationObject):Boolean
+ {
+ var obj:DisplayObject = automationObject as DisplayObject;
+ if(obj == null) //this happens for FlexNativeMenu in AIR as it is not a DisplayObject
+ return false;
+ if(obj.parent == null)
+ {
+ // when the focus of the popup objects are taken to some other application
+ // it is obsreverd that the parent is becoming null.
+ // dont know whether it is an issue with sdk.
+ // however since we have the list of poppup objects applicable to us
+ // we can check in that.
+ if((lastRemovedpopUpObject == automationObject as DisplayObject ) || (popUpObjects.indexOf(obj) != -1))
+ return true;
+
+ }
+
+ while(obj.parent)
+ {
+ if(obj.parent is SystemManagerProxy)
+ return true;
+ obj = obj.parent;
+
+ }
+ return false;
+ }
+
+ /**
+ * @private
+ */
+ public function isObjectPopUp(automationObject:IAutomationObject):Boolean
+ {
+ var obj:DisplayObject = automationObject as DisplayObject;
+ if(obj == null) //this happens for FlexNativeMenu in AIR as it is not a DisplayObject
+ return false;
+ if(obj.parent == null)
+ {
+ // when the focus of the popup objects are taken to some other application
+ // it is obsreverd that the parent is becoming null.
+ // dont know whether it is an issue with sdk.
+ // however since we have the list of poppup objects applicable to us
+ // we can check in that.
+ if((lastRemovedpopUpObject == automationObject as DisplayObject ) || (popUpObjects.indexOf(obj) != -1))
+ return true;
+
+ }
+ // we need to find out whether the object belongs to the system manager before it belongs to an application
+ // this is also needed to find the popups from the main application.
+ var applicationFound:Boolean = (obj is Application || isSparkApplication(obj))?true:false;
+ while(obj.parent)
+ {
+ if(obj.parent is SystemManagerProxy)
+ return true;
+ else
+ {
+ if(obj.parent is Application || isSparkApplication(obj.parent))
+ applicationFound = true;
+ else if ((obj.parent == sm1)&&(!applicationFound))
+ return true;
+ }
+
+ obj = obj.parent;
+
+ }
+ return false;
+ }
+
+ /**
+ * @private
+ */
+ public function recordHandler(te:Event):void
+ {
+ var re:AutomationRecordEvent = te as AutomationRecordEvent;
+ if(re == null)
+ return;
+ if (re.isDefaultPrevented())
+ {
+
+ // decrement the recording counter
+ // as the recording does not happen here
+ var recordedLinesCount1:Number= Automation.decrementRecordedLinesCount();
+ return;
+ }
+
+ if (!isAutomationComposite(re.automationObject))
+ {
+ if (re.cacheable && cachingEvents)
+ {
+ eventCache.push(re);
+ // decrement the recording counter
+ // as the recording does not happen here
+ var recordedLinesCount2:Number= Automation.decrementRecordedLinesCount();
+ }
+ else
+ dispatchRecordEvent(re, false);
+ }
+ else
+ {
+ // decrement the recording counter
+ // as the recording does not happen here
+ var recordedLinesCount3:Number= Automation.decrementRecordedLinesCount();
+ }
+ }
+
+ /**
+ * @private
+ */
+ public function addSynchronization(isComplete:Function,
+ target:Object = null):void
+ {
+ synchronization.push({isComplete: isComplete, target: target });
+ }
+
+ /**
+ * @private
+ */
+ public function isSynchronized(target:IAutomationObject):Boolean
+ {
+ for (var i:int = 0; i < synchronization.length; i++)
+ {
+ if (synchronization[i].isComplete())
+ synchronization.splice(i--, 1);
+ else if (target == synchronization[i].target ||
+ (target && target == synchronization[i].target) ||
+ synchronization[i].target == null)
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * @private
+ */
+ public function getMemberFromObject(obj:Object,
+ name:String):Object
+ {
+ var part:Object;
+ var component:Object;
+
+ part = createIDPart(obj as IAutomationObject);
+ component = obj;
+
+ var result:Object = null;
+
+ if (part != null && name in part)
+ result = part[name];
+ else if (name in obj)
+ result = obj[name];
+ else if (component != null)
+ {
+ if (name in component)
+ result = component[name];
+ else if (component is IStyleClient)
+ result = IStyleClient(component).getStyle(name);
+ }
+
+ return result;
+ }
+
+ /**
+ * @private
+ */
+ public function getPropertyValue(obj:Object,
+ pd:IAutomationPropertyDescriptor,
+ relativeParent:IAutomationObject = null):Object
+ {
+ return getMemberFromObject(obj, pd.name);
+ }
+
+ //--------------------------------------------------------------------------
+ //
+ // Mouse simulator methods
+ //
+ //--------------------------------------------------------------------------
+
+ /**
+ * @private
+ */
+ public function getMouseX(item:DisplayObject):Number
+ {
+ return item.globalToLocal(_currentMousePositions[_currentMousePositions.length - 1]).x;
+ }
+
+ /**
+ * @private
+ */
+ public function getMouseY(item:DisplayObject):Number
+ {
+ return item.globalToLocal(_currentMousePositions[_currentMousePositions.length - 1]).y;
+ }
+
+ /**
+ * @private
+ */
+ private function replayMouseEventInternal(target:IEventDispatcher, event:MouseEvent):Boolean
+ {
+ //feed mouseOut and rollOut events to the last-clicked object, for defocussing purposes.
+ if (lastMouseTarget && lastMouseTarget != target)
+ {
+ var sendMouseEvents:Boolean = true;
+ // check whether the new target is child of the last target.
+ // if it is a child we should not send mouseOut, rollOut events
+ if(lastMouseTarget is DisplayObjectContainer && target is DisplayObject)
+ {
+ if(DisplayObjectContainer(lastMouseTarget).contains(target as DisplayObject))
+ {
+ // make the inner most component as the last target.
+ // should we have a stack of these inner components
+ // so that we can playback rollOut for all of them?
+ lastMouseTarget = target;
+ sendMouseEvents = false;
+ }
+
+ }
+ if(sendMouseEvents)
+ {
+ var mouseOut:MouseEvent = new MouseEvent(MouseEvent.MOUSE_OUT);
+ var rollOut:MouseEvent = new MouseEvent(MouseEvent.ROLL_OUT, false);
+ lastMouseTarget.dispatchEvent(mouseOut);
+ lastMouseTarget.dispatchEvent(rollOut);
+ lastMouseTarget = target as IEventDispatcher;
+ }
+ }
+
+ if(!lastMouseTarget)
+ lastMouseTarget = target as IEventDispatcher;
+
+ return target.dispatchEvent(event);
+ }
+
+ private static var fakeMouseX:QName = new QName(mx_internal, "_mouseX");
+ private static var fakeMouseY:QName = new QName(mx_internal, "_mouseY");
+
+ /**
+ * @private
+ */
+ private function pushMouseSimulator(targetObj:Object, eventObj:Object):void
+ {
+ var target:DisplayObject = (targetObj is DisplayObject
+ ? DisplayObject(targetObj)
+ : null);
+ var event:MouseEvent = (eventObj is MouseEvent
+ ? MouseEvent(eventObj)
+ : null);
+
+ var pt:Point = (event != null
+ ? new Point(event.localX, event.localY)
+ : new Point(0, 0));
+
+;
+
+ pt = target != null ? target.localToGlobal(pt) : pt;
+
+ _currentMousePositions.push(pt);
+ _prevMouseTargets.push(target);
+
+
+ try
+ {
+ target.root[fakeMouseX] = pt.x;
+ target.root[fakeMouseY] = pt.y;
+ }
+ catch(e:Error)
+ {
+ traceMessage("AutomationManager", "pushMouseSimulator()", AutomationConstants.invalidInAIR);
+ }
+
+
+
+ Automation.mouseSimulator = this;
+ }
+
+ /**
+ * @private
+ */
+ private function popMouseSimulator():void
+ {
+ _currentMousePositions.pop();
+ _prevMouseTargets.pop();
+ var target:Object = _prevMouseTargets[_prevMouseTargets.length-1];
+ if(target && target.root)
+ {
+ try
+ {
+ target.root[fakeMouseX] = _currentMousePositions[_currentMousePositions.length-1].x;
+ target.root[fakeMouseY] = _currentMousePositions[_currentMousePositions.length-1].y;
+ }
+ catch(e:Error)
+ {
+ traceMessage("AutomationManager", "popMouseSimulator()", AutomationConstants.invalidInAIR);
+ }
+ }
+ else
+ {
+ //var sm:ISystemManager = Application.application.systemManager;
+ try
+ {
+ sm1[fakeMouseX] = undefined;
+ sm1[fakeMouseY] = undefined;
+ }
+ catch(e:Error)
+ {
+ traceMessage("AutomationManager", "popMouseSimulator()", AutomationConstants.invalidInAIR);
+ }
+
+
+ if (!_currentMousePositions.length)
+ Automation.mouseSimulator = null;
+ }
+ }
+
+ //--------------------------------------------------------------------------
+ //
+ // Replay helper methods
+ //
+ //--------------------------------------------------------------------------
+
+ /**
+ * @private
+ */
+ public function replayKeyboardEvent(to:IEventDispatcher, event:KeyboardEvent):Boolean
+ {
+ return replayKeyDownKeyUp(to, event.keyCode, event.ctrlKey, event.shiftKey);
+ }
+
+ /**
+ * @private
+ */
+ public function replayKeyDownKeyUp(to:IEventDispatcher,
+ keyCode:uint,
+ ctrlKey:Boolean = false,
+ shiftKey:Boolean = false,
+ altKey:Boolean = false):Boolean
+ {
+ map(function(type:String):void
+ {
+ var event:KeyboardEvent = new KeyboardEvent(type);
+ event.keyCode = keyCode;
+ event.ctrlKey = ctrlKey;
+ event.shiftKey = shiftKey;
+ event.altKey = altKey;
+ to.dispatchEvent(event);
+ },
+ KEY_CLICK_TYPES);
+
+ return true;
+ }
+
+ /**
+ * @private
+ */
+ public function replayMouseEvent(target:IEventDispatcher, event:MouseEvent):Boolean
+ {
[... 2400 lines stripped ...]