Native Client

Messaging System

This chapter describes the messaging system used to communicate between the JavaScript code and the Native Client module's C or C++ code in a Native client application. It introduces the concept of asynchronous programming and the basic steps required to set up a Native Client module that sends messages to and receive messages from JavaScript. This chapter assumes you are familiar with the material presented in the Application Structure chapter.

The "Hello, World" example is used here to illustrate basic programming techniques. You can find this code in the /examples/hello_world directory in the Native Client SDK download.

Reference information

For reference information related to the Pepper messaging API, see the following documentation:

Glossary

asynchronous programming
In the asynchronous programming model, function calls are executed and return immediately without waiting for a response. Using this model, functions are nonblocking; the web browser continues processing JavaScript and C/C++ functions.
Var
An object in a Native Client module that corresponds to a JavaScript variable.
web workers
Web workers provide a mechanism for running heavy-weight JavaScript code on background threads so that the main web page can continue to respond to user interaction. Web pages interact with web workers by using postMessage() to send messages. The way a web page interacts with a Native Client module is analogous to the way it interacts with web workers.

Introduction to the messaging system

Native Client modules and JavaScript communicate by sending messages to each other. Messages are simply strings; it's up to you to define how the strings are processed on both the JavaScript and Native Client side. For example, if you want to pass structured data, you must write functions to serialize and de-serialize the data.

When JavaScript posts a message to the Native Client module, the Pepper HandleMessage() function is invoked on the module side. Similarly, the Native Client module can post a message to JavaScript, and this message triggers a JavaScript event listener for message events in the DOM. (See the W3C specification on Document Object Model Events for more information.) In the "Hello, World" example, the JavaScript functions for posting and handling messages are named postMessage() and handleMessage() (but any names could be used). On the Native Client C++ side, the Pepper Library functions for posting and handling messages are:

  • void pp::Instance::PostMessage(const Var &message)
  • virtual void pp::Instance::HandleMessage(const Var &message)

If you want to receive messages from JavaScript, you need to implement the pp::Instance::HandleMessage() function in your Native Client module.

Design of the messaging system

The Native Client messaging system is analogous to the system used by the browser to allow web workers to communicate (see the W3 web worker specification). The Native Client messaging is designed to keep the web page responsive while the Native Client module is performing potentially heavy processing in the background. When JavaScript sends a message to the Native Client module, the postMessage() call returns as soon as it sends its message to the Native Client module. The JavaScript does not wait for a reply from Native Client, thus avoiding bogging down the main JavaScript thread. On the JavaScript side, you set up an event listener to respond to the message sent by the Native Client module when it has finished the requested processing and returns a message.

This asynchronous processing model keeps the main thread free while avoiding the following problems:

  • The JavaScript engine hangs while waiting for a synchronous call to return.
  • The browser pops up a dialog when a JavaScript entry point takes longer than a few moments.
  • The application hangs while waiting for an unresponsive Native Client module.

Communication tasks in the "Hello, World" example

The following sections describe how the "Hello, World" example posts and handles messages on both the JavaScript side and the Native Client side of the application..

JavaScript code

The JavaScript code in the "Hello, World" example:

  1. Sets up an event listener to listen for message events from the Native Client module.
  2. Implements an event handler that the event listener invokes to handle incoming message events.
  3. Calls postMessage() when the user clicks either the "Call fortyTwo()" or "Call reverseText()" button.

Native Client module

The C++ code in the Native Client module of the "Hello, World" example:

  1. Implements pp::Instance::HandleMessage() to handle messages sent by the JavaScript.
  2. Processes incoming messages. "Hello, World" implements four functions that it invokes from within HandleMessage() to process a message: FortyTwo(), ReverseText(), MarshallFortyTwo() and MarshallReverseText(). The first two functions do the actual work (i.e., generate data in response to the incoming message); the latter two functions (MarshallFortyTwo() and MarshallReverseText()) convert the generated data into the format required by the PostMessage() function.
  3. Calls PostMessage() to send results back to the JavaScript code, in the form of a Var that the JavaScript code can process. A pp::Var can be of type string, int32_t, double, bool, undefined, or Null.

Messaging in JavaScript code: A closer look

This section describes the messaging system code in the JavaScript portion of the "Hello, World" example.

Responding to user input

The following JavaScript code sets up the text input box and buttons on the HTML page. The "Call fortyTwo()" button invokes the JavaScript fortyTwo() function. The "Call reverseText()" button invokes the reverseText() function, which obtains the text from the input box and appends it to the message posted to the Native Client module.

<body onload="pageDidLoad()">

<h1>Native Client Simple Module</h1>
<p>
  <form name="helloForm"
        action=""
        method="get"
        onsubmit="return reverseText()">
    <input type="text" id="inputBox" name="inputBox" value="Hello world" /><p>
    <input type="button" value="Call fortyTwo()" onclick="fortyTwo()" />
    <input type="submit" value="Call reverseText()" />
  </form>
  …

Depending on which button the user presses, fortyTwo() or reverseText() is called. Each function invokes the postMessage() function to send a message to the Native Client module.

function fortyTwo() {
  helloWorldModule.postMessage('fortyTwo');
}

function reverseText() {
  // Grab the text from the text box, pass it into reverseText()
  var inputBox = document.forms.helloForm.inputBox;
  helloWorldModule.postMessage('reverseText:' + inputBox.value);
  // Note: a |false| return tells the <form> tag to cancel the GET action
  // when submitting the form.
  return false;
}

The reverseText() function posts a string as the message. In this example, the function name is the first part of the string, followed by a colon (:) and the value typed into the input box. When the Native Client module receives this message, it parses the string and separates the function name from the input value. The "function:data" message format is simply a convention used in this example. Message strings do not have any required format; you are free to come up with your own convention for how to structure message strings and how to interpret messages on both the JavaScript side and the Native Client side.

Setting up an event listener and handler

The following JavaScript code sets up an event listener for messages posted by the Native Client module. It then defines a message handler that simply prints the content of messages received from the module.

function moduleDidLoad() {
  helloWorldModule = document.getElementById('hello_world');
  // Add a message handler that accepts messages coming from the NaCl
  // module.
  helloWorldModule.addEventListener('message', handleMessage, false);
  updateStatus('SUCCESS');
}

// A simple 'message' event handler that prints the contents of the message
// in an alert panel.
function handleMessage(message_event) {
 alert(message_event.data);
} 

Note that the handleMessage() function is handed a message_event containing data that you can display or manipulate in JavaScript. The "Hello, World" application simply prints the data, which is either "42" or the reverse of the input string entered by the user.

Messaging in the Native Client module: A closer look

This section describes the messaging system code in the Native Client module portion of the "Hello, World" example.

Implementing HandleMessage()

If you want the Native Client module to receive and handle messages from JavaScript, you need to implement a HandleMessage() function for your module's pp::Instance class. The HelloWorldInstance::HandleMessage() function examines the message posted from JavaScript and invokes the requested function (MarshallFortyTwo() or MarshallReverseText()). In the case of MarshallReverseText(), the HandleMessage() function also parses the user-provided string from the message, and passes the string as an argument to MarshallReverseText(). Here is the "Hello, World" code for HandleMessage(), which calls PostMessage() at the end to send a reply message with the result back to the JavaScript side.

void HelloWorldInstance::HandleMessage(const pp::Var& var_message) {
  if (!var_message.is_string()) {
    return;
  }
  std::string message = var_message.AsString();
  pp::Var return_var;
  if (message == kFortyTwoMethodId) {
    // Note that no arguments are passed in to FortyTwo.
    return_var = MarshallFortyTwo();
  } else if (message.find(kReverseTextMethodId) == 0) {
    // The argument to reverseText is everything after the first ':'.
    size_t sep_pos = message.find_first_of(kMessageArgumentSeparator);
    if (sep_pos != std::string::npos) {
      std::string string_arg = message.substr(sep_pos + 1);
      return_var = MarshallReverseText(string_arg);
    }
  }
  // Post the return result back to the browser.  Note that HandleMessage() is
  // always called on the main thread, so it's OK to post the return message
  // directly from here.  The return post is asynchronous: PostMessage returns
  // immediately.
  PostMessage(return_var);
}

Implementing application-specific functions

Of course, your Native Client module will include application-specific functions to perform custom tasks in response to messages. In the "Hello, World" example, the module calls MarshallFortyTwo() and MarshallReverseText(), which in turn call FortyTwo() and ReverseText(). The latter two functions generate the data to be sent back to the JavaScript side. The former two functions, MarshallFortyTwo() and MarshallReverseText(), convert the generated data into the format required by PostMessage(). The Native Client module always returns its values in the form of a pp::Var that can be processed by the browser's JavaScript.

namespace hello_world {
/// Method name for ReverseText, as seen by JavaScript code.
const char* const kReverseTextMethodId = "reverseText";

/// Method name for FortyTwo, as seen by Javascript code. @see FortyTwo()
const char* const kFortyTwoMethodId = "fortyTwo";

/// Separator character for the reverseText method.
static const char kMessageArgumentSeparator = ':';

/// This is the module's function that invokes FortyTwo and converts the return
/// value from an int32_t to a pp::Var for return.
pp::Var MarshallFortyTwo() {
  return pp::Var(FortyTwo());
}

/// This function is passed the arg list from the JavaScript call to
/// @a reverseText.
/// It makes sure that there is one argument and that it is a string, returning
/// an error message if it is not.
/// On good input, it calls ReverseText and returns the result. The result is
/// then sent back via a call to PostMessage.
pp::Var MarshallReverseText(const std::string& text) {
  return pp::Var(ReverseText(text));
}

In the "Hello, World" example, the code for FortyTwo() and ReverseText() is in the file helper_functions.cc:

namespace hello_world {

int32_t FortyTwo() {
  return 42;
}

std::string ReverseText(const std::string& text) {
  std::string reversed_string(text);
  // Use reverse to reverse |reversed_string| in place.
  std::reverse(reversed_string.begin(), reversed_string.end());
  return reversed_string;
}
}

Note that MarshallFortyTwo() and MarshallReverseText() just invoke FortyTwo() and ReverseText() respectively. This is a typical design pattern that shows how plain C++ non-Pepper functions (functions that use standard types like string) can be called by Pepper functions (functions that use Var, for example). This design pattern, in which the plain C++ functions like FortyTwo() and ReverseText() are specified in a separate file (helper_functions.cc), allows those functions to be unit-tested with a command-line test (test_helper_functions.cc); this is easier than running tests inside Chrome.

Sending messages back to the JavaScript code

The Native Client module sends messages back to the JavaScript code using PostMessage(). In this example, the message is posted at the end of the Native Client module's HandleMessage() function:

PostMessage(return_var);

Authentication required

You need to be signed in with Google+ to do that.

Signing you in...

Google Developers needs your permission to do that.