This is a story all about how my life got flipped turned upside down….. wait what?!?! I can't start a blog post with The Fresh Prince.
Last week, when I was still in my 20's, I wrote a blog post about HTML5 History API needing a new event. This came about because the LeviRoutes framework would work better if it could understand when state had been pushed via History.pushState. Whilst investigating pushState and adding some tests to the LeviRoutes framework I wanted to be able to simulate an 'onpopstate' event.
Let's just quickly digress with a little bit about HTML DOM events. HTML defines a rich series of events that are fired when a user clicks on something, the page loads or….. well let's just say there are hundreds of events. Not only do the events get triggered when the user or system does something, but the developer can easily simulate events. If you want to click on a button via script. Simple:
var evt = document.createEvent("MouseEvent"); var anchor = document.getElementById(......); evt.initMouseEvent("click", true, true, window, 0, 0, 0, 0, 0, false,
false, false, false, 0, null);
Why is this programatic dispatch important? You can build individual tests that are responsive to the events that would fire to user events without having to build a system that tries to automate the UI by say, managing the mouse pointer through hardware control.
Digressions aside, I was building tests that would test my HTML5 History handling logic without me having to physically invoke a History.pushState command. In summary, separating the physical navigation from my logic.
var evt = document.createEvent("PopStateEvent"); evt.initPopStateEvent("popstate".....); window.dispatchEvent(evt);
This should have worked. Instead all I got was:
var evt = document.createEvent("PopStateEvent"); DOMException
Which if you read the Event specification is what occurs when an Event type is not implemented. I quickly jumped across to Firefox and tested the same code. It worked. So it must be a bug in WebKit.
Now, this is where my real story starts, and what I hope will demonstrate the power of Open Source software.
I was in a bind. I could raise a bug and hope someone might pick it up at some point in the future, or I could raise a bug and try to fix it myself. I chose the latter. I didn't think it would be hard – after all the Event system is already in WebKit and PopStateEvent is already implemented, it is only the hookup with createEvent that didn't.
Once I had a build, I decided to create a very simple test case to prove that it is still broken. With this in hand, I had a quick peek at the Webkit code. Google Code search is your friend here, I just searched for 'PopStateEvent' and it returned a list of important places to look.
Inspecting PopStateEvent.cpp and PopStateEvent.h, I could see that there was a create and initPopStateEvent methods so I was pretty sure I was in the roughly the correct place. I also knew that createEvent is on the document object in the DOM, so I did a quick search for createEvent and there was a file called 'Document.cpp', this looked promising. A quick search in Document.cpp highlighted the area where the events are created, and there was a suspicious lack of PopStateEvent.
I quickly raised a bug, on http://bugs.webkit.org/ detailing the error with a simple test case attached, and then went about fixing the code. Raising the bug seemed to take longer than the fix, which amounted to adding in a condition for the type of event, adding in a parameterless constructor and then calling it.
Pretty quick. My own test case passed, so I had a strong indication that it was fixed, but I knew if I submitted it without an automated test it would probably get rejected. The problem is that I had no idea how to build the automated tests or where to put them.
I had a quick scan through LayoutTests, and in the 'fast' directory there is an 'events' directory which seemed liked the logical place to start. I followed the examples of other tests, I created a simple test and an 'expected' results file and then gave the test runner a go. Boom! it failed. It took a little bit of looking, I found that the results of the test run were stored in '/tmp/layout-test-results/results.html' and it gives you a visual diff of the actual output vs the expected – it was a single new line character that was causing the problem.
That was me done. I created the ChangeLog and attached it to the bug, set r to ? (this was an oddity that I had to learn about). After the first review there were a couple of changes I needed to make. The second review indicated that I updated the wrong ChangeLog and some other smallish issues. But after that it was ok and submitted.
And here it is: http://trac.webkit.org/changeset/88187, it is not a complex fix but it is one I am proud of, if all the ports include the fix then my code will be used by the eleventy billion users of WebKit (ok – I have no idea of the number of users, but I know it is very large number) and now I can get on with fixing my LeviRoutes framework ;)
In my eyes, this is one of the powers of Open Source. Rather than just report a bug and hope someone picks it up, and then wait for the next major release of the software to see if it is fixed, I have the power to go in and fix the problem, and if it stands up to muster I can get the solution published.
About Me: Paul Kinlan
I lead the Chrome Developer Relations team at Google.
We want people to have the best experience possible on the web without having to install a native app or produce content in a walled garden.
Our team tries to make it easier for developers to build on the web by supporting every Chrome release, creating great content to support developers on web.dev, contributing to MDN, helping to improve browser compatibility, and some of the best developer tools like Lighthouse, Workbox, Squoosh to name just a few.