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.bradn...@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.ba...@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 anto...@xpra.org  Tue, 15 Feb 2022 22:44:21 +0700 +700
+ -- Antoine Martin anto...@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 <anto...@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 <anto...@xpra.org> 5.0-1237-1
-- TODO
+* Wed May 11 2022 Antoine Martin <anto...@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 <anto...@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
 

Reply via email to