/* * Copyright 2016 Jacek Caban for CodeWeavers * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ var compat_version; var tests = []; sync_test("elem_props", function() { var elem = document.documentElement; function test_exposed(prop, expect) { if(expect) ok(prop in elem, prop + " not found in element."); else ok(!(prop in elem), prop + " found in element."); } var v = document.documentMode; test_exposed("doScroll", v < 11); test_exposed("readyState", v < 11); test_exposed("clientTop", true); test_exposed("title", true); test_exposed("querySelectorAll", v >= 8); test_exposed("textContent", v >= 9); test_exposed("prefix", v >= 9); test_exposed("firstElementChild", v >= 9); test_exposed("onsubmit", v >= 9); test_exposed("getElementsByClassName", v >= 9); test_exposed("removeAttributeNS", v >= 9); test_exposed("addEventListener", v >= 9); if (v != 8 /* todo_wine */) test_exposed("hasAttribute", v >= 8); test_exposed("removeEventListener", v >= 9); test_exposed("dispatchEvent", v >= 9); test_exposed("msSetPointerCapture", v >= 10); if (v >= 9) test_exposed("spellcheck", v >= 10); elem = document.createElement("style"); test_exposed("media", true); test_exposed("type", true); test_exposed("disabled", true); test_exposed("media", true); test_exposed("sheet", v >= 9); test_exposed("readyState", v < 11); test_exposed("styleSheet", v < 11); }); sync_test("doc_props", function() { function test_exposed(prop, expect) { if(expect) ok(prop in document, prop + " not found in document."); else ok(!(prop in document), prop + " found in document."); } var v = document.documentMode; test_exposed("textContent", v >= 9); test_exposed("prefix", v >= 9); test_exposed("defaultView", v >= 9); test_exposed("head", v >= 9); test_exposed("addEventListener", v >= 9); test_exposed("removeEventListener", v >= 9); test_exposed("dispatchEvent", v >= 9); test_exposed("createEvent", v >= 9); test_exposed("parentWindow", true); if(v >= 9) ok(document.defaultView === document.parentWindow, "defaultView != parentWindow"); }); sync_test("docfrag_props", function() { var docfrag = document.createDocumentFragment(); function test_exposed(prop, expect) { if(expect) ok(prop in docfrag, prop + " not found in document fragent."); else ok(!(prop in docfrag), prop + " found in document fragent."); } var v = document.documentMode; test_exposed("compareDocumentPosition", v >= 9); }); sync_test("window_props", function() { function test_exposed(prop, expect) { if(expect) ok(prop in window, prop + " not found in window."); else ok(!(prop in window), prop + " found in window."); } var v = document.documentMode; test_exposed("postMessage", true); test_exposed("sessionStorage", true); test_exposed("localStorage", true); test_exposed("addEventListener", v >= 9); test_exposed("removeEventListener", v >= 9); test_exposed("dispatchEvent", v >= 9); test_exposed("getSelection", v >= 9); test_exposed("onfocusout", v >= 9); test_exposed("getComputedStyle", v >= 9); test_exposed("requestAnimationFrame", v >= 10); test_exposed("Map", v >= 11); test_exposed("Set", v >= 11); if(v >= 9) /* FIXME: native exposes it in all compat modes */ test_exposed("performance", true); test_exposed("console", v >= 10); }); sync_test("xhr_props", function() { var xhr = new XMLHttpRequest(); function test_exposed(prop, expect) { if(expect) ok(prop in xhr, prop + " not found in XMLHttpRequest."); else ok(!(prop in xhr), prop + " found in XMLHttpRequest."); } var v = document.documentMode; test_exposed("addEventListener", v >= 9); test_exposed("removeEventListener", v >= 9); test_exposed("dispatchEvent", v >= 9); }); sync_test("stylesheet_props", function() { var v = document.documentMode; var elem = document.createElement("style"); document.body.appendChild(elem); var sheet = v >= 9 ? elem.sheet : elem.styleSheet; function test_exposed(prop, expect) { if(expect) ok(prop in sheet, prop + " not found in style sheet."); else ok(!(prop in sheet), prop + " found in style sheet."); } test_exposed("href", true); test_exposed("title", true); test_exposed("type", true); test_exposed("media", true); test_exposed("ownerNode", v >= 9); test_exposed("ownerRule", v >= 9); test_exposed("cssRules", v >= 9); test_exposed("insertRule", v >= 9); test_exposed("deleteRule", v >= 9); test_exposed("disabled", true); test_exposed("parentStyleSheet", true); test_exposed("owningElement", true); test_exposed("readOnly", true); test_exposed("imports", true); test_exposed("id", true); test_exposed("addImport", true); test_exposed("addRule", true); test_exposed("removeImport", true); test_exposed("removeRule", true); test_exposed("cssText", true); test_exposed("rules", true); }); sync_test("xhr open", function() { var e = false; try { (new XMLHttpRequest()).open("GET", "https://www.winehq.org/"); }catch(ex) { e = true; } if(document.documentMode < 10) ok(e, "expected exception"); else ok(!e, "unexpected exception"); }); sync_test("style_props", function() { var style = document.body.style; function test_exposed(prop, expect) { if(expect) ok(prop in style, prop + " not found in style object."); else ok(!(prop in style), prop + " found in style object."); } var v = document.documentMode; test_exposed("removeAttribute", true); test_exposed("zIndex", true); test_exposed("z-index", true); test_exposed("filter", true); test_exposed("pixelTop", true); test_exposed("float", true); test_exposed("css-float", false); test_exposed("style-float", false); test_exposed("setProperty", v >= 9); test_exposed("removeProperty", v >= 9); test_exposed("background-clip", v >= 9); test_exposed("msTransform", v >= 9); test_exposed("transform", v >= 10); style = document.body.currentStyle; test_exposed("zIndex", true); test_exposed("z-index", true); test_exposed("filter", true); test_exposed("pixelTop", false); test_exposed("float", true); test_exposed("css-float", false); test_exposed("style-float", false); test_exposed("setProperty", v >= 9); test_exposed("removeProperty", v >= 9); test_exposed("background-clip", v >= 9); test_exposed("transform", v >= 10); if(window.getComputedStyle) { style = window.getComputedStyle(document.body); test_exposed("removeAttribute", false); test_exposed("zIndex", true); test_exposed("z-index", true); test_exposed("pixelTop", false); test_exposed("float", true); test_exposed("css-float", false); test_exposed("style-float", false); test_exposed("setProperty", v >= 9); test_exposed("removeProperty", v >= 9); test_exposed("background-clip", v >= 9); test_exposed("transform", v >= 10); } }); sync_test("JS objs", function() { var g = window; function test_exposed(func, obj, expect) { if(expect) ok(func in obj, func + " not found in " + obj); else ok(!(func in obj), func + " found in " + obj); } function test_parses(code, expect) { var success; try { eval(code); success = true; }catch(e) { success = false; } if(expect) ok(success === true, code + " did not parse"); else ok(success === false, code + " parsed"); } var v = document.documentMode; test_exposed("ScriptEngineMajorVersion", g, true); test_exposed("JSON", g, v >= 8); test_exposed("now", Date, true); test_exposed("toISOString", Date.prototype, v >= 9); test_exposed("isArray", Array, v >= 9); test_exposed("forEach", Array.prototype, v >= 9); test_exposed("indexOf", Array.prototype, v >= 9); test_exposed("trim", String.prototype, v >= 9); test_exposed("map", Array.prototype, v >= 9); /* FIXME: IE8 implements weird semi-functional property descriptors. */ if(v != 8) { test_exposed("getOwnPropertyDescriptor", Object, v >= 8); test_exposed("defineProperty", Object, v >= 8); test_exposed("defineProperties", Object, v >= 8); } test_exposed("getPrototypeOf", Object, v >= 9); test_parses("if(false) { o.default; }", v >= 9); test_parses("if(false) { o.with; }", v >= 9); test_parses("if(false) { o.if; }", v >= 9); }); sync_test("elem_by_id", function() { document.body.innerHTML = '
'; var id_elem = document.getElementById("testid"); ok(id_elem.tagName === "FORM", "id_elem.tagName = " + id_elem.tagName); var name_elem = document.getElementById("testname"); if(document.documentMode < 8) ok(id_elem === name_elem, "id_elem != id_elem"); else ok(name_elem === null, "name_elem != null"); }); sync_test("doc_mode", function() { compat_version = parseInt(document.location.search.substring(1)); trace("Testing compatibility mode " + compat_version); if(compat_version > 6 && compat_version > document.documentMode) { win_skip("Document mode not supported (expected " + compat_version + " got " + document.documentMode + ")"); reportSuccess(); return; } ok(Math.max(compat_version, 5) === document.documentMode, "documentMode = " + document.documentMode); if(document.documentMode > 5) ok(document.compatMode === "CSS1Compat", "document.compatMode = " + document.compatMode); else ok(document.compatMode === "BackCompat", "document.compatMode = " + document.compatMode); }); async_test("iframe_doc_mode", function() { var iframe = document.createElement("iframe"); iframe.onload = function() { var iframe_mode = iframe.contentWindow.document.documentMode; if(document.documentMode < 9) ok(iframe_mode === 5, "iframe_mode = " + iframe_mode); else ok(iframe_mode === document.documentMode, "iframe_mode = " + iframe_mode); next_test(); } iframe.src = "about:blank"; document.body.appendChild(iframe); }); sync_test("conditional_comments", function() { var div = document.createElement("div"); document.body.appendChild(div); function test_version(v) { var version = compat_version ? compat_version : 7; div.innerHTML = ""; ok(div.innerText === (version <= v ? "true" : ""), "div.innerText = " + div.innerText + " for version (<=) " + v); div.innerHTML = ""; ok(div.innerText === (version < v ? "true" : ""), "div.innerText = " + div.innerText + " for version (<) " + v); div.innerHTML = ""; ok(div.innerText === (version >= v && version < 10 ? "true" : ""), "div.innerText = " + div.innerText + " for version (>=) " + v); div.innerHTML = ""; ok(div.innerText === (version > v && version < 10 ? "true" : ""), "div.innerText = " + div.innerText + " for version (>) " + v); } test_version(5); test_version(6); test_version(7); test_version(8); }); var ready_states; async_test("script_load", function() { var v = document.documentMode; if(v < 9) { next_test(); return; } var elem = document.createElement("script"); ready_states = ""; elem.onreadystatechange = guard(function() { ok(v < 11, "unexpected onreadystatechange call"); ready_states += elem.readyState + ","; }); elem.onload = guard(function() { switch(v) { case 9: ok(ready_states === "loading,exec,loaded,", "ready_states = " + ready_states); break; case 10: ok(ready_states === "loading,exec,", "ready_states = " + ready_states); break; case 11: ok(ready_states === "exec,", "ready_states = " + ready_states); break; } next_test(); }); document.body.appendChild(elem); elem.src = "jsstream.php?simple"; external.writeStream("simple", "ready_states += 'exec,';"); }); sync_test("navigator", function() { var v = document.documentMode, re; var app = navigator.appVersion; ok(navigator.userAgent === "Mozilla/" + app, "userAgent = " + navigator.userAgent + " appVersion = " + app); re = v < 11 ? "^" + (v < 9 ? "4" : "5") + "\\.0 \\(compatible; MSIE " + (v < 7 ? 7 : v) + "\\.0; Windows NT [0-9].[0-9]; .*Trident/[678]\\.0.*\\)$" : "^5.0 \\(Windows NT [0-9].[0-9]; .*Trident/[678]\\.0.*rv:11.0\\) like Gecko$"; ok(new RegExp(re).test(app), "appVersion = " + app); ok(navigator.appCodeName === "Mozilla", "appCodeName = " + navigator.appCodeName); ok(navigator.appName === (v < 11 ? "Microsoft Internet Explorer" : "Netscape"), "appName = " + navigator.appName); ok(navigator.toString() === (v < 9 ? "[object]" : "[object Navigator]"), "navigator.toString() = " + navigator.toString()); }); sync_test("delete_prop", function() { var v = document.documentMode; var obj = document.createElement("div"), r, obj2; obj.prop1 = true; r = false; try { delete obj.prop1; }catch(ex) { r = true; } if(v < 8) { ok(r, "did not get an expected exception"); return; } ok(!r, "got an unexpected exception"); ok(!("prop1" in obj), "prop1 is still in obj"); /* again, this time prop1 does not exist */ r = false; try { delete obj.prop1; }catch(ex) { r = true; } if(v < 9) { ok(r, "did not get an expected exception"); return; }else { ok(!r, "got an unexpected exception"); ok(!("prop1" in obj), "prop1 is still in obj"); } r = (delete obj.className); ok(r, "delete returned " + r); ok("className" in obj, "className deleted from obj"); ok(obj.className === "", "className = " + obj.className); /* builtin propertiles don't throw any exception, but are not really deleted */ r = (delete obj.tagName); ok(r, "delete returned " + r); ok("tagName" in obj, "tagName deleted from obj"); ok(obj.tagName === "DIV", "tagName = " + obj.tagName); obj = document.querySelectorAll("*"); ok("0" in obj, "0 is not in obj"); obj2 = obj[0]; r = (delete obj[0]); ok("0" in obj, "0 is not in obj"); ok(obj[0] === obj2, "obj[0] != obj2"); /* test window object and its global scope handling */ obj = window; obj.globalprop1 = true; ok(globalprop1, "globalprop1 = " + globalprop1); r = false; try { delete obj.globalprop1; }catch(ex) { r = true; } if(v < 9) { ok(r, "did not get an expected exception"); }else { ok(!r, "got an unexpected globalprop1 exception"); ok(!("globalprop1" in obj), "globalprop1 is still in obj"); } globalprop2 = true; ok(obj.globalprop2, "globalprop2 = " + globalprop2); r = false; try { delete obj.globalprop2; }catch(ex) { r = true; } if(v < 9) { ok(r, "did not get an expected globalprop2 exception"); }else { ok(!r, "got an unexpected exception"); todo_wine. ok(!("globalprop2" in obj), "globalprop2 is still in obj"); } obj.globalprop3 = true; ok(globalprop3, "globalprop3 = " + globalprop3); r = false; try { delete globalprop3; }catch(ex) { r = true; } if(v < 9) { ok(r, "did not get an expected exception"); ok("globalprop3" in obj, "globalprop3 is not in obj"); }else { ok(!r, "got an unexpected globalprop3 exception"); ok(!("globalprop3" in obj), "globalprop3 is still in obj"); } globalprop4 = true; ok(obj.globalprop4, "globalprop4 = " + globalprop4); r = (delete globalprop4); ok(r, "delete returned " + r); todo_wine. ok(!("globalprop4" in obj), "globalprop4 is still in obj"); }); var func_scope_val = 1; var func_scope_val2 = 2; sync_test("func_scope", function() { var func_scope_val = 2; var f = function func_scope_val() { return func_scope_val; }; func_scope_val = 3; if(document.documentMode < 9) { ok(f() === 3, "f() = " + f()); return; } ok(f === f(), "f() = " + f()); f = function func_scope_val(a) { func_scope_val = 4; return func_scope_val; }; func_scope_val = 3; ok(f === f(), "f() = " + f()); ok(func_scope_val === 3, "func_scope_val = " + func_scope_val); ok(window.func_scope_val === 1, "window.func_scope_val = " + window.func_scope_val); f = function func_scope_val(a) { return (function() { return a ? func_scope_val(false) : func_scope_val; })(); }; ok(f === f(true), "f(true) = " + f(true)); window = 1; ok(window === window.self, "window = " + window); ! function func_scope_val2() {}; ok(window.func_scope_val2 === 2, "window.func_scope_val2 = " + window.func_scope_val2); var o = {}; (function(x) { ok(x === o, "x = " + x); ! function x() {}; ok(x === o, "x != o"); })(o); (function(x) { ok(x === o, "x = " + x); 1, function x() {}; ok(x === o, "x != o"); })(o); (function() { ! function x() {}; try { x(); ok(false, "expected exception"); }catch(e) {} })(o); }); sync_test("set_obj", function() { if(!("Set" in window)) return; var s = new Set, r; ok(Object.getPrototypeOf(s) === Set.prototype, "unexpected Set prototype"); function test_length(name, len) { ok(Set.prototype[name].length === len, "Set.prototype." + name + " = " + Set.prototype[name].length); } test_length("add", 1); test_length("clear", 0); test_length("delete", 1); test_length("forEach", 1); test_length("has", 1); ok(!("entries" in s), "entries are in Set"); ok(!("keys" in s), "keys are in Set"); ok(!("values" in s), "values are in Set"); r = Object.prototype.toString.call(s); ok(r === "[object Object]", "toString returned " + r); }); sync_test("map_obj", function() { if(!("Map" in window)) return; var s = new Map, r, i; ok(Object.getPrototypeOf(s) === Map.prototype, "unexpected Map prototype"); function test_length(name, len) { ok(Map.prototype[name].length === len, "Map.prototype." + name + " = " + Map.prototype[name].length); } test_length("clear", 0); test_length("delete", 1); test_length("forEach", 1); test_length("get", 1); test_length("has", 1); test_length("set", 2); ok(!("entries" in s), "entries are in Map"); ok(!("keys" in s), "keys are in Map"); ok(!("values" in s), "values are in Map"); todo_wine. ok("size" in Map.prototype, "size is not in Map.prototype"); r = Object.prototype.toString.call(s); ok(r === "[object Object]", "toString returned " + r); r = s.get("test"); ok(r === undefined, "get(test) returned " + r); r = s.has("test"); ok(r === false, "has(test) returned " + r); ok(s.size === 0, "size = " + s.size + " expected 0"); r = s.set("test", 1); ok(r === undefined, "set returned " + r); ok(s.size === 1, "size = " + s.size + " expected 1"); r = s.get("test"); ok(r === 1, "get(test) returned " + r); r = s.has("test"); ok(r === true, "has(test) returned " + r); s.size = 100; ok(s.size === 1, "size = " + s.size + " expected 1"); s.set("test", 2); r = s.get("test"); ok(r === 2, "get(test) returned " + r); r = s.has("test"); ok(r === true, "has(test) returned " + r); r = s["delete"]("test"); /* using s.delete() would break parsing in quirks mode */ ok(r === true, "delete(test) returned " + r); ok(s.size === 0, "size = " + s.size + " expected 0"); r = s["delete"]("test"); ok(r === false, "delete(test) returned " + r); var test_keys = [undefined, null, NaN, 3, "str", false, true, {}]; for(i in test_keys) { r = s.set(test_keys[i], test_keys[i] + 1); ok(r === undefined, "set(test) returned " + r); } ok(s.size === test_keys.length, "size = " + s.size + " expected " + test_keys.length); for(i in test_keys) { r = s.get(test_keys[i]); if(isNaN(test_keys[i])) ok(isNaN(r), "get(" + test_keys[i] + ") returned " + r); else ok(r === test_keys[i] + 1, "get(" + test_keys[i] + ") returned " + r); } var calls = []; i = 0; r = s.forEach(function(value, key) { if(isNaN(test_keys[i])) { ok(isNaN(key), "key = " + key + " expected NaN"); ok(isNaN(value), "value = " + value + " expected NaN"); }else { ok(key === test_keys[i], "key = " + key + " expected " + test_keys[i]); ok(value === key + 1, "value = " + value); } i++; }); ok(i === test_keys.length, "i = " + i); ok(r === undefined, "forEach returned " + r); s.set(3, "test2") calls = []; i = 0; s.forEach(function(value, key) { if(isNaN(test_keys[i])) ok(isNaN(key), "key = " + key + " expected " + test_keys[i]); else ok(key === test_keys[i], "key = " + key + " expected " + test_keys[i]); i++; }); ok(i === test_keys.length, "i = " + i); r = s.clear(); ok(r === undefined, "clear returned " + r); ok(s.size === 0, "size = " + s.size + " expected 0"); r = s.get(test_keys[0]); ok(r === undefined, "get returned " + r); s = new Map(); s.set(1, 10); s.set(2, 20); s.set(3, 30); i = true; s.forEach(function() { ok(i, "unexpected call"); s.clear(); i = false; }); s = new Map(); s.set(1, 10); s.set(2, 20); s.set(3, 30); i = 0; s.forEach(function(value, key) { i += key + value; r = s["delete"](key); ok(r === true, "delete returned " + r); }); ok(i === 66, "i = " + i); try { Map.prototype.set.call({}, 1, 2); ok(false, "expected exception"); }catch(e) { ok(e.number === 0xa13fc - 0x80000000, "e.number = " + e.number); } }); sync_test("elem_attr", function() { var v = document.documentMode; var elem = document.createElement("div"), r; r = elem.getAttribute("class"); ok(r === null, "class attr = " + r); r = elem.getAttribute("className"); ok(r === (v < 8 ? "" : null), "className attr = " + r); elem.className = "cls"; r = elem.getAttribute("class"); ok(r === (v < 8 ? null : "cls"), "class attr = " + r); r = elem.getAttribute("className"); ok(r === (v < 8 ? "cls" : null), "className attr = " + r); elem.setAttribute("class", "cls2"); ok(elem.className === (v < 8 ? "cls" : "cls2"), "elem.className = " + elem.className); r = elem.getAttribute("class"); ok(r === "cls2", "class attr = " + r); r = elem.getAttribute("className"); ok(r === (v < 8 ? "cls" : null), "className attr = " + r); elem.setAttribute("className", "cls3"); ok(elem.className === (v < 8 ? "cls3" : "cls2"), "elem.className = " + elem.className); r = elem.getAttribute("class"); ok(r === "cls2", "class attr = " + r); r = elem.getAttribute("className"); ok(r === "cls3", "className attr = " + r); });