| //////////////////////////////////////////////////////////////////////////////// |
| // |
| // 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 |
| { |
| pushMouseSimulator(target, event); |
| replayMouseEventInternal(target, event); |
| popMouseSimulator(); |
| |
| return true; |
| } |
| |
| /** |
| * @private |
| */ |
| public function replayClick(to:IEventDispatcher, sourceEvent:MouseEvent = null):Boolean |
| { |
| sourceEvent = sourceEvent || new MouseEvent(MouseEvent.CLICK); |
| |
| pushMouseSimulator(to, sourceEvent); |
| |
| map(function(type:String):void |
| { |
| var localX:int = sourceEvent && !isNaN(sourceEvent.localX) ? sourceEvent.localX : 2; |
| var localY:int = sourceEvent && !isNaN(sourceEvent.localY) ? sourceEvent.localY : 2; |
| var event:MouseEvent = new MouseEvent(type, true, false, localX, localY); |
| event.ctrlKey = sourceEvent.ctrlKey; |
| event.shiftKey = sourceEvent.shiftKey; |
| event.altKey = sourceEvent.altKey; |
| event.buttonDown = (type == MouseEvent.MOUSE_DOWN); |
| replayMouseEventInternal(to, event); |
| }, |
| MOUSE_CLICK_TYPES); |
| |
| popMouseSimulator(); |
| |
| return true; |
| } |
| |
| /** |
| * @private |
| */ |
| public function replayClickOffStage():Boolean |
| { |
| var x:Number = Automation.getMainApplication().screen.left - 1000; |
| var y:Number = Automation.getMainApplication().screen.top - 1000; |
| |
| map(function(type:String):void |
| { |
| var event:MouseEvent = new MouseEvent(type); |
| event.localX = x; |
| event.localY = y; |
| replayMouseEventInternal( |
| IEventDispatcher(Automation.getMainApplication()), event); |
| }, |
| MOUSE_CLICK_TYPES); |
| return true; |
| } |
| |
| /** |
| * @private |
| * The cache of IDParts and children cannot be maintained indefinetely because |
| * they get stale as application state chagnes. Hence control is required over |
| * the life of cache. The incrementCacheCounter and decrementCacheCounter |
| * APIs provide this control. The calls to these two APIs should be matched to maintian |
| * cache in a healthy state. |
| */ |
| public function incrementCacheCounter():int |
| { |
| ++hierarchyCacheCounter; |
| |
| if (cachedParts == null) |
| cachedParts = new Dictionary(); |
| |
| if (cachedChildren == null) |
| cachedChildren = new Dictionary(); |
| |
| if(cachedCompositor == null) |
| cachedCompositor = new Dictionary(); |
| |
| return hierarchyCacheCounter; |
| } |
| |
| /** |
| * @private |
| * Takes a flag to force clear the cache. |
| */ |
| public function decrementCacheCounter(clearNow:Boolean = false):int |
| { |
| if (clearNow || hierarchyCacheCounter < 1) |
| hierarchyCacheCounter = 1; |
| |
| --hierarchyCacheCounter; |
| |
| if (hierarchyCacheCounter <= 0) |
| clearHierarchyCache(); |
| |
| return hierarchyCacheCounter; |
| } |
| |
| /** |
| * @private |
| * Helper function to clear the cache contents. |
| */ |
| private function clearHierarchyCache():void |
| { |
| hierarchyCacheCounter = 0; |
| cachedParts = null; |
| cachedChildren = null; |
| cachedCompositor = null; |
| } |
| |
| /** |
| * @private |
| */ |
| private function clearEventCache():void |
| { |
| eventCache = []; |
| cachingEvents = false; |
| cachedTargetOriginator = null; |
| } |
| |
| /** |
| * @private |
| */ |
| private function dispatchRecordEvent(event:AutomationRecordEvent, |
| cacheable:Boolean = false):void |
| { |
| //the event is disqualifed if another event has already |
| //been dispatched in the current mouse down, up, click sequence |
| //should we limit this check to just cacheable event? maybe not |
| //but it may impact the text events that are cached |
| if (!cacheable || !inMouseSequence || !recordedEventInCurrentMouseSequence) |
| { |
| if (inMouseSequence) |
| { |
| //Events that are recorded during a mouse sequence, but are |
| //not from the same location of the current mouse target |
| //do not count. This is to avoid issues from text fields |
| //that do their own event caching, dispatch events late |
| //and look like they are part of the current mouse sequence |
| //but aren't. For example, in a datagrid, if a user clicks |
| //on an editable item and types something, that typing is |
| //queued, if they then click on another item, the text field |
| //will flush the typing. After that the data grid will record |
| //a new item click. We don't want to through that out because |
| //text field did some caching. They are unrelated. Do this |
| //check by looking seeing if the current target is on the path |
| //between cachedTargetOriginator and the root. |
| //trace(event.methodName + " " + getDistanceFromOriginalEvent(event) + ", r:" + event.automationObject + ", ct:" + cachedTargetOriginator); |
| if (getDistanceFromOriginalEvent(event) != int.MAX_VALUE) |
| recordedEventInCurrentMouseSequence = true; |
| } |
| |
| dispatchEvent(event); |
| } |
| |
| //The cache should only be cleared when the last event has occurred |
| //as a result of a low-level interaction. It's possible to dispatch |
| //several non-cached events during a mouse sequence, such as drag scroll events |
| if (cacheable || !inMouseSequence) |
| onFinishEventSequence(); |
| } |
| |
| public function recordCustomAutomationEvent(event:AutomationRecordEvent):Boolean |
| { |
| if(!recording) |
| return false; |
| |
| if(event.type != AutomationRecordEvent.CUSTOM_RECORD) |
| return false; |
| |
| if(!event.automationObject) |
| return false; |
| |
| // create the AutomatioRecord event |
| var eventObj:AutomationRecordEvent = new AutomationRecordEvent( AutomationRecordEvent.RECORD, |
| event.bubbles,event.cancelable,event.automationObject,event.replayableEvent, |
| event.args,event.name,event.cacheable); |
| if(!eventObj.args) |
| eventObj.args = new Array(); |
| if(!eventObj.name) |
| eventObj.name = "NoNamePassedByUser"; |
| eventObj.recordTriggeredByCustomHandling = true; |
| |
| |
| //dispatch this new event |
| dispatchEvent(eventObj); |
| return true; |
| } |
| |
| /** |
| * @private |
| */ |
| private function onFinishEventSequence():void |
| { |
| rebuildPartCache = true; |
| clearEventCache(); |
| decrementCacheCounter(); |
| } |
| |
| /** |
| * @private |
| */ |
| private function getChildIndex(parent:IAutomationObject, |
| child:IAutomationObject):String |
| { |
| //parent shouldn't be null, unless it's the parentApplication |
| if (parent != null) |
| { |
| parent = showInHierarchy(parent) ? parent : getParent(parent); |
| var parentsChildren:Array = getChildren(parent); |
| |
| for (var childNo:int = 0; childNo < parentsChildren.length; ++childNo) |
| { |
| if (child == parentsChildren[childNo]) |
| return "index:" + childNo; |
| } |
| } |
| |
| return "index:-1"; |
| } |
| |
| /** |
| * @private |
| */ |
| private function captureIDFromMouseDownEvent(event:Event):void |
| { |
| captureID(event); |
| |
| cachingEvents = true; |
| cachedTargetOriginator = EventDispatcher(event.target); |
| inMouseSequence = true; |
| recordedEventInCurrentMouseSequence = false; |
| } |
| |
| /** |
| * @private |
| */ |
| private function captureIDFromKeyDownEvent(event:Event):void |
| { |
| /* captureID(event); |
| |
| cachingEvents = true; |
| cachedTargetOriginator = EventDispatcher(event.target); |
| inMouseSequence = true; |
| recordedEventInCurrentMouseSequence = false; |
| */ } |
| |
| /** |
| * @private |
| */ |
| private function captureID(event:Event):void |
| { |
| if (flushCacheTimeoutID != -1) |
| clearTimeout(flushCacheTimeoutID); |
| |
| //flush cached events because: |
| // |
| //a) sometimes there is no CLICK or DOUBLE_CLICK event |
| //the events will only get flushed after a time out of about |
| //500 MS. It's possible for someone to mouse down before that |
| //timeout so check to here to see if any events need to be flushed |
| // |
| //b) keyboard events are never involved in caching |
| flushCachedEvents(); |
| |
| if (rebuildPartCache) |
| { |
| //force the clear of the cache because even though |
| //we match the increment here with a decrement after |
| //an automation event is recorded, not all |
| //low-level events will result in an automation |
| //event being dispatched |
| decrementCacheCounter(true); |
| incrementCacheCounter(); |
| |
| var o:Object = event.target; |
| while (o != null) |
| { |
| if (o is IAutomationObject) |
| { |
| if (isAutomationComposite(o as IAutomationObject)) |
| o = getAutomationComposite(o as IAutomationObject); |
| else |
| break; |
| } |
| else |
| o = DisplayObject(o).parent; |
| } |
| |
| var obj:IAutomationObject = (o as IAutomationObject); |
| while (obj) |
| { |
| var ao:IAutomationObject = (obj as IAutomationObject); |
| |
| // Earlier we were just using automationOwner as parent which is different |
| // from the parent we get while calculating id using createIdPart(). |
| // http://bugs.adobe.com/jira/browse/FLEXENT-1126 |
| // http://bugs.adobe.com/jira/browse/FLEXENT-1251 |
| // So using getParent() method here also which returns main application as |
| // parent in special cases. |
| var parent:IAutomationObject = getParent(obj, null, true); |
| |
| cachedParts[obj] = |
| (parent ? parent.createAutomationIDPart(ao) |
| : helpCreateIDPart(null, ao)); |
| |
| if(isAutomationComposite(ao)) |
| { |
| obj = getAutomationComposite(ao) as IAutomationObject; |
| } |
| else |
| { |
| obj = parent; |
| } |
| } |
| rebuildPartCache = false; |
| } |
| } |
| |
| |
| /** |
| * @private |
| */ |
| private var className2previousClassName:Object = {}; |
| |
| /** |
| * @private |
| */ |
| private function scoreChild(child:IAutomationObject, |
| part:Object,calculateIndexAnyway:Boolean):int |
| { |
| // this method is a very expensive method |
| // which tries to find the score for the passed child with the |
| // passed part. |
| |
| //get the properties to score against from getProperties |
| //rather than introspecting the automation object |
| //directly, this will get any cached values and ensure |
| //we use the same logic for automationName |
| |
| // first let us check whether the automation class name is matching, |
| // if not we can give a negative score and return. |
| |
| var propertyNames:Array = []; |
| var automationIndexNeeded:Boolean = false; |
| |
| var automationClassNameRequired:Boolean = false; |
| var propertyName:String; |
| for (propertyName in part) |
| { |
| if(propertyName != "automationIndex") |
| { |
| if(propertyName == "automationClassName") |
| automationClassNameRequired = true; |
| else |
| propertyNames.push(propertyName); |
| } |
| else |
| automationIndexNeeded = true; |
| |
| } |
| |
| var n:int = 0; |
| var i:int = 0; |
| //special case automationClassName because it's not a real property |
| //and we currently aren't putting it in the environment info so |
| //helpCreateIDPart will not populate it even if you ask for it |
| //because it has no descriptor |
| // we can ignore the other properties, if this is not equal. |
| var partPropertyValue:Object = null; |
| if (automationClassNameRequired) |
| { |
| childPropertyValue = getAutomationClassName(child); |
| partPropertyValue = part["automationClassName"]; |
| |
| equal = comparePropertyValues(partPropertyValue, childPropertyValue); |
| |
| //if they are not equal it is possible that a script recorded in one version |
| //is being used in another. So we check for compatible versions of this class |
| if((!equal)&&(automationEnvironment)) |
| { |
| var previousVersionClassNames:Array = className2previousClassName[childPropertyValue]; |
| |
| if( previousVersionClassNames == null) |
| { |
| // in the marshalled application if the tool libraries have not |
| // handled the requriemetns all applications will not be getting the |
| // env details. |
| var automationClass:IAutomationClass = |
| automationEnvironment.getAutomationClassByInstance(child); |
| |
| if(automationClass is IAutomationClass2) |
| { |
| previousVersionClassNames = IAutomationClass2(automationClass).previousVersionClassNames; |
| if(previousVersionClassNames == null) |
| previousVersionClassNames = []; |
| className2previousClassName[childPropertyValue] = previousVersionClassNames; |
| |
| } |
| } |
| n = previousVersionClassNames.length; |
| for( i = 0; i < n; i++) |
| { |
| equal = comparePropertyValues(partPropertyValue, previousVersionClassNames[i]); |
| if(equal) |
| break; |
| } |
| } |
| } |
| |
| if((!equal)&&(automationClassNameRequired)) |
| { |
| // we can ignore the other properties, if this is not equal. |
| score = -1; |
| allEqual = false; |
| } |
| else |
| { |
| var childProperties:Array = getProperties(child, propertyNames); |
| var childPropertyMap:Object = {}; |
| |
| for (var childPropertyNo:int = 0; childPropertyNo < childProperties.length; ++childPropertyNo) |
| { |
| childPropertyMap[propertyNames[childPropertyNo]] = childProperties[childPropertyNo]; |
| } |
| |
| var score:int = 0; |
| var allEqual:Boolean = true; |
| var criticalPropertiesToMatch:Array=["automationIndex","automationClassName","className","automationName","id"]; |
| var criticalPropertiesMismatchFound:Boolean = false; |
| var automationNameNeeded:Boolean = false; |
| |
| // iterate through part |
| for (propertyName in part) |
| { |
| // calculating automation index is a very expensive process. |
| // so we will delay it till all other required properties matches. |
| if((propertyName.toLowerCase() != "automationindex")&&(propertyName.toLowerCase() != "automationclassname")) |
| { |
| var equal:Boolean; |
| partPropertyValue = part[propertyName]; |
| var childPropertyValue:Object = (propertyName in childPropertyMap) |
| ? childPropertyMap[propertyName] |
| : null; |
| |
| equal = comparePropertyValues(partPropertyValue, childPropertyValue); |
| if((!equal)&&(propertyName.toLowerCase() == "automationname")&&((childPropertyValue=="")||(childPropertyValue==null))) |
| { |
| // in some components if the automationName is not specified, it is considered using the automationIndex. |
| // since we are not creating automation index, we will leave it the benefit of doubt and will |
| // create it again when we are forming automation index. |
| automationNameNeeded = true; |
| equal = true; |
| } |
| |
| |
| if (equal) |
| ++score; |
| else |
| { |
| // check what is not matching is not the critical property |
| if(criticalPropertiesToMatch.indexOf(propertyName) != -1) |
| criticalPropertiesMismatchFound = true; |
| |
| allEqual = false; |
| } |
| } |
| |
| } |
| if(allEqual || (!criticalPropertiesMismatchFound) || calculateIndexAnyway) |
| { |
| var requiredPropList:Array = new Array(); |
| |
| if( automationIndexNeeded) |
| requiredPropList.push("automationIndex"); |
| if(automationNameNeeded) |
| requiredPropList.push("automationName"); |
| if(requiredPropList.length) |
| { |
| var childProperties1:Array = getProperties(child, requiredPropList); |
| |
| childPropertyMap = {}; |
| for (var childPropertyNo1:int = 0; childPropertyNo1 < childProperties1.length; childPropertyNo1++) |
| { |
| childPropertyMap[requiredPropList[childPropertyNo1]] = childProperties1[childPropertyNo1]; |
| } |
| |
| if(childProperties1 && (childProperties1.length == requiredPropList.length)) |
| { |
| n = childProperties1.length; |
| for(i = 0; i < n; i++) |
| { |
| //equal = comparePropertyValues("automationIndex", childProperties[0]); |
| var partPropertyValue1:Object = part[requiredPropList[i]]; |
| propertyName = requiredPropList[i]; |
| var childPropertyValue1:Object = (propertyName in childPropertyMap) |
| ? childPropertyMap[propertyName] |
| : null; |
| equal = comparePropertyValues(partPropertyValue1, childPropertyValue1); |
| if (equal) |
| ++score; |
| else |
| { |
| // check what is not matching is not the critical property |
| if(criticalPropertiesToMatch.indexOf(propertyName) != -1) |
| criticalPropertiesMismatchFound = true; |
| allEqual = false; |
| } |
| } |
| } |
| } |
| } |
| if(criticalPropertiesMismatchFound) |
| { |
| score = -1; |
| } |
| } |
| |
| if(allEqual) |
| { |
| if(automationIndexNeeded) |
| score = int.MAX_VALUE; // in this case we dont need to analyse the children for further match |
| // there will be only one child with index matching and all other properties also matched. |
| else |
| score = int.MAX_VALUE-1; |
| } |
| return score; |
| } |
| |
| /** |
| * @private |
| */ |
| public function getAutomationComposite(obj:IAutomationObject):IAutomationObject |
| { |
| if(cachedCompositor) |
| { |
| var val:IAutomationObject = cachedCompositor[obj]; |
| if(val != null) |
| { |
| if(val != obj) |
| return val; |
| else |
| return null; |
| } |
| } |
| |
| var childFound:IAutomationObject = compositeAnalysis(obj); |
| if(cachedCompositor) |
| cachedCompositor[obj] = childFound; |
| |
| if(childFound == obj) |
| return null; |
| |
| return childFound; |
| } |
| |
| private function compositeAnalysis(obj:IAutomationObject):IAutomationObject |
| { |
| var hierarchyArray:Array = [obj]; |
| |
| //build an array of parents till we reach null. |
| // do not call is getParent as it would call isAutomationComposite |
| var parent:IAutomationObject = getParent(obj, null, true); |
| while(parent) |
| { |
| hierarchyArray.push(parent); |
| parent = getParent(parent, null, true); |
| } |
| |
| //start from the top finding each child |
| // if we do not find any child then the given object is composite. |
| // if we find all the children till the given object then it is not a composite. |
| |
| parent = hierarchyArray.pop(); |
| var childFound:IAutomationObject = parent; |
| |
| if(!hierarchyArray.length) |
| return childFound; |
| |
| var childToBeFound:IAutomationObject = hierarchyArray.pop(); |
| var ch:IAutomationObject; |
| do |
| { |
| // code modified below to avoid the usage of numAutomationChildren and |
| // getAutomationChildAt in a loop |
| //var childList:Array = parent.getAutomationChildren(); |
| var childList:Array = getAutomationChildrenArray(parent); |
| var numChildren:int = childList?childList.length:0; |
| |
| //var numChildren:int = parent.numAutomationChildren; |
| for(var i:int = 0; i < numChildren; ++i) |
| { |
| //ch = parent.getAutomationChildAt(i); |
| ch = childList[i] as IAutomationObject; |
| if(ch) |
| { |
| if(cachedCompositor) |
| cachedCompositor[ch] = ch; |
| |
| if(ch == childToBeFound) |
| { |
| childFound = childToBeFound; |
| break; |
| } |
| } |
| } |
| |
| // have we found the child? |
| if(i == numChildren) |
| { |
| if((childToBeFound is IUIComponent)&&(parent is IUIComponent )) |
| { |
| // if ((IUIComponent(obj).systemManager != null) && (parent == IUIComponent(parent).systemManager.document)) |
| // https://bugs.adobe.com/jira/browse/FLEXENT-1038 . |
| // when the passed object is a non ui object like repeater, using the same for the |
| // above check will result in skipping the below condition and hence |
| // it skips the identification of repeaters in popup objects like title window |
| // which are application children. |
| //if ((IUIComponent(childToBeFound).systemManager != null) && (parent == IUIComponent(parent).systemManager.document)) |
| // chnaging the condition checking for https://bugs.adobe.com/jira/browse/FLEXENT-1044 |
| if ((IUIComponent(childToBeFound)) && |
| (IUIComponent(childToBeFound).systemManager != null) && |
| (IUIComponent(parent))&& |
| (IUIComponent(parent).systemManager)&& |
| (parent == IUIComponent(parent).systemManager.document)) |
| { |
| var children:Array = getApplicationChildren(parent); |
| numChildren = children.length; |
| for(i = 0; i < numChildren; ++i) |
| { |
| ch = children[i]; |
| if(cachedCompositor) |
| cachedCompositor[ch] = ch; |
| if(ch == childToBeFound) |
| { |
| childFound = childToBeFound; |
| break; |
| } |
| } |
| } |
| |
| // have we found the child? |
| if(i == numChildren) |
| { |
| if(cachedCompositor) |
| cachedCompositor[childToBeFound] = parent; |
| childToBeFound = hierarchyArray.pop(); |
| continue; |
| } |
| } |
| } |
| parent = childToBeFound; |
| childToBeFound = hierarchyArray.pop(); |
| } |
| while(childToBeFound); |
| |
| return childFound; |
| } |
| |
| /** |
| * @private |
| */ |
| public function isAutomationComposite(obj:IAutomationObject):Boolean |
| { |
| if(cachedCompositor) |
| { |
| var val:IAutomationObject = cachedCompositor[obj]; |
| if(val != null) |
| { |
| if(val == obj) |
| return false; |
| else |
| return true; |
| } |
| } |
| |
| var childFound:IAutomationObject = compositeAnalysis(obj); |
| if(cachedCompositor) |
| cachedCompositor[obj] = childFound; |
| |
| if(obj == childFound) |
| return false; |
| |
| return true; |
| } |
| |
| /** |
| * @private |
| */ |
| private function uniqueAppIdReplyHandler(event:Event):void |
| { |
| // Marshalling events are needeed across applicaiton domain |
| // so this conversion shall fail in the same domain |
| if(event is MarshalledAutomationEvent) |
| return; |
| |
| if(_inUniqueAppIdRequestProcessing == false) |
| return; |
| |
| _inUniqueAppIdRequestProcessing = false; |
| |
| // in the reply event we expect two parameter |
| if(sm1.isTopLevelRoot() == false) |
| { |
| if(mainListenerObj) |
| { |
| _uniqueApplicationId = event["interAppDataToSubApp"][0] as String; |
| |
| } |
| } |
| } |
| /** |
| * @private |
| * this method is to get the provider for the swf bridge from different possible system managers |
| * In the main application of the AIR, we can have different system managers if we have the |
| * air windows open. so we need to check in all of them to find the bridge provider and use |
| * the identification based on the origin of the bridge provider. |
| */ |
| private function getSwfBridgeProviderDetails(bridgeParent:IEventDispatcher):Object |
| { |
| var providerType:int = -1; |
| var providerName:String; |
| // the bridge can be in the current application or in the windows. |
| // this will be applicable for the root application only as only the root application can |
| // have child windows. |
| |
| // first let us search in the main applicaiton system manager. |
| var bp:ISWFBridgeProvider; |
| |
| if(sm1MSm && (sm1MSm.swfBridgeGroup )) |
| { |
| bp = sm1MSm.swfBridgeGroup.getChildBridgeProvider(bridgeParent); |
| initUniqueAppId(); |
| providerName = _uniqueApplicationId; |
| providerType = -1; |
| } |
| if (!bp) |
| { |
| // now let us seach in the windows. |
| var count:int = allAirWindowList.length; |
| var index:int = 0; |
| while((!bp) && (index < count)) |
| { |
| var currentWindow:IUIComponent = allAirWindowList[index] as IUIComponent; |
| if(currentWindow) |
| { |
| var currentWindowSysManager:IMarshalSystemManager = currentWindow.systemManager as IMarshalSystemManager; |
| if(currentWindowSysManager) |
| { |
| if(currentWindowSysManager.swfBridgeGroup) |
| bp = currentWindowSysManager.swfBridgeGroup.getChildBridgeProvider(bridgeParent); |
| if(bp) |
| { |
| providerName = getAIRWindowUniqueID(currentWindow as DisplayObject); |
| providerType = index; |
| } |
| } |
| |
| } |
| index++; |
| } |
| } |
| var returnObject:Object = new Object(); |
| returnObject["ISWFBridgeProvider"] = bp; |
| returnObject["providerType"] = providerType; |
| returnObject["providerName"] = providerName; |
| |
| |
| return returnObject; |
| } |
| |
| private function initUniqueAppId():void |
| { |
| if(!_uniqueApplicationId) |
| { |
| if(sm1.isTopLevelRoot() == false) |
| { |
| // we need to get the uniqueapplication id from the parentapplication |
| dispatchUniqueAppIdRequestEvent(); |
| _uniqueApplicationId = Automation.getMainApplication().className + "_" + _uniqueApplicationId; |
| } |
| else |
| { |
| _uniqueApplicationId = Automation.getMainApplication().className; |
| } |
| } |
| } |
| private function uniqueAppIdRequestHandler(event:Event):void |
| { |
| // Marshalling events are needeed across applicaiton domain |
| // so this conversion shall fail in the same domain |
| if(event is MarshalledAutomationEvent) |
| return; |
| |
| // we get the bridge corresponding to the requesting application |
| // we need to find the swfLoader corresponding to the bridge |
| // get its index in its parents till we reach the sm of the |
| // current application. |
| //var bp:ISWFBridgeProvider = sm1.swfBridgeGroup. |
| // getChildBridgeProvider(event["interAppDataToMainApp"][0]); |
| |
| var providerType:int = -1; // -1 indicates the main application and any othe value indicates the window index |
| var providerName:String; |
| var returnObject:Object = getSwfBridgeProviderDetails ((event["interAppDataToMainApp"][0])); |
| |
| var bp:ISWFBridgeProvider = returnObject["ISWFBridgeProvider"]; |
| providerType = returnObject["providerType"]; |
| providerName = returnObject["providerName"]; |
| if(bp) |
| { |
| var unique_id_processed:String = getObjectIdInCurrentApplication(bp as DisplayObject); |
| // initUniqueAppId(); moved inside the getSwfBridgeProviderDetails |
| //unique_id_processed = unique_id_processed + "_" + _uniqueApplicationId; |
| unique_id_processed = unique_id_processed + "_" + providerName; |
| |
| if(mainListenerObj) |
| { |
| // unique id handling logic |
| var event1:MarshalledAutomationEvent = new MarshalledAutomationEvent( |
| MarshalledAutomationEvent.UNIQUE_APPID_REPLY); |
| var temp:Array = new Array(); |
| temp.push(unique_id_processed); |
| event1.interAppDataToSubApp = temp; |
| //dispatchMarshalledEventToSubApplications(event1); |
| dispatchToSwfBridgeChildren(event1); |
| } |
| } |
| } |
| |
| private var inEndRecordDetailsPassingDownToChildren:Boolean = false; |
| public function marhsalledEndRecordHandler(event:Event):void |
| { |
| if(!sm1MSm) |
| return; |
| |
| if(sm1MSm.useSWFBridge() == false) |
| return; |
| |
| // Marshalling events are needeed across applicaiton domain |
| // so this conversion shall fail in the same domain |
| if(event is MarshalledAutomationEvent) |
| return; |
| |
| if(inEndRecordDetailsPassingDownToChildren == true) |
| return; |
| |
| endRecording(); |
| |
| inEndRecordDetailsPassingDownToChildren = true; |
| // we should pass the information to our children also |
| var event1:MarshalledAutomationEvent = MarshalledAutomationEvent.marshal(event); |
| dispatchMarshalledEventToSubApplications(event1); |
| inEndRecordDetailsPassingDownToChildren = false; |
| } |
| |
| private var inBeginRecordDetailsPassingDownToChildren:Boolean = false; |
| public function marhsalledBeginRecordHandler(event:Event):void |
| { |
| if(!sm1MSm) |
| return; |
| |
| if(sm1MSm.useSWFBridge() == false) // we are the root application |
| return; |
| |
| // Marshalling events are needeed across applicaiton domain |
| // so this conversion shall fail in the same domain |
| if(event is MarshalledAutomationEvent) |
| return; |
| |
| if(inBeginRecordDetailsPassingDownToChildren == true) |
| return; |
| |
| beginRecording(); |
| |
| inBeginRecordDetailsPassingDownToChildren = true; |
| |
| // we should pass the information to our children also |
| var event1:MarshalledAutomationEvent = MarshalledAutomationEvent.marshal(event); |
| dispatchMarshalledEventToSubApplications(event1); |
| |
| inBeginRecordDetailsPassingDownToChildren = false; |
| |
| } |
| |
| |
| private function dispatchMarshalledEventToSubApplications(event:Event):void |
| { |
| dispatchToAllChildren(event); |
| } |
| |
| |
| // 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. |
| // we cannot process these objects as and when we get it, as our appDomain may not be |
| // ready then. So we store it and once the application creation is over we process these |
| // objects |
| private var possiblePopupsBeforeAppComplete:Array; |
| private function popupHandlerBeforeApplicationCompletion (event:Event):void |
| { |
| var currentAppObj:IAutomationObject = event["interAppDataToSubApp"][0] as IAutomationObject; |
| if(!(currentAppObj is IUIComponent) || currentAppObj == null || currentAppObj.automationDelegate) |
| { |
| return ; |
| } |
| else |
| { |
| if(!possiblePopupsBeforeAppComplete) |
| possiblePopupsBeforeAppComplete = new Array(); |
| // these are our objects. And some may be popups. |
| // let us add to a list and keep it so that once our application is complete |
| // we will try to add the delegate for the same. |
| possiblePopupsBeforeAppComplete.push(currentAppObj); |
| } |
| |
| } |
| |
| // FLEXENT-894 or 895 it was observerd that popupmenubutton menu popup object is |
| // creatd before application completion. |
| // So we store it and once the application creation is over we process these |
| // objects |
| private function processPopupsBeforeApplicationComplete():void |
| { |
| if(possiblePopupsBeforeAppComplete) |
| { |
| var count:int = possiblePopupsBeforeAppComplete.length; |
| var index:int = 0; |
| while(index < count) |
| { |
| handlePopupObject(possiblePopupsBeforeAppComplete.shift()); |
| index++; |
| } |
| |
| } |
| } |
| private var inPopupDataSendingDownToChildren:Boolean = false; |
| |
| private function popupHandler (event:Event):void |
| { |
| if(event is MarshalledAutomationEvent) |
| return ; |
| |
| if(inPopupDataSendingDownToChildren) |
| return; |
| |
| var currentAppObj:DisplayObject = event["interAppDataToSubApp"][0] as DisplayObject; |
| handlePopupObject(currentAppObj, event); |
| |
| } |
| |
| private function handlePopupObject(currentAppObj:DisplayObject, event:Event=null):void |
| { |
| if(createDelegate(currentAppObj)== true) |
| { |
| // we got a new popup. |
| // we need to add this to our pop up list also |
| // this is neeeded beause in automation we need to consider the popup's as the |
| // children of the application which has created the same |
| if(!popUpObjects) |
| popUpObjects = new Array(); |
| |
| if(currentAppObj as IUIComponent) |
| { |
| if( (currentAppObj as IUIComponent).owner == (currentAppObj as IUIComponent).parent ) |
| popUpObjects.push(currentAppObj); |
| else if ((currentAppObj as IUIComponent).owner is Application || |
| isSparkApplication((currentAppObj as IUIComponent).owner)) |
| // for the popups which are direct children of application we need to store |
| popUpObjects.push(currentAppObj); |
| } |
| /* |
| if(!((currentAppObj as IUIComponent)&& |
| ((currentAppObj as IUIComponent).owner != (currentAppObj as IUIComponent).parent))) |
| popUpObjects.push(currentAppObj); |
| |
| */ |
| |
| addDelegates(currentAppObj); |
| |
| // we only added the delegate for the outer object. |
| // we need to add for the children also. |
| currentAppObj.addEventListener(FlexEvent.CREATION_COMPLETE, popupCompleteHandler); |
| currentAppObj.parent.addEventListener(FlexEvent.REMOVE, popupRemoveHandler); |
| } |
| else |
| { |
| inPopupDataSendingDownToChildren = true; |
| var eventX:MarshalledAutomationEvent; |
| if(event) |
| { |
| // we should pass the information to our children also |
| eventX= MarshalledAutomationEvent.marshal(event); |
| } |
| else |
| { |
| eventX = new MarshalledAutomationEvent( |
| MarshalledAutomationEvent.POPUP_HANDLER_REQUEST); |
| var tempArr:Array = new Array(); |
| tempArr.push(currentAppObj); |
| eventX.interAppDataToSubApp = tempArr; |
| } |
| |
| dispatchMarshalledEventToSubApplications(eventX); |
| inPopupDataSendingDownToChildren = false; |
| } |
| } |
| private static var lastRemovedpopUpObject:DisplayObject; |
| private static function popupRemoveHandler(event:Event):void |
| { |
| lastRemovedpopUpObject = null; |
| if(!popUpObjects) |
| { |
| Automation.automationDebugTracer.traceMessage("AutomationManager", "popupRemoveHandler()", "How did we get an obejct to remove without adding ? "); |
| return; |
| } |
| var obejctToBeRemoved:Object = event.target; |
| |
| // remove the current objects from the popUps |
| var currentCount:int = popUpObjects.length; |
| var index:int = 0; |
| var requiredCount: int = 0; |
| var tempArray:Array = new Array(); |
| var objFound:Boolean = false; |
| while (index < currentCount) |
| { |
| if( ((popUpObjects[index]as DisplayObject).parent) != obejctToBeRemoved) |
| tempArray.push(popUpObjects[index]); |
| else |
| lastRemovedpopUpObject = popUpObjects[index]; |
| index ++; |
| } |
| popUpObjects = tempArray; |
| |
| |
| } |
| |
| public function getPopUpChildren():Array |
| { |
| return popUpObjects; |
| } |
| |
| |
| public function getPopUpChildrenCount():Number |
| { |
| if (!popUpObjects) |
| return 0; |
| |
| return popUpObjects.length; |
| } |
| |
| public function getPopUpChildObject(index:int):IAutomationObject |
| { |
| if(index < popUpObjects.length) |
| return popUpObjects[index] as IAutomationObject; |
| |
| return null; |
| } |
| |
| private function popupCompleteHandler(event:Event):void |
| { |
| var currentObject:DisplayObjectContainer = event.target as DisplayObjectContainer; |
| addPopupChildDelegates(currentObject); |
| currentObject.addEventListener(Event.ADDED, childAddedHandler, false, 0, true); |
| } |
| |
| |
| private static function addPopupChildDelegates(currentObject: DisplayObjectContainer):void |
| { |
| if(currentObject) |
| { |
| var childCount:int = currentObject.numChildren; |
| var index:int = 0; |
| |
| while(index < childCount) |
| { |
| var childObj:DisplayObject = currentObject.getChildAt(index); |
| createDelegate(childObj); |
| addDelegates(childObj); |
| addPopupChildDelegates(childObj as DisplayObjectContainer); |
| index++; |
| } |
| } |
| } |
| |
| private static function dragproxyStoreRequesthandler(event:Event):void |
| { |
| // we will only store one dragProxyObject |
| if(!currentDragProxyHolder) |
| currentDragProxyHolder = new Array(); |
| currentDragProxyHolder[0] = event["interAppDataToMainApp"][0] as DisplayObject; |
| |
| } |
| |
| |
| private function dragproxyRetrieveRequesthandler(event:Event):void |
| { |
| var tempEventObj:MarshalledAutomationEvent = new MarshalledAutomationEvent( |
| MarshalledAutomationEvent.DRAG_DROP_PROXY_RETRIEVE_REPLY); |
| var tempArr:Array = new Array(); |
| if(currentDragProxyHolder && currentDragProxyHolder.length!= 0) |
| tempArr.push(currentDragProxyHolder[0]); |
| else |
| tempArr.push(null); |
| tempEventObj.interAppDataToSubApp = tempArr; |
| currentDragProxyHolder = new Array(); |
| |
| dispatchMarshalledEventToSubApplications(tempEventObj); |
| } |
| |
| public function storeDragProxy(dragProxy:Object):void |
| { |
| // if the drag start happened in the sandbox root we wont get the event |
| // corresponding to the dragProxyReciever, so we need to store it explicitly |
| if(!currentDragProxyHolder) |
| currentDragProxyHolder = new Array(); |
| |
| currentDragProxyHolder[0] = dragProxy; |
| } |
| private static function dragProxyReciever(event:Event):void |
| { |
| if(event["name"] == "popUpChildren.addChild") |
| { |
| if(!currentDragProxyHolder) |
| currentDragProxyHolder = new Array(); |
| |
| currentDragProxyHolder[0] = event["value"]; |
| } |
| } |
| |
| private var inSynchronizationSendingToChildren:Boolean = false; |
| private function synchronizationHandler(event:Event):void |
| { |
| // we get this event when an application has started drag start |
| // and we get this in all applications. |
| |
| if(event is MarshalledAutomationEvent) |
| return; |
| |
| if(inSynchronizationSendingToChildren) |
| return; |
| |
| // we need this method so that it calls the synchronization |
| // method to clear the current status. |
| isSynchronized(null); |
| |
| inSynchronizationSendingToChildren = true; |
| |
| // we should pass the information to our children also |
| var event1:MarshalledAutomationEvent = MarshalledAutomationEvent.marshal(event); |
| dispatchMarshalledEventToSubApplications(event1); |
| |
| inSynchronizationSendingToChildren = false; |
| } |
| |
| private static function isCurrentAppSandboxRoot(passedSystemManager:ISystemManager):Boolean |
| { |
| var retVal:Boolean = false; |
| try |
| { |
| // get the root of the system manager of the current application |
| if(passedSystemManager["root"] && (passedSystemManager.getSandboxRoot() == passedSystemManager["root"])) |
| retVal = true; |
| } |
| catch(e:Error) |
| { |
| |
| } |
| |
| return retVal; |
| } |
| |
| private static function getMainListenerObject(passedSystemManager:ISystemManager, passedMarshaledSystemManager:IMarshalSystemManager):IEventDispatcher |
| { |
| var requiredObj:IEventDispatcher ; |
| if(passedSystemManager) |
| { |
| // we need to use the sanbox root if we belong to the |
| // same security domain as that of the root applucation |
| requiredObj = passedSystemManager.getSandboxRoot(); |
| //var passedMarshaledSystemManager:IMarshalSystemManager = passedSystemManager as IMarshalSystemManager; |
| |
| if (passedMarshaledSystemManager && passedMarshaledSystemManager.useSWFBridge() && (isCurrentAppSandboxRoot(passedSystemManager) == true)) |
| { |
| // this application is not the root level application, but it is |
| // first application of the security domain. So this objects's mainListenerObj is |
| // parent brige |
| requiredObj = passedMarshaledSystemManager.swfBridgeGroup.parentBridge; |
| } |
| } |
| return requiredObj; |
| } |
| |
| private function addListenerToParentApplication(obj:IEventDispatcher):void |
| { |
| // as an application X we need to listen to the events coming from the parent. |
| // these events are always sent from the parent to the child. So these events are listened by |
| // the children on their parent. When we recieve the event,if we are not handling these, we should |
| // pass the same to the children. i.e always sent from the parent -> child. These events are |
| // handled by the children. i,e these are always added on the parent. |
| |
| obj.addEventListener(MarshalledAutomationEvent.BEGIN_RECORDING, marhsalledBeginRecordHandler,false,0,true); |
| |
| obj.addEventListener(MarshalledAutomationEvent.END_RECORDING, marhsalledEndRecordHandler,false,0,true); |
| |
| //obj.addEventListener(MarshalledAutomationEvent.ENV_DETAILS , envDetailsEventHandler, false, 0, true); |
| if((obj as SystemManager)&& (obj as SystemManager).getSandboxRoot()) |
| ((obj as SystemManager).getSandboxRoot()).removeEventListener(MarshalledAutomationEvent.POPUP_HANDLER_REQUEST , popupHandlerBeforeApplicationCompletion, false); |
| |
| obj.addEventListener(MarshalledAutomationEvent.POPUP_HANDLER_REQUEST , popupHandler, false, 0, true); |
| |
| obj.addEventListener(MarshalledAutomationEvent.UPDATE_SYCHRONIZATION,synchronizationHandler, false, 0, true); |
| |
| obj.addEventListener(MarshalledAutomationEvent.INITIAL_DETAILS_REPLY,initialDetailsReplyHandler, false, 0, true); |
| |
| obj.addEventListener(MarshalledAutomationEvent.START_POINT_REPLY,startPointReplyHandler, false, 0, true); |
| |
| obj.addEventListener(MarshalledAutomationEvent.UNIQUE_APPID_REPLY , uniqueAppIdReplyHandler, false, 0, true); |
| |
| obj.addEventListener(MarshalledAutomationEvent.DRAG_DROP_PERFORM_REQUEST_TO_SUB_APP ,dragDropPerformRequesthandlerInSubApp, false, 0, true); |
| |
| } |
| private function addListenerToChildApplications(obj:IEventDispatcher):void |
| { |
| // as an application x, we need to listen to these events coming from the children. |
| // these events will be processed only by the root application. so if we are not the root applicaiton |
| // we just need to dispatch these events on the parent. So always these events are from the child -> Parent. |
| // i.e these events are always handled by the parent. So the listneres of the events should be added to the children. |
| |
| obj.addEventListener(MarshalledAutomationEvent.DRAG_DROP_PROXY_RETRIEVE_REQUEST, dragproxyRetrieveRequesthandler); |
| |
| obj.addEventListener(InterManagerRequest.SYSTEM_MANAGER_REQUEST,dragProxyReciever, false, 0, true); |
| |
| obj.addEventListener(MarshalledAutomationEvent.UPDATE_SYCHRONIZATION,synchronizationHandler, false, 0, true); |
| |
| obj.addEventListener(MarshalledAutomationEvent.INITIAL_DETAILS_REQUEST,initialDetailsRequestHandler, false, 0, true); |
| |
| obj.addEventListener(MarshalledAutomationEvent.UNIQUE_APPID_REQUEST , uniqueAppIdRequestHandler, false, 0, true); |
| |
| obj.addEventListener(MarshalledAutomationEvent.START_POINT_REQUEST , startPointRequestHandler, false, 0, true); |
| |
| obj.addEventListener(MarshalledAutomationEvent.DRAG_DROP_PERFORM_REQUEST_TO_ROOT_APP,dragDropPerformRequesthandlerInRootApp, false, 0, true); |
| |
| |
| } |
| |
| private function dragDropPerformRequesthandlerInSubApp(event:Event):void |
| { |
| if(event is MarshalledAutomationEvent) |
| return; |
| |
| // take the details and check whether the object belongs to us. |
| var details:Array = event["interAppDataToSubApp"]; |
| if(details && details.length == 2) |
| { |
| var target:IUIComponent = details[0] as IUIComponent; |
| if(target) |
| { |
| DragManagerAutomationImpl.setForcefulDragStart(); |
| var passedEventObj:Object = details[1]; |
| var dragEventInCurrentDomain:DragEvent = new DragEvent(passedEventObj["type"]); |
| dragEventInCurrentDomain.action = passedEventObj["action"]; |
| dragEventInCurrentDomain.localX = passedEventObj["localX"]; |
| dragEventInCurrentDomain.localY= passedEventObj["localY"]; |
| |
| DragManagerAutomationImpl.recordAutomatableDragDrop1(target as DisplayObject , dragEventInCurrentDomain); |
| } |
| |
| } |
| |
| } |
| |
| private function dragDropPerformRequesthandlerInRootApp(event:Event):void |
| { |
| if(event is MarshalledAutomationEvent) |
| return; |
| |
| // take the details and check whether the object belongs to us. |
| var details:Array = event["interAppDataToMainApp"]; |
| if(details.length == 2) |
| { |
| var target:IUIComponent = details[0] as IUIComponent; |
| if(target) |
| { |
| DragManagerAutomationImpl.setForcefulDragStart(); |
| var passedEventObj:Object = details[1]; |
| var dragEventInCurrentDomain:DragEvent = new DragEvent(passedEventObj["type"]); |
| dragEventInCurrentDomain.action = passedEventObj["action"]; |
| dragEventInCurrentDomain.localX = passedEventObj["localX"]; |
| dragEventInCurrentDomain.localY= passedEventObj["localY"]; |
| DragManagerAutomationImpl.recordAutomatableDragDrop1(target as DisplayObject ,dragEventInCurrentDomain ); |
| } |
| else |
| { |
| var eventObj:MarshalledAutomationEvent = new MarshalledAutomationEvent(MarshalledAutomationEvent.DRAG_DROP_PERFORM_REQUEST_TO_SUB_APP ); |
| eventObj.interAppDataToSubApp = details; |
| dispatchToAllChildren(eventObj); |
| |
| } |
| } |
| |
| } |
| |
| private static var listenerAdded:Boolean = false; |
| /* this funciton is to add the required event listeners of |
| automation manger */ |
| |
| private function addListenerToAllApplications(passedListenerObj:IEventDispatcher, |
| passedSystemManager:ISystemManager,passedMarshaledSystemManager:IMarshalSystemManager,fromAirWindow:Boolean = false):void |
| { |
| |
| //var passedMarshaledSystemManager:IMarshalSystemManager = passedSystemManager as IMarshalSystemManager; |
| if(!passedMarshaledSystemManager) |
| return; |
| |
| // we need to take all the children and add listener to all the child |
| // bridges in these application. |
| // an application in a different securty domain or applicaiton domain, when they |
| // have their child application, they have a bridge corresponding to their child application |
| // so the main application need to listen to all their child applications. |
| |
| addListenerToParentApplication(passedListenerObj); |
| |
| if(passedMarshaledSystemManager.useSWFBridge()) |
| { |
| // we need a special listener to the uniqueIdReply |
| // this event we will listen always to the parentBridge rather than the main |
| // communication object |
| var bridgeParent:IEventDispatcher = passedMarshaledSystemManager.swfBridgeGroup.parentBridge; |
| if(bridgeParent) |
| { |
| bridgeParent.addEventListener(MarshalledAutomationEvent.UNIQUE_APPID_REPLY, |
| uniqueAppIdReplyHandler, false, 0, true); |
| bridgeParent.addEventListener(MarshalledAutomationEvent.START_POINT_REPLY, |
| startPointReplyHandler, false, 0, true); |
| } |
| } |
| |
| |
| // only the sandbox root applicaiton needs to listen to the communication over the main listener object |
| if (fromAirWindow || (isCurrentAppSandboxRoot(passedSystemManager as ISystemManager))) |
| addListenerToChildApplications(passedSystemManager.getSandboxRoot()); |
| |
| |
| if (!passedMarshaledSystemManager.swfBridgeGroup) |
| return; |
| |
| var children:Array = passedMarshaledSystemManager.swfBridgeGroup.getChildBridges(); |
| for (var i:int; i < children.length; i++) |
| { |
| var childBridge:IEventDispatcher = IEventDispatcher(children[i]); |
| addListenerToChildApplications(childBridge); |
| } |
| listenerAdded = true; |
| |
| } |
| |
| public function dispatchToParent(event:Event):void |
| { |
| if(mainListenerObj.hasEventListener(event.type)) |
| mainListenerObj.dispatchEvent(event); |
| } |
| |
| private function dispatchToBridgeParent(event:Event):void |
| { |
| if(!sm1MSm) |
| return; |
| |
| var bridgeParent:IEventDispatcher = sm1MSm.swfBridgeGroup.parentBridge; |
| if(bridgeParent.hasEventListener(event.type)) |
| bridgeParent.dispatchEvent(event); |
| } |
| |
| |
| private function dispatchToSwfBridgeChildren(event:Event):void |
| { |
| dispatchToSwfBridgeChildrenOfPassedSystemManager(sm1,sm1MSm,event); |
| // we need to consider the possible swf bridge children from the windows also |
| dispatchToSwfBridgeChildrenOfAllWindows(event); |
| } |
| |
| private function dispatchToSwfBridgeChildrenOfAllWindows(event:Event):void |
| { |
| var count:int = allAirWindowList.length; |
| var index:int = 0; |
| while(index < count) |
| { |
| var currentWindow:IUIComponent = allAirWindowList[index] as IUIComponent; |
| if(currentWindow) |
| { |
| var currentWindowSysManager:ISystemManager = currentWindow.systemManager; |
| var currentWindowMarshaledSysManager:IMarshalSystemManager = IMarshalSystemManager( |
| currentWindow.systemManager.getImplementation("mx.managers::IMarshalSystemManager")); |
| |
| if(currentWindowSysManager) |
| { |
| dispatchToSwfBridgeChildrenOfPassedSystemManager(currentWindowSysManager,currentWindowMarshaledSysManager,event); |
| } |
| |
| } |
| index++; |
| } |
| } |
| |
| private function dispatchToSwfBridgeChildrenOfPassedSystemManager(passedSystemManager:ISystemManager, |
| passedMarshalledSystemManger:IMarshalSystemManager, |
| event:Event):void |
| { |
| //var passedMarshalledSystemManger:IMarshalSystemManager = passedSystemManager as IMarshalSystemManager; |
| if(!passedMarshalledSystemManger) |
| return; |
| |
| if (!passedMarshalledSystemManger.swfBridgeGroup) |
| return; |
| |
| var children:Array = passedMarshalledSystemManger.swfBridgeGroup.getChildBridges(); |
| for (var i:int; i < children.length; i++) |
| { |
| var childBridge:IEventDispatcher = IEventDispatcher(children[i]); |
| if(childBridge.hasEventListener(event.type)) |
| childBridge.dispatchEvent(event); |
| } |
| } |
| |
| |
| public function dispatchToAllChildren(event:Event):void |
| { |
| dispatchToAllChildrenOfPassedSystemManager(sm1,sm1MSm,event,false); |
| // we need to consider the possible children of the windows also. |
| dispathcToAllWindowChildApplications(event); |
| |
| } |
| |
| private function dispathcToAllWindowChildApplications(event:Event):void |
| { |
| var count:int = allAirWindowList.length; |
| var index:int = 0; |
| while(index < count) |
| { |
| var currentWindow:IUIComponent = allAirWindowList[index] as IUIComponent; |
| if(currentWindow) |
| { |
| var currentWindowSysManager:ISystemManager = currentWindow.systemManager; |
| var currentWindowMarshaledSysManager:IMarshalSystemManager = IMarshalSystemManager(currentWindow.systemManager.getImplementation("mx.managers::IMarshalSystemManager")); |
| if(currentWindowSysManager) |
| { |
| dispatchToAllChildrenOfPassedSystemManager(currentWindowSysManager,currentWindowMarshaledSysManager,event,true); |
| } |
| |
| } |
| index++; |
| } |
| } |
| |
| private function dispatchToAllChildrenOfPassedSystemManager(passedSystemManager:ISystemManager, passedMarshalledSystemManger:IMarshalSystemManager,event:Event, fromAirWindow:Boolean):void |
| { |
| //var passedMarshalledSystemManger:IMarshalSystemManager = passedSystemManager as IMarshalSystemManager; |
| if(!passedMarshalledSystemManger) |
| return; |
| |
| |
| // some children (of the same application domain) |
| // will be listening to the sandboxroot of the main app only |
| // hence we need to dispatch event on this object also. |
| if(fromAirWindow || (isCurrentAppSandboxRoot(passedSystemManager))) |
| passedSystemManager.getSandboxRoot().dispatchEvent(event); |
| |
| |
| if (!passedMarshalledSystemManger.swfBridgeGroup) |
| return; |
| |
| var children:Array = passedMarshalledSystemManger.swfBridgeGroup.getChildBridges(); |
| for (var i:int; i < children.length; i++) |
| { |
| var childBridge:IEventDispatcher = IEventDispatcher(children[i]); |
| if(childBridge.hasEventListener(event.type)) |
| childBridge.dispatchEvent(event); |
| } |
| } |
| |
| private var _inInitialDetailsRequestProcessing:Boolean = false; |
| private var _inStartPointRequestProcessing:Boolean = false; |
| |
| private static function initMainListeners():void |
| { |
| if(!_sm1MSm) |
| _sm1MSm = IMarshalSystemManager(sm1.getImplementation("mx.managers::IMarshalSystemManager")); |
| |
| if(!_mainListenerObj) |
| _mainListenerObj = getMainListenerObject(sm1,_sm1MSm); |
| |
| } |
| private function applicationCompleteHandler(event:Event):void |
| { |
| if(!(event is FlexEvent)) |
| return; |
| |
| initMainListeners(); |
| //_mainListenerObj.addEventListener(MarshalledAutomationEvent.POPUP_HANDLER_REQUEST , popupHandlerBeforeApplicationCompletion, false, 0, true); |
| |
| // add listener to the child bridges |
| addListenerToAllApplications(_mainListenerObj,sm1,sm1MSm); |
| |
| // process the possible popops |
| processPopupsBeforeApplicationComplete(); |
| // we will listen to the initial details from our parent. |
| var initialStatusRequest:MarshalledAutomationEvent = |
| new MarshalledAutomationEvent(MarshalledAutomationEvent.INITIAL_DETAILS_REQUEST); |
| _inInitialDetailsRequestProcessing = true; |
| dispatchToParent(initialStatusRequest); |
| |
| } |
| |
| private var eventDetailsFromToolToChildren:Array; |
| public function addEventListenersToAllParentApplications(eventDetailsArray:Array):void |
| { |
| // we dont need to store the events as we will never get a new parent. |
| addEventListeners(mainListenerObj,eventDetailsArray); |
| } |
| |
| |
| public function addEventListenersToAllChildApplications(eventDetailsArray:Array):void |
| { |
| eventDetailsFromToolToChildren = eventDetailsArray; |
| |
| addEventListenersToAllChildApplicationsOfPassedSystemManager(sm1,sm1MSm,eventDetailsFromToolToChildren,false); |
| // we need to consider the child application of the windows also |
| addEventListenersToAllChildApplicationsOfAllWindows(eventDetailsFromToolToChildren); |
| |
| } |
| |
| private function addEventListenersToAllChildApplicationsOfAllWindows(eventDetailsArray:Array):void |
| { |
| var count:int = allAirWindowList.length; |
| var index:int = 0; |
| while(index < count) |
| { |
| var currentWindow:IUIComponent = allAirWindowList[index] as IUIComponent; |
| if(currentWindow) |
| { |
| var currentWindowSysManager:ISystemManager = currentWindow.systemManager; |
| var currentWindowMarshalSysManager:IMarshalSystemManager = IMarshalSystemManager( |
| currentWindow.systemManager.getImplementation("mx.manager::IMarshalSystemManager")); |
| if(!currentWindowSysManager) |
| { |
| addEventListenersToAllChildApplicationsOfPassedSystemManager(currentWindowSysManager,currentWindowMarshalSysManager,eventDetailsArray,true); |
| } |
| |
| } |
| index++; |
| } |
| } |
| |
| private function addEventListenersToAllChildApplicationsOfPassedSystemManager(passedSystemManager:ISystemManager, |
| passedMarshalledSystemManger:IMarshalSystemManager, |
| eventDetailsArray:Array,fromAirWindow:Boolean):void |
| { |
| //var passedMarshalledSystemManger:IMarshalSystemManager = passedSystemManager as IMarshalSystemManager; |
| if(!passedMarshalledSystemManger) |
| return; |
| |
| |
| if(fromAirWindow || (isCurrentAppSandboxRoot(passedSystemManager))) |
| addEventListeners(passedSystemManager.getSandboxRoot(),eventDetailsArray); |
| |
| |
| if (!passedMarshalledSystemManger.swfBridgeGroup) |
| return; |
| |
| var children:Array = passedMarshalledSystemManger.swfBridgeGroup.getChildBridges(); |
| for (var i:int; i < children.length; i++) |
| { |
| var childBridge:IEventDispatcher = IEventDispatcher(children[i]); |
| addEventListeners(childBridge,eventDetailsArray); |
| } |
| } |
| |
| private function childBridgeHandler(event:FlexChangeEvent):void |
| { |
| // get the new bridge of the current application and add listeners to the same |
| var currentBridge:IEventDispatcher = event.data as IEventDispatcher; |
| handleBridge(currentBridge); |
| |
| } |
| |
| private function handleBridge(currentBridge:IEventDispatcher):void |
| { |
| if(currentBridge) |
| { |
| addEventListeners(currentBridge,eventDetailsFromToolToChildren); |
| addListenerToChildApplications(currentBridge); |
| } |
| } |
| |
| private function addEventListeners(obj:IEventDispatcher, eventDetailsArray:Array):void |
| { |
| // it is quite possible that tool library has not supported marshaling |
| // and hence these arrays would not have been initialised |
| if(!eventDetailsArray) |
| return; |
| var count:int = eventDetailsArray.length; |
| var index:int = 0; |
| |
| while(index < count) |
| { |
| var currentEventDetailsObj:EventDetails = eventDetailsArray[index] as EventDetails; |
| if(currentEventDetailsObj) |
| { |
| obj.addEventListener(currentEventDetailsObj.eventType, |
| currentEventDetailsObj.handlerFunction, |
| currentEventDetailsObj.useCapture, |
| currentEventDetailsObj.priority, |
| currentEventDetailsObj.useWeekRef); |
| } |
| index++; |
| } |
| } |
| |
| |
| private function initialDetailsRequestHandler(event:Event):void |
| { |
| if (event is MarshalledAutomationEvent) |
| return; |
| |
| var replyEvent:MarshalledAutomationEvent = new |
| MarshalledAutomationEvent(MarshalledAutomationEvent.INITIAL_DETAILS_REPLY); |
| // order of the elements in the array should not changed across versions. |
| // if some information is needed in further versions it needs to be added after this |
| // and handler can handle the data appropriately. |
| var tempArr:Array = new Array(); |
| tempArr.push(_recording); |
| tempArr.push(_automationEnvironmentHandlingClassName); |
| tempArr.push(_automationEnvironmentString); |
| replyEvent.interAppDataToSubApp = tempArr; |
| dispatchToAllChildren(replyEvent); |
| } |
| |
| private function startPointRequestHandler(event:Event):void |
| { |
| if (event is MarshalledAutomationEvent) |
| return; |
| var windowId:String = event["interAppDataToMainApp"][0] as String; |
| var replyEvent:MarshalledAutomationEvent = new |
| MarshalledAutomationEvent(MarshalledAutomationEvent.START_POINT_REPLY); |
| if(sm1.isTopLevelRoot() == false) |
| { |
| dispatchStartPointRequestEvent(windowId); |
| var tempArr:Array = new Array(); |
| //var p:Point = AutomationHelper.getStageStartPointInScreenCoords(windowId); |
| tempArr.push(_appStartPoint.x); |
| tempArr.push(_appStartPoint.y); |
| |
| replyEvent.interAppDataToSubApp = tempArr; |
| dispatchToAllChildren(replyEvent); |
| } |
| |
| if(sm1.isTopLevelRoot() == true) |
| { //start point request should be handled only by main application |
| //i.e., top level root application |
| var tempArr1:Array = new Array(); |
| var p:Point = AutomationHelper.getStageStartPointInScreenCoords(windowId); |
| tempArr1.push(p.x); |
| tempArr1.push(p.y); |
| |
| replyEvent.interAppDataToSubApp = tempArr1; |
| dispatchToAllChildren(replyEvent); |
| } |
| } |
| |
| private function initialDetailsReplyHandler(event:Event):void |
| { |
| // Marshalling events are needeed across applicaiton domain |
| // so this conversion shall fail in the same domain |
| if(event is MarshalledAutomationEvent) |
| return; |
| |
| if (!_inInitialDetailsRequestProcessing) |
| return; |
| |
| _inInitialDetailsRequestProcessing = false; |
| |
| |
| |
| if(sm1.isTopLevelRoot() == false) |
| { |
| try |
| { |
| var interAppData:Array = event["interAppDataToSubApp"]; |
| _recording = interAppData[0] as Boolean; |
| if(_recording == true) |
| { |
| _recording = false; // to make the beginRecording call happen |
| beginRecording(); |
| } |
| _automationEnvironmentString = interAppData[2]; |
| _automationEnvironmentHandlingClassName = interAppData[1]; |
| var envClass:Class = Class(getDefinitionByName(_automationEnvironmentHandlingClassName)); //( "mx.automation.tool.ToolEnvironment")); |
| if (envClass != null) |
| { |
| _automationEnvironment = new envClass(new XML(_automationEnvironmentString)); |
| } |
| } |
| catch(e:Error) |
| { |
| |
| } |
| } |
| } |
| |
| private function startPointReplyHandler(event:Event):void |
| { |
| // Marshalling events are needeed across application domain |
| // so this conversion shall fail in the same domain |
| if(event is MarshalledAutomationEvent) |
| return; |
| |
| if (!_inStartPointRequestProcessing) |
| return; |
| |
| _inStartPointRequestProcessing = false; |
| |
| if(sm1.isTopLevelRoot() == false) |
| { |
| try |
| { |
| var interAppData:Array = event["interAppDataToSubApp"]; |
| _appStartPoint = new Point(interAppData[0] as Number, interAppData[1] as Number); |
| } |
| catch(e:Error) |
| { |
| |
| } |
| } |
| } |
| |
| |
| private function getObjectIdInCurrentApplication(objectPassed:DisplayObject):String |
| { |
| |
| if(!objectPassed) |
| return null; |
| |
| var idString:String = null; |
| var orderArraay:Array = new Array(); |
| try |
| { |
| var object:DisplayObject = objectPassed; |
| var parent:DisplayObjectContainer = object.parent; |
| while(object && parent && (parent!= sm1)) |
| { |
| orderArraay.push(getChildIndex1(parent,object)); |
| object = parent; |
| parent = object.parent; |
| } |
| idString = orderArraay.join(""); |
| } |
| catch(e:Error) |
| { |
| |
| } |
| return idString; |
| } |
| private var _inUniqueAppIdRequestProcessing:Boolean = false; |
| |
| private function dispatchUniqueAppIdRequestEvent():void |
| { |
| if(!sm1MSm) |
| return; |
| |
| _inUniqueAppIdRequestProcessing = true; |
| var appIdReqEvent:MarshalledAutomationEvent = new MarshalledAutomationEvent( |
| MarshalledAutomationEvent.UNIQUE_APPID_REQUEST); |
| var tempArray:Array = new Array(); |
| tempArray.push(sm1MSm.swfBridgeGroup.parentBridge); |
| appIdReqEvent.interAppDataToMainApp = tempArray; |
| |
| dispatchToBridgeParent(appIdReqEvent); |
| |
| } |
| |
| public function registerNewApplication(application:DisplayObject):void |
| { |
| if(!(application is Application || isSparkApplication(application))) |
| return; |
| |
| if(application.root == sm1) |
| return; |
| |
| application.root.addEventListener(FlexChangeEvent.ADD_CHILD_BRIDGE , childBridgeHandler); |
| handleAlreadyPresentBridges(application); |
| } |
| |
| private function handleAlreadyPresentBridges(application:DisplayObject):void |
| { |
| var currentSysManager:IMarshalSystemManager = (application as IUIComponent).systemManager as IMarshalSystemManager |
| |
| if(!currentSysManager) |
| return; |
| |
| var childBridges:Array; |
| if(currentSysManager && currentSysManager.swfBridgeGroup) |
| childBridges = currentSysManager.swfBridgeGroup.getChildBridges(); |
| if(childBridges && childBridges.length) |
| { |
| var len:int = childBridges.length; |
| var index:int = 0; |
| while(index < len) |
| { |
| handleBridge(childBridges[index] as IEventDispatcher); |
| index++; |
| } |
| } |
| } |
| |
| public function registerNewFlexNativeMenu(menu:Object, sm:DisplayObject):void |
| { |
| createDelegateForFlexNativeMenu(menu, sm); |
| } |
| |
| private function createDelegateForFlexNativeMenu(menu:Object, sm:DisplayObject):Boolean |
| { |
| var component:IAutomationObject = menu as IAutomationObject; |
| var retValue:Boolean = false; |
| var appDomain:ApplicationDomain; |
| var className:String = "mx.controls::FlexNativeMenu"; |
| if(!sm) |
| { |
| var factory:IFlexModuleFactory = ModuleManager.getAssociatedFactory(menu); |
| if (factory != null) |
| { |
| appDomain = ApplicationDomain(factory.info()["currentDomain"]); |
| } |
| else |
| { |
| var message:String = "Factory module failure"; |
| traceMessage("AutomationManager","createDelegateForFlexNativeMenu()",message); |
| } |
| } |
| else |
| { |
| appDomain = (sm.loaderInfo) ? sm.loaderInfo.applicationDomain : |
| ApplicationDomain.currentDomain; |
| } |
| |
| var compClass:Class = appDomain.getDefinition(className) as Class; |
| var mainComponentClass:Class = compClass; |
| var delegateClass:Class = Automation.delegateDictionary[compClass] as Class; |
| |
| if(!delegateClass) |
| { |
| var componentClass:String = className; |
| do |
| { |
| try |
| { |
| className = getQualifiedSuperclassName(appDomain.getDefinition(className)); |
| compClass = appDomain.getDefinition(className) as Class; |
| delegateClass = Automation.delegateDictionary[compClass] as Class; |
| } |
| catch(e:Error) |
| { |
| traceMessage("AutomationManager","createDelegateForFlexNativeMenu()",e.message); |
| break; |
| } |
| } |
| while(!delegateClass); |
| |
| Automation.delegateDictionary[mainComponentClass] = delegateClass; |
| //trace("Added mapping for : " + componentClass); |
| } |
| |
| var c:Class = delegateClass; |
| if (c) |
| { |
| try |
| { |
| var delegate:Object = new c (menu); |
| } |
| catch(e:Error) |
| { |
| Automation.automationDebugTracer.traceMessage("AutomationManager","createDelegateForFlexNativeMenu()",e.message); |
| Automation.automationDebugTracer.traceMessage("AutomationManager","createDelegateForFlexNativeMenu()","Delegate object could not be created"); |
| } |
| |
| try |
| { |
| component.automationDelegate = delegate; |
| retValue = true; |
| } |
| catch(e:Error) |
| { |
| Automation.automationDebugTracer.traceMessage("AutomationManager","createDelegateForFlexNativeMenu()",e.message); |
| Automation.automationDebugTracer.traceMessage("AutomationManager","createDelegateForFlexNativeMenu()","object created but delegates not set"); |
| } |
| } |
| else |
| Automation.automationDebugTracer.traceMessage("AutomationManager","createDelegateForFlexNativeMenu()", "Unable to find definition for class : " + className); |
| |
| return retValue; |
| } |
| |
| public function registerNewWindow(newWindow:DisplayObject):void |
| { |
| // we need to find a uniqueId for this window in Automation Manager |
| // we are forming the unique id string as |
| // 'applicationName'::_::AIRWindow_index |
| lastRegisteredWindowCount++; |
| var currentWindowId:String = Automation.getMainApplication().automationName+ airWindowIdFixedString+ |
| String(lastRegisteredWindowCount); |
| |
| // we need bi directional mapping as we get the id we need to identify the window |
| // later from the plugin |
| allAirWindowsToIdDictionary[newWindow] = currentWindowId; |
| allAirIdToWindowsDictionary[currentWindowId] = newWindow; |
| // store the window in the list so that we can analyse the windows for eventhandling part. |
| allAirWindowList.push(newWindow); |
| |
| // we need to dispatch an event so that tool library will try to coomunicate |
| // to the plugin using this id to get associate the hwnd with this id. |
| dispatchEvent(new AutomationAirEvent(AutomationAirEvent.NEW_AIR_WINDOW, |
| true,true,currentWindowId)); |
| |
| inMouseSequence = false; |
| createDelegate(newWindow); |
| addDelegates(newWindow); |
| newWindow.stage.addEventListener(Event.ADDED, childAddedHandler, false, 0, true); |
| |
| |
| |
| |
| var sm2:ISystemManager = (newWindow as IUIComponent).systemManager; |
| var sm2MSm:IMarshalSystemManager = IMarshalSystemManager((newWindow as IUIComponent).systemManager.getImplementation("mx.managers::IMarshalSystemManager")); |
| if(!sm2) |
| return; |
| |
| var windowMainListenerObj:IEventDispatcher = getMainListenerObject(sm2,sm2MSm); |
| |
| addListenerToAllApplications(windowMainListenerObj,sm2,sm2MSm,true); |
| addEventListenersToAllChildApplicationsOfPassedSystemManager(sm2,sm2MSm,eventDetailsFromToolToChildren,true); |
| |
| sm2.addEventListener(FlexChangeEvent.ADD_CHILD_BRIDGE , childBridgeHandler); |
| sm2.addEventListener(AutomationRecordEvent.RECORD, |
| recordHandler, false, EventPriority.DEFAULT_HANDLER, true); |
| |
| sm2.getSandboxRoot().addEventListener(MouseEvent.MOUSE_DOWN, |
| captureIDFromMouseDownEvent, true, 0, true); |
| |
| sm2.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 |
| sm2.getSandboxRoot().addEventListener(MouseEvent.CLICK, |
| onEndMouseSequence, true, 0, true); |
| sm2.getSandboxRoot().addEventListener(MouseEvent.DOUBLE_CLICK, |
| onEndMouseSequence, true, 0, true); |
| |
| // sm2.getSandboxRoot().addEventListener(SandboxMouseEvent.CLICK_SOMEWHERE, |
| // onEndMouseSequence, true, 0, true); |
| // sm2.getSandboxRoot().addEventListener(SandboxMouseEvent.DOUBLE_CLICK_SOMEWHERE, |
| // onEndMouseSequence, true, 0, true); |
| |
| sm2.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) |
| sm2.getSandboxRoot().addEventListener(MouseEvent.MOUSE_UP, |
| onEndMouseSequence, true, 0, true); |
| //sm2.getSandboxRoot().addEventListener(SandboxMouseEvent.MOUSE_UP_SOMEWHERE, |
| // onEndMouseSequence, true, 0, true); |
| sm2.getSandboxRoot().addEventListener(FocusEvent.KEY_FOCUS_CHANGE, keyFocusChangeHandler, false, 0, true); |
| |
| |
| |
| } |
| |
| public function getAIRWindowUniqueID(newWindow:DisplayObject):String |
| { |
| return allAirWindowsToIdDictionary[newWindow]; |
| } |
| |
| |
| public function getApplicationNameFromAutomationIDPart(objectIdPart:AutomationIDPart):String |
| { |
| |
| // check whether the current class is an AIR window as it is the another object |
| // which can be the first level |
| // we have added a property by name 'applicationName' which is applicable only |
| // for the top level windows for AIR |
| if (objectIdPart.hasOwnProperty(AutomationManager.airWindowIndicatorPropertyName)) |
| { |
| // get the automationName |
| //var currentAutomationName:String = objectIdPart["automationName"]; |
| var currentAutomationName:String = getPassedUniqueName(objectIdPart); |
| var appNameArray:Array = currentAutomationName.split(airWindowIdFixedString); |
| if(appNameArray && appNameArray.length) |
| return appNameArray[0]; |
| } |
| |
| // if we reach here we are not the AIR top level window |
| // hence we can use the automationName to get the automation class name |
| //return (objectIdPart["automationName"].toString()); |
| return getPassedUniqueName(objectIdPart); |
| } |
| private function getPassedUniqueName(part:AutomationIDPart):String |
| { |
| if(part.hasOwnProperty("automationName")) |
| { |
| if(part["automationName"] is RegExp) |
| { |
| var obj1:RegExp = part["automationName"] as RegExp; |
| return obj1.source; |
| } |
| else |
| return (part["automationName"].toString()); |
| |
| } |
| else if(part.hasOwnProperty("id")) |
| { |
| if(part["id"] is RegExp) |
| { |
| var obj:RegExp = part["id"] as RegExp; |
| return obj.source; |
| } |
| else |
| return (part["id"].toString()); |
| } |
| else |
| { |
| // if we dont get any of the above conditions, we cannot support the |
| // marhslling. Hence we assume it is a single application scenario |
| // and get the current application name |
| return getUniqueApplicationID(); |
| } |
| } |
| |
| public function getAIRWindow(windowId:String):DisplayObject |
| { |
| if(windowId=="") |
| return Automation.getMainApplication() as DisplayObject; |
| |
| return allAirIdToWindowsDictionary[windowId]; |
| } |
| |
| |
| |
| |
| public function getAIRWindowUniqueIDFromObjectIDString(objectId:String ):String |
| { |
| var objectID:AutomationID = AutomationID.parse(objectId); |
| |
| // clone the automationID |
| var rid:AutomationID = objectID.clone(); |
| |
| //remove the application |
| var objectIdPart:AutomationIDPart = rid.removeFirst(); |
| |
| return getAIRWindowUniqueIDFromAutomationIDPart(objectIdPart); |
| } |
| |
| public function getAIRWindowUniqueIDFromAutomationIDPart(objectIdPart:AutomationIDPart ):String |
| { |
| |
| // check whether the current class is an AIR window as it is the another object |
| // which can be the first level |
| // we have added a property by name 'applicationName' which is applicable only |
| // for the top level windows for AIR |
| if (objectIdPart.hasOwnProperty(AutomationManager.airWindowIndicatorPropertyName)) |
| { |
| // get the automationName |
| //return objectIdPart["automationName"]; |
| return getPassedUniqueName(objectIdPart); |
| |
| } |
| // if we reach here we are not the AIR top level window |
| // hence we can use the automationName to get the automation class name |
| return null; |
| } |
| |
| public function getTopApplicationIndex(objectList:Array):int |
| { |
| // we need to find the application which can be on the top. This is based on |
| // the order of the SWFLoader not based on the order of applicaiton loading. |
| // let us for the application name list. |
| if(objectList) |
| { |
| var count:int = objectList.length; |
| if(count > 1) |
| { |
| var appNames:Array = new Array(); |
| var selectedObjectIndices:Array = new Array(); |
| var index:int = 0; |
| while(index < count) |
| { |
| var curentObj:Object = objectList[index]; |
| appNames.push(curentObj["applicationName"]); |
| selectedObjectIndices.push(index); |
| index++; |
| } |
| |
| var requiredIndex:int = 0; |
| var higherOrderObject:Array = new Array(); |
| var indexToSort:int = 1; |
| while(selectedObjectIndices.length > 1) |
| { |
| var resultObj:Object = sortCurrentList(appNames,selectedObjectIndices,indexToSort); |
| appNames = new Array(); |
| selectedObjectIndices = new Array(); |
| appNames = resultObj["slectedAppNames"] as Array; |
| selectedObjectIndices = resultObj["selectedObjectIndices"] as Array; |
| indexToSort = indexToSort+2; |
| } |
| |
| return selectedObjectIndices[0]; |
| } |
| else |
| return 0; |
| |
| |
| } |
| return -1; |
| } |
| |
| private function sortCurrentList(passedNamesArray:Array , passesSelectedObjectIndices:Array, indexToSort:int):Object |
| { |
| // find the higher order swf loader number from the current object. |
| var highestSwfOrder:int = -1; |
| var count:int = passedNamesArray.length; |
| var index:int = 0; |
| var selectedAppNames:Array = new Array(); |
| var selectedObjectIndices:Array = new Array(); |
| while(index < count) |
| { |
| var splitNames:Array = (passedNamesArray[index]as String).split("_");// TBD use constant |
| if(splitNames.length > indexToSort) |
| { |
| var currentOrderNumber:int = int(splitNames[splitNames.length-indexToSort-1]); |
| if( currentOrderNumber > highestSwfOrder) |
| { |
| highestSwfOrder = currentOrderNumber; |
| // clear the current order info arrays |
| selectedAppNames = new Array(); |
| selectedObjectIndices = new Array(); |
| selectedAppNames.push(passedNamesArray[index]); |
| selectedObjectIndices.push(passesSelectedObjectIndices[index]); |
| } |
| else if (currentOrderNumber == highestSwfOrder) |
| { |
| selectedAppNames.push(passedNamesArray[index]); |
| selectedObjectIndices.push(passesSelectedObjectIndices[index]); |
| } |
| } |
| index++; |
| } |
| |
| var resultObj:Object = { slectedAppNames:selectedAppNames, selectedObjectIndices:selectedObjectIndices}; |
| |
| return resultObj; |
| |
| } |
| |
| public function getAutomationChildrenArray(object:Object):Array |
| { |
| var automationObject:IAutomationObject = object as IAutomationObject; |
| var childArray:Array = null; |
| if (automationObject) |
| { |
| childArray = automationObject.getAutomationChildren(); |
| } |
| /* |
| if(object.hasOwnProperty("getAutomationChildren") ) |
| return object.getAutomationChildren(); |
| if(object.automationDelegate.hasOwnProperty("getAutomationChildren") ) |
| return object.automationDelegate.getAutomationChildren(); |
| */ |
| if(childArray) |
| return childArray; |
| |
| // let us ensure that we are not returning a null array. |
| return new Array(); |
| } |
| public function getPropertyValueFromPart(part:Object,obj:Object, |
| pd:IAutomationPropertyDescriptor, |
| relativeParent:IAutomationObject = null):Object |
| { |
| return getMemberFromPartOrObject(part,obj, pd.name); |
| |
| } |
| |
| |
| |
| public function getMemberFromPartOrObject(part:Object,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 createIDPartForSpecifiedProperties(properties:Array, |
| obj:IAutomationObject, |
| parent:IAutomationObject = null):AutomationIDPart |
| { |
| // create the part only with the requried properties |
| if (parent == null) |
| parent = getParent(obj, null, true); |
| |
| return helpCreateIDPartWithRequiredProperties(parent, obj,properties); |
| } |
| */ |
| |
| /** |
| * @private |
| */ |
| public function createIDPartForSpecifiedProperties(properties:Array,obj:IAutomationObject, |
| parent:IAutomationObject = null):AutomationIDPart |
| { |
| if (parent == null) |
| parent = getParent(obj, null, true); |
| |
| var parentObj:Object = parent as Object; |
| // temp solution till we get the interfaces part of UIComponent |
| |
| var part:AutomationIDPart = parent |
| ? parent.createAutomationIDPartWithRequiredProperties(obj,properties) as AutomationIDPart |
| : helpCreateIDPartWithRequiredProperties(null, obj,properties); |
| |
| /* |
| var part:AutomationIDPart = (parentObj &&(parentObj.automationDelegate) && (parentObj.automationDelegate.hasOwnProperty("createAutomationIDPartWithRequiredProperties"))) |
| ? parentObj.automationDelegate.createAutomationIDPartWithRequiredProperties(obj,properties) as AutomationIDPart |
| : helpCreateIDPartWithRequiredProperties(null, obj,properties); |
| */ |
| |
| return part; |
| } |
| |
| |
| /** |
| * @private |
| * |
| * Helper implementation of IAutomationIDHelper. Creates an id for |
| * a given child. This should not be used, instead use createID, |
| * or createIDPart. |
| */ |
| |
| public function helpCreateIDPartWithRequiredProperties(parent:IAutomationObject, |
| child:IAutomationObject,properyNamesList:Array, |
| automationNameCallback:Function = null, |
| automationIndexCallback:Function = null):AutomationIDPart |
| { |
| if(!properyNamesList) |
| return helpCreateIDPart(parent,child,automationNameCallback,automationIndexCallback); |
| else |
| return getIdPart(parent,child,properyNamesList,automationNameCallback,automationIndexCallback); |
| } |
| |
| |
| private function getIdPart(parent:IAutomationObject, |
| child:IAutomationObject,properyNamesList:Array = null, |
| automationNameCallback:Function = null, |
| automationIndexCallback:Function = null):AutomationIDPart |
| { |
| var part:AutomationIDPart = new AutomationIDPart(); |
| //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 ToolAdapter |
| //to not return null properties in Learn and ActiveScreen |
| var indexCalculated:Boolean = false; |
| for (var propNo:int = 0; propNo < properyNamesList.length; ++propNo) |
| { |
| var propertyName:String = properyNamesList[propNo]; |
| |
| 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)); |
| indexCalculated = true; |
| } |
| 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 |
| { |
| part[propertyName] = ""; |
| /// TBD |
| |
| var message:String = resourceManager.getString( |
| "automation_agent", "notDefined", [propertyName, child]); |
| //trace(message); |
| /* |
| throw new Error(message); |
| */ |
| } |
| } |
| } |
| |
| if ("automationName" in part && part.automationName == null) |
| { |
| |
| if(indexCalculated) |
| part.automationName = part.automationIndex; |
| else |
| { |
| // calculate the index and assign the same as the automation value |
| part.automationName = (automationIndexCallback == null ? |
| getChildIndex(getParent(child), child) |
| : automationIndexCallback(child)); |
| } |
| } |
| |
| return part; |
| } |
| |
| public function traceMessage(className:String, methodName:String, message:String):void |
| { |
| trace(className+":"+methodName+" - "+message); |
| } |
| |
| private function isSparkApplication(obj:DisplayObject):Boolean |
| { |
| if(AutomationHelper.isRequiredSparkClassPresent()){ |
| var sparkAppClass:Class = Class(ApplicationDomain.currentDomain.getDefinition("spark.components.Application")); |
| if(obj is sparkAppClass) |
| return true; |
| } |
| |
| return false; |
| } |
| } |
| |
| } |