1 2
The Next Opera Next Browser

This article originally appeared on earthli News and has been cross-posted here.


imageOpera started a public beta-testing program a few years ago called Opera Next. Whereas the stable version naturally moved along more slowly -- but always rock-solid -- Opera Next often had a more up-to-date HTML/CSS renderer (code-named Presto) and Javascript engine (code-named Carakan). Opera recently anounced that future versions -- Opera Next Next -- would be built on the WebKit HTML/CSS renderer and Google's open-source V8 Javascript engine instead.

Why is it good news?

This is, I think, good news for both Opera users and Opera as a company. Opera and WebKit: a personal perspective by Bruce Lawson is of the same mind, writing pragmatically that, "Operas Presto engine was a means to an end".

The browser landscape has changed significantly since IE dominated the world with over 90% market share in 2004. IE now has less than 50% worldwide browser share (desktop share is 54%), but Chrome and Firefox each have about 20% as well. Users for some sites, like Wikipedia, are divided up much more evenly between Chrome, Firefox and IE with Safari and Opera making up about 10%. The point is, that the browser market is considerably different than it once was -- and all participants are actively working against documented W3C standards and specifications. Sure, there are still browser-specific CSS prefixes and some highly specific implementations (e.g. the file API from Google) but they mostly stick to a process and all browser vendors have input into an open process. And Gecko and WebKit code is open source.

As Lawson puts it so well,

These days, web standards arent a differentiator between browsers. Excellent standards support is a given in modern browsers. Attempting to compete on standards support is like opening a restaurant and putting a sign in the window saying All our chefs wash their hands before handling food.

Why WebKit?

All of which is why Lawson (he's the guy who wrote the press release for Opera's move to WebKit) writes,

[i]t seems to me that WebKit simply isnt the same as the competitors against which we fought, and its level of standards support and pace of development match those that Opera aspires to.

The Trident code-base -- the renderer for Microsoft's browsers -- was and still is closed-source. While it has been much more actively developed in the last couple of years, back in the bad old days of IE6, the code base was stagnant, lacked innovation and had little to no standards support.

WebKit is certainly not any of these things. If you follow the WebKit changelogs, it's clear that the majority of changes are to implement an HTML5 feature or to improve performance or to use less memory for common tasks. This is a vibrant, highly active and open-source project with developers from at least two large development teams -- Apple and Google -- actively contributing to it.1

Opera adding their cadre of excellent engineers to the mix can only be a good thing -- for everyone involved. They've already written that they plan to port their superior HTML5 forms support to WebKit, a very useful feature on which other participants were dragging their feet.

Running just to stay in place

A while back, Opera made a valiant attempt to get the whole browser running in a hardware-accelerated mode and almost made it, but had to pull back just before release because of stability issues on some machines. The mode is still there if you want to enable it. Flow layout is great and WebGL also made it in, but the implementation lagged. As did many other features.

While Opera was first to implement a few features -- like HTML Forms or their excellent SVG support -- they were having trouble keeping up and implementing every working draft and standard. I wonder whether the Opera dev team took a look at the Comparison on Opera vs. Chrome and despaired of ever catching up. For the first 3/4 of the page, Opera and Chrome are dead-even. And then things go downhill for Opera from there: 3D Transforms, filter effects, masks, touch events, border images, device-orientation events. These are all things that are standardized (working drafts anyway) and that I have used -- or wanted to use -- in current web projects.

They probably had to make hard choices about where to invest their time, energy and money. Some have tried to argue that the departure of the Presto engine will adversely affect standards acceptance. The article Hey Presto, Opera switches to WebKit by Peter Bright writes the following:

Opera could have gone the route that Microsoft has chosen, trying to educate Web developers and providing tools to make cross-platform testing and development easier, but perhaps the company [...] felt that asking Web developers to stick to standards was [...] futile. Historically, the company has tried to do just this, but its success at influencing real Web developers has been limited; for all the emphasis placed on standards, many developers don't, in practice, care about them.

The developers over which Opera had influence were most likely already coding to standards. The move to WebKit isn't going to change any of that. Opera's 2% of the market was not enough to even get any developers to test with it, and many products clearly got managerial approval to just stick an if (Opera) { fail(); } statement in their web sites to prevent their site from appearing buggy in an untested browser. It's hard to see how Opera's move will change standards development or acceptance.

Why Opera?

I don't think that most users chose Opera because of its renderer. In all honesty, there were very few cases where Opera rendered a site better than other browsers -- and enough examples where Opera did not render as well, whether due to missing functionality or deliberate crippling by the site.

Innovation

Opera has led the way with many non-renderer innovations.

  • Tabbed browsing
  • Speed dial
  • Browsing sessions (tabs+history)
  • Popup blocking
  • Restart from last browsing session (with history)
  • Mouse gestures
  • Searchable window list (essential when dozens or hundreds of tabs are open)

These features were all pioneered by Opera and many have since been adopted by other major browsers (either natively or through extensions). It's certainly a good thing to think that the development team that brought you these innovations and features will be spending less time on HTML5 minutiae and more time on browser features like these.2



  1. The Chrome team has a stated goal of pushing changes in Chromium back upstream to the WebKit main line. Apple has historically done a lot of this as well. I'm not sure what the situation is now, so take my statement with a grain of salt.

  2. This is not to say that I haven't considered the possibility that Opera will, instead of moving the high-level dev staff to working on WebKit patches, simply drop them from the staff in order to save money. Their press release didn't indicate that they were slimming down, but then it wouldn't, would it? Time will tell.

Debugging PHP

This article originally appeared on earthli News and has been cross-posted here.


PHP is a programming language like any other; like any other, it's possible to construct a bug complex enough that it can only reasonably be solved with a debugger. Granted, most PHP code is quite simple and limited to single pages with single include files and a limited library or framework. However, the advent of PHP5 has ushered in more than one team with the courage to build a full-fledged web framework. You would think that the state of PHP development had concordantly improved to the point that debugging scripts -- on a local web server, at the very least -- would be a no-brainer.

You'd be surprised.

The developer of the earthli WebCore1 was courageous enough to attempt building a framework with PHP3. Since debuggers for PHP at that time (circa 1999) weren't on anyone's radar, PHP developers made do with the vaunted echo and print commands to simulate debugging. The WebCore quickly acquired a Javascript-based logger to which logging commands were written. Such methods only take one so far: for more complexly nested and recursive code which is more object-oriented and has a much larger stack, a debugger is really needed.

The port from PHP3 to PHP4 was accomplished without a debugger, but that was long ago. When it came time to port from PHP4 to PHP4.3, things were much harder. It is highly likely that very few developers encountered issues during that upgrade but complex libraries with heavy use of references felt the pain. PHP4.3 was ostensibly a maintenance release but included fixes for reference handling that not only caused a tsunami of new warnings but also subtly changed how references were handled. This was a portent of things to come in the far greater problems encountered when porting from PHP4.3 to PHP5.

Developers that never developed for PHP4 will need a bit of background on how references used to work. Succintly put, PHP4 by default created copies on variable assignment rather than assigning references. In order to get a reference instead, a developer had to explicitly request one with a special operator (&). Larger libraries with many methods soon became littered with ampersands. Even better, forgetting just one in a parameter caused PHP to create a new copy of the passed object. Changes made to that object within that routine were applied to the copy and mysteriously disappeared when the method call returned.

This was obviously an untenable situation so PHP5 -- based on the Zend 2.0 engine -- reversed the default. Under PHP5, assignments that previously created copies now created only references. Mix this with a large library with many such implicit copies hidden throughout the code and let the fun begin. Luckily, incredibly savvy developers who had read of this change enforced an iron discipline and limited these types of implicit copies to only a few, well-marked places.2 Thus, the massive pain entailed in a port from PHP4 to PHP5 was somewhat ameliorated.

As you, dear reader, can imagine, the need for a debugger at this point became overwhelming. Let the search begin.

PHPEclipse was the default editor throughout the development of the earthli WebCore, so the first step was to check out what they had to offer. It turns out that the current version of PHPEclipse supports both the XDebug and DBG debuggers. The XDebug debugger was mentioned much more often in the forums, so it seemed like a good place to start.

Though you can debug PHP using only the executable (PHP.EXE), you'd have to configure that executable to include all the extensions and settings that are already configured with the local web server you've likely got installed. It's not that it can't be done, but that the most convenient way to debug would be to just execute the page on the web server you're already using for testing. So, step one is to get the debugger extension loaded in your local server. If you're developing on Windows3, the WAMP server package is an excellent, highly-configurable solution. For porting from PHP4 to PHP5, it also offers the unique ability to change from one to the other within seconds. It has numerous addons corresponding to previous releases of Apache, MySql and PHP which seamlessly integrate with the main installation. What it does not have is an installer for configuring the appropriate version of either XDebug or DBG.

It seems that PHP developers don't, in general, use debuggers.

As usual with such things online -- that is, things that very few people do -- instructions are available, but they must be pieced together from several different locations. XDebug was up first and was, after many false starts, loaded by the local server. Some things to watch out for:

  • Get the first version of the extension; the wrong version is simply not loaded and the error message is either entirely suppressed or so well-hidden in the log files as to be well-nigh undetectable.
  • Instructions to set the property "zend_extension" on Windows are wrong; you must use "zend_extension_ts" instead (the threaded version).
  • Make sure you are editing the correct PHP.INI file; in WAMP, this is the file located in the bin folder of the currently-loaded version of Apache.

Once you are rewarded with the XDebug extension in the phpinfo() page, you're ready to start debugging. Following the instructions at the PHPEclipse wiki, though confusing, will get you stopping at a break point soon enough. Imagine that! A breakpoint in PHP! Press F6 to step over that line and ... wait ... and wait ... listen to the fan on your laptop start. Apache is using 100% CPU or as close to it as it can. Wait several minutes for things to sort themselves, but they never do. Use the Task Manager to kill the offending instance of Apache and you simply transfer the problem to Eclipse, which begins using 100% CPU. Long story short, debugging with XDebug never got farther than this relatively low point. The inital breakpoint worked, but nothing else.

On to DBG.

To keep things a bit shorter, DBG never even stopped at the initial breakpoint, regardless of settings.

The open-source world, it seems, has nothing to offer on the PHP debugging front.

On to Zend.

Zend makes the scripting engine used by PHP. They make numerous tools for analysis as well as the Zend Developer Studio. It costs 399 Euros and brags about its debugging capabilities right on the home page. It has a 30-day trial. It all sounds so promising. One 330MB-download and installation later and you've got the Zend Studio up and running. Once you've configured a new project, you can set up a debug configuration, which comes with copious well-written help as well as a "Test Debugger" button right in the configuration window.

As before, you can run the debug session using a local executable, but the more useful setup is to run the debugger through the local web server. To do this, you need to install the Zend debugger extension, which has much better instructions in the Zend Studio help.

Long story short, 399 Euros buys you a working debugger for PHP that flawlessly debugged code in several files -- including code located in "Include Paths" -- and was exactly like any other debugging experience in Eclipse.

So, if you need to debug PHP, you can either take the cheap route and hope that the open-source solutions work for your code or you can take the plunge and use the Zend Studio -- if you're actually earning money with PHP development, choice (B) is the logical one.

This PHP developer, on the other hand, is going to get his port from PHP4 to PHP5 done in the next 30 days.



  1. You guessed it: yours truly.

  2. Obviously, that was sarcasm. Discipline goes a long way, but only goes so far.

  3. The WebCore is currently developed on Windows because it's the most powerful machine available -- the PowerPC Mac Mini just doesn't hold up with Eclipse-based IDEs

Objects In JavaScript

JavaScript is a nominally object-oriented language with extremely loose typing. However, it is possible to build objects which are based on class definitions, or prototypes in JavaScript parlance. Any object in JavaScript can have properties and methods attached to it -- at any time. A prototype is nothing more than a function call which returns a generic object configured in a specific way.

Objects are created with the new operator; any function can be used as a constructor. Use the this property to access the object being constructed and to assign properties and methods to it. There are two ways to assign methods:

  • Connect functions to the object in the constructor
  • Use the prototype keyword to define functions as belonging to a particular class

When using a standard function, the function is connected to a particular name to create a method. When the function is called through the method, the this property is available within the function scope.

function MyClass (n)
{
  this.Name = "MyClass__" + n;
  this.PrintName = MyClassPrintName;
}

function MyClassPrintName ()
{
  document.Write (this.Name);
}

The prototype keyword offers a way of defining classes without cluttering the global namespace with functions.

function MyClass (n)
{
  this.Name = "MyClass__" + n;
}

function MyClass.prototype.PrintName = function
{
  document.Write (this.Name);
}

The object can now be created and used with the expected syntax:

var obj = new MyClass ("Johann");
obj.PrintName ();

With a little more effort, you can even have inheritance and method redefinition in JavaScript. See Inheritance in JavaScript for more information.

Inheritance In JavaScript

Objects in JavaScript showed you how to build and instantiate classes in JavaScript. The flexible prototype mechanism can also be used to effect a form of inheritance.

Assigning an Ancestor

Since an object definition is simply a function call which applies properties and methods to a generic object, inheritance is a series of chained function calls, all acting as constructors. The descendant simply calls the function for its ancestor, so it can set up inherited methods and properties. The example below shows how to assign the base class:

function BaseClass ()
{
  // initialize BaseClass properties
}

MyClass.prototype = new BaseClass;

function MyClass (n)
{
  // initialize MyClass properties
}

It is customary to declare classes and inheritance in usage order, to ensure browser compatibility.

Redefining Methods

Since methods are attached to an object as properties, they can easily be overridden by simply re-assigning the property. This emulates the polymorphism we expect from more strongly-typed languages. The example below uses the prototype syntax to declare methods for the two classes defined above.

BaseClass.prototype.update = function ()
{
  this.printName ();
}

MyClass.prototype.update = function ()
{
  this.printName ();
  this.printAddress ();
}

If the update() method is called on a MyClass object, it prints both the name and address, as expected.

Calling Ancestor Methods

The code above raises another question: how can you call inherited methods? It would be nice to be able to call the inherited version of update in order to execute printName() instead of repeating the code in the descendant implementation. The solution is again relatively obvious, but not elegant: create a property of type BaseClass and call its methods through this reference. The final definition of MyClass is shown below:

MyClass.prototype = new BaseClass;
      
function MyClass (n)
{
  this.parent = new BaseClass ();
}

MyClass.prototype.update = function ()
{
  this.parent.update ();
  this.printAddress ();
}

Note that this technique uses a separate copy of the ancestor; it will not have access to the shared properties set on the descendant unless they are explicitly synchronized. That is, if in the code above, printName() accesses the Name property of the object, it will use the Name from parent instead of from this. Nonetheless, it can still be useful when used with care.

See the Countdown Timer Object example for a real-world use of inheritance in JavaScript.

Countdown Timer Object

This tutorial expands on the timer built in Countdown Timer Basics. In that example, we ended up with the following code for displaying a countdown timer in an HTML page.

var StartValue = 10;

setTimeout ("HandleTimeout()", 1000);

function HandleTimeout ()
{
  var CountDownElement = document.getElementById ("countdown");
  StartValue--;
  CountDownElement.innerHTML = StartValue;
  setTimeout ("HandleTimeout()", 1000);
}

We will take this example and convert it to an object, so that a page can support multiple timers. Specifically, we will:

  • Show how to use the setTimeout function with a method
  • Use inheritance to generalize timer handling

The following examples make use of the prototype syntax used to define objects in JavaScript. See Objects In JavaScript for more information.

Methods as Callbacks

The first parameter to the setTimeout function can be a string or a function pointer. If it is a string, it is evaluated with eval(), or, more specifically, window.eval(). The distinction is important because it determines the execution context. That means that code passed to setTimeout is evaluated in the scope of the window -- references to this will refer to the window, not your timer object.

How do we get around this limitation? Again, a quick search on the Internet will tell you that you have to pass a function pointer -- and that you can't use the this reference to call it. Most suggestions run along the lines of the following example:

MyClass.prototype.ScheduleTimer = function ()
{
  var me = this;
  setTimeout( function() { me.update (); }, 1000);
}

Let's take a look at what's going on here:

  • Instead of passing a pointer to an exisiting function, we pass an anonymous function. This is simply a way of defining code to execute without cluttering the global namespace with callback handlers.
  • The reference to this is assigned to the variable me, which is available at the global scope.1 When setTimeout() executes the anonymous function, me refers to the correct object and will execute update() accordingly.

Multiple Timers?

The example as written above has a problem: it doesn't work when there is more than one MyClass defined on the page. If two timers are created and scheduled, the variable me will hold a reference to the second timer. When the two timers fire, they will both call update() on the second object. The solution isn't too difficult, but is also not so elegant. Using a global cannot be avoided; it's the only common scope for the eval() context and MyClass. However, we can use the JavaScript Array object to keep track of multiple references and retrieve the correct one when the timer is fired.2 This technique requires that the object is uniquely identifiable somehow (we use a Name property) and is shown below:

var Timers = new Array ();

MyClass.prototype.ScheduleTimer = function ()
{
  timerName = this.Name
  Timers[ timerName ] = this;
  setTimeout( function() 
              { 
                Timers[ timerName ].update (); 
              }
            , 1000);
}

Final Version of the Timer

The code below makes use of techniques illustrated in the following articles. Some error-handling code has been left away for clarity's sake.3

var Timers = new Array ();

function GenericTimer ()
{
  this.Name = "";
  this.Interval = 1000;
}
   
GenericTimer.prototype.ScheduleTimer = function ()
{
  timerName = this.Name
  Timers[ timerName ] = this;
  setTimeout( function() 
              { 
                Timers[ timerName ].update (); 
              }
            , 1000);
}
   
GenericTimer.prototype.Update = function ()
{
  this.ScheduleTimer ();
}

CountdownTimer.prototype = new GenericTimer;

function CountdownTimer (numSeconds, elementName)
{
  this.Name = elementName;
  this.Seconds = numSeconds;
  this.Element = document.getElementById (this.Name);
  this.ScheduleTimer ();
}

CountdownTimer.prototype.update = function ()
{
  this.Seconds--;
  this.Element.innerHTML = this.Seconds;
  this.ScheduleTimer ();
}

Note that, as described in the final section of Inheritance in JavaScript, we cannot call the inherited update() method through a parent because the ScheduleTimer() method makes use of the instance variables, Name and Interval.


  1. Variables declared with var are global. At the global scope, var is required; within a function, variables declared without var are available only within the function.

  2. This pattern would be much more difficult to implement without anonymous functions.

  3. elementName should be assigned, numSeconds greater than 0, etc.

Countdown Timer Basics

There are two main tasks involved in making a countdown timer:

  • Starting a timer and handling the event
  • Updating the page dynamically

Setting the Timer

As a quick search on the Internet will tell you, you request a timer in JavaScript with the setTimeout function. Specifically, this is the window.setTimeout method.1

The function takes two parameters: the JavaScript to execute when the timer fires and the number of milliseconds to wait until it does. A basic timer and handler is shown below:

setTimeout ("HandleTimeout()", 1000);

function HandleTimeout ()
{
  // Perform some work
  setTimeout ("HandleTimeout()", 1000);
}

Note that JavaScript only fires the timer once. If you want to fire again, you have to trigger the same timer from the event handler. Let's move on to updating the page dynamically from the function and see some results.

Updating the Page

In order to update the page, you need to identify the element in the page that will be updated and then dynamically change the content for that element. We will be using the DOM model that all modern browsers provide for access to page elements.2

Elements in the DOM are identified by unique ids รข an id is attached to an HTML tag like this:

<div id="countdown"></div>

This element can now be accessed using the DOM API function getElementById() on the global document object.

CountDownElement = document.getElementById ("countdown");

CountDownElement is an HTML DOM element which has many properties, one of which is innerHTML. Do not be alarmed that the W3C has deprecated this API (it was introduced by Internet Explorer); it is supported by Opera, Firefox and IE and Safari and can be considered stable.3

CountDownElement.innerHTML = "My Value";

First Version of a Timer

Shown below is a timer that counts down from 10 in 10 seconds. Since this is JavaScript, the script fails silently if the CountDownElement is not assigned (i.e. there is no HTML tag with an id equal to countdown). More precisely, it simply assigns the property innerHTML on a generic global object, CountDownElement, which has no effect on the HTML page itself.

var StartValue = 10;

setTimeout ("HandleTimeout()", 1000);

function HandleTimeout ()
{
  var CountDownElement = 
    document.getElementById ("countdown");
  StartValue--;
  CountDownElement.innerHTML = StartValue;
  setTimeout ("HandleTimeout()", 1000);
}

Go to the Countdown Timer Example to see it in action!

Making it Object-oriented

If you're used to working with objects, you probably don't like the idea of littering your namespace with a global counter and global functions. The version above is also limited to a single counter per page. Let's see if we can't wrap the code above into a class. See Countdown Timer Object for an object-oriented version.



  1. This distinction is important for understanding how to use setTimeout from within a method (see Countdown Timer Object).

  2. There are many examples on the Internet that perform a check for document.all or document.layers. These provide support for Internet Explorer 4.x and Netscape Navigator 4.x respectively. Any browser made in the last 5 years supports getElementById().

  3. See Dynamic Content in Firefox for more information and an example of official DOM support for ranges and selections. It's not quite as easy to follow as assigning the innerHTML property.

Debugging IE 6: Operation Aborted

Develop your web application using Firefox. Validate your (X)HTML, validate your CSS, test your JavaScript. Tweak graphics, tweak layout. Get the client to sign off. Now that everything's looking and working just right, it's time to get it running in IE. Fire up IE and load the application.

Internet Explorer cannot open the Internet site ''http://your.hostname.com/''. Operation aborted

?!?!

Lingering problems with PNG graphics, improperly interpreted CSS, imaginative approach to HTML layout -- these are the types of problems you expect in IE. But IE refusing to load any page in the application at all? That's a new one.

What happened?

The first step was to load the TamperData extension with Firefox to get a look at the HTTP request and response headers. From this, it appeared that the server was using an HTTP 1.1-only feature by setting the Transfer-Encoding to chunked. Forcing Firefox to use HTTP 1.0 disables this feature and returns a Content-Length instead. Go back to IE and force the HTTP compliance to version 1.0 and reload the page. Nothing. Same error.

So it doesn't seem to be anything on the server ... could it possibly be the content? This article, Internet Explorer Programming Bugs, yielded a wealth of information, including the following lead:

Apparently interacting with innerHTML and possibly using other JScript functionality causes IE to pop up "Internet Explorer cannot open the Internet site http://example.com. Operation aborted." messages after loading a page. ... It seems that IE doesn't like when somebody is trying to modify content of "document.body" by adding new elements (previous example) or by modifying its innerHTML (my case).

Go back to the application and remove all scripts from the page, including the dojo libraries included by Tacos, an Ajax/scripting framework for Tapestry. Reload the page in IE and it loads without problems.

Getting closer.

The Fix

Well, we can't just shut off Javascripting for a modern web application, so let's check for dojo/IE6 conflicts. Digging further turns up this post, [Dojo-checkins] [dojo] #557: IE 6 refuses to load page if dojo is loaded in HEAD and a BASE tag exists before that. The title says it all. Re-enabling the scripts and loading in Firefox confirms that there is indeed a base tag before the dojo scripts.

Ok. So that seems to be the problem. We use the @Shell Tapestry component to render the HTML head, which has a renderBaseTag property. Set this property to false and the page works in IE as designed1.

Addendum

We're using Tapestry 4.0.1 and had already extended the @Shell component to accept an array of scripts (analogous to the array of stylesheets it already accepts). Therefore, we simply changed the implementation to output the base tag at the end of the <head> section, to avoid any future conflicts in IE6.

After a cursory examination of the sources for Tapestry 4.1, it seems that both the delegate and ajaxDelegate are rendered before the base tag, if present. This means that Tapestry 4.1 applications that require a base tag will also not function in IE6.

We'll have to submit a patch in order to get this fixed once and for all.



  1. Well, kind of; now we have to fix PNGs and CSS and HTML and ... but we at least expected that kind of work. Outright refusal to load was something new and special, even for IE. Using Firefox 1.5, TamperData 9.8.1, IE 6 SP2, Dojo 0.4, Java 1.5, Tapestry 4.02 and Tacos 4.0.1

Testing in IE

As of October 31st, 2006, Microsoft is offering Internet Explorer 7 as an automatic update, so the number of users who have this version installed will increase dramatically. However, in all likelihood, you will need to continue to support IE6 for the foreseeable future as well. How can you test in both browsers on one machine? Multiple IE to the rescue!

image Download Multiple IE

This nifty utility builds on the excellent work documented in Multiple IEs in Windows by Joe Maddalone. For developers working with larger, enterprise clients who insist on using an older version of IE, the installer includes 3.0, 4.01, 5.01, 5.5 and 6.0, though anything older than 5.5 would be amazingly out of date.