Developing with Native Client

April 22, 2015

Blog | Development | Developing with Native Client
Developing with Native Client

Native Client is an open-source technology that allows developers to run compiled code in a browser, while maintaining the portability and safety that we all expect from web applications.

As of this writing, Google Chrome is the only browser that supports this technology, and the apps must be built using either C or C++. However, even with the limited support and language options, it’s still a compelling option.  There’s a great example of this in my last post on WebRTC.

To start developing using Native Client in a Windows environment, unzip the SDK, install Python v2.7 and with a few new system environment variable updates, you’ll be in business. Once complete, navigate to the SDKs example folders and run the ‘make’ batch files to compile the resulting .pexe file.

Using this file, combined with the .nmf manifest file, your web server can distribute a portable native client module solution to every Chrome browser navigating to your site.

Note: I’ll be talking about portable native client modules below. The differences between a portable and normal native client module can be found here.

Next, we need to ensure that our new module properly interacts with a a traditional HTML website properly. With a few lines of JavaScript, and the appropriate validation to ensure that we leverage the module in Chrome just for the time being, we can load our module into the page.

<div id="listenerId"></div>

<script>

  // Support for Portable Native Client is tied to Chrome and 
  // for the appropriate mime type value by the browser.
  if (isChrome() && navigator.mimeTypes[‘application/x-pnacl’])
  {
      var listener = document.getElementById(‘listenerId’);
      listener.addEventListener(‘load’, onLoad, true);
      listener.addEventListener(‘message’, onMessage, true);

      var module = document.createElement(‘embed’);
      module.id = ‘moduleId’;
      module.width = 0;
      module.height = 0;
      module.src = ‘path/to/manifest.nmf’;
      module.type = ‘application/x-pnacl’;
      listener.appendChild(module);
  }

  function onLoad() {
      // Module loaded successfully.
  }

  function onMessage(message) {
      // Message received
  }
</script>

Once our ‘load’ event has fired, we can begin communicating with the module using the ‘postMessage’ function, passing just about any JavaScript data type, including a key/value pair of the latter. See here for more details. Note that all communication to the module is done asynchronously, meaning that the postMessage function will return immediately.

 

function sendToModule(value1, value2) {
    var message = {
        input1: value1,
        input2: value2
    };

    var module = document.getElementById(‘moduleId’);
    module.postMessage(message);
}

 

To extract the data within the module, we need to confirm and convert the type by leveraging the helper methods included with the SDK, such as ‘is_dictionary’ and ‘is_number’.

 

virtual void HandleMessage(const pp::Var& var_message)
{
    int input1 = 0;

    // Check if the incoming variable is a dictionary of values
    if (!var_message.is_dictionary())
    {
        return;
    }

    // Convert the value to a dictionary of values
    pp::VarDictionary messageAsDictionary(var_message);

    // Safely extract the “input1” key as an integer value
    if (messageAsDictionary.HasKey(“input1”))
    {
        pp::Var input1Value = messageAsDictionary.Get(“input1”);
        if (input1Value.is_number())
        {
            input1 = input1Value.AsInt();
        }
    }
    // ... Continue handling any other values ...
}


To return data from the module back to JavaScript, we can invoke a similar PostMessage function with supported data types. Once again, several pieces of data can be returned by leveraging a dictionary of values. As noted above, all communication out of the module is done asynchronously. 

void ReturnValue(const char* value1, const char* value2)
{
    pp::VarDictionary dictionary;
    dictionary.Set(pp::Var(“output1”), pp::Var(value1))
    dictionary.Set(pp::Var(“output2”), pp::Var(value2))
    // ... Insert other values to return ...
    PostMessage(dictionary);
}

And finally, our message is received by the browser, with the data automatically converted into a JavaScript object, by the ‘message’ event handler previously defined. 

function onAudioEncoderMessage(message) {
    var value1 = message.data[‘output1’];
    var value2 = message.data[‘output2’];
    // ... Extract any other values and use the results ...
}

It’s great to be able to deliver a quality solution while using some relatively cutting edge technology in the process. The rather small number of use cases for Native Client is unfortunate, but perhaps the power to use native code in a safe and flexible way will help to pave the way for unique and blazing fast web apps.

 

Have you had the opportunity to use Native Client to address a challenge? We’d love to hear about it! Tweet us @GeekHive and let us know!

 

Phil Azzi, Developer, GeekHive

Phil Azzi

Technical Lead
Tags
  • .NET
  • Open Source

Recent Work

Check out what else we've been working on