Tales of a Developer Advocate

Developer Relations @ Google

  • window.name

    • 22 Jun 2011
    • 0 Responses
    •  views
    • Edit
    • Delete
    • Tags
    • Autopost

    I have learnt a lot of the last couple of days about inter-window and inter-iframe communication. I documented some of my frustrations about Web Messaging API’s and an attempted work around.

    For you to be able to pass data into a window (that isn’t on your domain) so that it is available before the onload event fires in the opened window, the only sane way I have found is to set the window name via window.open.

    Client:

    var w = window.open("list.html", "some data");

    Service:

    window.onload = function () {  alert(window.name); };

    Now that we can pass data between the windows, you can quickly imagine that you stringify a JSON object on open and parse it in the opened window. Pretty simple.

    The good news is that the work-around works in FF, WebKit and Opera as is, but not IE.

    To get it working with IE, it takes a few of hacks so I thought it best to document them here.

    When you open a window via window.open, the second parameter is the name, in IE it must only contain [A-Za-z0-9_], this means that you have to base64 encode the JSON object for it to be able to be sent across, but that is not enough because Base64 encoding can only use certain characters. Base64 will also likely include an == at the end, which is not an allowed character.

    However, IE doesn’t include a btoa and atob function for managing base64, so you will also need to find a library to use.

    To encode the data I used the following:

    var winname = window.btoa(
      unescape(
        encodeURIComponent(JSON.stringify(obj))
      )).replace(/=/g, "_")
    var w = window.open(e.target.href, winname);

    To decode the data I used the following:

    var obj = JSON.parse(window.atob(window.name.replace(/_/g, "=")));

    Pretty hacky, but it seems to work.

    As always, if anyone has a better suggestion, or there are any obvious flaws let me know.

    • Tweet
  • WebMessaging is broken

    • 21 Jun 2011
    • 4 Responses
    •  views
    • Edit
    • Delete
    • Tags
    • Autopost

    I have been working on a rather cool project recently that initially used a lot of WebMessaging (postMessage etc) to talk between all the components. However, even though these API’s look simple and easy to grok there are some bizaare limitations and usage of them is frustrating to say the least.

    Ignoring the fact that Chrome passes structured clones, and Firefox passes strings, that is a simple difference to resolve. It is not even the fact that WebKit supports MessageChannels and Entangled Ports and no one else seems too. These we can work around in sane ways.

    The normal developers flow is as follows: open a window/iframe, get a reference to that window, send it a message, have the frame or window handle the message.

    Client App:

    var w = window.open("test.html");
    w.postMessage({ data: "some more data"}, "*");

    Service App: test.html

    window.addEventListener("message", function(e) {
      // Do something with the data
    }, false);

    This would be pretty simple and intuitive, something that nearly every developer would be able to pick up in an instant. But this isn’t the case, if you want to send a window a message you have to wait for it to load – which might be a logical assumption, but given that if the page you are opening is outside the origin of the opener, you can’t easily tell when it loads. So the current solution is on the host page to postMessage back to the window.opener, and for the opener to handle the message.

    Client App:

    var w = window.open("test.html");
    window.addEventListener("message", function(e) {
        if(e.data.state && e.data.state != "ready") return; // do nothing.
        // Send data
        e.source.postMessage({ data: "some more data"}, e.origin);
    }, false);

    Service App: test.html

    window.addEventListener("load", function(e) {
        // tell the opener that it is ready to receive messages
        e.source.postMessage({ state: "ready" }, e.origin);
    }, false);
    
    window.addEventListener("message", function(e) {
        // Process data from opening window.
    }, false);

    This is bonkers! Developers just want it to work.

    There is a hack that allows you pass data to a window so that it is available to the window as soon as the script starts executing. It is probably not safe nor is it likely to be secure. When you open a window, you can pass it data immediately using the name parameter on window.open()

    window.open("test.html", "{ data: 'ABC123' }");

    And then on the opening page, you can read it back.

    var data = JSON.parse(window.name);

    There are problems here though:

    • We have no real proof of where the data came from, we can check window.opener but that is not enough
    • We have to ensure that the window.name is cleared down as soon as we parse it, because it will be available for the life time of the application.

    This is a quick hack, that allows the opened window to read the data that was passed to it as soon as it opened.

    What are your thoughts? Have you come across these limitations? Have you solved them in any other interesting ways?

    • Tweet
  • Landing my first WebKit patch. OnPopState Lock and Load.

    • 7 Jun 2011
    • 0 Responses
    •  views
    • Edit
    • Delete
    • Tags
    • Autopost

    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);

    anchor.dispatchEvent(evt);

    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.

    Where do you start? I started by downloading the latest WebKit code and building it. In all this process took longer than the actual fix.

    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.

    Bingo!

    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.

    Beautiful!

    • Tweet
  • About

    I help developers build really cool products on the Web.

    I work for Google as a Developer Advocate in London, specializing in Chrome, HTML5 and the Chrome Web Store.

    231036 Views
  • Archive

    • 2012 (5)
      • February (5)
    • 2011 (30)
      • July (1)
      • June (4)
      • May (10)
      • April (4)
      • March (1)
      • February (5)
      • January (5)
    • 2010 (35)
      • December (15)
      • November (5)
      • October (2)
      • August (5)
      • July (8)
    • 2009 (1)
      • January (1)
    • 2008 (8)
      • December (1)
      • April (2)
      • March (3)
      • February (2)
    • 2007 (17)
      • December (1)
      • September (1)
      • August (5)
      • May (1)
      • March (2)
      • February (4)
      • January (3)
    • 2006 (153)
      • November (3)
      • October (7)
      • September (11)
      • August (9)
      • July (9)
      • June (14)
      • May (11)
      • April (32)
      • March (23)
      • February (26)
      • January (8)
    • 2005 (274)
      • December (7)
      • November (43)
      • October (63)
      • September (54)
      • August (66)
      • July (13)
      • June (10)
      • May (10)
      • April (6)
      • January (2)
    • 2004 (1)
      • August (1)

    Get Updates

    Subscribe via RSS
    TwitterFriendfeedLinkedIn