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