1<html><head>
2<meta http-equiv="Pragma" content="no-cache" />
3<meta http-equiv="Expires" content="-1" />
4<link rel="stylesheet" href="test_page.css">
5<script>
6// Do a deep comparison of two values. Return true if their values are
7// identical, false otherwise.
8function deepCompare(left, right) {
9  if (typeof(left) !== typeof(right))
10    return false;
11  // If their identity is the same or they're basic types with the same value,
12  // they are equal.
13  if (left === right)
14    return true;
15  // If it's a basic type and we got here, we know they're not equal.
16  if (["undefined", "boolean", "number", "string", "function"].indexOf(
17          typeof(left)) > -1) {
18    return false;
19  }
20  // Use right_keys as a set containing all keys from |right| which we haven't
21  // yet compared.
22  var right_keys = {};
23  for (var key in right)
24    right_keys[key] = true;
25  for (var key in left) {
26    if (key in right_keys) {
27       if (!deepCompare(left[key], right[key]))
28        return false;
29    } else {
30      // |left| had a key that |right| didn't.
31      return false;
32    }
33    delete right_keys[key];
34  }
35  // If there are keys left in |right_keys|, it means they didn't exist in
36  // |left|, so the objects aren't equal.
37  if (Object.keys(right_keys).length > 0)
38    return false;
39  return true;
40}
41
42function AdjustHeight(frameWin) {
43  var div = frameWin.document.getElementsByTagName("div")[0];
44  var height = frameWin.getComputedStyle(div).height;
45  frameWin.frameElement.style.height = height;
46}
47
48// Called when the tests are completed. |result| should be "PASS" if the test(s)
49// passed, or information about the failure if the test(s) did not pass.
50function DidExecuteTests(result) {
51  var plugin = document.getElementById("plugin");
52  if (plugin.parentNode.removePlugin) {
53    plugin.parentNode.removeChild(plugin);
54    plugin = undefined;
55  }
56  if (CheckPostConditions())
57    sendAutomationMessage(result);
58
59  if (window == top)
60    return;
61
62  // Otherwise, we are in a subframe, so we can use this opportunity to resize
63  // ourselves.
64  AdjustHeight(window);
65}
66
67function AppendFrame(testcase, i) {
68  var p = document.createElement("P");
69  p.setAttribute("class", "frame-container");
70
71  var title = document.createElement("H2");
72  title.appendChild(document.createTextNode(testcase));
73  p.appendChild(title);
74
75  var frame = document.createElement("IFRAME");
76  var mode = ExtractSearchParameter("mode");
77  var websocket_host = ExtractSearchParameter("websocket_host");
78  var websocket_port = ExtractSearchParameter("websocket_port");
79  var ssl_server_port = ExtractSearchParameter("ssl_server_port");
80  var src = "?testcase=" + testcase;
81  if (mode == "nacl")
82    src += "&mode=nacl";
83  if (websocket_host != "")
84    src += "&websocket_host=" + websocket_host;
85  if (websocket_port != "")
86    src += "&websocket_port=" + websocket_port;
87  if (ssl_server_port != "")
88    src += "&ssl_server_port=" + ssl_server_port;
89  frame.setAttribute("src", src);
90
91  frame.setAttribute("onload", "LoadNext(" + (i + 1) + ")");
92  p.appendChild(frame);
93
94  document.body.appendChild(p);
95}
96
97function LoadNext(i) {
98  var links = document.links;
99  if (links.length > i)
100    AppendFrame(links[i].firstChild.nodeValue, i);
101}
102
103function RunAll() {
104  // Remove any existing frames.
105  var existing = document.getElementsByClassName("frame-container");
106  while (existing.length)
107    existing[0].parentNode.removeChild(existing[0]);
108
109  // Add new frames for each test, but do so one frame at a time.
110  LoadNext(0);
111}
112
113function ExtractSearchParameter(name) {
114  var nameIndex = location.search.indexOf(name + "=");
115  if (nameIndex != -1) {
116    var value = location.search.substring(nameIndex + name.length + 1);
117    var endIndex = value.indexOf("&");
118    if (endIndex != -1)
119      value = value.substring(0, endIndex);
120    return value;
121  }
122  return "";
123}
124
125// Parses the message, looking for strings of the form:
126// TESTING_MESSAGE:<message_type>:<message_contents>
127//
128// If the message_data is not a string or does not match the above format, then
129// undefined is returned.
130//
131// Otherwise, returns an array containing 2 items. The 0th element is the
132// message_type, one of:
133//  - AddPostCondition
134//  - ClearConsole
135//  - DidExecuteTests
136//  - EvalScript
137//  - LogHTML
138//  - RemovePluginWhenFinished
139//  - ReportProgress
140// The second item is the verbatim message_contents.
141function ParseTestingMessage(message_data) {
142  if (typeof(message_data) != "string")
143    return undefined;
144  var testing_message_prefix = "TESTING_MESSAGE";
145  var delim_str = ":";
146  var delim1 = message_data.indexOf(delim_str);
147  if (message_data.substring(0, delim1) !== testing_message_prefix)
148    return undefined;
149  var delim2 = message_data.indexOf(delim_str, delim1 + 1);
150  if (delim2 == -1)
151    delim2 = message_data.length;
152  var message_type = message_data.substring(delim1 + 1, delim2);
153  var message_contents = message_data.substring(delim2 + 1);
154  return [message_type, message_contents];
155}
156
157function ClearConsole() {
158  window.document.getElementById("console").innerHTML = "";
159}
160
161function LogHTML(html) {
162  window.document.getElementById("console").innerHTML += html;
163}
164
165function RemovePluginWhenFinished() {
166  window.document.getElementById("container").removePlugin = true;
167}
168
169function sendAutomationMessage(msg) {
170  if (window.domAutomationController) {
171    window.domAutomationController.setAutomationId(0);
172    window.domAutomationController.send(msg);
173  }
174}
175
176function LogTestTime(test_time) {
177  console.log(test_time);
178}
179
180// If something goes really wrong, the test running inside the plugin may not
181// terminate.  For example, if the plugin does not load, the test will never
182// send "PASS" to the browser.  In this case we should explicitly use the
183// automation controller to terminate the test.
184function InternalError(msg) {
185  LogHTML("<p>" + msg);
186  sendAutomationMessage(msg);
187}
188
189function EvalScript(script) {
190  try {
191    eval(script);
192  } catch(ex) {
193  }
194}
195
196var conditions = [];
197// Add a "PostCondition". These are bits of script that are run after the plugin
198// is destroyed. If they evaluate to false or throw an exception, it's
199// considered a failure.
200function AddPostCondition(script) {
201  conditions.push(script);
202}
203// Update the HTML to show the failure and update cookies so that ui_tests
204// doesn't count this as a pass.
205function ConditionFailed(error) {
206  error_string = "Post condition check failed: " + error;
207  InternalError(error_string);
208}
209// Iterate through the post conditions defined in |conditions| and check that
210// they all pass.
211function CheckPostConditions() {
212  var success = true;
213  for (var i = 0; i < conditions.length; ++i) {
214    var script = conditions[i];
215    try {
216      if (!eval(script)) {
217        ConditionFailed("\"" + script + "\"");
218        success = false;
219      }
220    } catch (ex) {
221      ConditionFailed("\"" + script + "\"" + " failed with exception: " +
222                      "\"" + ex.toString() + "\"");
223      success = false;
224    }
225  }
226  return success;
227}
228
229function IsTestingMessage(message_data) {
230  return (ParseTestingMessage(message_data) != undefined);
231}
232
233function handleTestingMessage(message_event) {
234  var type_contents_tuple = ParseTestingMessage(message_event.data);
235  if (type_contents_tuple) {
236    var type = type_contents_tuple[0];
237    var contents = type_contents_tuple[1];
238    if (type === "AddPostCondition")
239      AddPostCondition(contents);
240    else if (type === "ClearConsole")
241      ClearConsole();
242    else if (type === "DidExecuteTests")
243      DidExecuteTests(contents);
244    else if (type === "EvalScript")
245      EvalScript(contents);
246    else if (type === "LogHTML")
247      LogHTML(contents);
248    else if (type === "RemovePluginWhenFinished")
249      RemovePluginWhenFinished();
250    else if (type === "ReportProgress")
251      sendAutomationMessage(contents);
252    else if (type === "LogTestTime")
253      LogTestTime(contents);
254  }
255}
256
257function sendProgress() {
258  // We send "..." to signal that we're still working. See
259  // ppapi/tests/testing_instance.h for how this works.
260  sendAutomationMessage("...");
261}
262
263onload = function() {
264  var testcase = ExtractSearchParameter("testcase");
265  var mode = ExtractSearchParameter("mode");
266  document.title = 'Test ' + testcase;
267  var obj;
268  if (mode == "nacl_newlib") {
269    obj = document.createElement("EMBED");
270    obj.setAttribute("src", "ppapi_nacl_tests_newlib.nmf");
271    obj.setAttribute("type", "application/x-nacl");
272    obj.setAttribute("mode", mode);
273  } else if (mode == "nacl_glibc") {
274    obj = document.createElement("EMBED");
275    obj.setAttribute("src", "ppapi_nacl_tests_glibc.nmf");
276    obj.setAttribute("type", "application/x-nacl");
277    obj.setAttribute("mode", mode);
278  } else if (mode == "nacl_pnacl") {
279    obj = document.createElement("EMBED");
280    obj.setAttribute("src", "ppapi_nacl_tests_pnacl.nmf");
281    obj.setAttribute("type", "application/x-nacl");
282    obj.setAttribute("mode", mode);
283  } else if (mode == "nacl_pnacl_nonsfi") {
284    obj = document.createElement("EMBED");
285    obj.setAttribute("src", "ppapi_nacl_tests_pnacl_nonsfi.nmf");
286    obj.setAttribute("type", "application/x-nacl");
287    obj.setAttribute("mode", mode);
288  } else {
289    var mimeType = "application/x-ppapi-tests";
290    if (mimeType in navigator.mimeTypes) {
291      obj = document.createElement("EMBED");
292      obj.setAttribute("src", "http://a.b.c/test");
293      obj.setAttribute("type", mimeType);
294    } else {
295      document.getElementById("console").innerHTML =
296          '<span class="fail">FAIL</span>: ' +
297          '<span class="err_msg">Test plug-in is not registered.</span>';
298    }
299  }
300  if (obj) {
301    obj.setAttribute("width", 80);
302    obj.setAttribute("height", 80);
303    obj.setAttribute("style",
304                     "background-color:#AAAAAA;border:1px solid black;");
305    obj.setAttribute("id", "plugin");
306    obj.setAttribute("testcase", testcase);
307    obj.setAttribute("protocol", window.location.protocol);
308    var websocket_host = ExtractSearchParameter("websocket_host");
309    if (websocket_host != "")
310      obj.setAttribute("websocket_host", websocket_host);
311    var websocket_port = ExtractSearchParameter("websocket_port");
312    if (websocket_port != "")
313      obj.setAttribute("websocket_port", websocket_port);
314    var ssl_server_port = ExtractSearchParameter("ssl_server_port");
315    if (ssl_server_port != "")
316      obj.setAttribute("ssl_server_port", ssl_server_port);
317
318    var container = document.getElementById("container");
319    container.addEventListener("message", handleTestingMessage, true);
320
321    // "error" and "crash" events will only fire for NaCl, but adding these
322    // listeners doesn't hurt in the non-NaCl cases.
323    obj.addEventListener("error", function() {
324      InternalError("Plugin did not load. '" + obj.lastError + "'");
325    }, true);
326    obj.addEventListener("crash", function() {
327      InternalError("Plugin crashed. '" + obj.lastError + "'");
328    }, true);
329
330    // NaCl sends progress events while loading. When we get one, notify the
331    // domAutomationController so that it knows we're still working.
332    obj.addEventListener("loadstart", sendProgress, true);
333    obj.addEventListener("progress", sendProgress, true);
334    obj.addEventListener("load", sendProgress, true);
335    obj.addEventListener("loadend", sendProgress, true);
336
337    // Register a bad dispatchEvent to make sure it isn't used. See 'EVIL' note
338    // below.
339    var original = obj.dispatchEvent;
340    obj.dispatchEvent = function() {
341      InternalError("Bad dispatchEvent called!");
342    }
343    container.appendChild(obj);
344  }
345}
346
347// EVIL Note:
348// This part of the script does some nefarious things to make sure that it
349// doesn't affect the behavior of PostMessage (on which all the tests rely). In
350// particular, we replace document.createEvent, MessageEvent.initMessageEvent,
351// and the MessageEvent constructor. Previously, the NaCl integration
352// implementation made use of these and would fail (http://crbug.com/82604
353// and http://crbug.com/109775).
354document.createEvent = function() {
355  InternalError("Bad document.createEvent called!");
356}
357function MessageEvent() {
358  InternalError("Bad MessageEvent constructor called!");
359}
360MessageEvent.prototype.initMessageEvent = function() {
361  InternalError("Bad MessageEvent.initMessageEvent called!");
362}
363
364</script>
365</head><body>
366<div>
367  <div id="container"></div>
368  <div id="console"><span class="load_msg">loading...</span></div>
369</div>
370</body></html>
371