Welcome

This website is the byproduct of my continuing experiment with hypertext. It's ultimate aim is to be the online equivalent of a crumpled up notebook. Make of it what you will.

Random quote1:

"We can forgive a man for making a useful thing, as long as he
does not admire it. The only excuse for making a useless thing is
that one admires it immensely. All art is useless."

- Oscar Wilde

Categories:

FlexCPPBridge(1) Flex(5) AIR(7) FrndNet(2) SQL(1) CGI(1) SSH(1) Java(1) Win32(1) Latex(2) Emacs(5) Javascript(2) Notes(3) Gnus(1) CSS(2) Rant(5) C++(2) Linux(6) EmacsWiki(4) Web(4) C(5) Programming(11) General(5)

Latest entries:

  1. June 20:Extending the Flex HTTP Service to Support Binary Data
  2. June 17:Multiple Concurrent Uploads in Flex
  3. April 10:Custom Date Chooser Component for Range Selection
  4. March 26:Emacs and the Flex Compiler Shell
  5. March 13:Flex C++ Bridge
  6. February 15:Printing from an HTML AIR Application
  7. February 04:Loading Encrypted SWFs via loadBytes
  8. January 25:Websites as Cards on Your Desktop using AIR
  9. January 22:Simple Javascript Error Console for HTML AIR Applications
  10. January 16:Mutexes in Actionscript and Javascript

Total Posts: 77

anirudhs.chaosnet2.org uses valid XHTML3, valid CSS and valid RSS.


[1] Chosen at publish time from QuotePage.
[2] CHAOSnet is a networking protocol for LISPMs, ITS and others.
[3] Though the pages are served as text/html.

Notes

Extending the Flex HTTP Service to Support Binary Data

June 20, 2008 3:09 PM
HTTPService has lots of functionality that is indispensable and fits in along with other classes of the mx.rpc package. It handles concurrency and can decode responses into object, xml, e4x, array, etc. But when it comes to data in binary format, people tend to use a URLLoader instead of trying to get HTTPService to understand binary data. All you need to do is specify a custom channel which in turn uses your URLLoader and override the processResult function of HTTPService to handle binary data. In this article, we'll extend HTTPService to support GZIP encoded responses (which currently is missing from AIR1) in addition to decoding to the appropriate resultFormat.

First, we need to extend DirectHTTPChannel2 so that it sets URLLoader's dataFormat to binary:

/* Snippet from DirectHTTPBinaryChannel.as */
override protected function internalSend(msgResp:MessageResponder):void
{
    var httpMsgResp:DirectHTTPBinaryMessageResponder = DirectHTTPBinaryMessageResponder(msgResp);
    var urlRequest:URLRequest;
    
    try
    {
        urlRequest = createURLRequest(httpMsgResp.message);
    }
    catch(e: MessageSerializationError)
    {
        httpMsgResp.agent.fault(e.fault, httpMsgResp.message);
        return;
    }
    
    var urlLoader:URLLoader = httpMsgResp.urlLoader;
    /* yes, binary, finally */
    urlLoader.dataFormat = URLLoaderDataFormat.BINARY;
    urlLoader.addEventListener(ErrorEvent.ERROR, httpMsgResp.errorHandler);
    urlLoader.addEventListener(IOErrorEvent.IO_ERROR, httpMsgResp.errorHandler);
    urlLoader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, httpMsgResp.securityErrorHandler);
    urlLoader.addEventListener(Event.COMPLETE, httpMsgResp.completeHandler);
    urlLoader.load(urlRequest);
}

We would also have to write DirectHTTPBinaryMessageResponder even though it is the same as mx.messaging.channels.DirectHTTPMessageResponder because DirectHTTPMessageResponder is an internal class.

Now we can extend HTTPService, override the send() method to force the use of our channel and override the processResult() function to decide the fate of our ByteArray result:

/* Snippet from GzipHTTPService.as */
override public function send(parameters:Object = null):AsyncToken
{
    if ( useProxy == false )
    {
        /* force the use of our binary channel */
        if ( binaryChannelSet == null )
        {
            var dcs:ChannelSet = new ChannelSet();
            binaryChannel = new DirectHTTPBinaryChannel("direct_http_binary_channel");
            dcs.addChannel(binaryChannel);            
            channelSet = dcs
            binaryChannelSet = dcs;
        }
        else if ( channelSet != binaryChannelSet )
        {
            channelSet = binaryChannelSet;
        }       			
    }    		
    return super.send(parameters);	
}

override mx_internal function processResult(message:IMessage, 
                                            token:AsyncToken):Boolean
{
    var body:Object = message.body;
    
    if (body == null )
    {
        _result = null;
        return true;
    }
    else if ( body is ByteArray )
    {
        var barr:ByteArray = body as ByteArray;
        /* decode the gzip encoded result */
        var encoder:GZIPEncoder = new GZIPEncoder();				
        message.body = encoder.uncompressToByteArray(barr).toString();
        /* pass it on to HTTPService */
        return super.processResult(message, token);				
    }
    return false;
}

In this particular case, we pass the binary data to Paul Robertson's excellent GZIPEncoder3 and decode it to obtain the actual result and then pass it on to HTTPService's processResult so that the final result object is in the resultFormat you would expect.

The following MXML file illustrates the use of our new HTTPService:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" 
		layout="vertical" 
		xmlns:service="com.anirudh.rpc.*"
		creationComplete="service.send()">
<mx:Script>
	<![CDATA[
import mx.rpc.events.ResultEvent;
private function handleResult(event:ResultEvent):void
{
    trace(event.result);
}
	]]>
</mx:Script>
<service:GzipHTTPService id="service" url="test.dat" 
			 resultFormat="e4x" 
			 result="handleResult(event)" />
</mx:Application>

If you set a breakpoint in handleResult(), you would be able to confirm that the result is in E4X format even though our source file test.dat is an XML file compressed with GZIP.

The full code is available for download and is licensed under the MPL 1.1.

CategoryFlex Comment(s)


[1] More info on GZIP encoded HTTP responses in AIR.
[2] Wondered where the URLLoader was while looking at code of HTTPService? Its in DirectHTTPChannel.
[3] GZIPEncoder only works in AIR.


Multiple Concurrent Uploads in Flex

June 17, 2008 2:32 PM
Modern browsers impose limits on the number of connections opened to a particular host. This affects ajax calls and more interestingly, file uploads in flash / flex: Multiple uploads get queued up based on these connection limits.

File upload is easily done in Flash / Flex via FileReference. Multiple file upload means multiple calls to upload() for each FileReference object. Each upload() call will result in a separate HTTP request on the server side. These uploads will happen in parallel till you hit the connection number limit. In Firefox 2 and IE 7, upto two connections can be opened to each host. This means that if you call upload() on 4 files, though you will get the OPEN event1 for all 4 files, upload of the third and fourth will get queued till one of the earlier uploads finish. An interesting fact to note that the delay introduced here is not part of the normal browser request timeout calculation.

However, this impacts any timeout calculation you may attempt to perform on the flex side based on the OPEN event. The only way to confirm that the upload has actually begun is when you get the first PROGRESS event.

A possible way to get around this connection limit is by using CNAMES.

flex888 has a useful resource on Flex file uploads.

CategoryFlex Comment(s)


[1] Documentation for FileReference says that the OPEN event is dispatched when upload of a file has started. The more accurate statement would be that OPEN event is dispatched when the upload request is passed on to the browser. The browser may choose to internally queue this request.


Custom Date Chooser Component for Range Selection

April 10, 2008 2:21 PM
The DateChooser component in Flex supports selecting a range of dates if you set allowMultipleSelection to true. The way this works right now is: click on a starting date, hold down the Shift key and then click on the ending date. I've written a custom component by extending DateChooser that allows you to select a range of dates by holding the mouse button down on the starting date and moving the mouse to the ending date.

Demo

Click on the image below for a demo (view source enabled) of the DateIntervalChooser component:

DateIntervalChooser Component Demo.

As explained in the demo, the component supports range selection spanning multiple months by advancing the month when mouse moves out of the component while the mouse button is held down and the last date in the month is selected.

Getting the Component

The demo is view source enabled, that is, you can right click anywhere on the demo and choose view source to see and download the code.

The SWC version of the component is also available for download.

Using the Component

DateIntervalChooser works exactly like DateChooser except for the fact that you can't change the allowMultipleSelection property to false.

Sample MXML file using the component:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" xmlns:local="*">
  <mx:Script>
    <![CDATA[
private function updateDate(event:Event):void
{
    if ( dateChooser.selectedRanges && dateChooser.selectedRanges.length > 0 )
    {
        var obj:Object = dateChooser.selectedRanges[0];
        startDate.text = obj.rangeStart.toDateString();
        endDate.text = obj.rangeEnd.toDateString();
    }
}
	]]>
  </mx:Script>
  <mx:Panel layout="vertical" title="DateIntervalChooser Component Demo">  
    <mx:Text width="110" id="startDate" text="" />
    <mx:Text width="110" id="endDate" text="" />
    <local:DatePeriodChooser id="dateChooser" change="updateDate(event)" />
  </mx:Panel>
</mx:Application>

Why?

I'm not sure how useful people might find the component as such, but this component is proof to how easy it is to customize components in Flex.

CategoryFlex Comment(s)


Emacs and the Flex Compiler Shell

March 26, 2008 3:12 PM
GnuEmacs is my main IDE and even though Flex Builder 3 has features1 to kill for, M-x compile has to work flawlessly for me. But the problem with Emacs's M-x compile is that it starts a process each time. This can slow down things when you're working with a really large flex project and mxmlc has to load on each compile. I'm impressed with the speed and awesomeness of the Flex Compiler Shell, so I've hacked M-x compile to re-use an existing instance of the Flex Compiler Shell on each recompile. Also, I got compilation-mode to recognize errors spit out by mxmlc and fcsh so that C-x ` takes you to the correct line of the file where the error / warning was found.

Installation is pretty simple, just grab ani-fcsh.el off the Code Archives page, customize some variables like the path to fcsh and output path to suit your environment and load ani-fcsh.el when emacs starts up by putting this in your .emacs:

(load "pathtofile/ani-fcsh.el")

To use ani-fcsh, run M-x fcsh-compile when you are visiting the buffer of the MXML file you want to compile. This will start off fcsh in the compilation buffer, run mxmlc buffername -o configuredoutputpath and display results. Next time when you run M-x recompile or M-x compile, it will clear the buffer and send compile 1 to the fcsh process in the compilation buffer without killing and starting a new fcsh instance.

To restore the normal operation of M-x compile, run M-x fcsh-restore-compile.

Use this along with actionscript-mode for smoother flex development on Emacs.

If you only want M-x compile to recognize errors and warnings while using mxmlc, then put the following snippet in your .emacs:

(require 'compile)
;; To let compile understad flex error / warning messages. This regexp
;; has been tested only on win.
(add-to-list 'compilation-error-regexp-alist 'flex)
(add-to-list 'compilation-error-regexp-alist-alist
	     '(flex "^\\(.*\\)(\\(.*\\)):.* \\(Error\\|Warnin\\(g\\)\\): .*$" 1 2 nil (4)))

CategoryEmacs Comment(s)


[1] Profiling and debugging rocks with Flex Builder 3!


Flex C++ Bridge

March 13, 2008 6:20 PM
Ely Greenfield's Flex Ajax Bridge is a beautiful piece of work. It impressed me so much that I translated the javascript part of it to C++. Just like FABridge for javascript, the Flex C++ bridge allows you to do most of the things you can do with actionscript via C++. Of course, the syntax is not as pretty as it would be in javascript but it does let you develop C++ applications with a Flex UI.

What?

Nothing explains it like code. Take a look at the following C++ snippet1:

//addeventlistener to call a cpp function
oRootObj.Call("getbutton1").Call("addEventListener", "click", SampleCallback); 

//where SampleCallback is:
void SampleCallback(CASObject& obj, CFlexBridge* pBridge)
{
    CASObject oRootObj;
    pBridge->Root(oRootObj);
    oRootObj.Call("getpanel1").Call("settitle", "Title from CPP"); 
} 

//c++ way of saying Alert.show("StaticClassCalled")
pBridge->ClassRef("mx.controls.Alert").Call("show", "StaticClassCalled");

//create a datagrid and add it to the flex app
CASObject oDGrid = pBridge->Create("mx.controls.DataGrid");
oRootObj.Call("addChild", oDGrid);

Flex C++ Bridge is a C++ library that lets you communicate with Flex in a manner more suited for the normal C++ programmer, i.e, you can communicate to flex from c++ by writing code like shown above.

Once you put the Flex Ajax bridge into a Flex application, it is exposed to scripting in the browser. You can use a slightly modified version of the same FABridge.as2 (or even the same one) on the actionscript side and the flex application is exposed to the Flex C++ Bridge.

Flex is for the web, AIR is for the desktop. What is this for?

This is for C++ applications that need an awesome UI but do not want to re-write their existing c++ code and libraries to actionscript / javascript. It's a normal desktop application, you can interact with all your favorite C++ libraries and APIs and still have all the rich expressiveness that flex can deliver.

You could do all this before as well, but the bridge makes it really easy to use Flex from C++. A lot of the reasons for FABridge applies to this as well, but this is outside the browser realm so those reasons have to be filtered to suit that particular fact.

Where can I get it from?

The project is licensed under MPL 1.1 and both the C++ and actionscript source code is available at code.google.com.

It's open source, so feel free to participate and contribute to it.

Sample Applications

Note: The source (both flex and cpp) is available for all the examples.

AdvancedDataGrid that supports Excel formulae computation:

Computing Excel formulae in ADG.

Here, each individual cells in the ADG are editable. You can type in any Excel formula into it and hit the "Compute" button. The application invokes Excel using COM, computes the results and populates the result into the ADG.

Scan images right into flexbook:

Scan images using TWAIN into Flexbook.

When the Scan button is clicked, a TWAIN dialog pops up letting you use your scanner to scan images directly into the pages of the flexbook component.

Sample app showing two flash player instances each with a flex application:

Multiple flash player instances.

The bridge supports multiple flash player instances. It can talk to each instance in a different manner. If you look at the screenshot, both the instances are loading the same swf file. But the C++ code for one instance adds a datagrid and removes an element shown in the pie chart.

How does it work?

The flash player ActiveX control is added to a MFC dialog. Now the content in the flash player can talk to the C++ application via ExternalInterface. ExternalInterface.call("fnname") will dispatch a FlashCall message on the C++ side which will have the arguments passed to call() in XML. This XML has to be parsed to understand what the message was from the actionscript side.

All this complexity is hidden by the bridge. The bridge talks with the actionscript side of Ely's FABridge and facilitates calling and referencing to actionscript objects, classes and methods.

There are multiple worker threads waiting to process incoming or outgoing flash requests so that the main MFC thread does not block. The bridge can even support multiple flash player instances each with it's own bridge back to the C++ application.

Also, Actionscript exceptions are serialized and thrown on the C++ side.

C++ Syntax Rules

To start off, you need the root object which is the main application object of your flex application. Now you can access the public methods and properties of your application.

Getters and setters are treated differently: A property "width" will be translated to "getwidth" for retrieving the value and "setwidth" for setting the value. Ely's FABridge had camel casing here, but that has been removed so that constants like MOUSE_DOWN don't confuse the bridge.

The "Call" method shown in the snippets above take a string as the first argument that is the name of the method or property (suitably modified using above defined rules) and the arguments for it. Common types like string, int, reference to an AS object, custom anonymous AS object etc are converted internally to an ASObject thanks to copy constructors and operator overloads.

For more examples of the syntax, take a look at the Worker() method in ASWorkSample.cpp.

FABridge did not originally have support for accessing methods and variables of static classes. This was added by Devin Garner and I have incorporated his code into FABridge.as along with some of my changes.

Fine Print

Currently, it supports only Windows since it embeds the internet explorer flash ActiveX control in a MFC dialog.

But it's an open source project and I hope I'll get contributors to help me make it more platform agnostic.

I'd love to know what you guys think about this and how it's being used.

CategoryFlexCPPBridge Comment(s)


[1] Now, this is a better way to communicate rather than saying m_Shockwave.CallFunction("asfnname") where asfnname has to be exposed by using ExternalInterface.addCallback on the actionscript side.
[2] Minor changes to support passing of primitives from me and additional support for accessing static classes, variables and methods thanks to Devin Garner)


Printing from an HTML AIR Application

February 15, 2008 1:59 PM
Printing the main HTML page of an HTML AIR application is quite easy thanks to PrintJob. Sadly, the printing function familiar to javascript developers window.print() does not work in HTML AIR applications. The sample that follows gives a replace for window.print() in about 22 lines of javascript.

function doPrintAir() 
{
    var pjob = new window.runtime.flash.printing.PrintJob;
    if ( pjob.start() )
    {
        var poptions = new window.runtime.flash.printing.PrintJobOptions;
        poptions.printAsBitmap = true;
        try
        {
            pjob.addPage(window.htmlLoader, null, poptions);
            pjob.send();
        }
        catch (err)
        {
            alert("exception: " + err);
        }
    }
    else
    {
        alert("PrintJob couldn't start");
    }
}
//comment the line below if you do not want to mess with existing
//window.print
window.print = doPrintAir;

Code of a full HTML AIR application illustrating the use of the above code follows the screenshot:

AIR Printing Example.

print.html (Main HTML file of the AIR application):

<html>
  <head>
    <title>AIR Print Test</title>
    <script src="AIRAliases.js" type="text/javascript"></script>
    <script src="printing-air.js" type="text/javascript">
    </script>
  </head>
  <body style="border: 5px double grey;margin:20px 20px 20px 20px;background-color:#333;color:#fff;">
    <div style="margin: 20px 20px 20px 20px;">
      <h1> AIR - Printing from HTML / Javascript </h1>
      
      <p>Sample Text: The Sprite class is a basic display list building block: 
	a display list node that can display graphics and can also
	contain children.</p>
      
      <p>A Sprite object is similar to a movie clip, but does not have a
	timeline. Sprite is an appropriate base class for objects that do not
	require timelines. For example, Sprite would be a logical base class
	for user interface (UI) components that typically do not use the
	timeline.This is lots of text</p>

      <p style="text-align:center;">
      <!-- point to any png here -->
	<img src="deskworld.png" />
      </p>
      
      <input type="button" value="Print" onClick="window.print()"/>
      <input type="button" value="Exit" onClick="window.nativeWindow.close()"/>      
    </div>
  </body>
</html>

printing-air.js (Javascript file that contains the print function):

function doPrintAir() 
{
    var pjob = new window.runtime.flash.printing.PrintJob;
    if ( pjob.start() )
    {
        var poptions = new window.runtime.flash.printing.PrintJobOptions;
        poptions.printAsBitmap = true;
        try
        {
            pjob.addPage(window.htmlLoader, null, poptions);
            pjob.send();
        }
        catch (err)
        {
            alert("exception: " + err);
        }
    }
    else
    {
        alert("PrintJob couldn't start");
    }
}
//comment the line below if you do not want to mess with existing
//window.print
window.print = doPrintAir;

AirApp-print.xml (AIR application descriptor file):

<?xml version="1.0" encoding="UTF-8"?>
<application xmlns="http://ns.adobe.com/air/application/1.0.M6">
  <id>AirPrintApp</id>
  <filename>AirPrintApp</filename>
  <version>v1</version>
  <initialWindow>
    <content>print.html</content>
    <systemChrome>standard</systemChrome>
    <transparent>false</transparent>
    <visible>true</visible>
    <width>640</width>
    <height>575</height>
  </initialWindow>
  <name>AirPrintApp</name>
</application>

CategoryAIR Comment(s)


Loading Encrypted SWFs via loadBytes

February 4, 2008 7:25 PM
Ted Patrick had posted about the distinction between loader.load() and loader.loadBytes() where he also mentions that the latter can be used to load an obfuscated / encrypted SWF. I've implemented exactly that in an AIR application using AS3 Crypto Framework to handle the encryption / decryption just to figure out how easy it would be to do it and what it's security implications might be. There's also a companion Flex application that uses loadBytes() but only uses a simple substitution cipher to hide the actual SWF data.

The concept is pretty straightforward: Your SWF is encrypted or obfuscated so that someone else cannot just grab the SWF and decompile it. Your application loads the SWF in it's memory, performs some computation on it's contents to get back the actual SWF1 and then load it using loader.loadBytes().

However, the whole thing will crumble if the key to decrypting the SWF is embedded in your un-encrypted SWF (which anybody can decompile).

Flex Example for loadBytes

This is a lightweight example highlighting the capability of loadBytes(). If you hit any of the "Get" buttons, it performs a URLLoader request to get a SWF. Each byte of the SWF has been incremented by 13 (ROT13) to fool SWF decompilers. Only the "Get SWF and Decrypt" button will reverse the substitution cipher to get the proper SWF data. This SWF is then injected into the current application and it's content becomes visible below the buttons.

View Flex loadBytes Example (View Source Enabled)

Hitting the first button will throw an error because the SWF data is not proper. The second button should load the SWF content below the buttons.

Note: Simple substitution cipher techniques such as ROT 13 can be easily broken.

AIR Version using AS3 Crypto Framework

The following AIR application uses the AS3 Crypto Framework to perform AES 256 bit encryption and decryption. The password string supplied during encryption is the key for decryption. Without the correct password string, the output SWF will never match the actual SWF. The application also has a "Load SWF" button that injects an SWF using loadBytes(). This injected SWF will be running with full privileges and access to all AIR APIs (i.e, it is in the AIR application security context).

AirEncryptSWF: AIR Application that encrypts, decrypts and injects SWF code.

AIREncryptSWF for AIR Beta 3 (View Source Enabled) - Download

Note: When you hit encrypt or decrypt in the AIR application, the UI may not update2 for some time. Please wait patiently till the application becomes responsive again.

Ideally, you could also do a MD5 or SHA-1 hash on the contents of the actual SWF and compare this hash with the hash obtained after decrypting the SWF. Only if the hashes match should you call loadBytes().

The existence of loadBytes() gives us an option to introduce a level of difficulty for some random dude while trying to grab your SWF and view it's code. However, the security of the system depends upon even the smallest aspect of it's implementation and like in all client-side technologies, complete security may be impossible to achieve. Ideally, such techniques should be thought of only as a precautionary measure and not as a complete fool proof solution.

CategoryAIR Comment(s)


[1] We're assuming it's not very easy to get the contents of the decrypted SWF from flash player's memory.
[2] Sadly, distribution of calls to the crypto framework is not supported via callLater() or by listening to an enter frame event.


Websites as Cards on Your Desktop using AIR

January 25, 2008 7:09 PM
There are some sites that I visit every day. A custom HTML file on my hard disk has all the links. But one day, I thought, "Where's the drop shadow?" and Sitecards was born. Sitecards is a transparent AIR application that lays out all my favorite sites like cards (with drop shadow) on the desktop.

So how easy was it to go from "Where's my drop shadow?" to a working AIR application that not only shows the sites but supports drag and drop re-ordering and CRUD (create, read, update and delete) support for URL information to the local DB? Well, it was pretty easy, except for certain bits.

Sitecards

AIR application that lets you view your favorite websites as cards on your desktop.

Features:

  • Websites with drop shadow. :)
  • Drag and drop re-ordering of cards. Order is remembered.
  • Easily add, modify and remove favorite URLs.
  • Click on a card and you get your favorite website rendered fullscreen without any chrome.
  • Slider on the right lets you scale all the cards.
  • Sprinkled with lots of animation that is bound to make your head go dizzy!
  • Consumes memory and CPU like a demon!

Warning: Will contain bugs. When you do encounter them, let me know and they might get squashed.

Sitecards for AIR Beta 3 - Download

Screenshot:

SiteCards: Better than any other kind of cards.

Coding most of it was pretty easy. If I had attempted such a project in Visual C++ or GTK+, I would be far from finishing this project. It was solely due to the rapid development speed that I was able to try out many animation effects and choose the most dizzying ones.

If you run the application, you will notice that it comes pre-loaded with 12 URLs or DB entries. Currently, I'm populating the SQL database from actionscript when the application is run for the first time. But I could have packaged the database along with the AIR file and moved the database file to the application storage directory when it is run for the first time.

Nobody likes to code in everything that a browser does. With AIR, you don't have to. You get most of the functionality in the box. Of course, there is a price to pay. See the problems with HTMLLoader section.

Sitecards keeps track of the order of the cards in it's database. You can drag a card and drop it on top of the other and they will swap places. Again, this sort of functionality without AIR would have taken way more time.

Problems with HTMLLoader in AIR

One main problem I encountered while developing this application was the fact that HTMLLoader does not expose any error cases. These are my woes about HTMLLoader:

  1. If you give an invalid URL, there's no way to find out the problem directly using HTMLLoader because it does not dispatch any IOError or SecurityError events.
  2. It may or may not fire a Event.COMPLETE depending upon the page.
  3. If you pass a wrong URL to it while it is on a certain page, it will not move to a blank page. Rather, it will pretend that nothing happened.

The hack that I had to resort to was pretty ugly:

When the URL is updated from the UI, I first navigate to about:blank. Once that is done, I go to the new URL. Then a timer checks the HTMLLoader's DOM (if there is one) after some time to check if the domWindow.document.URL is still at about:blank. In the meantime, if the htmlDOMInitialize event fires, I stop the timer and assume everything is rosy. Otherwise when the timer fires, I load a URLLoader to figure out if anything went wrong and update the htmlText property of the HTMLLoader with an error message.

This was one ugly hack as I'm sure you would agree. Anybody who's going to write a browser in AIR is going to scream about HTMLLoader.

Apart from this, the entire development experience was fun.

CategoryAIR Comment(s)


Simple Javascript Error Console for HTML AIR Applications

January 22, 2008 3:34 PM
Javascript errors in a HTML AIR application are printed out to the console by adl. I've written a small AIR javascript library which lets you log the javascript error in your own style or see the error inside an HTML div element without the runtime exception dialog.

Using AIRJSConsole

  1. Download AIRJSConsole and put it in the source folder of your HTML AIR project.
  2. Include airjsconsole.js in your HTML application by using the script tag in the head tag:
<html>
  <head>
    <script src="airjsconsole.js" type="text/javascript" />
  </head>
  <body>
  <!-- Other HTML content -->
  </body>
</html>
  1. AIRJSConsole automatically appends a div tag to your body tag. This div tag is appended with javascript error details like type of error, source file name, line number and stack trace.
  2. You can configure AIRJSConsole to call your custom function with error details rather than append it to a div so that you can do whatever you want with the javascript error:
/* For details on traversing the stackTrace array look at
airConsole.describeError() in airjsconsole.js */
function dingDong(errDesc, stackTrace)
{
    alert(errDesc);
}

/* Call dingDong instead of appending to div */
airConsole.registerHandler(dingDong);

Now the dingDong() function will get called on every javascript error.

Here's a sample HTML AIR application:

Application descriptor (AirJSConsole-app.xml):

<?xml version="1.0" encoding="UTF-8"?>
<application xmlns="http://ns.adobe.com/air/application/1.0.M6">
  <id>AirJSConsole</id>
  <filename>AirJSConsole</filename>
  <version>v1</version>
  <initialWindow>
    <content>AirJSConsole.html</content>
    <visible>true</visible>
  </initialWindow>
  <name>AirJSConsole</name>
</application>

Main HTML File (AirJSConsole.html):

<html>
<head>
<title>AIR JS Console</title>
<script src="AIRAliases.js" type="text/javascript" />
<script src="airjsconsole.js" type="text/javascript" />
</head>
<body>
<p>This is lots of text</p>
<p>This is lots of text</p>
<p>This is lots of text</p>
<p>This is lots of text</p>
<input type="button" value="Cause JS Mischief" onClick="mischief();"/>
</body>
</html>

Now simply run adl AirJSConsole-app.xml from the flex console to run the sample.

Working

AIRJSConsole catches the HTMLUncaughtScriptExceptionEvent from the main HTMLLoader of the HTML AIR application and directs it to your own custom function or it's own internal logging functions.

On startup, it calls window.addEventListener() to register a function that gets called when the page is loaded. This function appends a div tag to the body tag. Any errors that have been accumulating before this div tag was created is stored in a buffer so that errors are not lost.

Now any uncaught javascript exception is directed to a function which either prints to the console, appends it to the div or calls your custom function.

Note: When a custom log function is registered, any error messages buffered because of unavailability of the div is passed on as HTML to the function. In this case, stackTraceArr will be null.

CategoryAIR Comment(s)


Mutexes in Actionscript and Javascript

January 16, 2008 11:42 AM
Threads in javascript and actionscript are non-existent. However, the runtime (browser or flash respectively) is in most cases multi-threaded. Even this does not imply that multiple actionscript functions can get called simultaneously. What confuses1 developers though is the fact that if there are multiple events, your callback function may get called multiple times and these will execute serially but before the screen updates. This can be mistook for what may seem like multi-threaded behavior especially if there is some constraint dependent upon the screen refreshing.

Usually when you want to simulate or fake multi-thread behavior2, you split your work into chunks and defer processing of the next chunk after a screen update so that the UI doesn't block when you are doing CPU intensive tasks. This situation is pretty common and necessary for the application to be responsive.

Alex Harui has generalized this into PseudoThread and it basically uses the stage's RENDER event to defer processing. His code performs as much Actionscript computation as possible limited by the time needed to maintain the frame rate.

Interestingly, I ran into a mutex implementation in javascript which uses a variant of a mutual exclusion algorithm intended for a system of multiple threads when the only communication between them is shared memory. More details are present in the link. The idea of the algorithm is that all critical section pieces are given a unique number. Each section waits for jobs that were registered before them to complete. In Javascript, this is done via setTimeout(). That is, all methods run, wait for methods before them to complete via setTimeout() and then run it's code.

Basically, this "mutex" enforces ordered execution of logical blocks.

Using the above logic, I have written an actionscript class called "PseudoMutex" which complements PseudoThread. Note that this mutex is actually only useful to order the execution of logical units between different threads3. It doesn't actually provide mutual exclusion, instead provides an illusion of ordering execution of code within our existing context switching code. If there was no pseudo thread, there would be no need for pseudo mutex.

The example (view source enabled) below tries to illustrate visually the working of the pseudo mutex:

PseudoMutex Example

PseudoMutex Example

There are 2 demos in the example. Both of them have 3 textarea components being updated with data (incremented counter from a shared source) via 3 pseudo thread functions. Pseudo mutexes are used to ensure the order in which the pseudo thread slices control to each thread function.

  1. The barrier demo shows how the pseudo mutex can be used to ensure that one thread checks for a shared value before the other. Thread 3 waits for thread 2 to reach a particular count and thread 1 only starts checking for a higher count once thread 3 has completed it's critical section.
  2. The ordering demo uses pseudo mutexes to ensure that the third thread gets first control (i.e number 1 from shared data source) and then control is passed to the first thread and finally to the thread. This order is entirely dependent on the order in which the critical sections were registered with the mutex.

Note: The pseudo thread code has been modified so that only one computation happens before a screen update. This is for demo purposes only.

Working

The pseudo thread calls the registered thread function till the function returns a value indicating that they have completed. The order in which the thread functions execute depends upon the order in which the constructor of pseudo thread was called. Apart from this, we have no control over when a particular thread function should get called. This is the deficiency that the pseudo mutex attempts to address.

The pseudo mutex returns false when it is not the right time for it to execute it's code. Then, the function in the pseudo thread also returns true indicating that the thread wants to yield. This is continued till the critical section is executed after which the pseudo thread can yield and resume work in it's own style.

Multiple mutexes are supported and each will have it's own queue of critical sections which get executed in order.

Visualizing the critical section in this is so drastically different from how you think of it in C or C++ that I'm not sure about how useful this code will be. Any feedback is appreciated.

CategoryFlex Comment(s)


[1] Another common cause for confusion is dispatchEvent(). Some developers don't realize that your event handlers will get called immediately when a dispatchEvent() is called.
[2] This sort of fake multi-threading was even possible in the old days of DOS C code. You could save your point in code using a setjmp() call and do a non-local jump back to the same point via longjmp() saving and restoring all the registers along the way.
[3] Thread = Pseudo Thread.


Copyright © 2004-2008 Anirudh Sasikumar. All rights reserved.
Last Updated: December 19, 2007 7:04 PM