Warning :)

PolyChrome is still in an early stage of development. Hopefully, one day it will be polished at least a bit more. For now, as you'll see in the documentation and the code, some things are not as nice or as clear as they could be, but you're welcome to contribute with comments, ideas or code.

Also, this documentation is not yet exhaustive, and we about quite a few issues already :)

Updating the extension

As of now, the extension has not been reviewed by Mozilla, therefore no updates of the extension will be automatically pushed to your browser. You'll have to manually check for new versions of the extension at https://addons.mozilla.org/en-US/firefox/addon/271847/.

Installation

To use the PolyChrome extension you'll need to install PolyML and the PolyChrome extension itself.

Test It

After you've done that, you can check whether it's working by opening up Firefox, right clicking the extension icon at the bottom right corner of the browser and clicking Start PolyML.

The label should change to Initializing.... This means that the neccessary heaps are being built for the first time. After the label changes to PolyML as in the screenshot below try entering some Standard ML code into the console (if you can't see the console click on the PolyChrome icon).

Troubleshooting

If some of the above steps did not work, please refer to the Troubleshooting section

User Interface

You've seen most of what you need to know already and you'll see the rest as you go, but a few quick additional snippets of information in case you find this confusing.

- this icon means that PolyML is not active for this document and no Standard ML code found on this page will be executed.

- this icon means that PolyML is active for this document.

Note that by default, PolyML will never be active for documents containing no SML code. For documents containing SML code, the behavior depends on the Always Enable setting.

Also note that once you've enabled PolyML for a certain tab containing SML code, it will remain enabled until you close the tab or stop PolyML via the context menu.

Hello World

This example will demonstrate how to write SML applications using the PolyChrome.

1.

First, create an empty document called hello.html. Paste the following code into the document, and open the document with Firefox.

	<!DOCTYPE html>
	<html>
		<head>
			<title>Hello SML World</title>
			
			<script type="application/x-polyml">
				val hello_div = valOf (DOM.getElementById DOM.document "hello")
				val _ = DOM.setInnerHTML hello_div "Hello Graphical SML World"
				
				val paper = valOf (DOM.getElementById DOM.document "paper")
				val context = Canvas.getContext paper "2d"
				val _ = Canvas.fillStyle context "green"
				val _ = Canvas.fillRect context 10 10 30 50
			</script>
		
		</head>
		<body>
			<h1 id="hello"></h1>
			<canvas id="paper"></canvas>
		</body>
	</html>

2.

That's it!

What happened here?

At first, Firefox rendered the HTML document, which is simply a blank page, because it contains an empty h1 element and an empty canvas element. Then, PolyChrome extension found the PolyML code on this page and executed it. The code used DOM library to select the h1 element and add some text into it and then used Canvas library to draw a rectangle in the canvas element.

To fully understand the above example you should be familiar with HTML and JavaScript. Even though, no JavaScript was written in this example, the usage of DOM and Canvas libraries very closely corresponds to the way you would write JavaScript. For instance, lets look at equivalent SML and JavaScript code side by side:

Standard ML
DOM.getElementById DOM.document "hello"
JavaScript
document.getElementById("hello")
Remarks
DOM.getElementById returns an option type, which is NONE if the element does not exist in the document. JavaScript returns null in such case.
Standard ML
DOM.setInnerHTML hello_div "Hello Graphical SML World"
JavaScript
hello_div.innerHTML = "Hello Graphical SML World"
Remarks
Notice that in case of JavaScript, innerHTML is not a function but rather an attribute. Such attributes can be accessed using the setters and getters provided in the SML DOM library.
Standard ML
Canvas.fillRect context 10 10 30 50
JavaScript
context.fillRect(10,10,30,50)
Remarks
In case of SML we pass the context object to a function, whereas in JS we call the method of the object itself.

Demos

A good way of trying out the extension is checking out the demos that can be opened via the context menu of the extension or by opening chrome://polychrome/content/demos/index.html (note that this link will only work in Firefox with PolyChrome installed).

These demos are not intended to be polished applications. They were created for a proof-of-concept reasons. Some of them, in fact, demonstrate the limitations of the extension.

Troubleshooting

Here are a few things you could take a look at to help in identifying some of the problems.

PolyML Not found

If PolyML is not installed or found on the machine, you will see this.

Click on the icon to go to settings of the extension and configure the path to your PolyML installation.

JavaScript Error Console

PolyChrome extension uses Firefox'es JavaScript Error Console, that can be opened by Tools > Error Console or by Ctrl-Shift-J. Note, that errors from all Firefox extensions or even websites are printed here, so not all of it may be reported by PolyChrome. Only JavaScript components of the extension will report errors here.

stdout

Currently, not all of the PolyML output is printed to the PolyML console in Firefox. Some of it goes to the stdout, that can be seen, for example, by opening Firefox from a terminal (e.g. on linux that would mean executing command "firefox" in the terminal)

Rebuilding Heaps

You'll find the "Rebuild Heaps" button in the settings page, in case that is your problem. For example, this could be needed if you updated the version of PolyML on your machine.

Architecture

To understand the limitations and potential applications of the PolyChrome extension, it is important to have some insight on how it works

Firefox

Firefox browser is as a collection of HTML rendering engine, DOM, JavaScript engine, networking components and many other components. When you enter a URL into the address bar and press enter, Firefox loads an HTML document from the server (or local filesystem), parses it into a Document Object Model (DOM), renders it on the screen and executes the JavaScript code embedded in the HTML document. JavaScript can modify how the document is rendered on screen and react to various user interaction events like mouse clicks and keyboard presses. DOM, which is the in memory representation of HTML, is reflected into JavaScript as a JavaScript object. Calling methods and modifying attributes of this DOM-JavaScript wrapper object affects the DOM and the way it is rendered to the user.

PolyChrome

After the document is loaded, parsed and rendered, PolyChrome looks at the DOM to see if the document contains any script tags with PolyML code. If that's the case a PolyML process is started specifically for this document. A socket communication is established between the JavaScript components and PolyML components. The code that was found on the page is sent through the socket to PolyML for evaluation.

JS FFI

Because we have this connection between JavaScript and PolyML and because JavaScript has access to DOM, we can now ask JavaScript to modify the DOM from within SML.

Read more about the FFI in section Using JavaScript FFI and read the next section to see what are the implications of using this socket-based-foreign-function-interface.

PolyChrome Components

Below is a rough scheme of the PolyChrome components and their relationships.

Additional References

MSc thesis on PolyChrome: http://www.inf.ed.ac.uk/publications/thesis/online/IM100814.pdf

Memory Sharing

DOM elements (e.g., input fields, buttons, paragraphs, images) appear in JavaScript as JavaScript objects. To work with these objects need to be able to store them and access their methods and attributes from within SML.

For example, every time, you call a DOM function that returns an HTML element, e.g.:

	val getElementById : Document -> string -> HTMLElement option

you are actually only getting a unique string identifier of this element. That is, JavaScript keeps track of all these objects that SML had interest in, by storing (string identifier, reference to the object) tuples. It only sends the string identifier of an object to SML.

If you lose the string identifier in SML, there is no way to get access to a the corresponding DOM or JavaScript object, however the reference remains stored in JavaScript, which could prevent JavaScript garbage collection and in this way leak memory.

Memory Management

To prevent the memory leaking, you will have to clean up any JavaScript references, when you don't need them in SML anymore. To do that you should use the jsffi.Memory structure

	signature JSFFI =
	sig
		...
		(* these are used for keeping the temporary memory, used for
		   storing javascript objects, tidy *)
		structure Memory :
		sig
			val switchNs : string -> unit
			val switchDefaultNs : unit -> unit
			val addFunctionReference : string -> fptr
			val addFunctionReferenceOW : string -> fptr
			val removeReference : string -> unit
			val deleteNs : string -> unit
			val clearNs : string -> unit
			val clearDefaultNs : unit -> unit
		end
		...
	end

For example:

	val a = valOf (DOM.getElementById DOM.document "test")
	(* if the value is not going to be ever used anymore *)
	val _ = jsffi.removeReference a

Namespacing

It might be cumbersome to keep track of all these references individually. For convenience, you can use the "namespacing" functionality.

	(* we'll be using this value throughout the lifetime of the application *)
	val permanent = valOf (DOM.getElementById DOM.document "permanent")
	fun temporary () =
		let
			val _ = jsffi.switchNs "temp"
			val temp1 = valOf (DOM.getElementById DOM.document "temp1")
			val temp2 = valOf (DOM.parent temp1)
			val temp3 = valOf (DOM.lastChild temp2)
			(* you can still access the "permanent" element here *)
			val _ = DOM.setValue temp3 (DOM.getValue permanent)
			val _ = jsffi.deleteNs "temp"
		in () end

Script Tags

There are several ways of embedding SML into the HTML documents.

Embedding code

This could be used for short snippets of code.

	<script type="application/x-polyml">
	  (* sml code *)
	</script>

Embedding sml files

This could be used to embed larger applications. In this case, the files are compiled immediately after they are downloaded.

	<script type="application/x-polyml" src="link/to/file1.sml"></script>
	<script type="application/x-polyml" src="link/to/file2.sml"></script>

Embedding zip files

This could be used for large applications including many files. The zip file is downloaded to the client and extracted into a temporary sandbox directory.

	<script type="application/x-polyml" src="link/to/file.zip"></script>

Sandbox

In all of the above cases, a temporary sandbox (it's not really a sandbox at the moment, that is you can freely browse the filesystem of the clients computer from the SML script) directory is created on the clients machine. The working directory of the toplevel PolyML shell is always set to that sandbox, thus you can access the downloaded files as they are all placed into this sandbox directory. These temporary directories are created in your_firefox_profile/extensions/polychrome@ed.ac.uk/storage/temporary

 

Obviously, a combination of the above could also be used. For example, you might include a large application by embedding the zip file and include a short snippet of code in HTML initializing the application.

Persistance

Currently, you can add a "persist" class to the script tags of your PolyChrome application, to tell the extension not to remove the sandbox directory of the application

For example, you can include your application in the document like this:

	<script type="application/x-polyml" src="link/to/file1.sml" class="persist"></script>

Then, your included application can create some files on the disk. The next time you open this application in the browser, those files can still be accessed by the application.

If you include multiple scripts, it's enough to add "persist" class to one of them as the persistant storage is provided to the application based on the document url.

Your PolyChrome application starts in the root of this persistant storage directory.

In the Settings page you can clear the persistant data of all applications. To delete data per application basis you'll have to do that manually by browsing to your-ff-profile/extensions/polychrome@ed.ac.uk/storage/persistant/.

It would be nice in the future to explore the HTML5 local storage capabilities, perhaps combine it with the file storage.

Using JavaScript FFI

PolyChrome can execute any Standard ML code. However, the HTML/DOM interaction is limited by what is provided in the DOM and Canvas libraries. Currently these 2 libraries don't provide much functionality and they will have to be extended in order to do something useful.

It is easy to extend the libraries or implement wrappers for any other existing JavaScript libraries, by using the JavaScript Foreign Function Interface (jsffi structure). In the demos page you'll find examples of jQuery and Raphael JavaScript libraries being used from SML.

jsffi

There are currently 4 functions that you can use to interact with JavaScript

	signature JSFFI =
	sig
		...
		(* these are used to call JS functions
		   when using exec_js_r, it is up to the implementation of the wrapper
		   function to convert the returned string to an appropriate type *)
		(* args: an fptr to an object, a function name, an argument list *)
		(* e.g. exec_js_r "document|" "getElementById" [arg.string "something"] *)
		val exec_js_r : string -> string -> JSON.T list list -> string
		val exec_js : string -> string -> JSON.T list list -> unit
		val exec_js_get : string -> string -> JSON.T list list -> string
		val exec_js_set : string -> string -> JSON.T list list -> unit
		...
	end

exec_js_r and exec_js are used to call JavaScript functions. The former expects something returned and the latter does not. They take 3 arguments. An object whose method will be called, a name of the method and a list of arguments.exec_js_get and exec_js_set are similar, but instead of calling methods they get and set attributes of the object.

To start with, you can use two fptr - "window|" and "document|". The foreign pointers are simply strings of this form "identifier|namespace". Window object in the browser is the root, global, ultimate object in the browser. Any JavaScript variable or function that is created in global scope becomes an attribute of the window object. Even the document object can be accessed using the window object fptr.

	exec_js_get "window|" "document" [];
	val it = "0|": string

Now, the fptrs "0|" and "document|" point to the same JavaScript object. Both of them can now be used as the first arguments to the exec_js functions.

For example, to call the getElementById function, we do:

	fun parse_element e = case e of "null" => NONE | x => SOME (HTMLElement x)
	fun getElementById (Document d) id = parse_element (exec_js_r d "getElementById" [arg.string id])

For example, to use the Raphael library, we can do this:

	...
	<script type="text/javascript" src="raphael/js/raphael-1.5.2.js"></script>
	<script type="application/x-polyml">
	datatype paper = Paper of fptr
	fun Raphael (x, y, w, h) = Paper (exec_js_r "window|" "Raphael" [arg.int x, arg.int y, arg.int w, arg.int h])
	</script>
	...

Accessing Deeper Attributes of JavaScript Objects

Javascript

	Raphael.getColor()

is equivalent to JavaScript

	window.Raphael.getColor()

which in SML can be achieved by

	exec_js_r "window|" "Raphael.getColor" []

Callbacks

Sometimes you need JavaScript to be able to reference PolyML memory. One of such cases is DOM events. To handle DOM events, the JavaScript FFI is used to add event listeners, but there has to be a way for the JavaScript event handler to call PolyML functions. This is currently quite cumbersome as it involves maintaining data structures to store the PolyML functions for later use. In the future, it would be nice to have this integrated in the jsffi structure.

Below is an implementation of DOM.addEventListener

	structure DOM : DOM =
	struct
	
		local open jsffi in
		
		...
		datatype EventListener = EventListener of string;
		datatype Event = Event of fptr
		datatype EventType = click | change | keypress | keyup | mouseover | mouseout | mousemove
		datatype EventCallback = EventCallback of Event -> unit
		
		(* we'll keep event callbacks here *)
		val eventCallbackTab = ref (Tab.empty : (HTMLElement * EventType * EventCallback) Tab.T)
		fun handle_event id event = let
				val (_, _, EventCallback f) = (Tab.get (!eventCallbackTab) (Name.mk id))
					handle UNDEF => (raise Error ())
				val _ = f (Event event)
				val _ = Memory.removeReference event (* clean up the memory *)
				val _ = ready ()
			in () end
		
		...
		
		fun addEventListener_ (HTMLElement e) et f add_function_reference = let
				val callback = "val _ = DOM.handle_event {id} {arg} ;"
				val id = add_function_reference callback
				val entry = (HTMLElement e, et, f)
				val _ = (eventCallbackTab := Tab.ins (Name.mk id, entry) (!eventCallbackTab))
				val _ = exec_js e "addEventListener" [arg.string (string_of_eventtype et),
													  arg.reference id, arg.bool false]
			in EventListener id end
		fun addEventListener e et f = addEventListener_ e et f Memory.addFunctionReference
		fun addEventListenerOW e et f = addEventListener_ e et f Memory.addFunctionReferenceOW
	
		fun removeEventListener (EventListener id) = let
				val (HTMLElement e, et, _) = (Tab.get (!eventCallbackTab) (Name.mk id))
							  handle UNDEF => (raise PolyChrome.DOMExn "Undefined listener");
				val _ = (eventCallbackTab := (Tab.delete (Name.mk id) (!eventCallbackTab)))
				val _ = exec_js e "removeEventListener" [arg.string (string_of_eventtype et),
														 arg.reference id, arg.bool false]
				val _ = Memory.removeReference id
			in () end
		
		...
		
		end
	end

Comments

We store the HTML element, event type and event callback function to the eventCallbackTab table. We need this table to be able to call the function anytime the event fires. The event type and the HTML element has to be stored, because that information is needed when we want to remove the event listener.

add_function_reference is used to create a JavaScript function, that when called, sends a string to PolyML, in this case "val _ = DOM.handle_event {id} {arg} ;", for evaluation. This way we can create a JavaScript function that can call an SML function. The obscure {id} bit is replaced by JavaScript with the string identifier of that JavaScript function. It is the same identifier that is used for all JavaScript objects that are shared between JavaScript and PolyML as discussed in the Memory Sharing section. Each of the following {arg} substrings are replaced with the argument that the JavaScript function was called with. For example, in this case, all callbacks passed to the window.document.addEventListener function are called with an event object, which in this case is going to be passed to the SML function as an argument (again, only the string identifier will be passed to SML).

The difference between Memory.addFunctionReference and Memory.addFunctionReferenceOW is that the latter can be used in case of rapidly firing events. All of the callbacks that are called using the mechanism described above are queued up in JavaScript. If, for example, you listen to the mousemove event, moving the mouse will queue up a lot of these event callbacks. If Memory.addFunctionReferenceOW is used, where OW stands for OverWritable, then only the last function call in the queue will be called when PolyML is ready to handle more events. By the way, that is what jsffi.ready() is needed for. It tells JavaScript that PolyML is ready to handle more events and the next callback from the queue is dispatched to PolyML.

To fully understand what's going on in here you will have to look at the implementation of jsffi in both SML and JS. Those can be found in jsffi.sml and JSFFI.js files.

DOM signature

signature DOM =
sig

	datatype Event = Event of jsffi.fptr
	datatype Window = Window of jsffi.fptr
	datatype EventListener = EventListener of string
	datatype HTMLElement = HTMLElement of jsffi.fptr
	datatype Timeout = Timeout of string
	datatype Interval = Interval of string
	datatype TimerCallback = TimerCallback of unit -> unit
	datatype EventType =
		change
	  | click
	  | keypress
	  | keyup
	  | mousemove
	  | mouseout
	  | mouseover
	datatype HTMLCollection = HTMLCollection of jsffi.fptr
	datatype EventCallback = EventCallback of Event -> unit
	datatype Document = Document of jsffi.fptr
	
	val document : Document
	val window : Window
	
	val fptr_of_HTMLElement : HTMLElement -> jsffi.fptr
	val string_of_eventtype : EventType -> string
	val parse_element : string -> HTMLElement option
	val parse_element_list : string -> HTMLElement list
	
	val getElementById : Document -> string -> HTMLElement option
	val getElementsByTagName : Document -> string -> HTMLCollection
	val parentNode : HTMLElement -> HTMLElement option
	val lastChild : HTMLElement -> HTMLElement option
	val removeChild : HTMLElement -> HTMLElement -> unit
	val previousSibling : HTMLElement -> HTMLElement option
	val getStyle : HTMLElement -> string -> string
	val replaceChild : HTMLElement -> HTMLElement -> HTMLElement -> unit
	val nextSibling : HTMLElement -> HTMLElement option
	val setStyle : HTMLElement -> string * string -> unit
	val firstChild : HTMLElement -> HTMLElement option
	val appendChild : HTMLElement -> HTMLElement -> unit
	val childNodes : HTMLElement -> HTMLElement list
	val createElement : Document -> string -> HTMLElement
	val createTextNode : Document -> string -> HTMLElement
	val getAttribute : HTMLElement -> string -> string
	val setAttribute : HTMLElement -> string * string -> unit
	val removeAttribute : HTMLElement -> string -> unit
	val getInnerHTML : HTMLElement -> string
	val setInnerHTML : HTMLElement -> string -> unit
	val getValue : HTMLElement -> string
	val setValue : HTMLElement -> string -> unit	
	val getHTMLCollectionItem : HTMLCollection -> int -> HTMLElement
	
	val addEventListener :
	   HTMLElement -> EventType -> EventCallback -> EventListener
	val addEventListenerOW :
	   HTMLElement -> EventType -> EventCallback -> EventListener
	val removeEventListener : EventListener -> unit
	val setInterval : Window -> TimerCallback -> int -> Timeout	
	val clearInterval : Window -> Interval -> unit
	val setTimeout : Window -> TimerCallback -> int -> Timeout
	val clearTimeout : Window -> Timeout -> unit
	
	val handle_event : string -> jsffi.fptr -> unit
	val handle_interval : string -> unit
	val handle_timeout : string -> unit
	
	val getClientX : Event -> int
	val getClientY : Event -> int	
	
	val alert : Window -> string -> unit

end
				

Canvas Signature

signature CANVAS =
sig

    datatype Context = Context of jsffi.fptr
    
    val getContext : DOM.HTMLElement -> string -> Context
    val stroke : Context -> unit
    val fillRect : Context -> int -> int -> int -> int -> unit
    val moveTo : Context -> int -> int -> unit
    val fill : Context -> unit
    val lineTo : Context -> int -> int -> unit
    val getStrokeStyle : Context -> string
    val beginPath : Context -> unit
    val fillStyle : Context -> string -> unit
    val getFillStyle : Context -> string
    val canvasHeight : Context -> int
    val arc : Context -> int -> int -> real -> real -> real -> bool -> unit
    val getLineWidth : Context -> string
    val canvasWidth : Context -> int
    val setStrokeStyle : Context -> string -> unit
    val setFillStyle : Context -> string -> unit
    val setLineWidth : Context -> real -> unit

end
				

JSON Signature

signature JSON =
sig

	exception notobj_exn of unit

	structure Tab : NAME_TAB
	structure Name : SSTR_NAMES

	datatype T =
		Bool of bool
	  | Int of int
	  | List of T list
	  | Null
	  | Object of T Tab.T
	  | Real of real
	  | String of string
	
	val empty : T
	val update : string * T -> T -> T
	val add : string * T -> T -> T
	val lookup : T -> string -> T option
	val get : T -> string -> T
	val delete : string -> T -> T
	val encode : T -> string

end
				

JSFFI Signature

signature JSFFI =
sig
    type fptr
    
    exception Error of unit
    
    structure Tab : NAME_TAB
    structure Name : SSTR_NAMES
    
    
    (* this is used in combination with the exec_js_r and exec_js*)
    structure arg :
    sig
        val string : string -> JSON.T list
        val reference : fptr -> JSON.T list
        val real : real -> JSON.T list
        val object : JSON.T -> JSON.T list
        val null : unit -> JSON.T list
        val list : JSON.T list -> JSON.T list
        val int : int -> JSON.T list
        val callback : string -> JSON.T list
        val bool : bool -> JSON.T list
    end
    
    (* these are used to call JS functions
       when using exec_js_r, it is up to the implementation of the wrapper
       function to convert the returned string to an appropriate type *)
    (* args: an fptr to an object, a function name, an argument list *)
    (* e.g. exec_js_r "document|" "getElementById" [arg.string "something"] *)
    val exec_js_r : string -> string -> JSON.T list list -> string
    val exec_js : string -> string -> JSON.T list list -> unit
    val exec_js_get : string -> string -> JSON.T list list -> string
    val exec_js_set : string -> string -> JSON.T list list -> unit
    
    (* this must be called after handling each event *)
    val ready : unit -> unit
    
    (* these are used for keeping the temporary memory, used for
       storing javascript objects, tidy *)
    structure Memory :
    sig
        val switchNs : string -> unit
        val switchDefaultNs : unit -> unit
        val addFunctionReference : string -> fptr
        val addFunctionReferenceOW : string -> fptr
        val removeReference : string -> unit
        val deleteNs : string -> unit
        val clearNs : string -> unit
        val clearDefaultNs : unit -> unit
    end
    
end
				

Development Guide

The source code and the bug tracker can be found at https://github.com/KidkArolis/PolyChrome.

Setting Up the Development Environment

Firefox extension is a zip archive with all of the extension files (with an extension .xpi), that is extracted to your Firefox profile once you install it. You could browse to your firefox profile and modify the code of the extension there, but there is a much better way of working on the code of the extension. Clone or fork the repository and create a "symlink" in your Firefox profile, pointing to the repo. Note, that this symlink is not a symlink in a usual sense, it is just a text file with a path to the extension. The simplest way of doing this is using the deploy script that you'll find in the root dir of the repo.

To use the deploy script you'll first need to create a deploy.cfg file based on the deploy.cfg.template

After that, simply executing

# ./deploy run

will place a text file in your Firefox profile with the path to the source code of the extension and will immediately run Firefox (with the error console opened up). After running the deploy script once, Firefox will always load the extension from your repository. However, you do need to restart the Firefox browser every time you modify the JavaScript components of the extension or overlays/styles, etc. If you can't see some changes that you expect to see after restarting Firefox, you might want to run the deploy script again, because it clears the extension registry/cache. To remove the extension, you can simply uninstall it via the Add-Wons manager in Firefox

Modifying SML components requires only the refresh of the HTML document. When extension is loaded using this "symlink", the extension knows it's running in development mode (function Utils.isDevelopmentMode) and therefore recompiles the SML components every time a document is opened.

Also, you should take a look at https://developer.mozilla.org/en/setting_up_extension_development_environment, especially the "Development preferences".

In general https://developer.mozilla.org/ is the place to look for all of the extension development related docs.

Packaging

Executing

# ./deploy package

will zip the neccessary files and create an .xpi file.

File Structure

/polychrome/chrome/content/core - contains the JavaScript components of the extension. Their relationships can be seen in the Architecture section

/polychrome/chrome/content/core/PolyChrome.js - is the entry script. PolyChrome.init function, called in the ff-overlay.xul, starts the magic.

/polychrome/chrome/content/ff-overlay.js - describes the GUI of the extension.

/polychrome/chrome/content/docs - contains this documentation that you're reading right now. A copy of this directory is placed in the "gh-pages" branch of the repo for online access via GitHub pages.

/polychrome/chrome/content/demos - contains all of the demos.

/polychrome/poly - contains the SML components of the extension.

/polychrome/poly/main.sml - is the entry script of the PolyML side of things. PolyChrome.main is the initialization function called in the polyml.sh bash script (that is executed in JavaScript).

/polychrome/poly/bin - contains some bash scripts for finding PolyML binary on the clients machine, running the main.sml script, and killing the PolyML process. This is the only bit of the whole extension that prevents it to be truly cross-platform. Hopefully, with some modifications in the next release of PolyML, these scripts won't be neccessary anymore.

/polychrome/poly/isaplib/ - contains the isaplib library that is used by the extension. Isaplib is part of the IsaPlanner

/polychrome/sandboxes/ - is used to store all temporary files downloaded from the server (when they're included using script tags).

Read https://developer.mozilla.org/en/building_an_extension for description of the other, Firefox specific, files, such as install manifest.