All right. As mentioned before, here it is: I found a bug in mx.managers.DepthManager in Flash 8, though I’m sure it affects all ActionScript 2.0 versions of the DepthManager to date. The bug involves a lack of error handling when DepthManager encounters an object without a depth, or rather, without a getDepth() method. It shows up through any use of the DepthManager’s methods, the PopUpManager (mx.managers.PopUpManager), the Alert class (mx.controls.Alert), etc., when an object on the stage does not have a getDepth function.
What Happens?
The issue manifests itself in many variations, usually by objects placed with the DepthManager inexplicably replacing previous objects placed with the DepthManager. Most notably, it manifests itself as modal windows (via PopUpManager) and Alerts not rendering to the screen, even though the modality seems to have worked and all other objects on the screen no longer can receive input.
Why does this happen?
The DepthManager works off a rather inefficient array to maintain its records. An object within the DepthManager, the DepthTable, is an indexed array containing record of every object on the stage and its depth. The DepthManager stores a reference to the object within the array at the position corresponding to the depth of the object. If the stage has one object at a depth of 32,000, depthTable[32000] = myObject, and we have a 32,000-length array with one object stored in it [an inefficiency]. Values in the manager’s depth table are based on available positions in the array, such as kTopmost being greater than the last movieclip or object in the array [it iterates one-by-one through each position in our 32000-length array; another inefficiency].
Excerpt from mx.managers.DepthManager.as var t:String = typeof(i); if (t == “movieclip” || (t == “object” && i.__getTextFormat != undefined)) if (i._parent == this) { depthTable[i.getDepth()] = i; }
However, the DepthManager code is assuming that the object has a getDepth function. If the object does not have the function, references to that function return undefined, setting depthTable[undefined] = i, which blows chunks all over the place, and any reference to any of DepthManager’s depth-returning methods will always return zero.
That zero is why nothing works anymore; the Alert class creates the new Window-class-like object, then creates a transparent movieclip below that object to trap all inputs, creating a sense of modality. The Alert class uses the DepthManager to create itself at kTopmost, which DepthManager says is zero. The Alert class then uses the DepthManager to create the modality movieclip above itself, kTopmost, which it then hopes to swapDepth with to get itself back above the modality clip. However, the Alert class, with itself at depth zero, creates the modality clip at depth zero because DepthManager said it was the kTopmost depth. Creating an object at depth zero toasts any object already at depth zero, thus the modality clip blows away the Alert clip and we get a SWF that no longer responds to anything, and no Alert ‘OK’ button to restore peace in the world. Likewise happens when a modal window is created through the PopUpManager.
So what can I do about it?
How do you get around this? Simple. Don’t put anything on the stage that extends from Object. If you put it on the stage, extend it from MovieClip. Even though the DepthManager code (above) explicitly calls getDepth on MovieClip or Object, it does not check to see if the object in question actually has a getDepth method. Object does not have getDepth by default, but MovieClip does. I suppose you could write your own getDepth function for your Object-inheriting class, or you could rewrite the DepthManager (which would mean also rewriting PopUpManager, Alert, ComboBox, and a
half-dozen other controls), but it is much simpler to extend from MovieClip.
Why it happened to me
I have a movie that is 170KB. There’s a lot of crazy mojo in there, and I want my prel0ader to load within the first 1KB, not after 170KB. So, I created my preloader, removed “Export in First Frame” from everything, and set my classes to export in frame 5 (after the end of my preloader loop). However, this made a few of my Object-extending components no longer work anymore since they were not referenced in the “Assets” layer of any of the other components (no reference=no load, much like the DataBindingClasses), to I had to drag them to the stage to ensure that they loaded.
*Poof*
My world exploded.
I lost two days of work tracking this little punk down. And you already know the rest of the story.
I’ve been working on creating a Flash component for within our LMS that allows a student to view their completion statuses in a curriculum, as
well as allows managers to view the curriculum status of all of their students. The component relies on Web Services to volley data with the server. I was having some trouble binding data from my WebServiceConnector to my DataSets because, as it turned out, Flash was holding on to a cached version of the WSDL–a cached version that was ultimately obsolete.
I was on the hunt to find the source, and kill the evil cache file. After clearing out every Temp folder I could think of, I started playing around in my Local Settings folder and it turns out that Flash will forever-store cached copies of your WSDL deep within your ‘Documents and settings’.
C:\Documents and Settings\[user name]\Local Settings\Application Data\Macromedia\[your flash version]\en\Configuration\WebServices\
I just booted every file in the WebServices directory, but I am assuming that you could get away with just deleting ‘WSDLCacheMap.xml’, or if you want to keep the remainder of your cache, just delete the appropriate WSDL cache file (ex: WSDLkgzmcu.wsdl). In any event, once you have exercised your delete button, simply restart Flash and reload the WSC instance, and you will be good to go.
This just toasted two hours of my morning, and ended with a “Huh. I never would have thought of that!” moment. And whenever I have one of those, I like to toss the problem and the solution up here in the hopes of saving two hours from someone else’s morning, someday.
Problem
We have a Scorm course. The course is coded in Flash and heavily relies on FLVs. For one of our client’s sites, we just rebuilt the Staging environment, upgrading to Windows 2003. Ever since the rebuild, this course in question hasn’t worked in the Staging environment. It works fine in Production, but not in Staging. It’s the same code, but it works in once place and not in the other. I hate it when that happens.
Turns out that we didn’t have and needed a MIME for FLVs.
Solution
Crack open INetMgr. (These directions are for IIS6)
- Right-click the server, and hit ‘Properties’
- In the Properties window, click the “MIME Types…” button
- In the MIME Types window, click “New…”
- Extension: .flv
- MIME Type: video/x-flv
- OK, OK, OK, you’re done
Thanks to Don DiCicco for finding the solution to this one. He googled up a link to a similar post on this same problem / solution. And thanks to good ol’ JT for posting the original solution, whoever you are.
“He’s a big pig. You can be a big pig, too.” ~Timon
Turns out that Flash isn’t the murderous killer with the 9-inch chef’s knife. It’s just a big pig. Flash, within this Scorm course, was passing Scorm data to an external JavaScript function. JS was opening an ActiveX XmlHttpRequest object to send the Scorm data to the service via a WebService call (think AJAX). However, Flash was requiring that this be a synchronous call (so much for AJAX). W3C regulates [Hypertext Transfer Protocol — HTTP/1.1 RFC 2616 Section 8.1.4] that “a single-user client should not maintain more than 2 connections with any server or proxy.” Flash is busy downloading external assets, so both connections were taken, and it then says “go and make a synchronous XML call.” XmlHttpRequest object doesn’t like making a synchronous call when there are no available connections to the server; it bugs out and the browser freezes.
Hopefully things will be better with Internet Explorer 7. It is slated to no longer use the ActiveX version of XmlHttpRequest object, but rather the XmlRequest object that Mozilla uses. Perhaps this one will handle itself a little better.
Flash movies with multiple external assets can cause the browser to lock up if external Flash assets (videos, images, voice-overs, etc, that are not stored within the SWF) are unmanaged or managed improperly. I’ll use a Scorm-compliant course as an example; the example course navigates from screen using inline Back and Next buttons, and each screen loads unique external animation and voice-over to deliver the lesson topic.
The browser freezes when:
- The user skips lesson information by clicking “Next” in rapid succession (assets are not downloaded to local cache before progressing to next screen)
- The user re-enters a course for which they have a previously saved Scorm-bookmark, and quickly navigate to where they left off, either by menu navigation or by rapidly clicking “Next” (assets are not downloaded to local cache before progressing to next screen)
- The user rapidly clicks “Back” through screens that they did not experience in their entirety (assets were not fully downloaded and stored in local cache)
- The user navigates to other areas of the course through menu navigation before they have experienced the current screen in its entirety (assets are not downloaded to local cache before proceeding)
The recurrence is exacerbated by:
- Slower connection speeds (it takes longer to download a single asset)
- Larger file sizes on external Flash assets (it takes longer to download a single asset)
- Having multiple external Flash assets on a single screen (it takes longer to download a single asset)
The course does not freeze when:
- The user linearly navigates through the course, experiencing each screen in its entirety before progressing to the next screen or area. (assets are allowed to fully download and store in local cache before proceeding to next screen)
- The user skips lesson information by clicking “Next” in rapid succession through courses that they have previously visited in this session and experienced in their entirety (assets are already in local cache)
- The user rapidly clicks “Back” through screens that they did experience in their entirety in this session (assets are already in local cache)
- The user navigates to other areas of the course through menu navigation after they have experienced the current screen in its entirety (assets are allowed to fully download and store in local cache before proceeding to next screen)
When a user navigates from Screen #1 to Screen #2, any outstanding, incomplete asset downloads from Screen #1 should be stopped before beginning Screen #2 asset downloads. Failure to do so may cause the browser’s request queue to overflow, and freeze the browser window.
Such is the case when assets are not properly managed: when a user navigates from Screen #1 to Screen #2, the Screen #1 assets continue to download with the #2 assets. Likewise, when the user navigates from Screen #2 to Screen #3, assets from Screens #1, #2, and #3 are all downloading. When Flash is downloading enough concurrent assets, a request queue overflows, freezing the browser.
IMPORTANT: Users clicking Next rapidly is not the only cause of this issue. Rapid Next is just the easiest way to recreate it. The important point is that users are clicking next faster than Flash can download the other assets. In other words, navigation is adding assets to the download queue faster than Flash can download them, and the queue is filling up.
A sample browser-freezing scenario:
(Ex: Unmanaged external assets. User rapidly clicks next.)
- User launches course. Flash switches to Screen #1 and begins downloading Screen #1 voice-over and Screen #1 animation. If applicable: HTML, Non-flash images, stylesheets, etc all download.
(2 assets downloading)
- User quickly hits Next. Flash switches to Screen #2. Does not sever Screen #1 asset connections. Begins downloading Screen #2 VO and Animation. If applicable: HTML, Non-flash images, stylesheets, etc all download, but do so slower.
(4 assets downloading)
- User quickly hits Next. Flash switches to Screen #3. Does not sever Screen #2 asset connections. Begins downloading Screen #3 VO and Animation. If applicable: HTML, Non-flash images, stylesheets, etc all download, but do so slower.
(6 assets downloading)
- …
- User repeatedly hits Next. Flash navigates through appropriate screens, downloading new assets but never severing previous asset connections.
(Many assets downloading)
- Browser request queue overflow.
- If applicable: HTML, non-flash images, stylesheets, etc, DO NOT download.
- Browser freezes.
Under a non-freezing scenario:
(Ex: Managed external assets and/or User proceeds slowly through course)
- User launches course. Flash switches to Screen #1 and begins downloading Screen #1 voice-over and Screen #1 animation. If applicable: HTML, Non-flash images, stylesheets, etc all download with no degradation.
(2 assets downloading)
- User hits Next. Flash switches to Screen #2. Severs Screen #1 asset connections. Begins downloading Screen #2 VO and Animation. If applicable: HTML, Non-flash images, stylesheets, etc all download with no degradation.
(2 assets downloading)
- User hits Next. Flash switches to Screen #3. Severs Screen #2 asset connections. Begins downloading Screen #3 VO and Animation. If applicable: HTML, Non-flash images, stylesheets, etc all download with no degradation.
(2 assets downloading)
- …
- User hits Next. Flash navigates through appropriate screens, downloading new assets. Previous asset connections are severed.
(2 assets downloading)
- No Browser request queue overflow occurs
Example Code that causes the problem:
narrator = createClassObject(MediaDisplay, “My_VoiceOver”); narrator.setMedia(”/Assets/MyVoiceOver.mp3″, “MP3″);
// … code omitted …
// … do some stuff …
// … do some stuff …
// … code omitted …
// commented out code
// destroyObject(”My_VoiceOver”);
The code above creates a new object that loads and plays an MP3. However, (because the line of code is commented out) the object is never destroyed and continues to download. If this function is called repeatedly, each time against a different voice-over file, the queue will overflow and the browser will freeze. The same will occur wherever destroyObject (or similar command) is not called.
To fix the code in the above scenario, destroyObject must be uncommented, allowing the object to be destroyed, thus stopping the MP3 download.
Note: This is not the only code scenario that would create this browser-freezing problem. There are many possible commands and variations.
|