Native Client - Application - Messaging System

Messaging System

This section 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 section assumes you are familiar with the material presented in the Application Structure section.

The “Hello, World” example for getting started with NaCl is used here to illustrate basic programming techniques. You can find this code in the /getting_started/part2 directory in the Native Client SDK download.

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

Native Client modules and JavaScript communicate by sending messages to each other. The most basic form of a message is a string. Messages support many JavaScript types, including ints, arrays, array buffers, and dictionaries (see pp::Varpp:VarArrayBuffer, and the general messaging system documentation). It’s up to you to decide on the type of message and define how to process the messages on both the JavaScript and Native Client side. For the “Hello, World” example, we will work with string-typed messages only.

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 onDocument 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 thepp::Instance::HandleMessage() function in your Native Client module.

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 system 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.

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.

The JavaScript code and HTML in the “Hello, World” example can be found in the example.jscommon.js, andindex.html files. The important steps are:

  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() to communicate with the NaCl module, after the page loads.
function attachDefaultListeners() {
  // The NaCl module embed is created within the listenerDiv
  var listenerDiv = document.getElementById('listener');
  // ...

  // register the handleMessage function as the message event handler.
  listenerDiv.addEventListener('message', handleMessage, true);
  // ...
}
// This function is called by common.js when a message is received from the
// NaCl module.
function handleMessage(message) {
  // In the example, we simply log the data that's received in the message.
  var logEl = document.getElementById('log');
  logEl.textContent += message.data;
}

// In the index.html we have set up the appropriate divs:
<body {attrs}>
  <!-- ... -->
  <div id="listener"></div>
  <div id="log"></div>
</body>
// From example.js, Step 3:
function moduleDidLoad() {
  // After the NaCl module has loaded, common.naclModule is a reference to the
  // NaCl module's <embed> element.
  //
  // postMessage sends a message to it.
  common.naclModule.postMessage('hello');
}

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. This example simply checks that JavaScript has sent a “hello” message and not some other message.
  3. Calls PostMessage() to send an acknowledgement back to the JavaScript code. The acknowledgement is a string in the form of a Var that the JavaScript code can process. In general, a pp::Var can be several JavaScript types, see the messaging system documentation.
class HelloTutorialInstance : public pp::Instance {
 public:
  // ...

  // === Step 1: Implement the HandleMessage function. ===
  virtual void HandleMessage(const pp::Var& var_message) {

    // === Step 2: Process the incoming message. ===
    // Ignore the message if it is not a string.
    if (!var_message.is_string())
      return;

    // Get the string message and compare it to "hello".
    std::string message = var_message.AsString();
    if (message == kHelloString) {
      // === Step 3: Send the reply. ===
      // If it matches, send our response back to JavaScript.
      pp::Var var_reply(kReplyString);
      PostMessage(var_reply);
    }
  }
};

This section describes in more detail the messaging system code in the JavaScript portion of the “Hello, World” example.

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 logs the content of messages received from the module.

// From common.js

// Listen for the DOM content to be loaded. This event is fired when
// parsing of the page's document has finished.
document.addEventListener('DOMContentLoaded', function() {
  var body = document.body;
  // ...
  var loadFunction = common.domContentLoaded;
  // ... set up parameters ...
  loadFunction(...);
}

// This function is exported as common.domContentLoaded.
function domContentLoaded(...) {
  // ...
  if (common.naclModule == null) {
    // ...
    attachDefaultListeners();
    // initialize common.naclModule ...
  } else {
    // ...
  }
}

function attachDefaultListeners() {
  var listenerDiv = document.getElementById('listener');
  // ...
  listenerDiv.addEventListener('message', handleMessage, true);
  // ...
}
// From example.js
function handleMessage(message) {
  var logEl = document.getElementById('log');
  logEl.textContent += message.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 logs this data to the log div.

This section describes in more detail the messaging system code in the Native Client module portion of the “Hello, World” example.

If you want the Native Client module to receive and handle messages from JavaScript, you need to implement aHandleMessage() function for your module’s pp::Instance class. TheHelloWorldInstance::HandleMessage() function examines the message posted from JavaScript. First it examines that the type of the pp::Var is indeed a string (not a double, etc.). It then interprets the data as a string with var_message.AsString(), and checks that the string matches kHelloString. After examining the message received from JavaScript, the code calls PostMessage() to send a reply message back to the JavaScript side.

namespace {

// The expected string sent by the JavaScript.
const char* const kHelloString = "hello";
// The string sent back to the JavaScript code upon receipt of a message
// containing "hello".
const char* const kReplyString = "hello from NaCl";

}  // namespace

class HelloTutorialInstance : public pp::Instance {
 public:
  // ...
  virtual void HandleMessage(const pp::Var& var_message) {
    // Ignore the message if it is not a string.
    if (!var_message.is_string())
      return;

    // Get the string message and compare it to "hello".
    std::string message = var_message.AsString();
    if (message == kHelloString) {
      // If it matches, send our response back to JavaScript.
      pp::Var var_reply(kReplyString);
      PostMessage(var_reply);
    }
  }
};

While the “Hello, World” example is very simple, your Native Client module will likely include application-specific functions to perform custom tasks in response to messages. For example the application could be a compression and decompression service (two functions exported). The application could set up an application-specific convention that messages coming from JavaScript are colon-separated pairs of the form <command>:<data>. The Native Client module message handler can then split the incoming string along the : character to determine which command to execute. If the command is “compress”, then data to process is an uncompressed string. If the command is “uncompress”, then data to process is an already-compressed string. After processing the data asynchronously, the application then returns the result to JavaScript.

The Native Client module sends messages back to the JavaScript code using 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. In this example, the message is posted at the end of the Native Client module’s HandleMessage() function:

PostMessage(var_reply);

Besides strings, pp::Var can represent other types of JavaScript objects. For example, messages can be JavaScript objects. These richer types can make it easier to implement an application’s messaging protocol.

To send a dictionary from the NaCl module to JavaScript simply create a pp::VarDictionary and then callPostMessage with the dictionary.

pp::VarDictionary dictionary;
dictionary.Set(pp::Var("command"), pp::Var(next_command));
dictionary.Set(pp::Var("param_int"), pp::Var(123));
pp::VarArray an_array;
an_array.Set(0, pp::Var("string0"));
an_array.Set(1, pp::Var("string1"))
dictionary.Set(pp::Var("param_array"), an_array);
PostMessage(dictionary);

Here is how to create a similar object in JavaScript and send it to the NaCl module:

var dictionary = {
  command: next_command,
  param_int: 123,
  param_array: ['string0', 'string1']
}
nacl_module.postMessage(dictionary);

To receive a dictionary-typed message in the NaCl module, test that the message is truly a dictionary type, then convert the message with the pp::VarDictionary class.

virtual void HandleMessage(const pp::Var& var) {
  if (var.is_dictionary()) {
    pp::VarDictionary dictionary(var);
    // Use the dictionary
    pp::VarArray keys = dictionary.GetKeys();
    // ...
  } else {
    // ...
  }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值