开发者

Flash events on mouse over

Is there any way to find out what methods get called when moving the mouse over an object in a Fla开发者_JAVA技巧sh project?


If you try the following you'll be able to trace every listener on your object. It'll call all the listeners without any arguments which will throw an error. If you catch the error you can parse error.getStackTrace to see the locations of the listeners.

var members:Object = getMemberNames(yourObject);

for each (var name:QName in members) 
{
    if (name.localName == "listeners") 
    {
        for (var i : int = 0; i < yourObject[name].length; i++) 
        {
            var func:Function = yourObject[name][i];

            try
            {
                func.call();
            }
            catch(error:Error)
            {
                trace(error.getStackTrace());
            }
        }
    }
}

Hope this helps.

(just to be sure, you'll need a Debug Player for this)


No, it's not possible unless you override addEventListener and keep track of the added listeners yourself.

The only similar method provided by the native EventDispatcher is hasEventListener which would only allow you to check whether or not there is a registered listener for a given event type.


So far @rvmook's partial solution is the closest from my point of view. It would help if you use DisplayObjectContainer's getObjectsUnderPoint() method to get a list of display objects you roll over, then loop through them and check which one has rollover/mouseover event handlers, then continue drilling down.

So one short solution would be:

  1. Load the swf you want to find out the name of that rollover/mouseover handler.*
  2. Add a rollover handler(with bubbles set to true)
  3. In the rollover handler loop through the objects under the mouse that have rollover/mouseover handlers and get their details.

Note! getObjectsUnderPoint() works if the loading swf has permissions from the domain hosting the loaded swf. One way to find areInaccessibleObjectsUnderPoint() method. If you own the loaded swf there shouldn't be any problems. Otherwise you either need a crossdomain.xml on the domain hosting the loaded swf granting the loader swf's domain access (and the loader swf should pass new LoaderContext(true) as the second parameter for load() method of the Loader intance) or use a server side script in the language of your choice to proxy/copy over the loaded swf first.

Here's a basic example of what I mean:

package{
    import flash.display.*;
    import flash.events.*;
    import flash.geom.Point;
    import flash.net.URLRequest;
    import flash.sampler.getMemberNames;

    public class BasicInfoTest extends Sprite{

        private var cursor:Point = new Point();

        public function BasicInfoTest(){
            init();
        }
        private function init():void{
            var loader:Loader = addChild(new Loader) as Loader;
            loader.load(new URLRequest('B.swf'));
            addEventListener(MouseEvent.ROLL_OVER,onOver);
        }
        private function onOver(event:MouseEvent):void{
            cursor.x = mouseX;cursor.y = mouseY;
            var obj:Array = getObjectsUnderPoint(cursor);
            var numObj:int = obj.length; 
            for(var i:int = 0 ; i < numObj ; i++){//look for objects under cursor that have rollover/mouseover event handlers
                if(obj[i].hasEventListener(MouseEvent.ROLL_OVER) || obj[i].hasEventListener(MouseEvent.MOUSE_OVER)){
                    var members:Object = getMemberNames(obj[i]);//use @rvmook's method to get listeners
                    for each (var name:QName in members){
                        if (name.localName == "listeners"){
                            for (var j : int = 0; j < obj[i][name].length; j++){
                                var func:Function = obj[i][name][j];
                                try{
                                    func.call();
                                }catch(error:Error){
                                    trace('Methods called on mouse over:',error.message.split('on ')[1].split('.')[0]);//parse error message, you might need to adapt this
                                    trace('StackTrace',error.getStackTrace()); 
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

This should do if you only need to find out the name of the method. If you need further information, you could access the loaded swf's bytearray and parse the actionscript bytecode to get information. I must admit binary and assembly are a bit out of my reach, but luckily there some great libraries out there to decompile swf files at runtime in as3. AS3SWF is a brilliant one, but does not deal much with actionscript tags, while as3commons is a great collection of libraries the specialize on the code aspect.

Here is an adaptation of the previous example that uses the as3-commons libraries(bytecode,lang,logging and reflect) to display the method signature and body(as AVM2 instructions):

package{
    import flash.display.*;
    import flash.events.*;
    import flash.geom.Point;
    import flash.net.*;
    import flash.sampler.getMemberNames;
    import flash.utils.ByteArray;

    import org.as3commons.bytecode.swf.SWFFile;
    import org.as3commons.bytecode.swf.SWFFileIO;
    import org.as3commons.bytecode.tags.DoABCTag;

    public class AdvancedInfo extends Sprite{

        private var cursor:Point = new Point();
        private var methodInfo:Array;

        public function AdvancedInfo(){
            init();
        }
        private function init():void{
            var byteLoader:URLLoader = new URLLoader(new URLRequest('B.swf'));
            byteLoader.dataFormat = URLLoaderDataFormat.BINARY;
            byteLoader.addEventListener(Event.COMPLETE,bytesLoaded);
        }
        private function bytesLoaded(event:Event):void{
            var ba:ByteArray = event.target.data as ByteArray;//get swf bytes
            var swfFile:SWFFile = new SWFFileIO().read(ba);//read the bytes using as3-commons
            var abcTags:Array = swfFile.getTagsByType(DoABCTag);//get actionscript bytecode (ABC) tags
            for each(var tag:DoABCTag in abcTags) methodInfo = tag.abcFile.methodInfo;//loop though tags and get method information
            //display and rollOver
            var d:Loader = addChild(new Loader()) as Loader;
            d.loadBytes(ba);
            addEventListener(MouseEvent.ROLL_OVER, rolledOver,true,0,true);
        }
        private function getMethodDetails(methodName:String):String{
            var result:String = '';
            for(var i:int = 0 ; i < methodInfo.length; i++){
                if(methodInfo[i].methodName == methodName){
                    result += 'signature:\t'+methodInfo[i]+'\n';
                    result += 'body:\t'+methodInfo[i].methodBody;
                    return result;
                }
              }
            return result;
        }
        private function rolledOver(event:MouseEvent):void{
            cursor.x = mouseX;cursor.y = mouseY;
            var obj:Array = getObjectsUnderPoint(cursor);
            var numObj:int = obj.length; 
            for(var i:int = 0 ; i < numObj ; i++){
                if(obj[i].hasEventListener(MouseEvent.ROLL_OVER) || obj[i].hasEventListener(MouseEvent.MOUSE_OVER)){
                    var members:Object = getMemberNames(obj[i]);
                    for each (var name:QName in members){
                        if (name.localName == "listeners"){
                            for (var j : int = 0; j < obj[i][name].length; j++){
                                var func:Function = obj[i][name][j];
                                try{
                                    func.call();
                                }catch(error:Error){
                                    var methodName:String = error.message.split('on ')[1].split('.')[0].split('/')[1].split('()')[0]; 
                                    trace(getMethodDetails(methodName));
                                }
                            }
                        }
                    }
                }
            }
        }

    }
}

For documentation purposes here's the code for the SWF I loaded:

package {
    import flash.events.*;
    import flash.display.*;

    public class B extends Sprite {
        public function B() {
            addEventListener(Event.ADDED_TO_STAGE, init)
        }
        private function init(event:Event = null) : void {
            for (var i : int = 0; i < 1000 ; i++) {
                var b:Sprite = addChild(new Sprite()) as Sprite;
                b.graphics.lineStyle(Math.random()*3);
                b.graphics.drawCircle(-3, -3, 3);
                b.x = 3+Math.random() * stage.stageWidth - 6;
                b.y = 3+Math.random() * stage.stageHeight - 6;
                b.buttonMode = true;
                b.addEventListener(MouseEvent.ROLL_OVER, onRollOver);
            }
        }
        private function onRollOver(event : MouseEvent) : void {
            event.currentTarget.scaleX = event.currentTarget.scaleY = .1 + Math.random() * 2.1;
        }
    }
}

and here is a sample of the method detail trace using the getMethodDetails in my AdvancedInfo example:

signature:  private function QName[Namespace[private::B]:onRollOver](QName[Namespace[public::flash.events]:MouseEvent]) : QName[Namespace[public]:void]
body:   
    private function QName[Namespace[private::B]:onRollOver](QName[Namespace[public::flash.events]:MouseEvent]) : QName[Namespace[public]:void]
    {   
        //maxStack=5, localCount=3, initScopeDepth=9, maxScopeDepth=10
        0:debugfile     [/Users/george/Documents/Flex Builder 3/Del/src;;B.as]:2
        2:debugline     [28]:4
        4:getlocal_0        :5
        5:pushscope     :6
        6:debug     [1, 18, 0, 28]:11
        11:debugline        [29]:13
        13:getlocal_1       :14
        14:getproperty      [QName[Namespace[public]:currentTarget]]:16
        16:getlocal_1       :17
        17:getproperty      [QName[Namespace[public]:currentTarget]]:19
        19:pushdouble       [0.1]:21
        21:getlex       [QName[Namespace[public]:Math]]:23
        23:callproperty     [QName[Namespace[public]:random], 0]:26
        26:pushdouble       [2.1]:28
        28:multiply     :29
        29:add      :30
        30:dup      :31
        31:setlocal_2       :32
        32:setproperty      [Multiname[name=scaleY, nsset=[Namespace[private::B], Namespace[public], Namespace[private::B.as$25], Namespace[packageInternalNamespace], Namespace[namespace::http://adobe.com/AS3/2006/builtin], Namespace[protectedNamespace::B], Namespace[staticProtectedNamespace::B], Namespace[staticProtectedNamespace::flash.display:Sprite], Namespace[staticProtectedNamespace::flash.display:DisplayObjectContainer], Namespace[staticProtectedNamespace::flash.display:InteractiveObject], Namespace[staticProtectedNamespace::flash.display:DisplayObject], Namespace[staticProtectedNamespace::flash.events:EventDispatcher], Namespace[staticProtectedNamespace::Object]]]]:34
        34:getlocal_2       :35
        35:kill     [2]:37
        37:setproperty      [Multiname[name=scaleX, nsset=[Namespace[private::B], Namespace[public], Namespace[private::B.as$25], Namespace[packageInternalNamespace], Namespace[namespace::http://adobe.com/AS3/2006/builtin], Namespace[protectedNamespace::B], Namespace[staticProtectedNamespace::B], Namespace[staticProtectedNamespace::flash.display:Sprite], Namespace[staticProtectedNamespace::flash.display:DisplayObjectContainer], Namespace[staticProtectedNamespace::flash.display:InteractiveObject], Namespace[staticProtectedNamespace::flash.display:DisplayObject], Namespace[staticProtectedNamespace::flash.events:EventDispatcher], Namespace[staticProtectedNamespace::Object]]]]:39
        39:debugline        [30]:41
        41:returnvoid       :42
    }
traits=(no traits)

For more information on the AVM2 instructions visit the documentation or the SWF File format specs (PDF link).

Other options that involve 3rd party software which I haven't fully explored would be:

  1. Using FlashFirebug - Personally I haven't managed to get it running, maybe I didn't setup correctly.
  2. Use getObjectsUnderPoint to trace information on objects(instance name/etc.) with rollover/mouseover handlers and then use a commercial decompiler to look for the handler using the information gained.

HTH


Maybe it would help you to know the event flow in AS3: http://help.adobe.com/en_US/ActionScript/3.0_ProgrammingAS3/WS5b3ccc516d4fbf351e63e3d118a9b90204-7e4f.html

In short, there are two phases, Capturing and Bubbling. You are interested in the bubbling phase, when the event travels from the bottom to the top of the display stack. In this phase, various objects catch the event as it travels and execute various event listeners (the methods you are interested in). If you have access to the source code of these objects, you can insert a debug message in these methods.

I can't think of anything else.


How large is the source code? You may be able to get away with performing a project search for "addEventListener(MouseEvent". Put breakpoints on the function reference for every instance you can, and see which one is firing for the behaviour you're looking for. Probably a little more work than you're hoping for, but it's an idea.

Note, there are other ways that an application could trigger mouse-over-like behaviour. For example, MovieClip instances have a mouseX and mouseY property that someone could be monitoring on an Event.ENTER_FRAME listener to trigger a visual change as well. You may need to inspect for that behaviour as well.


  1. In your code, use trace("this was called") in the functions you want to monitor if called
  2. Install Flash debugger versions http://www.adobe.com/support/flashplayer/downloads.html
  3. You can monitor traces at runtime with http://code.google.com/p/flash-tracer/
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜