Palm

Cross Platform Text-Indexer

by on Nov.25, 2010, under Web Dev, webOS

While working on a recent Facebook release we ran into a performance bottleneck within the Mojo.Format.runTextIndexer API. Performance on device was on the order of a couple of seconds to process the content for feed items 30-50. This combined with the other tasks in the formatting and rendering cycle led to poor performance in the news stream.

For those who are not familiar with the Mojo.Format.runTextIndexer API, this API scans human generated text for URLs, phone numbers and email addresses, replacing the plain text representation with HTML links to the particular object. Additionally this API will replace emoticons with image representations on supporting platforms.

With the news stream scene being the primary scene in the application it was vital that we make this as performant as possible so I spent some time profiling this and determined that the bulk of the time in this operation was spent in the generic C indexer logic. Since at it’s core this is just a text scanning heuristic that, for the most part, does not rely on any C-level constructs, I decided to investigate a Javascript implementation to see if this would be more performant.

The result was a Javascript implementation that was many magnitudes faster than the core implementation (multiple seconds vs. ~10ms for a variety of Facebook news feeds), allowing for much faster execution as well as the creation of the library being released today.

While this library is designed primarily for webOS applications, it has also been designed to work in cross-platform environments for all functionality other than the emoticon replacement which requires platform specific logic to determine the proper image to replace.

Implementation

There are two key components to this implementation, the cross platform link indexer and the platform specific emoticon processor.

The link indexer is a two-stage regular expression, using the quite daunting INDEXER_REGEX regex to extract possible linker tokens with Javascript being used to determine the meaning of each token (or throwing it out just text content).

The emoticon processor is implemented by scanning for known, or possibly known, emoticons using the EMOTICON_REGEX regex and then passing these tokens to the platform’s text indexer implementation. Doing this allows for our own custom implementation while retaining the look and feel of emoticons used by the rest of the platform. For non-Mojo platforms this logic is disabled and emoticons will be left unmodified, with minimal change.

Warnings

HTML Content

Like the API this aims to replace, this API does not handle HTML context when replacing content. As a consequence this algorithm can break HTML content. In order to prevent this, the input and outputs for the TextIndxer.run API should be considered text or minimal HTML that will not match any of the replacements.

It is still possible to use this with content containing HTML by processing in a manner similar to the following although the input still needs to be filtered for XSS and other security concerns.

        var srcText = $(this).val(),
            womb = $("<div>" + srcText + "</div>");

        womb.contents()
            .filter(function() {
                return this.nodeType === Node.TEXT_NODE;
            })
            .each(function() {
                var text = $(this),
                    indexedText = TextIndexer.run(text.text()),
                    womb = $("<div>" + indexedText + "</div>");
                womb.contents().each(function() {
                    text.before(this);
                });

                text.remove();
            });

        $("#previewContent").html(womb.html());

Framework Override

By default this library overrides the Mojo.Format.runTextIndexer API (when used within the Mojo framework). Care should be taken with each OS upgrade to ensure that this override does not break any expected behavior. If uncomfortable with this override then it can be removed by removing these lines from the library and using the TextIndexer.run API directly.

    // Mojo Framework override. Unused on non-Mojo platforms and may be removed if undesired in Mojo apps
    if (window.Mojo && Mojo.Format) {
        // Override the Mojo API if it exists in this context.
        Mojo.Format.runTextIndexer = TextIndexer.run;
    }

Code

The code is available on Github within the text-indexer repository. A cross-platform live demo is also available here.

3 Comments :, , , more...

Implementation: Facebook for webOS Add Tag UI

by on Jun.04, 2010, under webOS

Add Tag Interface

Facebook Add Tag Interface

Yesterday we released Facebook for webOS 1.2.5 Beta which brings many improvements to our photo functionality including tagging support. Now that the release is out the door, I’d like to take some time to discuss the cooler aspects of the implementation particularly the Add Tag UI.

Design

The core layout of the Add Tag UI is an overlay container containing a fixed position header and a scrolling list below that. The entire screen is covered by these two elements, with the header section maintain its height and the content section expanding to cover the remainder.

This fixed header layout is fairly common throughout the app, the most common example being the navbar used in the majority of the application’s scenes, and is fairly easy to solve if the header is a fixed height and the content scrolls under the header. All that needs to be done is to position the header control using position: fixed and apply a margin-top to the list such that the first element in the list appears at the bottom of the fixed element.

I’ve never really like this solution as it requires that the header height be fixed and the list section be “aware” of the header rather than being only concerned about it’s own layout. With this in mind (and also wanting to play around with some of the cool things that WebKit offers but I have not been able to use on projects due to the IE factor), I decided to try out the flexible box layout type in webOS.

Flexible Box Layout

To provide some background, one of the key goals of the flexible box layout module is the ability to specify the size and growth of an element relative to both it’s siblings and it’s parent. While this was previously possible by using percentage layouts, each of the elements had to be aware of the size that their siblings expected and all elements needed to use the same relative system.

This is not the case with this layout scheme. Rather than defining a percentage of the container size, elements are defined with an affinity for the excess space in the container. This means that the layout first attempts to fit everything as it would normally layout, then adjusts each element based on the difference in size between the children and the parent. In practice this can generate much more stable, but still fluid layouts when creating applications in HTML+CSS.

The box-flex (-moz-box-flex, -webkit-box-flex) is used to define the resize affinity.

Values here can range from zero to any positive decimal value. When the value is zero, the element will not change size based on extra or lack of space in its parent. When this value is larger than zero the element is considered flexible. This means that when the layout engine determines that the parent box is either overflowed or underflowed these elements will expand or shrink such that all of the children fill the container. The differences in magnitudes of this value determines how the flexible elements are resized.

As an example a vertically oriented flexible box with 3 children whose flex values are 0, 1, and 2 and has 60px of open space will add 20px to the second element, 40px to the third element and leave the height of the first unchanged. On the other end of the spectrum if there is an overflow of 60px, the elements will be shrunk by the same dimensions above.

The flexible box model is supported by Safari 3+, Chrome 3+, Firefox 3+, and of course webOS. As covering the entirety of the specification was out of the scope of this post I would recommend reading some of the other excellent sources on this topic as well as the specification itself.

Implementation

For the Add Tag UI, we take advantage of the flexible overflow case outlined above. This allows the upper section to layout at it’s natural size using -webkit-box-flex: 0 and the user list section to expand to fill the remainder of the page using -webkit-box-flex: 1. By defining the list section to be a Scroller with an embedded List, we can maintain fixed behavior for the upper section and still allow for an arbitrary number of users.

Implemented this looks something like the following:

<div class="tag-selector">
  <div class="tag-selector-panel">
    <div x-mojo-element="TextField"></div>
    <div class="form-wrapper">
      <div x-mojo-element="Button"></div>
      <div x-mojo-element="Button"></div>
    </div>
  </div>
  <div class="tag-scroller" x-mojo-element="Scroller">
    <div x-mojo-element="List"></div>
  </div>
</div>

With the CSS doing most of the heavy lifting:

.tag-selector {
  position: absolute;
  top: 0;
  left: 0;
  bottom: 0;
  right: 0;

  z-index: 1000;

  display: -webkit-box;
  -webkit-box-orient: vertical;
}

.tag-scroller {
  position: static !important;
  -webkit-box-flex: 1; 
}

A live demo for Firefox and Safari/Chrome available is available here. Note that the demo is running a modified version to handle desktop and cross-browser differences. These are mostly experimental prefix concerns i.e. display: -webkit-box becomes

    display: -webkit-box;
    display: -moz-box;
    display: box;

Although there are a few hacks in place to force common layout under both WebKit and Gecko (See Bug 570036). Such is life developing with CSS3 :)

Future Posts

As a final note, I’d like to start a series of these posts. Are there any sections of the Facebook app that you would like me to provide an overview of their implementation? Feel free to comment on this post or drop me a line at kpdecker@gmail.com.

2 Comments :, , , , more...

Javascript Operation Queue

by on May.28, 2010, under Dev, Palm, Web Dev, webOS

When developing for a platform that relies on asynchronous APIs, such as webOS, the application logic frequently will need to block on a given operation prior to executing other dependent components. This may include anything from loading user data after authentication to saving data after initialization of a given data structure among others.

One method of handling this problem is to make the blocker component explicitly aware of the dependent components and the unique interface to each component, which works for simple cases or situations where the dependent to blocking relationship is one-to-one, but this quickly becomes complicated as a number of dependent components grows. In the extreme cases the blocker may have to be aware of significant portions of the system, leading to maintenance concerns.

Alternatively the blocker call can allow dependent components to register their interest in the completion of the operation and upon completion the blocker can simply notify the components on this list in a generic fashion. This allows that components to remain loosely coupled and has the added benefit of allowing for run-time conditional relationships without requiring that the blocker be aware of the state of the dependent.

Implementing such a notification system is fairly straightforward in Javascript: Simply collect waiting callbacks in an array or other structure then executing each upon completion of the blocking call.

Library

While simple to implement, my experience onFacebook for webOS has shown that a library to implement this behavior is worth the initial effort as manually writing nearly identical for loops over callbacks for the umpteenth time becomes tedious and error-prone.

To this end, we developed and open sourced the OperationQueue class which provides a common implementation that doesn’t require far too many for loops :)

Usage

To use the OperationQueue class you simply need to enqueue your dependent operations using the queue API.

queue can accept a single function which will be called upon successful completion.

    opQueue.queue(function(result) {
        // Do something with the data that we were waiting for!
        console.log("Blocking Operation Completed!");
    });
It also accepts an object with any combination of onSuccess and onFailure fields who will be called for each respective event.
    opQueue.queue({
        onSuccess: function(result) {
            // Do something with the data that we were waiting for!
            console.log("Blocking Operation Completed!");
        },
        onFailure: function(result) {
            console.log("Blocking Operation Failed");
        }
    });

These calls may occur at anytime. If the blocking operation has already completed then calls to queue will result in immediate execution of the queued operation. In this case the result object will not be included.

For the blocking call itself the getSuccessHandler and getFailureHandler generators will return callback functions that may be used to directly on completion or may be passed as callback handlers to the async API. These methods also accept a function parameter which will be called prior to their completion.

Used directly:
(opQueue.getSuccessHandler())();
As a callback:
ServiceRequestWrapper.request('palm://com.palm.preferences/systemProperties', {
        method:"Get",
        parameters:{"key": "com.palm.properties.nduid" },
        onSuccess: opQueue.getSuccessHandler(function(response) {
                Mojo.Log.info("Device ID: %j", response);
            }),
        onFailure: opQueue.getFailureHandler()
    });

For more complicated use cases, the reset function allows for enabling and disabling queuing at any time. For example, if you need to initially allow all operations to proceed and then block only while a given operation is in progress, the getSuccessHandler API may be called immediately after instantiation of the queue and then reset called prior to execution of the blocking operation.

Source:

Operation queue is available in the webos-samples repository on github, within the tipsAndTricks subproject.

One final note: While this was written for a webOS application, it does not depend on any webOS-specific constructs and may be used in any Javascript environment. To see it in action, check out the demo in any browser!

Comments Off on Javascript Operation Queue :, , , , more...

Facebook for webOS: Seed Status

by on May.18, 2010, under Dev, Palm, webOS

With the 1.2.1 release of Facebook for webOS, we are now officially supporting the ability to seed status updates via an application manager launch. This allows for applications that do not wish to interact directly with the Facebook APIs to provide Facebook status posts with minimal effort.

All that is required is a single service request to the application manager.
function seedFacebookStatus(text) {
    ServiceRequestWrapper.request('palm://com.palm.applicationManager', {
        method: 'launch',
        parameters: {
            id: "com.palm.app.facebook",
            params: { status: text }
        }
    });
}

This call will launch the Facebook application, open to the new stream, and populate the update status control in the Facebook application. Once populated the user may edit the message as they please and submit.

A sample project is available on github.

Are there any other features that you would like to see via launch APIs in the Facebook application or for Facebook APIs in general on webOS? Feel free to leave a comment on this post with any ideas that you may have.

9 Comments :, , more...

ServiceRequestWrapper Update

by on May.04, 2010, under Dev, Palm, webOS

This article is part of my Palm Dev Day summary series as well as a follow up to the original service request garbage collection post from last month.

After reexamining the original ServiceRequestWrapper implementation and the possible use cases, some improvements began to show through:

  • Subscribe requests were not protected from garbage collection after the initial complete callback (Thanks to Johan for pointing this out)
  • Requests were not being automatically cancelled on completion
  • The class did not need to be instantiatable as the 90% case can be handled by a singleton

With these issues in mind I decided that a rewrite was in order to make the class easier to use, as this is what the goal was in the first place :).

Non-Subscription Requests

Usage for non-subscription requests now involves a single call, ServiceRequestWrapper.request that is a “fire and forget” call meaning that cleanup is completely automated.

For example a call to determine the device ID can be done as follows:
    ServiceRequestWrapper.request('palm://com.palm.preferences/systemProperties', {
            method:"Get",
            parameters:{"key": "com.palm.properties.nduid" },
            onSuccess: function(response) {
                Mojo.Log.info("Device ID: %j", response);
            }
      });

Note that are no special requirements to cleanup the request object for these types of calls. Upon completion the request object will be cleaned from both the ServiceRequestWrapper data structures as well as any system level data structures.

Subscription Requests

The subscription case is not as simple as the framework can not reliably determine when the request is complete and future results are no longer desired. In order to reliably cleanup subscription requests ServiceRequestWrapper places the cleanup responsibility on the caller, via the cancel method, much in the same way as the Mojo.Service.Request API.

In practice this is not much harder than dealing with the single case. The following example monitors the system preferences for two changes to an arbitrary preference and then cancels any further action on that subscription.

var count = 0;
ServiceRequestWrapper.request('palm://com.palm.systemservice', {
    method:"getPreferences",
    parameters:{ keys: [ "food" ], subscribe: true },
    onSuccess: function(response, request) {
        Mojo.Log.info("Preference changed: %j", response);
        count++;
        if (count === 2) {
            request.cancel();
        }
    }
});

The request API also returns the a request object, which is identical to the 2nd parameter passed to callbacks, for those that need to cancel the request outside of the scope of a callback.

var subsRequest = ServiceRequestWrapper.request('palm://com.palm.systemservice', {
    method:"getPreferences",
    parameters:{ keys: [ "food" ], subscribe: true },
    onSuccess: function(response, request) {
        Mojo.Log.info("Preference changed: %j", response);
    }
});

// And then a miracle occurs

subRequest.cancel();

Source

The updated library is available on github in the palm/webos-samples repository.
Comments Off on ServiceRequestWrapper Update :, , , more...

Palm Dev Day: Adventures in Facebook

by on Apr.27, 2010, under Palm, webOS

Last Friday and Saturday Palm hosted their Dev Day event at their Sunnyvale campus, which I had the pleasure of presenting some of the techniques we learned while developing the Facebook for webOS application.

This presentation covered everything from our best practices to debugging and development tools to the common libraries we developed for the application. I would recommend webOS developers take a look as the topics covered deal with many common app development concerns.

As part of this presentation we have open sourced a collection of the libraries and tools that are used in the Facebook app development.

The slides from my presentation are available on slideshare and a video should be published in the near future. Additional presentations from the event have been made available under the palmdev slideshare tag

On a personal note, I would like to say that it was awesome to meet some of the developers who are using webOS, particularly in the Apps Lab breakout section!

Stay tuned for in depth posts on the topics discussed in my presentation.

Comments Off on Palm Dev Day: Adventures in Facebook :, , , more...

Garbage Collection Gotchas in webOS

by on Apr.02, 2010, under Palm, webOS

Making the jump to mobile development from desktop and server development can lead to quite a few gotchas due to the resource constraints inherent to mobile environments. This became very clear to me today while debugging an intermittent failure in the Facebook application’s news and notifications scenes.

Like every other debugging session for intermittent issues there were quite a few choice words used in the process and many dead ends, but eventually it became apparent that some vital Mojo.Service.Request service requests were not calling any of their notification callbacks. Expecting the onComplete callback to occur at the very least, I was very perplexed until I spoke with some of my esteemed colleagues at Palm and they pointed me to the garbage collector as the possible culprit for this situation.

The code in question was instantiating Request objects but not maintaining references to these objects. This is fine up until the point that the garbage collector runs and the request object as well as all callbacks are collected. At this point these requests could no longer notify the application of their completion, blocking further processing of the queues associated with these requests.

After storing the instantiated Request objects until completion all of the calls that were previously disappearing into the ether began to return as expected.

The moral of the story is that you need to be very careful about what references are maintained when working with garbage-collected languages in memory-constrained environments. If you want something to stay around, like callback methods, you need to be certain that it is referenced somewhere. To ease this task for Mojo.Service.Request calls (controller.serviceRequest calls are safe as the controller maintains a reference to the request object or cancels the request prior to popping the scene), we implemented the following wrapper:

var ServiceRequestWrapper = Class.create({
    initialize: function() {
        this.requestId = 0;
        this.requests = {};
    },

    request: function(url, optionsIn, resubscribe) {
        Mojo.Log.info("Service request wrapper url: '%s' method: '%s' CALLED", url, optionsIn.method);
        var options = Object.clone(optionsIn),
            requestId = this.requestId++;
        options.onComplete = this.completeHandler.bind(this, url, optionsIn, requestId);

        this.requests[requestId] = new Mojo.Service.Request(url, options, resubscribe);
        return this.requests[requestId];
    },

    completeHandler: function(url, optionsIn, requestId, response) {
        Mojo.Log.info("Service request wrapper url: '%s' method: '%s' requestId: %d COMPLETE", url, optionsIn.method, requestId);
        delete this.requests[requestId];

        optionsIn.onComplete && optionsIn.onComplete(response);
    }
});

By instantiating this on a location that will not be collected during the application’s lifetime and using the request method rather than direct Mojo.Service.Request calls for all requests that the result is necessary you can avoid this problematic scenario as well as save the choice words for a later time :)

7 Comments :, , , more...

Visit our friends!

A few highly recommended friends...