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