commit xpra-html5 for openSUSE:Factory
Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package xpra-html5 for openSUSE:Factory checked in at 2022-05-31 15:47:33 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/xpra-html5 (Old) and /work/SRC/openSUSE:Factory/.xpra-html5.new.1548 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Package is "xpra-html5" Tue May 31 15:47:33 2022 rev:3 rq:979972 version:5.0+git20220516.caf9971 Changes: -------- --- /work/SRC/openSUSE:Factory/xpra-html5/xpra-html5.changes 2022-04-27 21:41:09.825001869 +0200 +++ /work/SRC/openSUSE:Factory/.xpra-html5.new.1548/xpra-html5.changes 2022-05-31 15:48:44.476042356 +0200 @@ -1,0 +2,7 @@ +Mon May 23 21:12:32 UTC 2022 - scott.bradnick@suse.com + +- Updating uglifyjs to '3.15.5'. +- Update to version 5.0+git20220516.caf9971: + * Last known commit working w/o error(s). + +------------------------------------------------------------------- Old: ---- uglify-js-3.15.4.tar.gz xpra-html5-4.5.2+git20220421.ecc3a08.tar.gz New: ---- uglify-js-3.15.5.tar.gz xpra-html5-5.0+git20220516.caf9971.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ xpra-html5.spec ++++++ --- /var/tmp/diff_new_pack.ngmKPF/_old 2022-05-31 15:48:44.860042611 +0200 +++ /var/tmp/diff_new_pack.ngmKPF/_new 2022-05-31 15:48:44.864042613 +0200 @@ -16,13 +16,13 @@ # -%define uglifyjs_version 3.15.4 +%define uglifyjs_version 3.15.5 %define minifier uglifyjs %define python python3 Name: xpra-html5 Release: 0 -Version: 4.5.2+git20220421.ecc3a08 +Version: 5.0+git20220516.caf9971 Summary: HTML5 client for Xpra License: GPL-2.0+ AND BSD-3-Clause AND LGPL-3.0+ AND MIT URL: https://xpra.org/ ++++++ _service ++++++ --- /var/tmp/diff_new_pack.ngmKPF/_old 2022-05-31 15:48:44.904042640 +0200 +++ /var/tmp/diff_new_pack.ngmKPF/_new 2022-05-31 15:48:44.908042642 +0200 @@ -6,6 +6,28 @@ <service name="obs_scm" mode="disabled"> <param name="url">https://github.com/Xpra-org/xpra-html5</param> <param name="scm">git</param> + <!-- GOOD + <param name="revision">8aea0a5</param> + <param name="revision">0f51cab</param> + <param name="revision">1fe2469</param> + <param name="revision">09d46e4</param> + <param name="revision">3e2e896</param> + <param name="revision">7908dab</param> + <param name="revision">9512aca</param> + <param name="revision">e181fd1</param> + <param name="revision">caf9971</param> + --> + <!-- QUESTIONABLE + <param name="revision">ced316f</param> + <param name="revision">9e26e8e</param> + <param name="revision">8336b02</param> + --> + <!-- BAD + <param name="revision">799330a</param> + <param name="revision">fbb091f</param> + --> + <!-- TESTING --> + <param name="revision">caf9971</param> <param name="versionformat">@PARENT_TAG@+git%cd.%h</param> <param name="versionrewrite-pattern">v(.*)</param> <param name="changesgenerate">enable</param> ++++++ _servicedata ++++++ --- /var/tmp/diff_new_pack.ngmKPF/_old 2022-05-31 15:48:44.924042653 +0200 +++ /var/tmp/diff_new_pack.ngmKPF/_new 2022-05-31 15:48:44.928042656 +0200 @@ -1,6 +1,6 @@ <servicedata> <service name="tar_scm"> <param name="url">https://github.com/Xpra-org/xpra-html5</param> - <param name="changesrevision">ecc3a0819807d5174d28b6bc76e498d9af3b92fa</param></service></servicedata> + <param name="changesrevision">caf9971d54bc818f1740b06c646a74316bffb98f</param></service></servicedata> (No newline at EOF) ++++++ uglify-js-3.15.4.tar.gz -> uglify-js-3.15.5.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/UglifyJS-3.15.4/README.md new/UglifyJS-3.15.5/README.md --- old/UglifyJS-3.15.4/README.md 2022-04-09 19:16:11.000000000 +0200 +++ new/UglifyJS-3.15.5/README.md 2022-05-10 23:26:10.000000000 +0200 @@ -1373,3 +1373,19 @@ // TypeError: const 'a' has already been declared ``` UglifyJS may modify the input which in turn may suppress those errors. +- Later versions of Chrome and Node.js will give incorrect results with the + following: + ```javascript + try { + class A { + static 42; + static get 42() {} + } + console.log("PASS"); + } catch (e) { + console.log("FAIL"); + } + // Expected: "PASS" + // Actual: "FAIL" + ``` + UglifyJS may modify the input which in turn may suppress those errors. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/UglifyJS-3.15.4/lib/compress.js new/UglifyJS-3.15.5/lib/compress.js --- old/UglifyJS-3.15.4/lib/compress.js 2022-04-09 19:16:11.000000000 +0200 +++ new/UglifyJS-3.15.5/lib/compress.js 2022-05-10 23:26:10.000000000 +0200 @@ -649,7 +649,7 @@ } if (!HOP(tw.safe_ids, def.id)) { if (!safe) return false; - if (safe.read) { + if (safe.read || tw.in_loop) { var scope = tw.find_parent(AST_BlockScope); if (scope instanceof AST_Class) return false; if (def.scope.resolve() !== scope.resolve()) return false; @@ -6081,18 +6081,15 @@ } if (node instanceof AST_Call) { var exp = node.expression; - var tail = exp.tail_node(); - if (!(tail instanceof AST_LambdaExpression)) { + if (exp instanceof AST_LambdaExpression) { + node.args.forEach(function(arg) { + arg.walk(tw); + }); + exp.walk(tw); + } else { descend(); - return mark_expression(exp); + mark_expression(exp); } - if (exp !== tail) exp.expressions.slice(0, -1).forEach(function(node) { - node.walk(tw); - }); - node.args.forEach(function(arg) { - arg.walk(tw); - }); - tail.walk(tw); return true; } if (node instanceof AST_Class) { @@ -6105,9 +6102,10 @@ if (prop.static) { prop.value.walk(tw); } else { - push(tw); + push(); + segment.block = node; prop.value.walk(tw); - pop(tw); + pop(); } }); return true; @@ -6172,6 +6170,8 @@ if (node === self) root = segment; if (node instanceof AST_Lambda) { if (node.name) references[node.name.definition().id] = false; + var parent = tw.parent(); + var in_iife = parent && parent.TYPE == "Call" && parent.expression === node; var marker = node.uses_arguments && !tw.has_directive("use strict") ? function(node) { if (node instanceof AST_SymbolFunarg) references[node.definition().id] = false; } : function(node) { @@ -6187,11 +6187,13 @@ || node.parent_scope.find_variable(ref.name) === def)) { references[def.id] = false; references[ldef.id] = false; - } else { + } else if (in_iife) { var save = segment; pop(); mark(ref, true); segment = save; + } else { + mark(ref, true); } return true; }); @@ -6214,7 +6216,8 @@ } else { descend(); } - return mark_expression(exp); + mark_expression(exp); + return true; } if (node instanceof AST_Switch) { node.expression.walk(tw); @@ -6246,9 +6249,7 @@ if (node instanceof AST_Try) { var save_try = in_try; in_try = node; - var save = segment; walk_body(node, tw); - segment = save; if (node.bcatch) { if (node.bcatch.argname) node.bcatch.argname.mark_symbol(function(node) { if (node instanceof AST_SymbolCatch) { @@ -6266,7 +6267,6 @@ } } in_try = save_try; - segment = save; if (node.bfinally) node.bfinally.walk(tw); return true; } @@ -6316,11 +6316,9 @@ } function mark_expression(exp) { - if (compressor.option("ie")) { - var sym = root_expr(exp); - if (sym instanceof AST_SymbolRef) sym.walk(tw); - } - return true; + if (!compressor.option("ie")) return; + var sym = root_expr(exp); + if (sym instanceof AST_SymbolRef) sym.walk(tw); } function walk_cond(condition, consequent, alternative) { @@ -11142,9 +11140,15 @@ && assign instanceof AST_Assign && assign.operator == "=" && self.left.equivalent_to(assign.left)) { - self.right = assign.right; - assign.right = self; - return assign; + return make_node(AST_Assign, self, { + operator: "=", + left: assign.left, + right: make_node(AST_Binary, self, { + operator: self.operator, + left: self.left, + right: assign.right, + }), + }).optimize(compressor); } } if (compressor.option("comparisons")) switch (self.operator) { @@ -13382,7 +13386,7 @@ }); var body = []; fn.variables.each(function(def, name) { - if (name == "arguments") return; + if (!arrow && name == "arguments" && def.orig.length == 1) return; names.set(name, true); scope.enclosed.push(def); scope.variables.set(name, def); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/UglifyJS-3.15.4/lib/scope.js new/UglifyJS-3.15.5/lib/scope.js --- old/UglifyJS-3.15.4/lib/scope.js 2022-04-09 19:16:11.000000000 +0200 +++ new/UglifyJS-3.15.5/lib/scope.js 2022-05-10 23:26:10.000000000 +0200 @@ -469,6 +469,7 @@ this.uses_arguments = false; this.def_variable(new AST_SymbolFunarg({ name: "arguments", + scope: this, start: this.start, end: this.end, })); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/UglifyJS-3.15.4/package.json new/UglifyJS-3.15.5/package.json --- old/UglifyJS-3.15.4/package.json 2022-04-09 19:16:11.000000000 +0200 +++ new/UglifyJS-3.15.5/package.json 2022-05-10 23:26:10.000000000 +0200 @@ -3,7 +3,7 @@ "description": "JavaScript parser, mangler/compressor and beautifier toolkit", "author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)", "license": "BSD-2-Clause", - "version": "3.15.4", + "version": "3.15.5", "engines": { "node": ">=0.8.0" }, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/UglifyJS-3.15.4/test/compress/arrows.js new/UglifyJS-3.15.5/test/compress/arrows.js --- old/UglifyJS-3.15.4/test/compress/arrows.js 2022-04-09 19:16:11.000000000 +0200 +++ new/UglifyJS-3.15.5/test/compress/arrows.js 2022-05-10 23:26:10.000000000 +0200 @@ -1018,3 +1018,91 @@ expect_stdout: "NaN" node_version: ">=4" } + +issue_5414_1: { + options = { + arrows: true, + if_return: true, + inline: true, + toplevel: true, + } + input: { + (() => { + (() => { + if (!console) + var arguments = 42; + while (console.log(arguments)); + })(); + })(); + } + expect: { + (() => { + if (!console) + var arguments = 42; + while (console.log(arguments)); + })(); + } + expect_stdout: true + node_version: ">=4" +} + +issue_5414_2: { + options = { + arrows: true, + inline: true, + side_effects: true, + toplevel: true, + } + input: { + (() => { + (() => { + if (!console) + var arguments = 42; + while (console.log(arguments)); + })(); + })(); + } + expect: { + (() => { + if (!console) + var arguments = 42; + while (console.log(arguments)); + })(); + } + expect_stdout: true + node_version: ">=4" +} + +issue_5416: { + options = { + dead_code: true, + evaluate: true, + inline: true, + loops: true, + unused: true, + } + input: { + var f = () => { + while ((() => { + console; + var a = function g(arguments) { + console.log(arguments); + }(); + })()); + }; + f(); + } + expect: { + var f = () => { + { + arguments = void 0; + console; + console.log(arguments); + var arguments; + } + }; + f(); + } + expect_stdout: "undefined" + node_version: ">=4" +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/UglifyJS-3.15.4/test/compress/classes.js new/UglifyJS-3.15.5/test/compress/classes.js --- old/UglifyJS-3.15.4/test/compress/classes.js 2022-04-09 19:16:11.000000000 +0200 +++ new/UglifyJS-3.15.5/test/compress/classes.js 2022-05-10 23:26:10.000000000 +0200 @@ -2571,3 +2571,33 @@ expect_stdout: "PASS PASS" node_version: ">=12" } + +issue_5436: { + options = { + merge_vars: true, + } + input: { + function f(a) { + class A { + p = a; + } + var b = "FAIL"; + A == b && b(); + return new A(); + } + console.log(f("PASS").p); + } + expect: { + function f(a) { + class A { + p = a; + } + var b = "FAIL"; + A == b && b(); + return new A(); + } + console.log(f("PASS").p); + } + expect_stdout: "PASS" + node_version: ">=12" +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/UglifyJS-3.15.4/test/compress/destructured.js new/UglifyJS-3.15.5/test/compress/destructured.js --- old/UglifyJS-3.15.4/test/compress/destructured.js 2022-04-09 19:16:11.000000000 +0200 +++ new/UglifyJS-3.15.5/test/compress/destructured.js 2022-05-10 23:26:10.000000000 +0200 @@ -3535,3 +3535,34 @@ expect_stdout: "PASS" node_version: ">=6" } + +issue_5423: { + options = { + merge_vars: true, + toplevel: true, + } + input: { + var a, b; + function f({ + [function() { + if (++a) + return 42; + }()]: c + }) {} + f(b = f); + console.log(typeof b); + } + expect: { + var a, b; + function f({ + [function() { + if (++a) + return 42; + }()]: c + }) {} + f(b = f); + console.log(typeof b); + } + expect_stdout: "function" + node_version: ">=6" +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/UglifyJS-3.15.4/test/compress/merge_vars.js new/UglifyJS-3.15.5/test/compress/merge_vars.js --- old/UglifyJS-3.15.4/test/compress/merge_vars.js 2022-04-09 19:16:11.000000000 +0200 +++ new/UglifyJS-3.15.5/test/compress/merge_vars.js 2022-05-10 23:26:10.000000000 +0200 @@ -3702,3 +3702,33 @@ ] node_version: ">=4" } + +issue_5420: { + options = { + merge_vars: true, + toplevel: true, + } + input: { + do { + var a = "FAIL 1"; + a && a.p; + a = "FAIL 2"; + try { + continue; + } catch (e) {} + var b = "FAIL 3"; + } while (console.log(b || "PASS")); + } + expect: { + do { + var a = "FAIL 1"; + a && a.p; + a = "FAIL 2"; + try { + continue; + } catch (e) {} + var b = "FAIL 3"; + } while (console.log(b || "PASS")); + } + expect_stdout: "PASS" +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/UglifyJS-3.15.4/test/compress/reduce_vars.js new/UglifyJS-3.15.5/test/compress/reduce_vars.js --- old/UglifyJS-3.15.4/test/compress/reduce_vars.js 2022-04-09 19:16:11.000000000 +0200 +++ new/UglifyJS-3.15.5/test/compress/reduce_vars.js 2022-05-10 23:26:10.000000000 +0200 @@ -7861,3 +7861,38 @@ } expect_stdout: "NaN" } + +issue_5434: { + options = { + evaluate: true, + reduce_vars: true, + unused: true, + } + input: { + console.log(function(a) { + for (var i = 0; i < 2; i++) { + var b = "FAIL"; + f && f(); + a = b; + var f = function() { + b = "PASS"; + }; + } + return a; + }()); + } + expect: { + console.log(function(a) { + for (var i = 0; i < 2; i++) { + var b = "FAIL"; + f && f(); + a = b; + var f = function() { + b = "PASS"; + }; + } + return a; + }()); + } + expect_stdout: "PASS" +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/UglifyJS-3.15.4/test/compress/yields.js new/UglifyJS-3.15.5/test/compress/yields.js --- old/UglifyJS-3.15.4/test/compress/yields.js 2022-04-09 19:16:11.000000000 +0200 +++ new/UglifyJS-3.15.5/test/compress/yields.js 2022-05-10 23:26:10.000000000 +0200 @@ -1525,3 +1525,25 @@ ] node_version: ">=10" } + +issue_5425: { + options = { + assignments: true, + ie: true, + toplevel: true, + unused: true, + yields: true, + } + input: { + var a = "FAIL"; + var b = function* f() {}(a ? a = "PASS" : 42); + console.log(a, typeof f); + } + expect: { + var a = "FAIL"; + (function* f() {})(a && (a = "PASS")); + console.log(a, typeof f); + } + expect_stdout: "PASS undefined" + node_version: ">=4" +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/UglifyJS-3.15.4/test/sandbox.js new/UglifyJS-3.15.5/test/sandbox.js --- old/UglifyJS-3.15.4/test/sandbox.js 2022-04-09 19:16:11.000000000 +0200 +++ new/UglifyJS-3.15.5/test/sandbox.js 2022-05-10 23:26:10.000000000 +0200 @@ -52,8 +52,11 @@ return typeof expected == typeof actual && strip_func_ids(expected) == strip_func_ids(actual); }; exports.patch_module_statements = function(code) { - var count = 0, has_default = "", imports = []; - code = code.replace(/\bexport(?:\s*\{[^{}]*}\s*?(?:$|\n|;)|\s+default\b(?:\s*(\(|\{|class\s*\{|class\s+(?=extends\b)|(?:async\s+)?function\s*(?:\*\s*)?\())?|\b)/g, function(match, header) { + var count = 0, has_default = "", imports = [], strict_mode = ""; + code = code.replace(/^\s*("|')use strict\1\s*;?/, function(match) { + strict_mode = match; + return ""; + }).replace(/\bexport(?:\s*\{[^{}]*}\s*?(?:$|\n|;)|\s+default\b(?:\s*(\(|\{|class\s*\{|class\s+(?=extends\b)|(?:async\s+)?function\s*(?:\*\s*)?\())?|\b)/g, function(match, header) { if (/^export\s+default/.test(match)) has_default = "var _uglify_export_default_;"; if (!header) return ""; if (header.length == 1) return "0, " + header; @@ -78,7 +81,7 @@ return ""; }); imports.push(""); - return has_default + imports.join("\n") + code; + return strict_mode + has_default + imports.join("\n") + code; }; function is_error(result) { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/UglifyJS-3.15.4/test/ufuzz/index.js new/UglifyJS-3.15.5/test/ufuzz/index.js --- old/UglifyJS-3.15.4/test/ufuzz/index.js 2022-04-09 19:16:11.000000000 +0200 +++ new/UglifyJS-3.15.5/test/ufuzz/index.js 2022-05-10 23:26:10.000000000 +0200 @@ -128,7 +128,7 @@ var SUPPORT = function(matrix) { for (var name in matrix) { - matrix[name] = typeof sandbox.run_code(matrix[name]) == "string"; + matrix[name] = !sandbox.is_error(sandbox.run_code(matrix[name])); } return matrix; }({ @@ -1824,7 +1824,13 @@ declared.push(internal); } if (SUPPORT.class_field && rng(2)) { - s += internal || createObjectKey(recurmax, stmtDepth, canThrow); + if (internal) { + s += internal; + } else if (fixed && bug_static_class_field) { + s += getDotKey(); + } else { + s += createObjectKey(recurmax, stmtDepth, canThrow); + } if (rng(5)) { async = bug_async_class_await && fixed && 0; generator = false; @@ -2110,7 +2116,7 @@ } else if (options) { var uglified = UglifyJS.minify(beautified.code, JSON.parse(options)); var expected, actual; - if (typeof uglify_code != "string" || uglified.error) { + if (sandbox.is_error(uglify_code) || uglified.error) { expected = uglify_code; actual = uglified.error; } else { @@ -2147,7 +2153,7 @@ m[component] = o; m.validate = true; var result = UglifyJS.minify(original_code, m); - if (typeof uglify_code != "string") { + if (sandbox.is_error(uglify_code)) { return !sandbox.same_stdout(uglify_code, result.error); } else if (result.error) { errorln("Error testing options." + component + "." + name); @@ -2175,7 +2181,7 @@ m[component] = false; m.validate = true; var result = UglifyJS.minify(original_code, m); - if (typeof uglify_code != "string") { + if (sandbox.is_error(uglify_code)) { return !sandbox.same_stdout(uglify_code, result.error); } else if (result.error) { errorln("Error testing options." + component); @@ -2204,7 +2210,16 @@ errorln(); errorln(); errorln("//-------------------------------------------------------------"); - if (typeof uglify_code == "string") { + if (sandbox.is_error(uglify_code)) { + errorln("// !!! uglify failed !!!"); + errorln(uglify_code); + if (original_erred) { + errorln(); + errorln(); + errorln("original stacktrace:"); + errorln(original_result); + } + } else { errorln("// uglified code"); try_beautify(uglify_code, toplevel, uglify_result, errorln); errorln(); @@ -2213,15 +2228,6 @@ errorln(original_result); errorln("uglified result:"); errorln(uglify_result); - } else { - errorln("// !!! uglify failed !!!"); - errorln(uglify_code); - if (errored) { - errorln(); - errorln(); - errorln("original stacktrace:"); - errorln(original_result); - } } errorln("//-------------------------------------------------------------"); if (!ok) { @@ -2426,22 +2432,23 @@ }, }; var minify_options = require("./options.json"); -if (typeof sandbox.run_code("A:if (0) B:; else B:;") != "string") { +if (sandbox.is_error(sandbox.run_code("A:if (0) B:; else B:;"))) { minify_options.forEach(function(o) { if (!("mangle" in o)) o.mangle = {}; if (o.mangle) o.mangle.v8 = true; }); } var bug_async_arrow_rest = function() {}; -if (SUPPORT.arrow && SUPPORT.async && SUPPORT.rest && typeof sandbox.run_code("async (a = f(...[], b)) => 0;") != "string") { +if (SUPPORT.arrow && SUPPORT.async && SUPPORT.rest && sandbox.is_error(sandbox.run_code("async (a = f(...[], b)) => 0;"))) { bug_async_arrow_rest = function(ex) { return ex.name == "SyntaxError" && ex.message == "Rest parameter must be last formal parameter"; }; } -var bug_async_class_await = SUPPORT.async && SUPPORT.class_field && typeof sandbox.run_code("var await; async function f() { class A { static p = await; } }") != "string"; -var bug_for_of_async = SUPPORT.for_await_of && typeof sandbox.run_code("var async; for (async of []);") != "string"; -var bug_for_of_var = SUPPORT.for_of && SUPPORT.let && typeof sandbox.run_code("try {} catch (e) { for (var e of []); }") != "string"; -if (SUPPORT.destructuring && typeof sandbox.run_code("console.log([ 1 ], {} = 2);") != "string") { +var bug_async_class_await = SUPPORT.async && SUPPORT.class_field && sandbox.is_error(sandbox.run_code("var await; async function f() { class A { static p = await; } }")); +var bug_for_of_async = SUPPORT.for_await_of && sandbox.is_error(sandbox.run_code("var async; for (async of []);")); +var bug_for_of_var = SUPPORT.for_of && SUPPORT.let && sandbox.is_error(sandbox.run_code("try {} catch (e) { for (var e of []); }")); +var bug_static_class_field = SUPPORT.class_field && sandbox.is_error(sandbox.run_code("class A { static 42; static get 42() {} }")); +if (SUPPORT.destructuring && sandbox.is_error(sandbox.run_code("console.log([ 1 ], {} = 2);"))) { beautify_options.output.v8 = true; minify_options.forEach(function(o) { if (!("output" in o)) o.output = {}; @@ -2450,7 +2457,7 @@ } beautify_options = JSON.stringify(beautify_options); minify_options = minify_options.map(JSON.stringify); -var original_code, original_result, errored; +var original_code, original_result, original_erred; var uglify_code, uglify_result, ok; for (var round = 1; round <= num_iterations; round++) { process.stdout.write(round + " of " + num_iterations + "\r"); @@ -2458,7 +2465,7 @@ original_code = createTopLevelCode(); var orig_result = [ run_code(original_code), run_code(original_code, true) ]; if (orig_result.some(function(result, toplevel) { - if (typeof result == "string") return; + if (!sandbox.is_error(result)) return; println(); println(); println("//============================================================="); @@ -2480,19 +2487,20 @@ o.validate = true; uglify_code = UglifyJS.minify(original_code, o); original_result = orig_result[toplevel ? 1 : 0]; - errored = typeof original_result != "string"; + original_erred = sandbox.is_error(original_result); if (!uglify_code.error) { uglify_code = uglify_code.code; uglify_result = run_code(uglify_code, toplevel); ok = sandbox.same_stdout(original_result, uglify_result); + var uglify_erred = sandbox.is_error(uglify_result); // ignore v8 parser bug - if (!ok && bug_async_arrow_rest(uglify_result)) ok = true; + if (!ok && uglify_erred && bug_async_arrow_rest(uglify_result)) ok = true; // ignore runtime platform bugs - if (!ok && uglify_result.message == "Script execution aborted.") ok = true; + if (!ok && uglify_erred && uglify_result.message == "Script execution aborted.") ok = true; // handle difference caused by time-outs if (!ok) { - if (errored && is_error_timeout(original_result)) { - if (is_error_timeout(uglify_result)) { + if (original_erred && is_error_timeout(original_result)) { + if (uglify_erred && is_error_timeout(uglify_result)) { // ignore difference in error message ok = true; } else { @@ -2500,21 +2508,23 @@ if (!orig_result[toplevel ? 3 : 2]) orig_result[toplevel ? 3 : 2] = run_code(original_code, toplevel, 10000); ok = sandbox.same_stdout(orig_result[toplevel ? 3 : 2], uglify_result); } - } else if (is_error_timeout(uglify_result)) { + } else if (uglify_erred && is_error_timeout(uglify_result)) { // ignore spurious time-outs var waited_result = run_code(uglify_code, toplevel, 10000); ok = sandbox.same_stdout(original_result, waited_result); } } // ignore declaration order of global variables - if (!ok && !toplevel && uglify_result.name != "SyntaxError" && original_result.name != "SyntaxError") { - ok = sandbox.same_stdout(run_code(sort_globals(original_code)), run_code(sort_globals(uglify_code))); + if (!ok && !toplevel) { + if (!(original_erred && original_result.name == "SyntaxError") && !(uglify_erred && uglify_result.name == "SyntaxError")) { + ok = sandbox.same_stdout(run_code(sort_globals(original_code)), run_code(sort_globals(uglify_code))); + } } // ignore numerical imprecision caused by `unsafe_math` - if (!ok && o.compress && o.compress.unsafe_math && typeof original_result == typeof uglify_result) { - if (typeof original_result == "string") { + if (!ok && o.compress && o.compress.unsafe_math) { + if (typeof original_result == "string" && typeof uglify_result == "string") { ok = fuzzy_match(original_result, uglify_result); - } else if (sandbox.is_error(original_result)) { + } else if (original_erred && uglify_erred) { ok = original_result.name == uglify_result.name && fuzzy_match(original_result.message, uglify_result.message); } if (!ok) { @@ -2523,19 +2533,19 @@ } } // ignore difference in error message caused by Temporal Dead Zone - if (!ok && errored && uglify_result.name == "ReferenceError" && original_result.name == "ReferenceError") ok = true; + if (!ok && original_erred && uglify_erred && original_result.name == "ReferenceError" && uglify_result.name == "ReferenceError") ok = true; // ignore difference due to implicit strict-mode in `class` if (!ok && /\bclass\b/.test(original_code)) { var original_strict = run_code('"use strict";\n' + original_code, toplevel); - if (/^(Syntax|Type)Error$/.test(uglify_result.name)) { - ok = typeof original_strict != "string"; + if (uglify_erred && /^(Syntax|Type)Error$/.test(uglify_result.name)) { + ok = sandbox.is_error(original_strict); } else { ok = sandbox.same_stdout(original_strict, uglify_result); } } // ignore difference in error message caused by `import` symbol redeclaration - if (!ok && errored && /\bimport\b/.test(original_code)) { - if (is_error_redeclaration(uglify_result) && is_error_redeclaration(original_result)) ok = true; + if (!ok && original_erred && uglify_erred && /\bimport\b/.test(original_code)) { + if (is_error_redeclaration(original_result) && is_error_redeclaration(uglify_result)) ok = true; } // ignore difference due to `__proto__` assignment if (!ok && /\b__proto__\b/.test(original_code)) { @@ -2544,24 +2554,32 @@ ok = sandbox.same_stdout(original_proto, uglify_proto); } // ignore difference in error message caused by `in` - if (!ok && errored && is_error_in(uglify_result) && is_error_in(original_result)) ok = true; + if (!ok && original_erred && uglify_erred) { + if (is_error_in(original_result) && is_error_in(uglify_result)) ok = true; + } // ignore difference in error message caused by spread syntax - if (!ok && errored && is_error_spread(uglify_result) && is_error_spread(original_result)) ok = true; + if (!ok && original_erred && uglify_erred) { + if (is_error_spread(original_result) && is_error_spread(uglify_result)) ok = true; + } // ignore difference in depth of termination caused by infinite recursion - if (!ok && errored && is_error_recursion(original_result)) { - if (is_error_recursion(uglify_result) || typeof uglify_result == "string") ok = true; + if (!ok && original_erred && is_error_recursion(original_result)) { + if (!uglify_erred || is_error_recursion(uglify_result)) ok = true; } // ignore difference in error message caused by destructuring - if (!ok && errored && is_error_destructuring(uglify_result) && is_error_destructuring(original_result)) { - ok = true; + if (!ok && original_erred && uglify_erred) { + if (is_error_destructuring(original_result) && is_error_destructuring(uglify_result)) ok = true; } // ignore difference in error message caused by call on class - if (!ok && errored && is_error_class_constructor(uglify_result) && is_error_class_constructor(original_result)) { - ok = true; + if (!ok && original_erred && uglify_erred) { + if (is_error_class_constructor(original_result) && is_error_class_constructor(uglify_result)) { + ok = true; + } } // ignore difference in error message caused by setting getter-only property - if (!ok && errored && is_error_getter_only_property(uglify_result) && is_error_getter_only_property(original_result)) { - ok = true; + if (!ok && original_erred && uglify_erred) { + if (is_error_getter_only_property(original_result) && is_error_getter_only_property(uglify_result)) { + ok = true; + } } // ignore errors above when caught by try-catch if (!ok) { @@ -2573,7 +2591,7 @@ } } else { uglify_code = uglify_code.error; - ok = errored && uglify_code.name == original_result.name; + ok = original_erred && uglify_code.name == original_result.name; } if (verbose || (verbose_interval && !(round % INTERVAL_COUNT)) || !ok) log(options); if (!ok && isFinite(num_iterations)) { ++++++ xpra-html5-4.5.2+git20220421.ecc3a08.tar.gz -> xpra-html5-5.0+git20220516.caf9971.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xpra-html5-4.5.2+git20220421.ecc3a08/docs/CHANGELOG.md new/xpra-html5-5.0+git20220516.caf9971/docs/CHANGELOG.md --- old/xpra-html5-4.5.2+git20220421.ecc3a08/docs/CHANGELOG.md 2022-04-21 11:18:32.000000000 +0200 +++ new/xpra-html5-5.0+git20220516.caf9971/docs/CHANGELOG.md 2022-05-16 11:39:03.000000000 +0200 @@ -2,6 +2,30 @@ All notable changes to this project will be documented in this file. +## [5.0] 2022-05-11 +* auto-fullscreen, alt-tabbing with window previews +* decode images using an offscreen worker thread +* decode `avif` images, grayscale and palette `png` +* handle `void` paint packets +* increase default non-vsynced target framerate +* tell servers to use 'scroll' encoding less aggressively +* keycloak authentication (requires xpra server version 4.4 or later) +* support pre-mapped windows (requires xpra server version 4.4 or later) +* support clipboard pasting file into the session +* detect inverted vertical scrolling (ie: on MacOS) +* improved dead key mapping for non-us layouts +* 64-bit rencode decoding bug with Safari (and IE) +* notification errors with bencoder +* avoid popping up the on-screen keyboard on mobile touch events +* updated on-screen simple-keyboard UI and file saver library +* shifted characters with simple-keyboard +* prevent stuck keys +* focus and raise windows when their title bar is clicked +* spurious focus events when minimizing windows +* fix AES encryption when used with authentication and rencodeplus +* build script refactoring + + ## [4.5.2] 2021-12-17 * fix toolbar position * install default settings in /etc/xpra/html5-client/ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xpra-html5-4.5.2+git20220421.ecc3a08/docs/Configuration.md new/xpra-html5-5.0+git20220516.caf9971/docs/Configuration.md --- old/xpra-html5-4.5.2+git20220421.ecc3a08/docs/Configuration.md 2022-04-21 11:18:32.000000000 +0200 +++ new/xpra-html5-5.0+git20220516.caf9971/docs/Configuration.md 2022-05-16 11:39:03.000000000 +0200 @@ -60,7 +60,6 @@ |`file_transfer`|Enable file-transfers|Yes| |`swap_keys` |Swap Command and Control keys|Yes on MacOS| |`scroll_reverse_x` |Reverse X axis of the mouse pointer|No| -|`scroll_reverse_y` |Reverse Y axis of the mouse pointer|Yes on MacOS| |`floating_menu` |Show a floating menu|Yes| |`toolbar_position` |Default position of the toolbar (ie: `top`, `top-right`)|`top-left`| |`autohide` |Hide most of the toolbar until the pointer hovers over it|No| diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xpra-html5-4.5.2+git20220421.ecc3a08/html5/connect.html new/xpra-html5-5.0+git20220516.caf9971/html5/connect.html --- old/xpra-html5-4.5.2+git20220421.ecc3a08/html5/connect.html 2022-04-21 11:18:32.000000000 +0200 +++ new/xpra-html5-5.0+git20220516.caf9971/html5/connect.html 2022-05-16 11:39:03.000000000 +0200 @@ -283,7 +283,13 @@ <input type="checkbox" id="swap_keys"> <span>Swap command and control key</span> </li> <li class="list-group-item"> - <input type="checkbox" id="scroll_reverse_y"> <span>Reverse vertical scrolling</span> + <span>Reverse vertical scrolling</span> + <select id="scroll_reverse_y"> + <option value="false">No</option> + <option value="true">Yes</option> + <option value="auto">Auto</option> + </select> + <br /> <input type="checkbox" id="scroll_reverse_x"> <span>Reverse horizontal scrolling</span> </li> <li class="list-group-item"> @@ -338,7 +344,7 @@ "bandwidth_limit", "encoding", "keyboard_layout", "audio_codec", "toolbar_position", "display", "shadow_display", "override_width", - "encryption", + "encryption", "scroll_reverse_y", "vrefresh", ]; const BOOLEAN_PROPERTIES = ["keyboard", "clipboard", "printing", "file_transfer", @@ -346,7 +352,7 @@ "offscreen", "exit_with_children", "exit_with_client", "sharing", "steal", "reconnect", "swap_keys", - "scroll_reverse_x", "scroll_reverse_y", + "scroll_reverse_x", "video", "mediasource_video", "ssl", "insecure", "floating_menu", "autohide", "clock", @@ -1161,7 +1167,7 @@ "exit_with_children", "exit_with_client", "sharing", "steal", "reconnect", "swap_keys", "video", "mediasource_video", "floating_menu", "autohide", "clock", - "scroll_reverse_x", "scroll_reverse_y", + "scroll_reverse_x", "debug_main", "debug_keyboard", "debug_geometry", "debug_mouse", "debug_clipboard", "debug_draw", "debug_audio", "debug_network"]; const default_on = ["steal", "clipboard", "printing", "file_transfer", "reconnect", "floating_menu", "clock", "exit_with_children", "exit_with_client"]; //even on 64-bit, video decoding is too slow @@ -1170,7 +1176,6 @@ //} if (Utilities.isMacOS()) { default_on.push("swap_keys"); - default_on.push("scroll_reverse_y"); } if (Utilities.isMobile()) { //show the on-screen keyboard by default on mobile: @@ -1181,6 +1186,10 @@ const def = default_on.includes(prop); document.getElementById(prop).checked = getboolparam(prop, def); } + //scroll_reverse_y has 3 options: Yes, No, Auto + const scroll_reverse_y = getboolparam("scroll_reverse_y", "auto"); + document.getElementById('scroll_reverse_y').value = scroll_reverse_y; + function toggle_mediasource_video() { $('#mediasource_video').prop("disabled", !video.checked); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xpra-html5-4.5.2+git20220421.ecc3a08/html5/index.html new/xpra-html5-5.0+git20220516.caf9971/html5/index.html --- old/xpra-html5-4.5.2+git20220421.ecc3a08/html5/index.html 2022-04-21 11:18:32.000000000 +0200 +++ new/xpra-html5-5.0+git20220516.caf9971/html5/index.html 2022-05-16 11:39:03.000000000 +0200 @@ -263,15 +263,23 @@ } return v; }; + const getboolparam = function(prop, default_value) { let v = Utilities.getparam(prop); if (v==null && prop in default_settings) { v = default_settings[prop]; } - if (v===null) { - return default_value; + return Utilities.stristrue(v, default_value); + }; + const getboolautoparam = function(prop, default_value) { + let v = Utilities.getparam(prop); + if (v==null && prop in default_settings) { + v = default_settings[prop]; } - return ["true", "on", "1", "yes", "enabled"].indexOf(String(v).toLowerCase())!==-1; + if (v=="auto") { + return "auto"; + } + return Utilities.stristrue(v, default_value); }; const getintparam = function(prop, default_value) { let v = Utilities.getparam(prop); @@ -624,7 +632,7 @@ const reconnect = getboolparam("reconnect", true); const swap_keys = getboolparam("swap_keys", Utilities.isMacOS()); const scroll_reverse_x = getboolparam("scroll_reverse_x", false); - const scroll_reverse_y = getboolparam("scroll_reverse_y", Utilities.isMacOS()); + const scroll_reverse_y = getboolautoparam("scroll_reverse_y", null); const floating_menu = getboolparam("floating_menu", true); const toolbar_position = getparam("toolbar_position"); const autohide = getboolparam("autohide", false); @@ -958,102 +966,7 @@ } function init_clipboard(client) { - const pasteboard = $('#pasteboard'); - //clipboard hooks: - pasteboard.on('paste', function (e) { - let paste_data; - if (navigator.clipboard && navigator.clipboard.readText) { - navigator.clipboard.readText().then(function(text) { - cdebug("clipboard", "paste event, text=", text); - const paste_data = unescape(encodeURIComponent(text)); - client.clipboard_buffer = paste_data; - client.send_clipboard_token(paste_data); - }, function(err) { - cdebug("clipboard", "paste event failed:", err); - }); - } - else { - let datatype = "text/plain"; - let clipboardData = (e.originalEvent || e).clipboardData; - //IE: must use window.clipboardData because the event clipboardData is null! - if (!clipboardData) { - clipboardData = window.clipboardData; - } - if (Utilities.isIE()) { - datatype = "Text"; - } - paste_data = unescape(encodeURIComponent(clipboardData.getData(datatype))); - cdebug("clipboard", "paste event, data=", paste_data); - client.clipboard_buffer = paste_data; - client.send_clipboard_token(paste_data); - } - return false; - }); - pasteboard.on('copy', function (e) { - const clipboard_buffer = client.get_clipboard_buffer(); - pasteboard.text(decodeURIComponent(escape(clipboard_buffer))); - pasteboard.select(); - cdebug("clipboard", "copy event, clipboard buffer=", clipboard_buffer); - client.clipboard_pending = false; - return true; - }); - pasteboard.on('cut', function (e) { - const clipboard_buffer = client.get_clipboard_buffer(); - pasteboard.text(decodeURIComponent(escape(clipboard_buffer))); - pasteboard.select(); - cdebug("clipboard", "cut event, clipboard buffer=", clipboard_buffer); - client.clipboard_pending = false; - return true; - }); - function may_set_clipboard() { - cdebug("clipboard", "pending=", client.clipboard_pending, "buffer=", client.clipboard_buffer); - if (!client.clipboard_pending) { - return; - } - let clipboard_buffer = client.get_clipboard_buffer(); - const clipboard_datatype = (client.get_clipboard_datatype() || "").toLowerCase(); - const is_text = clipboard_datatype.indexOf("text")>=0 || clipboard_datatype.indexOf("string")>=0; - if (!is_text) { - //maybe just abort here instead? - clipboard_buffer = ""; - } - pasteboard.text(clipboard_buffer); - pasteboard.select(); - cdebug("clipboard", "click event, with pending clipboard datatype=", clipboard_datatype, ", buffer=", clipboard_buffer); - //for IE: - let success = false; - if (window.hasOwnProperty("clipboardData") && window.clipboardData.hasOwnProperty("setData") && typeof window.clipboardData.setData === "function") { - try { - if (Utilities.isIE()) { - window.clipboardData.setData("Text", clipboard_buffer); - } - else { - window.clipboardData.setData(clipboard_datatype, clipboard_buffer); - } - success = true; - } - catch (e) { - success = false; - } - } - if (!success && is_text) { - success = document.execCommand('copy'); - } - else { - //probably no point in trying again? - } - if (success) { - //clipboard_buffer may have been cleared if not set to text: - client.clipboard_buffer = clipboard_buffer; - client.clipboard_pending = false; - } - } - $('#screen').on('click', function (e) { - may_set_clipboard(); - }); - $("#screen").keypress(function() { - may_set_clipboard(); - }); + client.init_clipboard(); } function init_keyboard(client) { @@ -1071,11 +984,21 @@ "{enter}" : "return", }, }); + window.kb = kb; + window.keyboardShifted = false; function onChange(input){ clog("Input changed", input); } function onKeyPress(button){ forward_key(true, button); + if (button == "{shift}" || button == "{lock}") { + if (window.keyboardShifted) { + window.kb.setOptions({layoutName: 'default'}); + } else { + window.kb.setOptions({layoutName: 'shift'}); + } + window.keyboardShifted = !window.keyboardShifted; + } } function onKeyReleased(button){ forward_key(false, button); @@ -1119,28 +1042,10 @@ } function init_file_transfer(client) { - function send_file(f) { - clog("file:", f.name, ", type:", f.type, ", size:", f.size); - const fileReader = new FileReader(); - fileReader.onloadend = function (evt) { - const u8a = new Uint8Array(evt.target.result); - var buf = u8a; - if (client.packet_encoder!="rencodeplus") { - buf = Utilities.Uint8ToString(u8a); - } - client.send_file(f.name, f.type, f.size, buf); - }; - fileReader.readAsArrayBuffer(f); - } - function sendAllFiles(files) { - for (let i = 0, f; f = files[i]; i++) { - send_file(f); - } - } function handleFileSelect(evt) { evt.stopPropagation(); evt.preventDefault(); - sendAllFiles(evt.dataTransfer.files); + client.send_all_files(evt.dataTransfer.files); } function handleDragOver(evt) { evt.stopPropagation(); @@ -1154,7 +1059,7 @@ $("#upload").on("change", function(evt) { evt.stopPropagation(); evt.preventDefault(); - sendAllFiles(this.files); + client.send_all_files(this.files); return false; }); $("#upload_form").on('submit',function(evt){ @@ -1453,8 +1358,7 @@ }); } - $(document).ready(function() { - clog("document is ready, browser is", navigator.platform, "64-bit:", Utilities.is_64bit()); + function load_default_settings() { const xhr = new XMLHttpRequest(); xhr.open("GET", "./default-settings.txt"); xhr.onload = function() { @@ -1476,6 +1380,11 @@ init_page(); }; xhr.send(); + } + + $(document).ready(function() { + clog("document is ready, browser is", navigator.platform, "64-bit:", Utilities.is_64bit()); + load_default_settings(); }); </script> </body> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xpra-html5-4.5.2+git20220421.ecc3a08/html5/js/Client.js new/xpra-html5-5.0+git20220516.caf9971/html5/js/Client.js --- old/xpra-html5-4.5.2+git20220421.ecc3a08/html5/js/Client.js 2022-04-21 11:18:32.000000000 +0200 +++ new/xpra-html5-5.0+git20220516.caf9971/html5/js/Client.js 2022-05-16 11:39:03.000000000 +0200 @@ -18,9 +18,13 @@ const XPRA_CLIENT_FORCE_NO_WORKER = false; const CLIPBOARD_IMAGES = true; const CLIPBOARD_EVENT_DELAY = 100; -const DECODE_WORKER = !Utilities.isMobile(); +const DECODE_WORKER = !!window.createImageBitmap; const rencode_ok = rencode && rencode_selftest(); const SHOW_START_MENU = true; +const FILE_SIZE_LIMIT = 4*1024*1024*1024; //are we even allowed to allocate this much memory? +const FILE_CHUNKS_SIZE = 128*1024; +const MAX_CONCURRENT_FILES = 5; +const CHUNK_TIMEOUT = 10*1000; function XpraClient(container) { @@ -63,14 +67,18 @@ this.encoding = "auto"; //basic set of encodings: //(more may be added after checking via the DecodeWorker) - this.supported_encodings = ["jpeg", "png", "png/P", "png/L", "rgb", "rgb32", "rgb24", "scroll", "webp", "void", "avif"]; + this.supported_encodings = ["jpeg", "png", "png/P", "png/L", "rgb", "rgb32", "rgb24", "scroll", "void"]; //extra encodings we enable if validated via the decode worker: //(we also validate jpeg and png as a sanity check) - this.check_encodings = this.supported_encodings; + this.check_encodings = ["jpeg", "png", "png/P", "png/L", "rgb", "rgb32", "rgb24", "scroll", "webp", "void", "avif"]; this.debug_categories = []; this.start_new_session = null; this.clipboard_enabled = false; this.file_transfer = false; + this.remote_file_size_limit = 0; + this.remote_file_chunks = 0; + this.send_chunks_in_progress = new Map(); + this.receive_chunks_in_progress = new Map(); this.keyboard_layout = null; this.printing = false; this.key_packets = []; @@ -91,7 +99,7 @@ this.PING_FREQUENCY = 5000; this.INFO_FREQUENCY = 1000; this.uuid = Utilities.getHexUUID(); - this.offscreen_api = XpraOffscreenWorker.isAvailable(); + this.offscreen_api = DECODE_WORKER && XpraOffscreenWorker.isAvailable(); }; XpraClient.prototype.init_state = function() { @@ -142,7 +150,7 @@ this.wheel_delta_y = 0; this.mouse_grabbed = false; this.scroll_reverse_x = false; - this.scroll_reverse_y = false; + this.scroll_reverse_y = "auto"; // clipboard this.clipboard_direction = default_settings["clipboard_direction"] || "both"; this.clipboard_datatype = null; @@ -363,6 +371,8 @@ 'set-clipboard-enabled': this._process_set_clipboard_enabled, 'clipboard-request': this._process_clipboard_request, 'send-file': this._process_send_file, + 'ack-file-chunk': this._process_ack_file_chunk, + 'send-file-chunk': this._process_send_file_chunk, 'open-url': this._process_open_url, 'setting-change': this._process_setting_change, 'pointer-position': this._process_pointer_position, @@ -637,6 +647,7 @@ */ XpraClient.prototype.init_keyboard = function() { const me = this; + this.query_keyboard_map(); // modifier keys: this.num_lock_modifier = null; this.alt_modifier = null; @@ -702,6 +713,27 @@ }); }; +XpraClient.prototype.query_keyboard_map = function() { + var keyboard = navigator.keyboard; + this.keyboard_map = {}; + if (!navigator.keyboard) { + return; + } + keyboard.getLayoutMap().then(keyboardLayoutMap => { + clog("got a keyboard layout map:", keyboardLayoutMap); + clog("keys:", Array.from(keyboardLayoutMap.keys())); + for (const key of keyboardLayoutMap.keys()) { + const value = keyboardLayoutMap[key]; + cdebug("keyboard", key, "=", value); + this.keyboard_map[key] = value; + } + }); + if (keyboard.addEventListener) { + keyboard.addEventListener("layoutchange", function() { + clog("keyboard layout has changed!"); + }); + } +}; XpraClient.prototype._keyb_get_modifiers = function(event) { /** @@ -843,9 +875,10 @@ let str = event.key || String.fromCharCode(keycode); let unpress_now = false; this.debug("keyboard", "last keycode pressed=", this.last_keycode_pressed, ", keycode=", keycode, ", pressed=", pressed, ", str=", str); - if (this.last_keycode_pressed!=keycode && !pressed && str=="Dead") { + const dead = str.toLowerCase()=="dead"; + if (dead && ((this.last_keycode_pressed!=keycode && !pressed) || pressed)) { //dead key unpress without first getting a key pressed event, - //send a pair: + //or just a regular pressed dead key, in both cases send a pair: pressed = true; unpress_now = true; } @@ -857,7 +890,7 @@ this.last_keycode_pressed = 0; } - this.debug("keyboard", "processKeyEvent(", pressed, ", ", event, ") key=", keyname, "keycode=", keycode); + this.debug("keyboard", "processKeyEvent(", pressed, ", ", event, ") key=", keyname, "keycode=", keycode, "dead=", dead); //sync numlock if (keycode==144 && pressed) { @@ -866,7 +899,13 @@ let key_language = null; //some special keys are better mapped by name: - if (keyname in KEY_TO_NAME){ + const map_str = this.keyboard_map[keyname]; + if (dead && map_str && map_str in DEAD_KEYS) { + keyname = DEAD_KEYS[map_str]; + str = map_str; + this.debug("keyboard", "dead key:", keyname); + } + else if (keyname in KEY_TO_NAME){ keyname = KEY_TO_NAME[keyname]; } else if (keyname=="" && str in KEY_TO_NAME){ @@ -1258,6 +1297,11 @@ "ping-echo-sourceid" : true, "vrefresh" : this.vrefresh, }); + if (rencode_ok) { + this._update_capabilities({ + "file-chunks" : FILE_CHUNKS_SIZE, + }); + } if (SHOW_START_MENU) { this._update_capabilities({ "xdg-menu-update" : true, @@ -1431,7 +1475,7 @@ // printing "file-transfer" : this.file_transfer, "printing" : this.printing, - "file-size-limit" : 10, + "file-size-limit" : 4*1024, "flush" : true, }); }; @@ -1603,6 +1647,30 @@ this.send(["button-action", wid, button, pressed, [x, y], modifiers, buttons]); }; +// Source: https://deepmikoto.com/coding/1--javascript-detect-mouse-wheel-direction +XpraClient.prototype.detect_vertical_scroll_direction = function(e, window) { + if ( !e ) { + //IE? In any case, detection won't work: + return 0; + } + let delta = null; + if ( e.wheelDelta ) { // will work in most cases + delta = e.wheelDelta; + } else if ( e.detail ) { // fallback for Firefox + delta = -e.detail; + } + if (delta == null) { + return 0; + } + if (delta>0) { + return -1; + } + if (delta<0) { + return 1; + } + return 0; +}; + XpraClient.prototype._window_mouse_scroll = function(ctx, e, window) { ctx.do_window_mouse_scroll(e, window); }; @@ -1628,10 +1696,9 @@ if (this.scroll_reverse_x) { px = -px; } - if (this.scroll_reverse_y) { + if (this.scroll_reverse_y===true || (this.scroll_reverse_x=="auto" && this.detect_vertical_scroll_direction(e, window) < 0 && py > 0)) { py = -py; } - const apx = Math.abs(px); const apy = Math.abs(py); if (this.server_precise_wheel) { @@ -1682,6 +1749,121 @@ this.wheel_delta_y = (this.wheel_delta_y>=0) ? wy : -wy; }; +XpraClient.prototype.init_clipboard = function() { + const me = this; + window.addEventListener("paste", function (e) { + let clipboardData = (e.originalEvent || e).clipboardData; + //IE: must use window.clipboardData because the event clipboardData is null! + if (!clipboardData) { + clipboardData = window.clipboardData; + } + if (clipboardData && clipboardData.files) { + const files = clipboardData.files; + me.clog("paste got", files.length, "files"); + for (let i = 0; i < files.length; i++) { + let file = files.item(i); + //lastModified: 1634740745068 + //lastModifiedDate: Wed Oct 20 2021 21:39:05 GMT+0700 (Indochina Time) {} + //name: "addresses.png" + //size: 17698 + //type: "image/png" + //webkitRelativePath: "" + me.send_file(file); + } + e.preventDefault(); + return; + } + let paste_data; + if (navigator.clipboard && navigator.clipboard.readText) { + navigator.clipboard.readText().then(function(text) { + me.cdebug("clipboard", "paste event, text=", text); + const paste_data = unescape(encodeURIComponent(text)); + me.clipboard_buffer = paste_data; + me.send_clipboard_token(paste_data); + }, function(err) { + me.cdebug("clipboard", "paste event failed:", err); + }); + } + else { + let datatype = "text/plain"; + if (Utilities.isIE()) { + datatype = "Text"; + } + paste_data = unescape(encodeURIComponent(clipboardData.getData(datatype))); + cdebug("clipboard", "paste event, data=", paste_data); + me.clipboard_buffer = paste_data; + me.send_clipboard_token(paste_data); + } + }); + window.addEventListener("copy", function (e) { + const clipboard_buffer = me.get_clipboard_buffer(); + const pasteboard = $("#pasteboard"); + pasteboard.text(decodeURIComponent(escape(clipboard_buffer))); + pasteboard.select(); + me.cdebug("clipboard", "copy event, clipboard buffer=", clipboard_buffer); + me.clipboard_pending = false; + }); + window.addEventListener("cut", function (e) { + const clipboard_buffer = me.get_clipboard_buffer(); + const pasteboard = $("#pasteboard"); + pasteboard.text(decodeURIComponent(escape(clipboard_buffer))); + pasteboard.select(); + me.cdebug("clipboard", "cut event, clipboard buffer=", clipboard_buffer); + me.clipboard_pending = false; + }); + $('#screen').on('click', function (e) { + me.may_set_clipboard(); + }); + $("#screen").keypress(function() { + me.may_set_clipboard(); + }); +} + +XpraClient.prototype.may_set_clipboard = function(e) { + this.cdebug("clipboard", "pending=", this.clipboard_pending, "buffer=", this.clipboard_buffer); + if (!this.clipboard_pending) { + return; + } + let clipboard_buffer = this.get_clipboard_buffer(); + const clipboard_datatype = (this.get_clipboard_datatype() || "").toLowerCase(); + const is_text = clipboard_datatype.indexOf("text")>=0 || clipboard_datatype.indexOf("string")>=0; + if (!is_text) { + //maybe just abort here instead? + clipboard_buffer = ""; + } + const pasteboard = $("#pasteboard"); + pasteboard.text(clipboard_buffer); + pasteboard.select(); + this.cdebug("clipboard", "click event, with pending clipboard datatype=", clipboard_datatype, ", buffer=", clipboard_buffer); + //for IE: + let success = false; + if (window.hasOwnProperty("clipboardData") && window.clipboardData.hasOwnProperty("setData") && typeof window.clipboardData.setData === "function") { + try { + if (Utilities.isIE()) { + window.clipboardData.setData("Text", clipboard_buffer); + } + else { + window.clipboardData.setData(clipboard_datatype, clipboard_buffer); + } + success = true; + } + catch (e) { + success = false; + } + } + if (!success && is_text) { + success = document.execCommand('copy'); + } + else { + //probably no point in trying again? + } + if (success) { + //clipboard_buffer may have been cleared if not set to text: + this.clipboard_buffer = clipboard_buffer; + this.clipboard_pending = false; + } +} + XpraClient.prototype._poll_clipboard = function(e) { if (this.clipboard_enabled === false) { @@ -1732,10 +1914,9 @@ return; } const client = this; - client.debug("clipboard", "read_clipboard()"); + client.debug("clipboard", "read_clipboard_text()"); //warning: this can take a while, //so we may send the click before the clipboard contents... - this.clipboard_pending = true; navigator.clipboard.readText().then(function(text) { client.debug("clipboard", "paste event, text=", text); const clipboard_buffer = unescape(encodeURIComponent(text)); @@ -1779,14 +1960,11 @@ if (default_settings !== undefined && default_settings.auto_fullscreen_desktop_class !== undefined && default_settings.auto_fullscreen_desktop_class.length > 0) { var auto_fullscreen_desktop_class = default_settings.auto_fullscreen_desktop_class; if (win.windowtype == "DESKTOP" && win.metadata['class-instance'].includes(auto_fullscreen_desktop_class)) { - var any_visible = false; for (let i in client.id_to_window) { const iwin = client.id_to_window[i]; - if (iwin.wid == win.wid) continue; - any_visible ||= !iwin.minimized; - } - if (any_visible) { - return; + if (iwin.wid != win.wid && !iwin.minimized) { + return; + } } } } @@ -2325,6 +2503,10 @@ ctx._connection_change(); } + // file transfer attributes: + ctx.remote_file_size_limit = hello["file-size-limit"] || 0; + ctx.remote_file_chunks = Math.max(0, Math.min(ctx.remote_file_size_limit*1024*1024, hello["file-chunks"] || 0)); + // start sending our own pings ctx._send_ping(); ctx.ping_timer = setInterval(function () { @@ -4036,25 +4218,235 @@ * File transfers and printing */ XpraClient.prototype._process_send_file = function(packet, ctx) { - const basefilename = packet[1]; - const mimetype = packet[2]; + const basefilename = Utilities.s(packet[1]); + const mimetype = Utilities.s(packet[2]); const printit = packet[3]; - const datasize = packet[5]; + const filesize = packet[5]; const data = packet[6]; + const options = packet[7] || {}; + const send_id = Utilities.s(packet[8]); // check the data size for file - if(data.length != datasize) { - ctx.warn("send-file: invalid data size, received", data.length, "bytes, expected", datasize); + if (filesize<=0 || filesize>FILE_SIZE_LIMIT) { + ctx.cerror("send-file: invalid data size, received", data.length, "bytes, expected", filesize); + return; + } + let digest = null; + for (let hash_fn of ["sha512", "sha384", "sha256", "sha224", "sha1"]) { + if (options[hash_fn]) { + try { + digest = forge.md[hash_fn].create(); + break; + } + catch (e) { + this.error("Error: no", hash_fn, "checksum available:", e); + } + } + } + if (data.length==filesize) { + //got the whole file + if (digest) { + digest.update(Utilities.Uint8ToString(data)); + ctx.log("digest.update(", data, ")"); + ctx.log("digest update string:", Utilities.Uint8ToString(data)); + ctx.verify_digest(digest, options[digest.algorithm]); + } + ctx._got_file(basefilename, data, printit, mimetype, options); + return; + } + if (!send_id) { + ctx.cerror("send-file: partial file is missing send-id"); + return; + } + const chunk_id = Utilities.s(options["file-chunk-id"] || ""); + if (!chunk_id) { + ctx.cerror("send-file: partial file is missing file-chunk-id"); + return; + } + const chunk = 0; + if (ctx.receive_chunks_in_progress.size>MAX_CONCURRENT_FILES) { + ctx.cancel_file(chunk_id, "too many concurrent files being downloaded", chunk); + return; + } + //start receiving chunks: + const timer = setTimeout(function() { + ctx._check_chunk_receiving(chunk_id, chunk); + }, CHUNK_TIMEOUT); + const openit = true; + const chunk_state = [ + Date.now(), + [], basefilename, mimetype, + printit, openit, filesize, + options, digest, 0, false, send_id, + timer, chunk, + ]; + ctx.receive_chunks_in_progress.set(chunk_id, chunk_state); + ctx.send(["ack-file-chunk", chunk_id, true, "", chunk]); + ctx.log("receiving chunks for", basefilename, "with transfer id", chunk_id); +}; + +XpraClient.prototype._check_chunk_receiving = function(chunk_id, chunk_no) { + const chunk_state = this.receive_chunks_in_progress.get(chunk_id); + this.debug("main", "check_chunk_receiving(", chunk_id, ",", chunk_no, ") chunk_state=", chunk_state); + if (!chunk_state) { + return; + } + if (chunk_state[10]) { + //transfer has been cancelled return; } + chunk_state[12] = 0 //this timer has been used + if (chunk_state[-1]==0) { + this.cerror("Error: chunked file transfer", chunk_id, "timed out"); + this.receive_chunks_in_progress.delete(chunk_id); + } +}; + +XpraClient.prototype.transfer_progress_update = function(send, transfer_id, elapsed, position, total, error) { + //this.clog("progress:", Math.round(position*100/total)); +} + +XpraClient.prototype.cancel_file = function(chunk_id, message, chunk) { + const chunk_state = this.receive_chunks_in_progress.get(chunk_id); + if (chunk_state) { + //mark it as cancelled: + chunk_state[10] = true; + //free the buffers + chunk_state[1] = []; + //stop the timer + const timer = chunk_state[12]; + if (timer) { + clearTimeout(timer); + chunk_state[12] = 0; + } + //remove this transfer after a little while, + //so in-flight packets won't cause errors + setTimeout(() => this.receive_chunks_in_progress.delete(chunk_id), 20000); + } + this.send(["ack-file-chunk", chunk_id, false, message, chunk]); +} + +XpraClient.prototype._process_send_file_chunk = function(packet, ctx) { + const chunk_id = Utilities.s(packet[1]), + chunk = packet[2], + file_data = packet[3], + has_more = packet[4]; + ctx.debug("main", "_process_send_file_chunk(", chunk_id, chunk, ""+file_data.length+" bytes", has_more, ")"); + const chunk_state = ctx.receive_chunks_in_progress.get(chunk_id); + if (!chunk_state) { + ctx.cerror("Error: cannot find the file transfer id", chunk_id); + ctx.cancel_file(chunk_id, "file transfer id"+chunk_id+"not found", chunk); + return; + } + if (chunk_state[10]) { + ctx.debug("main", "got chunk for a cancelled file transfer, ignoring it"); + return; + } + const filesize = chunk_state[6]; + function progress(position, error) { + const start = chunk_state[0]; + const send_id = chunk_state[-3]; + ctx.transfer_progress_update(false, send_id, Date.now()-start, position, filesize, error); + } + if (chunk_state[13]+1!=chunk) { + ctx.cerror("Error: chunk number mismatch, expected", chunk_state[13]+1, "but got", chunk); + ctx.cancel_file(chunk_id, "chunk number mismatch", chunk); + ctx.file_progress(-1, "chunk no mismatch") + return + } + //update chunk number: + chunk_state[13] = chunk; + const written = chunk_state[9] + file_data.length + if (written>filesize) { + ctx.cerror("Error: too much data received"); + progress(-1, "file size mismatch"); + return + } + chunk_state[9] = written; + chunk_state[1].push(file_data); + const digest = chunk_state[8]; + if (digest) { + digest.update(Utilities.Uint8ToString(file_data)); + } + ctx.send(["ack-file-chunk", chunk_id, true, "", chunk]); + if (has_more) { + progress(written); + const timer = chunk_state[12]; + if (timer) { + clearTimeout(timer); + } + //remote end will send more after receiving the ack + chunk_state[-2] = setTimeout(() => { + ctx._check_chunk_receiving(chunk_id, chunk); + }, CHUNK_TIMEOUT); + return; + } + ctx.receive_chunks_in_progress.delete(chunk_id); + //check file size and digest then process it: + if (written!=filesize) { + ctx.cerror("Error: expected a file of", filesize, "bytes, got", written); + progress(-1, "file size mismatch"); + return + } + const options = chunk_state[7]; + if (digest) { + ctx.verify_digest(digest, options[digest.algorithm]); + } + progress(written); + const start_time = chunk_state[0]; + const elapsed = Date.now()-start_time; + ctx.clog(filesize, "bytes received in", chunk, "chunks, took", Math.round(elapsed*1000), "ms"); + const filename = chunk_state[2]; + const mimetype = chunk_state[3]; + const printit = chunk_state[4]; + //join all the data into a single typed array: + const data = new Uint8Array(filesize); + let start = 0; + const chunks = chunk_state[1]; + for (let i=0; i<chunks.length; ++i) { + data.set(chunks[i], start); + start += chunks[i].length; + } + ctx._got_file(filename, data, mimetype, printit, mimetype, options); +}; + + +XpraClient.prototype.verify_digest = function(digest, expected_value) { + const algo = digest.algorithm; + const value = digest.digest().data; + const hex_value = Utilities.convertToHex(value); + if (hex_value!=expected_value.toLowerCase()) { + this.error("Error verifying", algo, "file checksum"); + this.error(" expected", expected_value, "but got", hex_value); + throw "invalid "+algo+" checksum"; + } + this.log("verified", algo, "digest of file transfer"); +} + + +XpraClient.prototype._got_file = function(basefilename, data, printit, mimetype, options) { + function check_digest(algo) { + const digest = options[algo]; + if (digest) { + //h = libfn() + //h.update(file_data) + //l("digest: - expected", algo, h.hexdigest(), digest) + //if digest!=h.hexdigest(): + // ctx.digest_mismatch(filename, digest, h.hexdigest(), algo) + } + } + check_digest("sha256") + check_digest("sha1") + check_digest("md5") if (printit) { - ctx.print_document(basefilename, data, mimetype); + this.print_document(basefilename, data, mimetype); } else { - ctx.save_file(basefilename, data, mimetype); + this.save_file(basefilename, data, mimetype); } }; + XpraClient.prototype.save_file = function(filename, data, mimetype) { if (!this.file_transfer || !this.remote_file_transfer) { this.warn("Received file-transfer data but this is not enabled!"); @@ -4089,15 +4481,140 @@ } }; -XpraClient.prototype.send_file = function(filename, mimetype, size, buffer) { + +XpraClient.prototype.send_all_files = function(files) { + for (let i = 0, f; f = files[i]; i++) { + this.send_file(f); + } +} +XpraClient.prototype.send_file = function(f) { + clog("send_file:", f.name, ", type:", f.type, ", size:", f.size); + const me = this; + const fileReader = new FileReader(); + fileReader.onloadend = function (evt) { + const u8a = new Uint8Array(evt.target.result); + var buf = u8a; + if (client.packet_encoder!="rencodeplus") { + buf = Utilities.Uint8ToString(u8a); + } + me.do_send_file(f.name, f.type, f.size, buf); + }; + fileReader.readAsArrayBuffer(f); +} +XpraClient.prototype.do_send_file = function(filename, mimetype, size, buffer) { if (!this.file_transfer || !this.remote_file_transfer) { this.warn("cannot send file: file transfers are disabled!"); return; } - const packet = ["send-file", filename, mimetype, false, this.remote_open_files, size, buffer, {}]; + let cdata = buffer; + const options = {}; + const chunk_size = Math.min(FILE_CHUNKS_SIZE, this.remote_file_chunks || 0); + if (chunk_size>0 && size>chunk_size) { + if (this.send_chunks_in_progress.size>=MAX_CONCURRENT_FILES) { + throw Exception("too many file transfers in progress:"+this.send_chunks_in_progress.size); + } + //chunking is supported and the file is big enough + const chunk_id = Utilities.getHexUUID(); + options["file-chunk-id"] = chunk_id; + //timer to check that the other end is requesting more chunks: + const timer = setTimeout(() => { + this._check_chunk_sending(chunk_id, 0); + }, CHUNK_TIMEOUT); + const chunk_state = [Date.now(), buffer, chunk_size, timer, 0]; + this.send_chunks_in_progress.set(chunk_id, chunk_state); + cdata = "" + this.debug("main", "using chunks, sending initial file-chunk-id=", chunk_id, ", for chunk size", chunk_size); + } + else { + //send everything now: + this.debug("main", "sending full file:", size, "bytes, chunk size", chunk_size); + } + const packet = ["send-file", filename, mimetype, false, this.remote_open_files, size, cdata, options]; this.send(packet); }; +XpraClient.prototype._check_chunk_sending = function(chunk_id, chunk_no) { + const chunk_state = this.send_chunks_in_progress.get(chunk_id); + this.debug("main", "chunk id", chunk_id, "chunk_no", chunk_no, "found chunk_state", new Boolean(chunk_state)); + if (!chunk_state) { + return; + } + chunk_state[3] = 0 //timer has fired + if (chunk_state[13]==chunk_no) { + this.error("Error: chunked file transfer", chunk_id, "timed out"); + this.error(" on chunk", chunk_no) + this.cancel_sending(chunk_id) + } +}; + +XpraClient.prototype.cancel_sending = function(chunk_id) { + const chunk_state = this.send_chunks_in_progress.get(chunk_id); + this.debug("main", "cancel_sending", chunk_id, "chunk state found:", new Boolean(chunk_state)); + if (!chunk_state) { + return; + } + const timer = chunk_state[3]; + if (timer) { + chunk_state[3] = 0; + clearTimeout(timer); + } + this.send_chunks_in_progress.delete(chunk_id); +}; + +XpraClient.prototype._process_ack_file_chunk = function(packet, ctx) { + //the other end received our send-file or send-file-chunk, + //send some more file data + ctx.debug("main", "ack-file-chunk: ", packet); + const chunk_id = Utilities.s(packet[1]), + state = packet[2], + error_message = packet[3]; + let chunk = packet[4]; + if (!state) { + ctx.debug("main", "the remote end is cancelling the file transfer:") + ctx.debug("main", " %s", Utilities.s(error_message)); + ctx.cancel_sending(chunk_id); + return; + } + const chunk_state = ctx.send_chunks_in_progress.get(chunk_id); + if (!chunk_state) { + ctx.error("Error: cannot find the file transfer id '%r'", chunk_id); + return; + } + if (chunk_state[4]!=chunk) { + this.error("Error: chunk number mismatch", chunk_state, "vs", chunk); + this.cancel_sending(chunk_id); + return; + } + const start_time = chunk_state[0], + chunk_size = chunk_state[2]; + let timer = chunk_state[3], + data = chunk_state[1]; + if (!data) { + //all sent! + const elapsed = Date.now()-start_time; + ctx.log(chunk, "chunks of", chunk_size, "bytes sent in", Math.round(elapsed), "ms", + 8*chunk*chunk_size/elapsed, "bps"); + ctx.cancel_sending(chunk_id); + return; + } + if (chunk_size<=0) { + throw Exception("invalid chunk size "+chunk_size); + } + //carve out another chunk: + const cdata = data.subarray(0, chunk_size); + data = data.subarray(chunk_size); + chunk += 1; + if (timer) { + clearTimeout(timer); + } + timer = setTimeout(() => { + ctx._check_chunk_sending(chunk_id, chunk); + }, CHUNK_TIMEOUT); + ctx.send_chunks_in_progress.set(chunk_id, [start_time, data, chunk_size, timer, chunk]); + ctx.send(["send-file-chunk", chunk_id, chunk, cdata, data.length>0]); +} + + XpraClient.prototype.start_command = function(name, command, ignore) { const packet = ["start-command", name, command, ignore]; this.send(packet); @@ -4107,7 +4624,7 @@ const url = packet[1]; //const send_id = packet[2]; if (!ctx.open_url) { - ctx.cwarn("Warning: received a request to open URL '%s'", url); + ctx.cwarn("Warning: received a request to open URL", url); ctx.clog(" but opening of URLs is disabled"); return; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xpra-html5-4.5.2+git20220421.ecc3a08/html5/js/Keycodes.js new/xpra-html5-5.0+git20220516.caf9971/html5/js/Keycodes.js --- old/xpra-html5-4.5.2+git20220421.ecc3a08/html5/js/Keycodes.js 2022-04-21 11:18:32.000000000 +0200 +++ new/xpra-html5-5.0+git20220516.caf9971/html5/js/Keycodes.js 2022-05-16 11:39:03.000000000 +0200 @@ -38,6 +38,10 @@ "End" : "End", "PageDown" : "Next", }; +DEAD_KEYS = { + "`" : "dead_grave", + "'" : "dead_acute", +}; NUMPAD_TO_NAME = { //Num pad: "NumpadDivide" : "KP_Divide", diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xpra-html5-4.5.2+git20220421.ecc3a08/html5/js/Protocol.js new/xpra-html5-5.0+git20220516.caf9971/html5/js/Protocol.js --- old/xpra-html5-4.5.2+git20220421.ecc3a08/html5/js/Protocol.js 2022-04-21 11:18:32.000000000 +0200 +++ new/xpra-html5-5.0+git20220516.caf9971/html5/js/Protocol.js 2022-05-16 11:39:03.000000000 +0200 @@ -691,12 +691,18 @@ protocol.is_worker = true; // we create a custom packet handler which posts packet as a message protocol.set_packet_handler(function (packet, ctx) { - let raw_draw_buffer = []; + let raw_buffer = []; if ((packet[0] === 'draw') && (packet[7].hasOwnProperty("buffer"))) { - raw_draw_buffer = packet[7].buffer; + //zero-copy the draw buffer + raw_buffer = packet[7].buffer; packet[7] = null; } - postMessage({'c': 'p', 'p': packet}, raw_draw_buffer); + else if ((packet[0] === 'send-file-chunk') && (packet[3].hasOwnProperty("buffer"))) { + //zero-copy the file data buffer + raw_buffer = packet[3].buffer; + packet[3] = null; + } + postMessage({'c': 'p', 'p': packet}, raw_buffer); }, null); // attach listeners from main thread self.addEventListener('message', function(e) { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xpra-html5-4.5.2+git20220421.ecc3a08/html5/js/Utilities.js new/xpra-html5-5.0+git20220516.caf9971/html5/js/Utilities.js --- old/xpra-html5-4.5.2+git20220421.ecc3a08/html5/js/Utilities.js 2022-04-21 11:18:32.000000000 +0200 +++ new/xpra-html5-5.0+git20220516.caf9971/html5/js/Utilities.js 2022-05-16 11:39:03.000000000 +0200 @@ -68,6 +68,13 @@ } }, + stristrue : function(v, default_value) { + if (v===null) { + return default_value; + } + return ["true", "on", "1", "yes", "enabled"].indexOf(String(v).toLowerCase())!==-1; + }, + getHexUUID: function() { const s = []; const hexDigits = "0123456789abcdef"; @@ -113,6 +120,14 @@ str; }, + convertToHex: function (str) { + var hex = ''; + for(var i=0;i<str.length;i++) { + hex += ''+str.charCodeAt(i).toString(16).padStart(2, "0"); + } + return hex; + }, + getPlatformProcessor: function() { //mozilla property: if (navigator.oscpu){ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xpra-html5-4.5.2+git20220421.ecc3a08/html5/js/lib/rencode.js new/xpra-html5-5.0+git20220516.caf9971/html5/js/lib/rencode.js --- old/xpra-html5-4.5.2+git20220421.ecc3a08/html5/js/lib/rencode.js 2022-04-21 11:18:32.000000000 +0200 +++ new/xpra-html5-5.0+git20220516.caf9971/html5/js/lib/rencode.js 2022-05-16 11:39:03.000000000 +0200 @@ -396,8 +396,8 @@ } else { //oh, IE... - const left = this.getUint32(byteOffset); - const right = this.getUint32(byteOffset+4); + const left = dv.getInt32(0); + const right = dv.getUint32(4); s = 2**32*left + right; } dec.pos += 9; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xpra-html5-4.5.2+git20220421.ecc3a08/packaging/debian/changelog new/xpra-html5-5.0+git20220516.caf9971/packaging/debian/changelog --- old/xpra-html5-4.5.2+git20220421.ecc3a08/packaging/debian/changelog 2022-04-21 11:18:32.000000000 +0200 +++ new/xpra-html5-5.0+git20220516.caf9971/packaging/debian/changelog 2022-05-16 11:39:03.000000000 +0200 @@ -1,7 +1,27 @@ -xpra-html5 (5.0-r1237-1) UNRELEASED; urgency=low - * TODO +xpra-html5 (5.0-r1275-1) UNRELEASED; urgency=low + * auto-fullscreen, alt-tabbing with window previews + * decode images using an offscreen worker thread + * decode `avif` images, grayscale and palette `png` + * handle `void` paint packets + * increase default non-vsynced target framerate + * tell servers to use 'scroll' encoding less aggressively + * keycloak authentication (requires xpra server version 4.4 or later) + * support pre-mapped windows (requires xpra server version 4.4 or later) + * support clipboard pasting file into the session + * detect inverted vertical scrolling (ie: on MacOS) + * improved dead key mapping for non-us layouts + * 64-bit rencode decoding bug with Safari (and IE) + * notification errors with bencoder + * avoid popping up the on-screen keyboard on mobile touch events + * updated on-screen simple-keyboard UI and file saver library + * shifted characters with simple-keyboard + * prevent stuck keys + * focus and raise windows when their title bar is clicked + * spurious focus events when minimizing windows + * fix AES encryption when used with authentication and rencodeplus + * build script refactoring - -- Antoine Martin antoine@xpra.org Tue, 15 Feb 2022 22:44:21 +0700 +700 + -- Antoine Martin antoine@xpra.org Wed, 11 May 2022 16:55:59 +0700 +700 xpra-html5 (4.5.2-r1106-1) UNRELEASED; urgency=low * fix toolbar position diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xpra-html5-4.5.2+git20220421.ecc3a08/packaging/debian/control new/xpra-html5-5.0+git20220516.caf9971/packaging/debian/control --- old/xpra-html5-4.5.2+git20220421.ecc3a08/packaging/debian/control 2022-04-21 11:18:32.000000000 +0200 +++ new/xpra-html5-5.0+git20220516.caf9971/packaging/debian/control 2022-05-16 11:39:03.000000000 +0200 @@ -1,5 +1,5 @@ Package: xpra-html5 -Version: 5.0-r1237-1 +Version: 5.0-r1275-1 Source: xpra-html5 Maintainer: Antoine Martin <antoine@xpra.org> Standards-Version: 3.9.3 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xpra-html5-4.5.2+git20220421.ecc3a08/packaging/rpm/xpra-html5.spec new/xpra-html5-5.0+git20220516.caf9971/packaging/rpm/xpra-html5.spec --- old/xpra-html5-4.5.2+git20220421.ecc3a08/packaging/rpm/xpra-html5.spec 2022-04-21 11:18:32.000000000 +0200 +++ new/xpra-html5-5.0+git20220516.caf9971/packaging/rpm/xpra-html5.spec 2022-05-16 11:39:03.000000000 +0200 @@ -4,7 +4,7 @@ # later version. See the file COPYING for details. %define version 5.0 -%define release 1.r1237%{?dist} +%define release 1.r1275%{?dist} %define minifier uglifyjs %define python python3 @@ -77,8 +77,28 @@ %endif %changelog -* Tue Feb 15 2022 Antoine Martin <antoine@xpra.org> 5.0-1237-1 -- TODO +* Wed May 11 2022 Antoine Martin <antoine@xpra.org> 5.0-1237-1 +- auto-fullscreen, alt-tabbing with window previews +- decode images using an offscreen worker thread +- decode `avif` images, grayscale and palette `png` +- handle `void` paint packets +- increase default non-vsynced target framerate +- tell servers to use 'scroll' encoding less aggressively +- keycloak authentication (requires xpra server version 4.4 or later) +- support pre-mapped windows (requires xpra server version 4.4 or later) +- support clipboard pasting file into the session +- detect inverted vertical scrolling (ie: on MacOS) +- improved dead key mapping for non-us layouts +- 64-bit rencode decoding bug with Safari (and IE) +- notification errors with bencoder +- avoid popping up the on-screen keyboard on mobile touch events +- updated on-screen simple-keyboard UI and file saver library +- shifted characters with simple-keyboard +- prevent stuck keys +- focus and raise windows when their title bar is clicked +- spurious focus events when minimizing windows +- fix AES encryption when used with authentication and rencodeplus +- build script refactoring * Fri Dec 17 2021 Antoine Martin <antoine@xpra.org> 4.5.2-1106-1 - fix toolbar position diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xpra-html5-4.5.2+git20220421.ecc3a08/setup.py new/xpra-html5-5.0+git20220516.caf9971/setup.py --- old/xpra-html5-4.5.2+git20220421.ecc3a08/setup.py 2022-04-21 11:18:32.000000000 +0200 +++ new/xpra-html5-5.0+git20220516.caf9971/setup.py 2022-05-16 11:39:03.000000000 +0200 @@ -223,6 +223,7 @@ ], "materialicons-regular.ttf" : [ "/usr/share/fonts/truetype/material-design-icons-iconfont/MaterialIcons-Regular.ttf", + "/usr/share/fonts/material-icons-fonts/MaterialIcons-Regular.ttf", ], "materialicons-regular.woff" : [ "/usr/share/fonts/woff/material-design-icons-iconfont/MaterialIcons-Regular.woff", @@ -302,6 +303,8 @@ "-o", dst, "--compress", ] + elif minifier=="hjsmin": + minify_cmd = ["hjsmin", "-i", fsrc, "-o", dst] else: assert minifier=="yuicompressor" try: @@ -520,7 +523,7 @@ def help(): print("invalid number of arguments, usage:") print("%s sdist" % (args[0],)) - print("%s install [ROOT] [INSTALL_DIR] [MINIFIER]" % (args[0],)) + print("%s install [ROOT] [INSTALL_DIR] [CONFIG_DIR] [MINIFIER]" % (args[0],)) print("%s deb" % (args[0],)) print("%s rpm" % (args[0],)) print("%s set-version VERSION" % (args[0],)) ++++++ xpra-html5.obsinfo ++++++ --- /var/tmp/diff_new_pack.ngmKPF/_old 2022-05-31 15:48:45.312042911 +0200 +++ /var/tmp/diff_new_pack.ngmKPF/_new 2022-05-31 15:48:45.316042913 +0200 @@ -1,5 +1,5 @@ name: xpra-html5 -version: 4.5.2+git20220421.ecc3a08 -mtime: 1650532712 -commit: ecc3a0819807d5174d28b6bc76e498d9af3b92fa +version: 5.0+git20220516.caf9971 +mtime: 1652693943 +commit: caf9971d54bc818f1740b06c646a74316bffb98f
participants (1)
-
Source-Sync