| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 // Set to true when the Document is loaded IFF "test=true" is in the query | |
| 6 // string. | |
| 7 var isTest = false; | |
| 8 | |
| 9 // Set to true when loading a "Release" NaCl module, false when loading a | |
| 10 // "Debug" NaCl module. | |
| 11 var isRelease = false; | |
| 12 | |
| 13 // Javascript module pattern: | |
| 14 // see http://en.wikipedia.org/wiki/Unobtrusive_JavaScript#Namespaces | |
| 15 // In essence, we define an anonymous function which is immediately called and | |
| 16 // returns a new object. The new object contains only the exported definitions; | |
| 17 // all other definitions in the anonymous function are inaccessible to external | |
| 18 // code. | |
| 19 var common = (function() { | |
| 20 | |
| 21 function isHostToolchain(tool) { | |
| 22 return tool == 'win' || tool == 'linux' || tool == 'mac'; | |
| 23 } | |
| 24 | |
| 25 /** | |
| 26 * Return the mime type for NaCl plugin. | |
| 27 * | |
| 28 * @param {string} tool The name of the toolchain, e.g. "glibc", "newlib" etc. | |
| 29 * @return {string} The mime-type for the kind of NaCl plugin matching | |
| 30 * the given toolchain. | |
| 31 */ | |
| 32 function mimeTypeForTool(tool) { | |
| 33 // For NaCl modules use application/x-nacl. | |
| 34 var mimetype = 'application/x-nacl'; | |
| 35 if (isHostToolchain(tool)) { | |
| 36 // For non-NaCl PPAPI plugins use the x-ppapi-debug/release | |
| 37 // mime type. | |
| 38 if (isRelease) | |
| 39 mimetype = 'application/x-ppapi-release'; | |
| 40 else | |
| 41 mimetype = 'application/x-ppapi-debug'; | |
| 42 } else if (tool == 'pnacl' && isRelease) { | |
| 43 mimetype = 'application/x-pnacl'; | |
| 44 } | |
| 45 return mimetype; | |
| 46 } | |
| 47 | |
| 48 /** | |
| 49 * Check if the browser supports NaCl plugins. | |
| 50 * | |
| 51 * @param {string} tool The name of the toolchain, e.g. "glibc", "newlib" etc. | |
| 52 * @return {bool} True if the browser supports the type of NaCl plugin | |
| 53 * produced by the given toolchain. | |
| 54 */ | |
| 55 function browserSupportsNaCl(tool) { | |
| 56 // Assume host toolchains always work with the given browser. | |
| 57 // The below mime-type checking might not work with | |
| 58 // --register-pepper-plugins. | |
| 59 if (isHostToolchain(tool)) { | |
| 60 return true; | |
| 61 } | |
| 62 var mimetype = mimeTypeForTool(tool); | |
| 63 return navigator.mimeTypes[mimetype] !== undefined; | |
| 64 } | |
| 65 | |
| 66 /** | |
| 67 * Inject a script into the DOM, and call a callback when it is loaded. | |
| 68 * | |
| 69 * @param {string} url The url of the script to load. | |
| 70 * @param {Function} onload The callback to call when the script is loaded. | |
| 71 * @param {Function} onerror The callback to call if the script fails to load. | |
| 72 */ | |
| 73 function injectScript(url, onload, onerror) { | |
| 74 var scriptEl = document.createElement('script'); | |
| 75 scriptEl.type = 'text/javascript'; | |
| 76 scriptEl.src = url; | |
| 77 scriptEl.onload = onload; | |
| 78 if (onerror) { | |
| 79 scriptEl.addEventListener('error', onerror, false); | |
| 80 } | |
| 81 document.head.appendChild(scriptEl); | |
| 82 } | |
| 83 | |
| 84 /** | |
| 85 * Run all tests for this example. | |
| 86 * | |
| 87 * @param {Object} moduleEl The module DOM element. | |
| 88 */ | |
| 89 function runTests(moduleEl) { | |
| 90 console.log('runTests()'); | |
| 91 common.tester = new Tester(); | |
| 92 | |
| 93 // All NaCl SDK examples are OK if the example exits cleanly; (i.e. the | |
| 94 // NaCl module returns 0 or calls exit(0)). | |
| 95 // | |
| 96 // Without this exception, the browser_tester thinks that the module | |
| 97 // has crashed. | |
| 98 common.tester.exitCleanlyIsOK(); | |
| 99 | |
| 100 common.tester.addAsyncTest('loaded', function(test) { | |
| 101 test.pass(); | |
| 102 }); | |
| 103 | |
| 104 if (typeof window.addTests !== 'undefined') { | |
| 105 window.addTests(); | |
| 106 } | |
| 107 | |
| 108 common.tester.waitFor(moduleEl); | |
| 109 common.tester.run(); | |
| 110 } | |
| 111 | |
| 112 /** | |
| 113 * Create the Native Client <embed> element as a child of the DOM element | |
| 114 * named "listener". | |
| 115 * | |
| 116 * @param {string} name The name of the example. | |
| 117 * @param {string} tool The name of the toolchain, e.g. "glibc", "newlib" etc. | |
| 118 * @param {string} path Directory name where .nmf file can be found. | |
| 119 * @param {number} width The width to create the plugin. | |
| 120 * @param {number} height The height to create the plugin. | |
| 121 * @param {Object} attrs Dictionary of attributes to set on the module. | |
| 122 */ | |
| 123 function createNaClModule(name, tool, path, width, height, attrs) { | |
| 124 var moduleEl = document.createElement('embed'); | |
| 125 moduleEl.setAttribute('name', 'nacl_module'); | |
| 126 moduleEl.setAttribute('id', 'nacl_module'); | |
| 127 moduleEl.setAttribute('width', width); | |
| 128 moduleEl.setAttribute('height', height); | |
| 129 moduleEl.setAttribute('path', path); | |
| 130 moduleEl.setAttribute('src', path + '/' + name + '.nmf'); | |
| 131 | |
| 132 // Add any optional arguments | |
| 133 if (attrs) { | |
| 134 for (var key in attrs) { | |
| 135 moduleEl.setAttribute(key, attrs[key]); | |
| 136 } | |
| 137 } | |
| 138 | |
| 139 var mimetype = mimeTypeForTool(tool); | |
| 140 moduleEl.setAttribute('type', mimetype); | |
| 141 | |
| 142 // The <EMBED> element is wrapped inside a <DIV>, which has both a 'load' | |
| 143 // and a 'message' event listener attached. This wrapping method is used | |
| 144 // instead of attaching the event listeners directly to the <EMBED> element | |
| 145 // to ensure that the listeners are active before the NaCl module 'load' | |
| 146 // event fires. | |
| 147 var listenerDiv = document.getElementById('listener'); | |
| 148 listenerDiv.appendChild(moduleEl); | |
| 149 | |
| 150 // Host plugins don't send a moduleDidLoad message. We'll fake it here. | |
| 151 var isHost = isHostToolchain(tool); | |
| 152 if (isHost) { | |
| 153 window.setTimeout(function() { | |
| 154 moduleEl.readyState = 1; | |
| 155 moduleEl.dispatchEvent(new CustomEvent('loadstart')); | |
| 156 moduleEl.readyState = 4; | |
| 157 moduleEl.dispatchEvent(new CustomEvent('load')); | |
| 158 moduleEl.dispatchEvent(new CustomEvent('loadend')); | |
| 159 }, 100); // 100 ms | |
| 160 } | |
| 161 | |
| 162 // This is code that is only used to test the SDK. | |
| 163 if (isTest) { | |
| 164 var loadNaClTest = function() { | |
| 165 injectScript('nacltest.js', function() { | |
| 166 runTests(moduleEl); | |
| 167 }); | |
| 168 }; | |
| 169 | |
| 170 // Try to load test.js for the example. Whether or not it exists, load | |
| 171 // nacltest.js. | |
| 172 injectScript('test.js', loadNaClTest, loadNaClTest); | |
| 173 } | |
| 174 } | |
| 175 | |
| 176 /** | |
| 177 * Add the default "load" and "message" event listeners to the element with | |
| 178 * id "listener". | |
| 179 * | |
| 180 * The "load" event is sent when the module is successfully loaded. The | |
| 181 * "message" event is sent when the naclModule posts a message using | |
| 182 * PPB_Messaging.PostMessage() (in C) or pp::Instance().PostMessage() (in | |
| 183 * C++). | |
| 184 */ | |
| 185 function attachDefaultListeners() { | |
| 186 var listenerDiv = document.getElementById('listener'); | |
| 187 listenerDiv.addEventListener('load', moduleDidLoad, true); | |
| 188 listenerDiv.addEventListener('message', handleMessage, true); | |
| 189 listenerDiv.addEventListener('crash', handleCrash, true); | |
| 190 if (typeof window.attachListeners !== 'undefined') { | |
| 191 window.attachListeners(); | |
| 192 } | |
| 193 } | |
| 194 | |
| 195 | |
| 196 /** | |
| 197 * Called when the Browser can not communicate with the Module | |
| 198 * | |
| 199 * This event listener is registered in attachDefaultListeners above. | |
| 200 */ | |
| 201 function handleCrash(event) { | |
| 202 if (common.naclModule.exitStatus == -1) { | |
| 203 updateStatus('CRASHED'); | |
| 204 } else { | |
| 205 updateStatus('EXITED [' + common.naclModule.exitStatus + ']'); | |
| 206 } | |
| 207 if (typeof window.handleCrash !== 'undefined') { | |
| 208 window.handleCrash(common.naclModule.lastError); | |
| 209 } | |
| 210 } | |
| 211 | |
| 212 /** | |
| 213 * Called when the NaCl module is loaded. | |
| 214 * | |
| 215 * This event listener is registered in attachDefaultListeners above. | |
| 216 */ | |
| 217 function moduleDidLoad() { | |
| 218 common.naclModule = document.getElementById('nacl_module'); | |
| 219 updateStatus('RUNNING'); | |
| 220 | |
| 221 if (typeof window.moduleDidLoad !== 'undefined') { | |
| 222 window.moduleDidLoad(); | |
| 223 } | |
| 224 } | |
| 225 | |
| 226 /** | |
| 227 * Hide the NaCl module's embed element. | |
| 228 * | |
| 229 * We don't want to hide by default; if we do, it is harder to determine that | |
| 230 * a plugin failed to load. Instead, call this function inside the example's | |
| 231 * "moduleDidLoad" function. | |
| 232 * | |
| 233 */ | |
| 234 function hideModule() { | |
| 235 // Setting common.naclModule.style.display = "None" doesn't work; the | |
| 236 // module will no longer be able to receive postMessages. | |
| 237 common.naclModule.style.height = '0'; | |
| 238 } | |
| 239 | |
| 240 /** | |
| 241 * Return true when |s| starts with the string |prefix|. | |
| 242 * | |
| 243 * @param {string} s The string to search. | |
| 244 * @param {string} prefix The prefix to search for in |s|. | |
| 245 */ | |
| 246 function startsWith(s, prefix) { | |
| 247 // indexOf would search the entire string, lastIndexOf(p, 0) only checks at | |
| 248 // the first index. See: http://stackoverflow.com/a/4579228 | |
| 249 return s.lastIndexOf(prefix, 0) === 0; | |
| 250 } | |
| 251 | |
| 252 /** Maximum length of logMessageArray. */ | |
| 253 var kMaxLogMessageLength = 20; | |
| 254 | |
| 255 /** An array of messages to display in the element with id "log". */ | |
| 256 var logMessageArray = []; | |
| 257 | |
| 258 /** | |
| 259 * Add a message to an element with id "log". | |
| 260 * | |
| 261 * This function is used by the default "log:" message handler. | |
| 262 * | |
| 263 * @param {string} message The message to log. | |
| 264 */ | |
| 265 function logMessage(message) { | |
| 266 logMessageArray.push(message); | |
| 267 if (logMessageArray.length > kMaxLogMessageLength) | |
| 268 logMessageArray.shift(); | |
| 269 | |
| 270 document.getElementById('log').textContent = logMessageArray.join('\n'); | |
| 271 console.log(message); | |
| 272 } | |
| 273 | |
| 274 /** | |
| 275 */ | |
| 276 var defaultMessageTypes = { | |
| 277 'alert': alert, | |
| 278 'log': logMessage | |
| 279 }; | |
| 280 | |
| 281 /** | |
| 282 * Called when the NaCl module sends a message to JavaScript (via | |
| 283 * PPB_Messaging.PostMessage()) | |
| 284 * | |
| 285 * This event listener is registered in createNaClModule above. | |
| 286 * | |
| 287 * @param {Event} message_event A message event. message_event.data contains | |
| 288 * the data sent from the NaCl module. | |
| 289 */ | |
| 290 function handleMessage(message_event) { | |
| 291 if (typeof message_event.data === 'string') { | |
| 292 for (var type in defaultMessageTypes) { | |
| 293 if (defaultMessageTypes.hasOwnProperty(type)) { | |
| 294 if (startsWith(message_event.data, type + ':')) { | |
| 295 func = defaultMessageTypes[type]; | |
| 296 func(message_event.data.slice(type.length + 1)); | |
| 297 return; | |
| 298 } | |
| 299 } | |
| 300 } | |
| 301 } | |
| 302 | |
| 303 if (typeof window.handleMessage !== 'undefined') { | |
| 304 window.handleMessage(message_event); | |
| 305 return; | |
| 306 } | |
| 307 | |
| 308 logMessage('Unhandled message: ' + message_event.data); | |
| 309 } | |
| 310 | |
| 311 /** | |
| 312 * Called when the DOM content has loaded; i.e. the page's document is fully | |
| 313 * parsed. At this point, we can safely query any elements in the document via | |
| 314 * document.querySelector, document.getElementById, etc. | |
| 315 * | |
| 316 * @param {string} name The name of the example. | |
| 317 * @param {string} tool The name of the toolchain, e.g. "glibc", "newlib" etc. | |
| 318 * @param {string} path Directory name where .nmf file can be found. | |
| 319 * @param {number} width The width to create the plugin. | |
| 320 * @param {number} height The height to create the plugin. | |
| 321 * @param {Object} attrs Optional dictionary of additional attributes. | |
| 322 */ | |
| 323 function domContentLoaded(name, tool, path, width, height, attrs) { | |
| 324 // If the page loads before the Native Client module loads, then set the | |
| 325 // status message indicating that the module is still loading. Otherwise, | |
| 326 // do not change the status message. | |
| 327 updateStatus('Page loaded.'); | |
| 328 if (!browserSupportsNaCl(tool)) { | |
| 329 updateStatus( | |
| 330 'Browser does not support NaCl (' + tool + '), or NaCl is disabled'); | |
| 331 } else if (common.naclModule == null) { | |
| 332 updateStatus('Creating embed: ' + tool); | |
| 333 | |
| 334 // We use a non-zero sized embed to give Chrome space to place the bad | |
| 335 // plug-in graphic, if there is a problem. | |
| 336 width = typeof width !== 'undefined' ? width : 200; | |
| 337 height = typeof height !== 'undefined' ? height : 200; | |
| 338 attachDefaultListeners(); | |
| 339 createNaClModule(name, tool, path, width, height, attrs); | |
| 340 } else { | |
| 341 // It's possible that the Native Client module onload event fired | |
| 342 // before the page's onload event. In this case, the status message | |
| 343 // will reflect 'SUCCESS', but won't be displayed. This call will | |
| 344 // display the current message. | |
| 345 updateStatus('Waiting.'); | |
| 346 } | |
| 347 } | |
| 348 | |
| 349 /** Saved text to display in the element with id 'statusField'. */ | |
| 350 var statusText = 'NO-STATUSES'; | |
| 351 | |
| 352 /** | |
| 353 * Set the global status message. If the element with id 'statusField' | |
| 354 * exists, then set its HTML to the status message as well. | |
| 355 * | |
| 356 * @param {string} opt_message The message to set. If null or undefined, then | |
| 357 * set element 'statusField' to the message from the last call to | |
| 358 * updateStatus. | |
| 359 */ | |
| 360 function updateStatus(opt_message) { | |
| 361 if (opt_message) { | |
| 362 statusText = opt_message; | |
| 363 } | |
| 364 var statusField = document.getElementById('statusField'); | |
| 365 if (statusField) { | |
| 366 statusField.innerHTML = statusText; | |
| 367 } | |
| 368 } | |
| 369 | |
| 370 // The symbols to export. | |
| 371 return { | |
| 372 /** A reference to the NaCl module, once it is loaded. */ | |
| 373 naclModule: null, | |
| 374 | |
| 375 attachDefaultListeners: attachDefaultListeners, | |
| 376 domContentLoaded: domContentLoaded, | |
| 377 createNaClModule: createNaClModule, | |
| 378 hideModule: hideModule, | |
| 379 logMessage: logMessage, | |
| 380 updateStatus: updateStatus | |
| 381 }; | |
| 382 | |
| 383 }()); | |
| 384 | |
| 385 // Listen for the DOM content to be loaded. This event is fired when parsing of | |
| 386 // the page's document has finished. | |
| 387 document.addEventListener('DOMContentLoaded', function() { | |
| 388 var body = document.body; | |
| 389 | |
| 390 // The data-* attributes on the body can be referenced via body.dataset. | |
| 391 if (body.dataset) { | |
| 392 var loadFunction; | |
| 393 if (!body.dataset.customLoad) { | |
| 394 loadFunction = common.domContentLoaded; | |
| 395 } else if (typeof window.domContentLoaded !== 'undefined') { | |
| 396 loadFunction = window.domContentLoaded; | |
| 397 } | |
| 398 | |
| 399 // From https://developer.mozilla.org/en-US/docs/DOM/window.location | |
| 400 var searchVars = {}; | |
| 401 if (window.location.search.length > 1) { | |
| 402 var pairs = window.location.search.substr(1).split('&'); | |
| 403 for (var key_ix = 0; key_ix < pairs.length; key_ix++) { | |
| 404 var keyValue = pairs[key_ix].split('='); | |
| 405 searchVars[unescape(keyValue[0])] = | |
| 406 keyValue.length > 1 ? unescape(keyValue[1]) : ''; | |
| 407 } | |
| 408 } | |
| 409 | |
| 410 if (loadFunction) { | |
| 411 var toolchains = body.dataset.tools.split(' '); | |
| 412 var configs = body.dataset.configs.split(' '); | |
| 413 | |
| 414 var attrs = {}; | |
| 415 if (body.dataset.attrs) { | |
| 416 var attr_list = body.dataset.attrs.split(' '); | |
| 417 for (var key in attr_list) { | |
| 418 var attr = attr_list[key].split('='); | |
| 419 var key = attr[0]; | |
| 420 var value = attr[1]; | |
| 421 attrs[key] = value; | |
| 422 } | |
| 423 } | |
| 424 | |
| 425 var tc = toolchains.indexOf(searchVars.tc) !== -1 ? | |
| 426 searchVars.tc : toolchains[0]; | |
| 427 var config = configs.indexOf(searchVars.config) !== -1 ? | |
| 428 searchVars.config : configs[0]; | |
| 429 var pathFormat = body.dataset.path; | |
| 430 var path = pathFormat.replace('{tc}', tc).replace('{config}', config); | |
| 431 | |
| 432 isTest = searchVars.test === 'true'; | |
| 433 isRelease = path.toLowerCase().indexOf('release') != -1; | |
| 434 | |
| 435 loadFunction(body.dataset.name, tc, path, body.dataset.width, | |
| 436 body.dataset.height, attrs); | |
| 437 } | |
| 438 } | |
| 439 }); | |
| OLD | NEW |