| <html><head> | 
 | <meta http-equiv="Pragma" content="no-cache" /> | 
 | <meta http-equiv="Expires" content="-1" /> | 
 | <link rel="stylesheet" href="test_page.css"> | 
 | <script> | 
 | // Do a deep comparison of two values. Return true if their values are | 
 | // identical, false otherwise. | 
 | function deepCompare(left, right) { | 
 |   if (typeof(left) !== typeof(right)) | 
 |     return false; | 
 |   // If their identity is the same or they're basic types with the same value, | 
 |   // they are equal. | 
 |   if (left === right) | 
 |     return true; | 
 |   // If it's a basic type and we got here, we know they're not equal. | 
 |   if (["undefined", "boolean", "number", "string", "function"].indexOf( | 
 |           typeof(left)) > -1) { | 
 |     return false; | 
 |   } | 
 |   // Use right_keys as a set containing all keys from |right| which we haven't | 
 |   // yet compared. | 
 |   var right_keys = {}; | 
 |   for (var key in right) | 
 |     right_keys[key] = true; | 
 |   for (var key in left) { | 
 |     if (key in right_keys) { | 
 |        if (!deepCompare(left[key], right[key])) | 
 |         return false; | 
 |     } else { | 
 |       // |left| had a key that |right| didn't. | 
 |       return false; | 
 |     } | 
 |     delete right_keys[key]; | 
 |   } | 
 |   // If there are keys left in |right_keys|, it means they didn't exist in | 
 |   // |left|, so the objects aren't equal. | 
 |   if (Object.keys(right_keys).length > 0) | 
 |     return false; | 
 |   return true; | 
 | } | 
 |  | 
 | function AdjustHeight(frameWin) { | 
 |   var div = frameWin.document.getElementsByTagName("div")[0]; | 
 |   var height = frameWin.getComputedStyle(div).height; | 
 |   frameWin.frameElement.style.height = height; | 
 | } | 
 |  | 
 | // Called when the tests are completed. |result| should be "PASS" if the test(s) | 
 | // passed, or information about the failure if the test(s) did not pass. | 
 | function DidExecuteTests(result) { | 
 |   var plugin = document.getElementById("plugin"); | 
 |   if (plugin.parentNode.removePlugin) { | 
 |     plugin.parentNode.removeChild(plugin); | 
 |     plugin = undefined; | 
 |   } | 
 |   if (CheckPostConditions()) | 
 |     sendAutomationMessage(result); | 
 |  | 
 |   if (window == top) | 
 |     return; | 
 |  | 
 |   // Otherwise, we are in a subframe, so we can use this opportunity to resize | 
 |   // ourselves.  This can only be done if the parent frame has the same origin. | 
 |   if (window.frameElement) | 
 |     AdjustHeight(window); | 
 | } | 
 |  | 
 | function AppendFrame(testcase, i) { | 
 |   var p = document.createElement("P"); | 
 |   p.setAttribute("class", "frame-container"); | 
 |  | 
 |   var title = document.createElement("H2"); | 
 |   title.appendChild(document.createTextNode(testcase)); | 
 |   p.appendChild(title); | 
 |  | 
 |   var frame = document.createElement("IFRAME"); | 
 |   var mode = ExtractSearchParameter("mode"); | 
 |   var websocket_host = ExtractSearchParameter("websocket_host"); | 
 |   var websocket_port = ExtractSearchParameter("websocket_port"); | 
 |   var ssl_server_port = ExtractSearchParameter("ssl_server_port"); | 
 |   var src = "?testcase=" + testcase; | 
 |   if (mode == "nacl") | 
 |     src += "&mode=nacl"; | 
 |   if (websocket_host != "") | 
 |     src += "&websocket_host=" + websocket_host; | 
 |   if (websocket_port != "") | 
 |     src += "&websocket_port=" + websocket_port; | 
 |   if (ssl_server_port != "") | 
 |     src += "&ssl_server_port=" + ssl_server_port; | 
 |   frame.setAttribute("src", src); | 
 |  | 
 |   frame.setAttribute("onload", "LoadNext(" + (i + 1) + ")"); | 
 |   p.appendChild(frame); | 
 |  | 
 |   document.body.appendChild(p); | 
 | } | 
 |  | 
 | function LoadNext(i) { | 
 |   var links = document.links; | 
 |   if (links.length > i) | 
 |     AppendFrame(links[i].firstChild.nodeValue, i); | 
 | } | 
 |  | 
 | function RunAll() { | 
 |   // Remove any existing frames. | 
 |   var existing = document.getElementsByClassName("frame-container"); | 
 |   while (existing.length) | 
 |     existing[0].parentNode.removeChild(existing[0]); | 
 |  | 
 |   // Add new frames for each test, but do so one frame at a time. | 
 |   LoadNext(0); | 
 | } | 
 |  | 
 | function ExtractSearchParameter(name) { | 
 |   var nameIndex = location.search.indexOf(name + "="); | 
 |   if (nameIndex != -1) { | 
 |     var value = location.search.substring(nameIndex + name.length + 1); | 
 |     var endIndex = value.indexOf("&"); | 
 |     if (endIndex != -1) | 
 |       value = value.substring(0, endIndex); | 
 |     return value; | 
 |   } | 
 |   return ""; | 
 | } | 
 |  | 
 | // Parses the message, looking for strings of the form: | 
 | // TESTING_MESSAGE:<message_type>:<message_contents> | 
 | // | 
 | // If the message_data is not a string or does not match the above format, then | 
 | // undefined is returned. | 
 | // | 
 | // Otherwise, returns an array containing 2 items. The 0th element is the | 
 | // message_type, one of: | 
 | //  - AddPostCondition | 
 | //  - ClearConsole | 
 | //  - DidExecuteTests | 
 | //  - EvalScript | 
 | //  - LogHTML | 
 | //  - RemovePluginWhenFinished | 
 | //  - ReportProgress | 
 | // The second item is the verbatim message_contents. | 
 | function ParseTestingMessage(message_data) { | 
 |   if (typeof(message_data) != "string") | 
 |     return undefined; | 
 |   var testing_message_prefix = "TESTING_MESSAGE"; | 
 |   var delim_str = ":"; | 
 |   var delim1 = message_data.indexOf(delim_str); | 
 |   if (message_data.substring(0, delim1) !== testing_message_prefix) | 
 |     return undefined; | 
 |   var delim2 = message_data.indexOf(delim_str, delim1 + 1); | 
 |   if (delim2 == -1) | 
 |     delim2 = message_data.length; | 
 |   var message_type = message_data.substring(delim1 + 1, delim2); | 
 |   var message_contents = message_data.substring(delim2 + 1); | 
 |   return [message_type, message_contents]; | 
 | } | 
 |  | 
 | function ClearConsole() { | 
 |   window.document.getElementById("console").innerHTML = ""; | 
 | } | 
 |  | 
 | function LogHTML(html) { | 
 |   window.document.getElementById("console").innerHTML += html; | 
 | } | 
 |  | 
 | function RemovePluginWhenFinished() { | 
 |   window.document.getElementById("container").removePlugin = true; | 
 | } | 
 |  | 
 | function sendAutomationMessage(msg) { | 
 |   if (window.domAutomationController) { | 
 |     window.domAutomationController.setAutomationId(0); | 
 |     window.domAutomationController.send(msg); | 
 |   } | 
 | } | 
 |  | 
 | function LogTestTime(test_time) { | 
 |   console.log(test_time); | 
 | } | 
 |  | 
 | // If something goes really wrong, the test running inside the plugin may not | 
 | // terminate.  For example, if the plugin does not load, the test will never | 
 | // send "PASS" to the browser.  In this case we should explicitly use the | 
 | // automation controller to terminate the test. | 
 | function InternalError(msg) { | 
 |   LogHTML("<p>" + msg); | 
 |   sendAutomationMessage(msg); | 
 | } | 
 |  | 
 | function EvalScript(script) { | 
 |   try { | 
 |     eval(script); | 
 |   } catch(ex) { | 
 |   } | 
 | } | 
 |  | 
 | var conditions = []; | 
 | // Add a "PostCondition". These are bits of script that are run after the plugin | 
 | // is destroyed. If they evaluate to false or throw an exception, it's | 
 | // considered a failure. | 
 | function AddPostCondition(script) { | 
 |   conditions.push(script); | 
 | } | 
 | // Update the HTML to show the failure and update cookies so that ui_tests | 
 | // doesn't count this as a pass. | 
 | function ConditionFailed(error) { | 
 |   error_string = "Post condition check failed: " + error; | 
 |   InternalError(error_string); | 
 | } | 
 | // Iterate through the post conditions defined in |conditions| and check that | 
 | // they all pass. | 
 | function CheckPostConditions() { | 
 |   var success = true; | 
 |   for (var i = 0; i < conditions.length; ++i) { | 
 |     var script = conditions[i]; | 
 |     try { | 
 |       if (!eval(script)) { | 
 |         ConditionFailed("\"" + script + "\""); | 
 |         success = false; | 
 |       } | 
 |     } catch (ex) { | 
 |       ConditionFailed("\"" + script + "\"" + " failed with exception: " + | 
 |                       "\"" + ex.toString() + "\""); | 
 |       success = false; | 
 |     } | 
 |   } | 
 |   return success; | 
 | } | 
 |  | 
 | function IsTestingMessage(message_data) { | 
 |   return (ParseTestingMessage(message_data) != undefined); | 
 | } | 
 |  | 
 | function handleTestingMessage(message_event) { | 
 |   var type_contents_tuple = ParseTestingMessage(message_event.data); | 
 |   if (type_contents_tuple) { | 
 |     var type = type_contents_tuple[0]; | 
 |     var contents = type_contents_tuple[1]; | 
 |     if (type === "AddPostCondition") | 
 |       AddPostCondition(contents); | 
 |     else if (type === "ClearConsole") | 
 |       ClearConsole(); | 
 |     else if (type === "DidExecuteTests") | 
 |       DidExecuteTests(contents); | 
 |     else if (type === "EvalScript") | 
 |       EvalScript(contents); | 
 |     else if (type === "LogHTML") | 
 |       LogHTML(contents); | 
 |     else if (type === "RemovePluginWhenFinished") | 
 |       RemovePluginWhenFinished(); | 
 |     else if (type === "ReportProgress") | 
 |       sendAutomationMessage(contents); | 
 |     else if (type === "LogTestTime") | 
 |       LogTestTime(contents); | 
 |   } | 
 | } | 
 |  | 
 | function sendProgress() { | 
 |   // We send "..." to signal that we're still working. See | 
 |   // ppapi/tests/testing_instance.h for how this works. | 
 |   sendAutomationMessage("..."); | 
 | } | 
 |  | 
 | onload = function() { | 
 |   var testcase = ExtractSearchParameter("testcase"); | 
 |   var mode = ExtractSearchParameter("mode"); | 
 |   document.title = 'Test ' + testcase; | 
 |   var obj; | 
 |   if (mode == "nacl_newlib") { | 
 |     obj = document.createElement("EMBED"); | 
 |     obj.setAttribute("src", "ppapi_nacl_tests_newlib.nmf"); | 
 |     obj.setAttribute("type", "application/x-nacl"); | 
 |     obj.setAttribute("mode", mode); | 
 |   } else if (mode == "nacl_glibc") { | 
 |     obj = document.createElement("EMBED"); | 
 |     obj.setAttribute("src", "ppapi_nacl_tests_glibc.nmf"); | 
 |     obj.setAttribute("type", "application/x-nacl"); | 
 |     obj.setAttribute("mode", mode); | 
 |   } else if (mode == "nacl_pnacl") { | 
 |     obj = document.createElement("EMBED"); | 
 |     obj.setAttribute("src", "ppapi_nacl_tests_pnacl.nmf"); | 
 |     obj.setAttribute("type", "application/x-nacl"); | 
 |     obj.setAttribute("mode", mode); | 
 |   } else if (mode == "nacl_pnacl_nonsfi") { | 
 |     obj = document.createElement("EMBED"); | 
 |     obj.setAttribute("src", "ppapi_nacl_tests_pnacl_nonsfi.nmf"); | 
 |     obj.setAttribute("type", "application/x-nacl"); | 
 |     obj.setAttribute("mode", mode); | 
 |   } else if (mode == "mojo") { | 
 |     obj = document.createElement("EMBED"); | 
 |     obj.setAttribute("src", | 
 |                      "test_data/ppapi/tests/mojo/pnacl/ppapi_tests_mojo.nmf"); | 
 |     obj.setAttribute("type", "application/x-pnacl"); | 
 |     obj.setAttribute("mode", mode); | 
 |   } else { | 
 |     var mimeType = "application/x-ppapi-tests"; | 
 |     if (mimeType in navigator.mimeTypes) { | 
 |       obj = document.createElement("EMBED"); | 
 |       obj.setAttribute("src", "http://a.b.c/test"); | 
 |       obj.setAttribute("type", mimeType); | 
 |     } else { | 
 |       document.getElementById("console").innerHTML = | 
 |           '<span class="fail">FAIL</span>: ' + | 
 |           '<span class="err_msg">Test plugin is not registered.</span>'; | 
 |     } | 
 |   } | 
 |  | 
 |   if (obj) { | 
 |     obj.setAttribute("width", 80); | 
 |     obj.setAttribute("height", 80); | 
 |     obj.setAttribute("style", | 
 |                      "background-color:#AAAAAA;border:1px solid black;"); | 
 |     obj.setAttribute("id", "plugin"); | 
 |     obj.setAttribute("testcase", testcase); | 
 |     obj.setAttribute("protocol", window.location.protocol); | 
 |     var websocket_host = ExtractSearchParameter("websocket_host"); | 
 |     if (websocket_host != "") | 
 |       obj.setAttribute("websocket_host", websocket_host); | 
 |     var websocket_port = ExtractSearchParameter("websocket_port"); | 
 |     if (websocket_port != "") | 
 |       obj.setAttribute("websocket_port", websocket_port); | 
 |     var ssl_server_port = ExtractSearchParameter("ssl_server_port"); | 
 |     if (ssl_server_port != "") | 
 |       obj.setAttribute("ssl_server_port", ssl_server_port); | 
 |  | 
 |     var container = document.getElementById("container"); | 
 |     container.addEventListener("message", handleTestingMessage, true); | 
 |  | 
 |     // "error" and "crash" events will only fire for NaCl, but adding these | 
 |     // listeners doesn't hurt in the non-NaCl cases. | 
 |     obj.addEventListener("error", function() { | 
 |       InternalError("Plugin did not load. '" + obj.lastError + "'"); | 
 |     }, true); | 
 |     obj.addEventListener("crash", function() { | 
 |       InternalError("Plugin crashed. '" + obj.lastError + "'"); | 
 |     }, true); | 
 |  | 
 |     // NaCl sends progress events while loading. When we get one, notify the | 
 |     // domAutomationController so that it knows we're still working. | 
 |     obj.addEventListener("loadstart", sendProgress, true); | 
 |     obj.addEventListener("progress", sendProgress, true); | 
 |     obj.addEventListener("load", sendProgress, true); | 
 |     obj.addEventListener("loadend", sendProgress, true); | 
 |  | 
 |     // Register a bad dispatchEvent to make sure it isn't used. See 'EVIL' note | 
 |     // below. | 
 |     var original = obj.dispatchEvent; | 
 |     obj.dispatchEvent = function() { | 
 |       InternalError("Bad dispatchEvent called!"); | 
 |     } | 
 |     container.appendChild(obj); | 
 |   } | 
 | } | 
 |  | 
 | // EVIL Note: | 
 | // This part of the script does some nefarious things to make sure that it | 
 | // doesn't affect the behavior of PostMessage (on which all the tests rely). In | 
 | // particular, we replace document.createEvent, MessageEvent.initMessageEvent, | 
 | // and the MessageEvent constructor. Previously, the NaCl integration | 
 | // implementation made use of these and would fail (http://crbug.com/82604 | 
 | // and http://crbug.com/109775). | 
 | document.createEvent = function() { | 
 |   InternalError("Bad document.createEvent called!"); | 
 | } | 
 | function MessageEvent() { | 
 |   InternalError("Bad MessageEvent constructor called!"); | 
 | } | 
 | MessageEvent.prototype.initMessageEvent = function() { | 
 |   InternalError("Bad MessageEvent.initMessageEvent called!"); | 
 | } | 
 |  | 
 | </script> | 
 | </head><body> | 
 | <div> | 
 |   <div id="container"></div> | 
 |   <div id="console"><span class="load_msg">loading...</span></div> | 
 | </div> | 
 | </body></html> |