Home | History | Annotate | Download | only in coding
      1 .. _message-system:
      2 
      3 ################
      4 Messaging System
      5 ################
      6 
      7 .. contents::
      8   :local:
      9   :backlinks: none
     10   :depth: 2
     11 
     12 This chapter describes the messaging system used to communicate between the
     13 JavaScript code and the Native Client module's C or C++ code in a
     14 Native Client application. It introduces the concept of asynchronous
     15 programming and the basic steps required to set up a Native Client module
     16 that sends messages to and receive messages from JavaScript. This chapter
     17 assumes you are familiar with the material presented in the
     18 :doc:`Application Structure <application-structure>` chapter.
     19 
     20 .. Note::
     21   :class: note
     22 
     23   The "Hello, World" example for getting started with NaCl is used here to
     24   illustrate basic programming techniques. You can find this code in
     25   the ``/getting_started/part2`` directory in the Native Client SDK download.
     26 
     27 Reference information
     28 =====================
     29 
     30 For reference information related to the Pepper messaging API, see the
     31 following documentation:
     32 
     33 * `pp::Instance class </native-client/pepper_stable/cpp/classpp_1_1_instance>`_
     34   HandleMessage(), PostMessage())
     35 * `pp::Module class </native-client/pepper_stable/cpp/classpp_1_1_module>`_
     36 * `pp::Var class </native-client/pepper_stable/cpp/classpp_1_1_var>`_
     37 
     38 Introduction to the messaging system
     39 ====================================
     40 
     41 Native Client modules and JavaScript communicate by sending messages to each
     42 other. The most basic form of a message is a string.  Messages support many
     43 JavaScript types, including ints, arrays, array buffers, and dictionaries (see
     44 `pp::Var </native-client/pepper_stable/cpp/classpp_1_1_var>`_,
     45 `pp:VarArrayBuffer
     46 </native-client/pepper_stable/cpp/classpp_1_1_var_array_buffer>`_, and the
     47 general `messaging system documentation
     48 </native-client/pepper_stable/c/struct_p_p_b___messaging__1__0>`_).  It's up to
     49 you to decide on the type of message and define how to process the messages on
     50 both the JavaScript and Native Client side. For the "Hello, World" example, we
     51 will work with string-typed messages only.
     52 
     53 When JavaScript posts a message to the Native Client module, the
     54 Pepper ``HandleMessage()`` function is invoked on the module
     55 side. Similarly, the Native Client module can post a message to
     56 JavaScript, and this message triggers a JavaScript event listener for
     57 ``message`` events in the DOM. (See the W3C specification on
     58 `Document Object Model Events
     59 <http://www.w3.org/TR/DOM-Level-2-Events/events.html>`_ for more
     60 information.) In the "Hello, World" example, the JavaScript functions for
     61 posting and handling messages are named ``postMessage()`` and
     62 ``handleMessage()`` (but any names could be used). On the Native Client
     63 C++ side, the Pepper Library functions for posting and handling
     64 messages are:
     65 
     66 * ``void pp::Instance::PostMessage(const Var &message)``
     67 * ``virtual void pp::Instance::HandleMessage(const Var &message)``
     68 
     69 If you want to receive messages from JavaScript, you need to implement the
     70 ``pp::Instance::HandleMessage()`` function in your Native Client module.
     71 
     72 Design of the messaging system
     73 ------------------------------
     74 
     75 The Native Client messaging system is analogous to the system used by
     76 the browser to allow web workers to communicate (see the `W3 web
     77 worker specification <http://www.w3.org/TR/workers>`_).  The Native
     78 Client messaging system is designed to keep the web page responsive while the
     79 Native Client module is performing potentially heavy processing in the
     80 background. When JavaScript sends a message to the Native Client
     81 module, the ``postMessage()`` call returns as soon as it sends its message
     82 to the Native Client module. The JavaScript does not wait for a reply
     83 from Native Client, thus avoiding bogging down the main JavaScript
     84 thread. On the JavaScript side, you set up an event listener to
     85 respond to the message sent by the Native Client module when it has
     86 finished the requested processing and returns a message.
     87 
     88 This asynchronous processing model keeps the main thread free while
     89 avoiding the following problems:
     90 
     91 * The JavaScript engine hangs while waiting for a synchronous call to return.
     92 * The browser pops up a dialog when a JavaScript entry point takes longer
     93   than a few moments.
     94 * The application hangs while waiting for an unresponsive Native Client module.
     95 
     96 Communication tasks in the "Hello, World" example
     97 =================================================
     98 
     99 The following sections describe how the "Hello, World" example posts
    100 and handles messages on both the JavaScript side and the Native Client
    101 side of the application.
    102 
    103 JavaScript code
    104 ---------------
    105 
    106 The JavaScript code and HTML in the "Hello, World" example can be
    107 found in the ``example.js``, ``common.js``, and ``index.html`` files.
    108 The important steps are:
    109 
    110 #. Sets up an event listener to listen for ``message`` events from the
    111    Native Client module.
    112 #. Implements an event handler that the event listener invokes to handle
    113    incoming ``message`` events.
    114 #. Calls ``postMessage()`` to communicate with the NaCl module,
    115    after the page loads.
    116 
    117 Step 1: From common.js
    118 ^^^^^^^^^^^^^^^^^^^^^^
    119 
    120 .. naclcode::
    121 
    122   function attachDefaultListeners() {
    123     // The NaCl module embed is created within the listenerDiv
    124     var listenerDiv = document.getElementById('listener');
    125     // ...
    126 
    127     // register the handleMessage function as the message event handler.
    128     listenerDiv.addEventListener('message', handleMessage, true);
    129     // ...
    130   }
    131 
    132 
    133 Step 2: From example.js
    134 ^^^^^^^^^^^^^^^^^^^^^^^
    135 
    136 .. naclcode::
    137 
    138   // This function is called by common.js when a message is received from the
    139   // NaCl module.
    140   function handleMessage(message) {
    141     // In the example, we simply log the data that's received in the message.
    142     var logEl = document.getElementById('log');
    143     logEl.textContent += message.data;
    144   }
    145 
    146   // In the index.html we have set up the appropriate divs:
    147   <body {attrs}>
    148     <!-- ... -->
    149     <div id="listener"></div>
    150     <div id="log"></div>
    151   </body>
    152 
    153 
    154 Step 3: From example.js
    155 ^^^^^^^^^^^^^^^^^^^^^^^
    156 
    157 .. naclcode::
    158 
    159   // From example.js, Step 3:
    160   function moduleDidLoad() {
    161     // After the NaCl module has loaded, common.naclModule is a reference to the
    162     // NaCl module's <embed> element.
    163     //
    164     // postMessage sends a message to it.
    165     common.naclModule.postMessage('hello');
    166   }
    167 
    168 
    169 Native Client module
    170 --------------------
    171 
    172 The C++ code in the Native Client module of the "Hello, World" example:
    173 
    174 #. Implements ``pp::Instance::HandleMessage()`` to handle messages sent
    175    by the JavaScript.
    176 #. Processes incoming messages. This example simply checks that JavaScript
    177    has sent a "hello" message and not some other message.
    178 #. Calls ``PostMessage()`` to send an acknowledgement back to the JavaScript
    179    code.  The acknowledgement is a string in the form of a ``Var`` that the
    180    JavaScript code can process.  In general, a ``pp::Var`` can be several
    181    JavaScript types, see the `messaging system documentation
    182    </native-client/pepper_stable/c/struct_p_p_b___messaging__1__0>`_.
    183 
    184 
    185 .. naclcode::
    186 
    187   class HelloTutorialInstance : public pp::Instance {
    188    public:
    189     // ...
    190 
    191     // === Step 1: Implement the HandleMessage function. ===
    192     virtual void HandleMessage(const pp::Var& var_message) {
    193 
    194       // === Step 2: Process the incoming message. ===
    195       // Ignore the message if it is not a string.
    196       if (!var_message.is_string())
    197         return;
    198 
    199       // Get the string message and compare it to "hello".
    200       std::string message = var_message.AsString();
    201       if (message == kHelloString) {
    202         // === Step 3: Send the reply. ===
    203         // If it matches, send our response back to JavaScript.
    204         pp::Var var_reply(kReplyString);
    205         PostMessage(var_reply);
    206       }
    207     }
    208   };
    209 
    210 
    211 Messaging in JavaScript code: More details.
    212 ===========================================
    213 
    214 This section describes in more detail the messaging system code in the
    215 JavaScript portion of the "Hello, World" example.
    216 
    217 Setting up an event listener and handler
    218 ----------------------------------------
    219 
    220 The following JavaScript code sets up an event listener for messages
    221 posted by the Native Client module. It then defines a message handler
    222 that simply logs the content of messages received from the module.
    223 
    224 Setting up the 'message' handler on load
    225 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    226 
    227 .. naclcode::
    228 
    229   // From common.js
    230 
    231   // Listen for the DOM content to be loaded. This event is fired when
    232   // parsing of the page's document has finished.
    233   document.addEventListener('DOMContentLoaded', function() {
    234     var body = document.body;
    235     // ...
    236     var loadFunction = common.domContentLoaded;
    237     // ... set up parameters ...
    238     loadFunction(...);
    239   }
    240 
    241   // This function is exported as common.domContentLoaded.
    242   function domContentLoaded(...) {
    243     // ...
    244     if (common.naclModule == null) {
    245       // ...
    246       attachDefaultListeners();
    247       // initialize common.naclModule ...
    248     } else {
    249       // ...
    250     }
    251   }
    252 
    253   function attachDefaultListeners() {
    254     var listenerDiv = document.getElementById('listener');
    255     // ...
    256     listenerDiv.addEventListener('message', handleMessage, true);
    257     // ...
    258   }
    259 
    260 
    261 Implementing the handler
    262 ^^^^^^^^^^^^^^^^^^^^^^^^
    263 
    264 .. naclcode::
    265 
    266   // From example.js
    267   function handleMessage(message) {
    268     var logEl = document.getElementById('log');
    269     logEl.textContent += message.data;
    270   }
    271 
    272 
    273 Note that the ``handleMessage()`` function is handed a message_event
    274 containing ``data`` that you can display or manipulate in JavaScript. The
    275 "Hello, World" application simply logs this data to the ``log`` div.
    276 
    277 
    278 Messaging in the Native Client module: More details.
    279 ====================================================
    280 
    281 This section describes in more detail the messaging system code in
    282 the Native Client module portion of the "Hello, World" example.  
    283 
    284 Implementing HandleMessage()
    285 ----------------------------
    286 
    287 If you want the Native Client module to receive and handle messages
    288 from JavaScript, you need to implement a ``HandleMessage()`` function
    289 for your module's ``pp::Instance`` class. The
    290 ``HelloWorldInstance::HandleMessage()`` function examines the message
    291 posted from JavaScript. First it examines that the type of the
    292 ``pp::Var`` is indeed a string (not a double, etc.). It then
    293 interprets the data as a string with ``var_message.AsString()``, and
    294 checks that the string matches ``kHelloString``. After examining the
    295 message received from JavaScript, the code calls ``PostMessage()`` to
    296 send a reply message back to the JavaScript side.
    297 
    298 .. naclcode::
    299 
    300   namespace {
    301 
    302   // The expected string sent by the JavaScript.
    303   const char* const kHelloString = "hello";
    304   // The string sent back to the JavaScript code upon receipt of a message
    305   // containing "hello".
    306   const char* const kReplyString = "hello from NaCl";
    307 
    308   }  // namespace
    309 
    310   class HelloTutorialInstance : public pp::Instance {
    311    public:
    312     // ...
    313     virtual void HandleMessage(const pp::Var& var_message) {
    314       // Ignore the message if it is not a string.
    315       if (!var_message.is_string())
    316         return;
    317 
    318       // Get the string message and compare it to "hello".
    319       std::string message = var_message.AsString();
    320       if (message == kHelloString) {
    321         // If it matches, send our response back to JavaScript.
    322         pp::Var var_reply(kReplyString);
    323         PostMessage(var_reply);
    324       }
    325     }
    326   };
    327 
    328 
    329 Implementing application-specific functions
    330 -------------------------------------------
    331 
    332 While the "Hello, World" example is very simple, your Native Client
    333 module will likely include application-specific functions to perform
    334 custom tasks in response to messages. For example the application
    335 could be a compression and decompression service (two functions
    336 exported).  The application could set up an application-specific
    337 convention that messages coming from JavaScript are colon-separated
    338 pairs of the form ``<command>:<data>``.  The Native Client module
    339 message handler can then split the incoming string along the ``:``
    340 character to determine which command to execute.  If the command is
    341 "compress", then data to process is an uncompressed string.  If the
    342 command is "uncompress", then data to process is an already-compressed
    343 string. After processing the data asynchronously, the application then
    344 returns the result to JavaScript.
    345 
    346 
    347 Sending messages back to the JavaScript code
    348 --------------------------------------------
    349 
    350 The Native Client module sends messages back to the JavaScript code
    351 using ``PostMessage()``. The Native Client module always returns
    352 its values in the form of a ``pp::Var`` that can be processed by the
    353 browser's JavaScript. In this example, the message is posted at the
    354 end of the Native Client module's ``HandleMessage()`` function:
    355 
    356 .. naclcode::
    357 
    358   PostMessage(var_reply);
    359 
    360 
    361 Sending and receiving other ``pp::Var`` types
    362 ---------------------------------------------
    363 
    364 Besides strings, ``pp::Var`` can represent other types of JavaScript
    365 objects. For example, messages can be JavaScript objects. These
    366 richer types can make it easier to implement an application's
    367 messaging protocol.
    368 
    369 To send a dictionary from the NaCl module to JavaScript simply create
    370 a ``pp::VarDictionary`` and then call ``PostMessage`` with the
    371 dictionary.
    372 
    373 .. naclcode::
    374 
    375   pp::VarDictionary dictionary;
    376   dictionary.Set(pp::Var("command"), pp::Var(next_command));
    377   dictionary.Set(pp::Var("param_int"), pp::Var(123));
    378   pp::VarArray an_array;
    379   an_array.Set(0, pp::Var("string0"));
    380   an_array.Set(1, pp::Var("string1"))
    381   dictionary.Set(pp::Var("param_array"), an_array);
    382   PostMessage(dictionary);
    383 
    384 
    385 Here is how to create a similar object in JavaScript and send it to
    386 the NaCl module:
    387 
    388 .. naclcode::
    389 
    390   var dictionary = {
    391     command: next_command,
    392     param_int: 123,
    393     param_array: ['string0', 'string1']
    394   }
    395   nacl_module.postMessage(dictionary);
    396 
    397 
    398 To receive a dictionary-typed message in the NaCl module, test that
    399 the message is truly a dictionary type, then convert the message
    400 with the ``pp::VarDictionary`` class.
    401 
    402 .. naclcode::
    403 
    404   virtual void HandleMessage(const pp::Var& var) {
    405     if (var.is_dictionary()) {
    406       pp::VarDictionary dictionary(var);
    407       // Use the dictionary
    408       pp::VarArray keys = dictionary.GetKeys();
    409       // ...
    410     } else {
    411       // ...
    412     }
    413   }
    414