Functions registered with ExternalInterface.addCallback not available in Javascript

13,773

Solution 1

Calls made to JS via ExternalInterface are wrapped within a try { } block and that causes subsequent JS errors to get suppressed.

A workaround for the same is to cause a function closure in JavaScript and execute the actual code after a timeout.

Example:

function myFnCalledByEI (arg1, arg2) {

    setTimeout(myActualFunction () {
       // You can use arg1 and arg2 here as well!
       // Errors raised within this function will not be
       // suppressed.
    }, 0);

};

Solution 2

This might be similar to the issue you are experiencing.

http://www.google.com/support/forum/p/translator-toolkit-api/thread?tid=58cda1b34ae1e944&hl=en

Solution 3

Here was our scenario once we narrowed down all the conditions:

  • Only on FireFox/Windows
  • Only when wmode=transparent
  • Only when using the js alert() function

In this specific scenario, ExternalInterface.call() would not fire right away. It only worked after creating a tiny delay with Timer class.

If we made wmode=window, or removed the alert() - everything worked. Try using console.log() to display debug text in firebug.

The other gotcha? Whether your js function returns an array or object vs a string. Surprisingly returning the native js array was interpreted by an array in Flash. Try outputting info about your return data like this:

var myRetVal = flash.external.ExternalInterface.call("my_js_func");
debug_txt.text = flash.utils.describeType(myRetVal).toString();
Share:
13,773
Selene
Author by

Selene

Updated on July 20, 2022

Comments

  • Selene
    Selene almost 2 years

    I'm working on a Flash game that needs to call some Javascript on the page and get data back from it. Calling Javascript from Flash works. Calling the Flash functions from Javascript (often) doesn't.

    I'm using the Gaia framework.

    What happens:

    1. The swf is loaded in with SWFObject
    2. There's a button in the Flash file. On click, it uses ExternalInterface.call() to call a Javascript function. This works.
    3. The Javascript function calls a Flash function that was exposed with ExternalInterface.addCallback().
    4. Sometimes, the Javascript produces the following error: TypeError: myFlash.testCallback is not a function.
    5. When the error happens, it affects all functions registered with addCallback(). Gaia and some of its included libraries use addCallback(), and calling those functions from Javascript also produces the TypeError.
    6. Waiting a long time before pressing the button in Flash doesn't solve the error.
    7. Having Flash re-try addCallback() periodically doesn't solve the error
    8. When the error occurs, ExternalInterface.available = true and ExternalInterface.objectID contains the correct name for the Flash embed object.
    9. When the error occurs, document.getElementById('myflashcontent') correctly returns the Flash embed object.

    Edited to add:

    • This issue shows up in Firefox 3.6, but not Chrome or IE8. I haven't tried older browsers.
    • I'm running the Debug version of the Flash player.
    • My calls to ExternalInterface.addCallback() are wrapped in a try...catch block. When the JS error occurs, the catch block is not triggered. It's a silent failure.
    • The error occurs when testing on a webhost, with the swf loaded from the same server as the page it's on.
    • I set allowScriptAccess = always.
    • Setting flash.system.Security.allowDomain("mydomain") doesn't fix the error.

    From my Page class:

    public class MyPage extends AbstractPage
    {
        // declarations of stage instances and class variables
    
        // other functions
    
        override public function transitionIn():void 
        {
            send_button.addEventListener(MouseEvent.MOUSE_UP, callJS);
            exposeCallbacks();
    
            super.transitionIn();
        }
    
        private function exposeCallbacks():void
        {
            trace("exposeCallbacks()");
            if (ExternalInterface.available) {
                trace("ExternalInterface.objectID: " + ExternalInterface.objectID);
                try {
                    ExternalInterface.addCallback("testCallback", simpleTestCallback);
                    trace("called ExternalInterface.addCallback");
                }
                catch (error:SecurityError) {
                    trace("A SecurityError occurred: " + error.message + "\n");
                }
                catch (error:Error) {
                    trace("An Error occurred: " + error.message + "\n");
                }
            }
            else {
                trace("exposeCallbacks() - ExternalInterface not available");
            }
        }
    
        private function simpleTestCallback(str:String):void
        {
            trace("simpleTestCallback(str=\"" + str + "\")");
        }
    
        private function callJS(e:Event):void
        {
            if (ExternalInterface.available) {
                ExternalInterface.call("sendTest", "name", "url");
            }
            else {
                trace("callJS() - ExternalInterface not available");
            }
        }
    
    }
    

    My Javascript:

    function sendTest(text, url) {
        var myFlash = document.getElementById("myflashcontent");
    
        var callbackStatus = "";
        callbackStatus += '\nmyFlash[testCallback]: ' + myFlash['testCallback'];
        //console.log(callbackStatus);
    
        var errors = false;
        try {
            myFlash.testCallback("test string");
        }
        catch (err) {
            alert("Error: " + err.toString());
            error = true;
        }
    
        if (!error) {
            alert("Success");
        }
    }
    
    var params = {
        quality: "high",
        scale: "noscale",
        wmode: "transparent",
        allowscriptaccess: "always",
        bgcolor: "#000000"
    };
    var flashVars = {
        siteXML: "xml/site.xml"
    };
    var attributes = {
        id: "myflashcontent",
        name: "myflashcontent"
    };
    
    // load the flash movie.
    swfobject.embedSWF("http://myurl.com/main.swf?v2", "myflashcontent",
    "728", "676", "10.0.0", serverRoot + "expressInstall.swf",
    flashVars, params, attributes, function(returnObj) {
        console.log('Returned ' + returnObj.success);
        if (returnObj.success) { returnObj.ref.focus(); }
    });
    
  • Selene
    Selene about 14 years
    If you look at my code, you'll see the addCallback() calls are wrapped in a try..catch block. The catch block never triggers. I'm encountering the error in Firefox 3.6, so it's not an IE6/7 issue.
  • Selene
    Selene almost 14 years
    I can call JS via ExternalInterface fine. It's the other direction that's giving me problems.
  • Shamasis Bhattacharya
    Shamasis Bhattacharya almost 13 years
    I guess then, I am unable to provide any inputs on this as of now. If I come across something while your question is still unanswered, I will then surely post my thoughts. :)