Written in Javascript by me in early 2005, and then rewritten by Jason in Java in 2006, there has not been much crosscheck hacking going on these last two years. The reasons for this are many and complex but the two most important are 1) The current crosscheck functionality is sufficient for our needs, and 2) we've been drawn off onto side projects and so haven't really spent much time enhancing the core framework.
Ironically, it has been during these past two years of inactivity that interest in Crosscheck has grown. This is a natural result of the mainstreaming of both Ajax and Javascript as a language. As use of these techniques matures, it's only logical that development should gravitate towards more disciplined techniques --And, it is the recent interest in Crosscheck which has made me realize how woefully inadequate it is for the mainstream.
If Crosscheck is to remain a viable solution, there are some changes that need to be made, and some big bones that need breaking and resetting. It's been a long time coming, but here are my thoughts on the matter.
A Return To Javascript
When we first wrote Crosscheck, the target Javascript interpreter was the venerable Rhino 1.5R5. While this was (and is) a wonderful piece of software, it was slow and there were certain deficiencies in the language itself which made the test environment less robust. As a result, we ended up having to write significant amounts of Java code to work around these deficiencies and make the system more performant; as the amount of Java piled up, we finally decided to implement almost the entire thing in Java.
This brought some nice features to the table: complete isolation of test environments, fast test execution, and a higher fidelity runtime (more resembling an actual browser). Unfortunately, it brought with it a downside too, the biggest angle of which was that our system was implemented in Java, while most of our users were Javascript programmers. It was a big mistake to underestimate this cost, since our codebase was instantly opaque to 95% of the eyes that would be looking at it. We became a bottle neck to submitting patches, and community development slowed to a crawl.
Fast forward two years, and past the release of Rhino 1.7R1.
Since the release of the Crosscheck Java implementation, rhino has steadily improved in its capability to the point where most of the issues which drove us to Java have been addressed
- Performance
- Language Problems
The javascript engine itself is much faster in both the compilation and execution than it was 2 years ago. This alone will mitigate much of the problems with slowness we encountered back in the early days. Also, we now have the capability to compile the javascript code to generated java classes which can be stored on disk and immediately loaded and interpreted, which means that if we implement the bulk of Crosscheck in Javascript, it will still perform somewhat like compiled java.
The latest Rhino allows you to define getters and setters for object properties which means that you can mimic browser functionality in javascript which could previously only be implemented in Java. For example:
var div = document.createElement('div')
div.cssText = 'color: blue; border: 2px solid red;'
div.style.color //=> 'blue'
div.style.border //=> '2px solid red'
//the reverse is also true
div.style.color = 'green'
div.cssText //=> 'color: green; border: 2px solid red;'
could not be done in earlier releases, because there was no way to "watch" the cssText property and update the properties on the style objects accordingly. So, we had to do it in Java.
Most importantly, as I stated earlier, our community will be able to expand and be more self-sustaining if the bulk of Crosscheck is implemented in a language they understand.
That said, there are certain parts of Crosscheck that will remain in Java. Specifically the test runner and loader. This will allow us to keep on doing the things that make Crosscheck special (like true test isolation) while making it majority Javascript.
Refactoring The Cross-Browser Aspect
Crosscheck's raison d'ĂȘtre is its ability to simulate different browser s. Unfortunately, its architecture for doing so is ad-hoc, and ultimately infeasible in the long term. In our innocence, we started by using inheritance to model browser incompatibilities, and, once embarked upon that path, did not want expend the necessary mental and physical energy to alter course.
It seems reasonable enough. You have a Mozilla 1.8 environment which inherits from mozilla 1.7 and behaves slightly differently. IE 6, which inherits from IE5, etc... After all, IE Events bubble, but don't capture, whereas mozilla events do. Both IE have the ActiveXObject constructor, but neither Mozilla browser does. Experience however, has shown that modelling it like this works in only the simplest cases.
Things fall apart quickly when supposedly related browsers start differing wildly in certain aspects of their functionality. IE7 has many API changes (read bugfixes) that make it more compatible with other mainstream browsers. Its Crosscheck implementation should share much more code with other implementations like Firefox. But, since its ancestor (IE6) branched off from the Mozilla side of the tree along time ago, doing this is awkward. Compound that by the fact that IE7 in quirks mode behaves a lot more like IE6, and your inheritance strategy is completely unraveled.
That's why I propose something along the lines of pluggable behaviors to simulate browser differences. Each browser environment can then be defined as a nothing more than a collection of behaviors. This has several advantages:
- Fine Grained Control
- Behavior Cataloging
- Error Diagnostics
Because each behavior can (theoretically) be specified independent of any other behavior, you can share 90%/50% or 0% of your implementation with any other browser. This should give us the flexibility we need to rapidly accomodate new browsers as they come.
Right now, browser differences are implemented as mish-mash of if statements, overrided methods, and subclassing of various objects in the system, so where exactly the differences between browsers lie can be very difficult to discern. Have a look at the Crosscheck site. Where is the list of browser incompatibilities? If the differences themselves were properly encapsulated as the units of code-reuse, then we could generate that list programmatically by iterating through them.
This last advantage is more conjecture on my part, but, if, for example, a certain behavior triggered an error, you might be able to label it with common reasons why it happened. That way, Crosscheck could not only help you detect that an error occured, but also help you determine why.
Of course, the behaviors themselves can inherit from each other or from other classes. That's their business, but they can be swapped in and swapped out as you like.
Better HTML Support
Let's face it, Crosscheck's HTML support sucks. Any html you use has to be well-formed XHTML, it doesn't interpret any tags like <style>, or <script>, Forget about loading things off a real network, or iframes, or any number of dynamics related to the DOM itself.
We've got to get Crosscheck parsing and interpreting (as best it can) real HTML as it is found in the wild. While in my opinion this isn't really as necessary as most people think, it is nevertheless still necessary enough to be necessary.
:-)
We've started in many different directions, but now it's time to finish the fight

Delicious
Digg
Reddit
Magnoliacom
Google
Glad to see there is life
Glad to see there is life coming in the project. When I first stumbled on it almost a year ago I loved the idea of finally having a good continuous integration story for JavaScript. Things didn't work out quite the way I hoped though, as I started to have to change implementation code to have tests that work.
Looking forward to the next step...
Well, as we begin to release
Well, as we begin to release the new versions of Crosscheck, I hope you stick with us to give us your feedback.
Post new comment