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