4

I have a bizarre situation in IE where JS can't call up into flash using ExternalInterface after I hit "refresh". I know the movie is getting loaded and the code that does the ExternalInterface.addCallback() appears to be completing without any error

Here's a rundown of the steps to reproduce:

  1. Open IE and load up the movie for the first time, the ExternalInterface callback methods are available to JavaScript.
  2. If I hit refresh, the callback methods aren't available and I get the error Object doesn't support this property or method.
  3. If I clear my cache and refresh the page, they are available again.
  4. If I then hit refresh again without clearing my cache, they're unavailable.
  5. If I close the browser and reopen, they're available again.

I've run into this situation before and I'm pretty sure that the extra delay required to download and instantiate the swf is what's allowing ExternalInterface to get set up properly. The way I worked around this before was to add a random number to the end of the swf's url, so that it's never used from cache, but that's not a real solution.

Does anyone know how to solve this?

edit:

I should have mentioned as well that after refreshing, 'ExternalInterface.available' is 'true', but 'ExternalInterface.objectId' is 'null'.

I've tried randomizing the value of the object id and embed name and the id of the container div and in every case, ExternalInterface.objectId remains null.


More info:

I can't see how the way I'm inserting the movie would make a difference, but I thought I would include the code just to be sure. My movie is not affected by the "click to activate" issue and I don't want to use SWFObject in this case since the flash movie is a fallback in case HTML5 audio is not available.

var docContainer = document.createElement('div');
docContainer.innerHTML = '<object '
        + 'classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" '
        + 'codebase="http://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=10,0,0,0" '
        + 'id="mp3player" '
        + 'width="300" '
        + 'height="500">'
    + '<param name="allowScriptAccess" value="always" />'
    + '<param name="movie" value="mp3player.swf" />'
    + '<param name="quality" value="high" />'
    + '<param name="bgcolor" value="#ffffff" />'
    + '<embed ' 
        +'src="mp3player.swf" '
        + 'quality="high" '
        + 'bgcolor="#ffffff" '
        + 'width="300" '
        + 'height="500" '
        + 'swLiveConnect="true" '
        + 'name="mp3player" '
        + 'id="mp3player" '
        + 'allowScriptAccess="always" '
        + 'type="application/x-shockwave-flash" '
        + 'pluginspage="http://www.adobe.com/go/getflashplayer" />'
    + '</object>';
document.body.appendChild(docContainer);
Andrew
  • 14,204
  • 15
  • 60
  • 104
  • When you are calling JS methods SWF, i mean to say on which event? – Imran May 26 '11 at 06:38
  • @Andrew: Did you find a solution for this bug? Can it be solved without using SwfObject? – J. Volkya Aug 31 '11 at 01:24
  • No. I never did. Given my original observations and @Paul's comment in his answer, I think the best thing to do is try to load the content after the page's `onload` event has fired (which is what swfobject does). In my case, I simply randomized a querystring variable in the SWF name for IE users and the bug is no longer a factor, though that goes against sane caching strategies. Both solutions are less than optimal. – Andrew Aug 31 '11 at 06:46

3 Answers3

6

In case anyone is wondering WHY this happens, at least for Internet Explorer it seems that the Flash player is loaded as an ActiveX control, which is completely seperate from the DOM and JavaScript modules. When the .swf is cached, it seems that the ActiveX control can load and run it before Javascript is ready to accept events from it.

This means that when Flash tries to use the ExternalInterface to add the callbacks, it will fail because the JavaScript and the DOM have not been loaded.

I fixed the problem by waiting for the first ENTER_FRAME event, and then registering my callbacks there. Like this:

protected function registerExternalCallbacks(event:Event):void {

    removeEventListener(Event.ENTER_FRAME, registerExternalCallbacks);

    if (ExternalInterface.available) {
        ExternalInterface.addCallback("flash_play", play);
        ExternalInterface.addCallback("flash_setVolume", setVolume);

        ExternalInterface.call("player_ready");
    }
}

// and then when the .swf loads, register the function on the event:
addEventListener(Event.ENTER_FRAME, registerExternalCallbacks);

This will make the player wait until the callbacks can be added reliably, and then calls a javascript function called player_ready to signal that it is ready to be used.

AHM
  • 5,145
  • 34
  • 37
  • Major thanks for adding your answer. I've not tried this, but your explanation and solution make perfect sense. It's bothered me since asking this question that I had to bypass the cache and if I ever get the chance to revisit the code, I'll use this solution. – Andrew Nov 12 '12 at 13:21
6

The issue is that the ExternalInterface class will stop working once the swf file is cached in your browser.

To overcome this obstacle, you have to modify your html file and your swf files in the following ways: Note: This solution was tested in IE7

The HTML file - Add parameters to your swf file to make it unique. So the browser will think you need to download a new swf file everytime the page loads.

For example, if you're using the SWFObject library (which you don't have to), then you would make the following adjustments:

var cachecontrol = Math.floor(Math.random()*99999);
var flashvars = {};
var params = {};
var attributes = {id:"movie"};
swfobject.embedSWF("movie.swf?nocache"+cachecontrol, "flashcontent", "100%", "100%", "10.1.0", "expressInstall.swf", flashvars, params, attributes);

As well, add the following meta tag to ensure your html file is not cached:

The SWF file(s) - If you're loading a swf file from a master swf, then you would have to apply the same logic: var cachecontrol:String = Math.floor(Math.random()*9999).toString(); loader.load(new URLRequest("movie.swf?nocache="+cachecontrol));

Here is the source of the solution: http://www.emanuelz.com.mx/blog/cache-control-tips-for-flash-120 Note: You don't have to do anymore than what is described above.

Momo
  • 59
  • 1
  • 2
  • Thanks for this. That's in fact what I ended up doing. It'a a pest because we serve all SWF files off a CDN and this technique prevents them from caching the file for us. I've had to do it for IE only. Drives me crazy. – Andrew Feb 24 '12 at 04:51
  • this worked for me when everything else failed. was having the same exact problem, even in ie8 and ie9. darn ie. – Skwerl Jun 28 '12 at 21:10
2

There's a bunch of IE bugs w/ ExternalInterface (and flash in general, ime) along different versions. Most recommended fixes suggest using the SwfObject js library, which fixes all the ones I know about. http://www.timelesssky.com/blog/internet-explorer-flash-8-externalinterface-bug

Paul
  • 35,689
  • 11
  • 93
  • 122
  • 1
    Does anyone have any idea what SwfObject does differently that allows it to overcome this problem? To me, personally, it seems a shame to add a library to a page that doesn't always need it. – Andrew May 26 '11 at 04:37
  • 1
    It's been a *long* time since I was doing a project that needed that stuff, but if memory serves it has something to do w/ how/when they load the object/embed tags. iirc, it was something to do with how IE was caching the plugin content. – Paul May 26 '11 at 04:40
  • I should have mentioned that adding SwfObject does require a good deal of refactoring of my code. SwfObject embeds the code after onload and the only way to know if the page is ready or not is to hang everything on its callback. This is inconsistent with the way the HTML5 portion of my code works and makes things a lot messier than I like. I'll look at SwfObject and try to figure out how it get around this (assuming it actually does in this case). – Andrew May 26 '11 at 04:43
  • Understood; I think, though, that it specifically hooks into the lifecycle that way for the reason we're discussing. – Paul May 26 '11 at 04:44