xpra icon
Bug tracker and wiki

Ticket #2312: clipboard-html5-images.patch

File clipboard-html5-images.patch, 11.3 KB (added by Antoine Martin, 8 months ago)

support for images in and out with latest chrome - almost working

  • xpra/x11/gtk_x11/clipboard.py

     
    481481        self.get_contents("TARGETS", got_targets)
    482482
    483483    def choose_targets(self, targets):
    484         #if "image/png" in targets:
    485         #    return ("image/png",)
     484        if "image/png" in targets:
     485            return ("image/png",)
    486486        return tuple(x for x in targets if x in TEXT_TARGETS)
    487487
    488488    def do_selection_clear_event(self, event):
     
    648648            img.save(buf, "PNG")
    649649            data = buf.getvalue()
    650650            buf.close()
     651            import binascii
     652            log.info("PNG HEX: %s", binascii.hexlify(data))
    651653        return data
    652654
    653655
  • html5/js/Client.js

     
    126126        this.clipboard_server_buffers = {};
    127127        this.clipboard_pending = false;
    128128        this.clipboard_targets = ["UTF8_STRING", "TEXT", "STRING", "text/plain"];
     129        if (navigator.clipboard && navigator.clipboard.write) {
     130                this.clipboard_targets.push("image/png");
     131        }
    129132        // printing / file-transfer:
    130133        this.remote_printing = false;
    131134        this.remote_file_transfer = false;
     
    11081111
    11091112XpraClient.prototype._make_hello = function() {
    11101113        var selections = ["CLIPBOARD"];
    1111         this.log("navigator.clipboard=", navigator.clipboard);
    1112         this.log("navigator.clipboard.readText=", navigator.clipboard.readText);
    1113         this.log("navigator.clipboard.writeText=", navigator.clipboard.writeText);
    11141114        if (!navigator.clipboard || !navigator.clipboard.readText || !navigator.clipboard.writeText) {
    11151115                //we'll need the primary contents to try to stay up to date
    11161116                selections = ["CLIPBOARD", "PRIMARY"];
     
    29772977                return;
    29782978        }
    29792979        var selection = packet[1];
    2980         var target = packet[3];
    2981         var is_valid_target = ctx.clipboard_targets.includes(target);
    2982         ctx.debug("clipboard", "clipboard token:", packet);
    2983         ctx.debug("target=", target, "is valid:", is_valid_target);
    2984         // if we have navigator.clipboard, we just set the clipboard value,
    2985         // otherwise we don't actually set anything here,
     2980        var targets = [];
     2981        var target = null;
     2982        var dtype = null;
     2983        var dformat = null;
     2984        var wire_encoding = null;
     2985        var wire_data = null;
     2986        if (packet.length>=3) {
     2987                targets = packet[2];
     2988        }
     2989        if (packet.length>=8) {
     2990                target = packet[3];
     2991                dtype = packet[4];
     2992                dformat = packet[5];
     2993                wire_encoding = packet[6];
     2994                wire_data = packet[7];
     2995                //always keep track of the latest server buffer
     2996                ctx.clipboard_server_buffers[selection] = [target, dtype, dformat, wire_encoding, wire_data];
     2997        }
     2998
     2999        var is_valid_target = target && ctx.clipboard_targets.includes(target);
     3000        ctx.debug("clipboard", "clipboard token received");
     3001        ctx.debug("clipboard", "targets=", targets);
     3002        ctx.debug("clipboard", "target=", target, "is valid:", is_valid_target);
     3003        ctx.debug("clipboard", "dtype=", dtype, "dformat=", dformat, "wire-encoding=", wire_encoding);
     3004        // if we have navigator.clipboard support in the browser,
     3005        // we can just set the clipboard value here,
     3006        // otherwise we don't actually set anything
    29863007        // because we can't (the browser security won't let us)
    29873008        // we just record the value and actually set the clipboard
    29883009        // when we get a click, control-C or control-X event
    29893010        // (when access to the clipboard is allowed)
    29903011        if (is_valid_target) {
    2991                 var data = packet[7];
    2992                 var datatype = packet[4].toLowerCase();
    2993                 var is_text = datatype.indexOf("text")>=0 || datatype.indexOf("string")>=0;
     3012                var is_text = dtype.toLowerCase().indexOf("text")>=0 || dtype.toLowerCase().indexOf("string")>=0;
    29943013                if (is_text) {
    29953014                        try {
    2996                                 data = Utilities.Uint8ToString(data);
     3015                                wire_data = Utilities.Uint8ToString(wire_data);
    29973016                        }
    29983017                        catch (e) { }
    2999                 }
    3000                 if (ctx.clipboard_buffer!=data) {
    3001                         ctx.clipboard_datatype = packet[4];
    3002                         ctx.clipboard_buffer = data;
    3003                         ctx.clipboard_pending = true;
    3004                         if (navigator.clipboard && navigator.clipboard.writeText) {
    3005                                 if (is_text) {
    3006                                         navigator.clipboard.writeText(data).then(function() {
    3007                                                 ctx.debug("clipboard", "writeText succeeded")
    3008                                                 ctx.clipboard_pending = false;
    3009                                         }, function() {
    3010                                                 ctx.debug("clipboard", "writeText failed")
    3011                                         });
     3018                        if (ctx.clipboard_buffer!=wire_data) {
     3019                                ctx.clipboard_datatype = dtype;
     3020                                ctx.clipboard_buffer = wire_data;
     3021                                ctx.clipboard_pending = true;
     3022                                if (navigator.clipboard && navigator.clipboard.writeText) {
     3023                                        if (is_text) {
     3024                                                navigator.clipboard.writeText(data).then(function() {
     3025                                                        ctx.debug("clipboard", "writeText succeeded")
     3026                                                        ctx.clipboard_pending = false;
     3027                                                }, function() {
     3028                                                        ctx.debug("clipboard", "writeText failed")
     3029                                                });
     3030                                        }
    30123031                                }
    30133032                        }
    30143033                }
    3015                 //keep track of the latest server buffer
    3016                 ctx.clipboard_server_buffers[selection] = data;
     3034                else if (dtype=="image/png" && dformat==8 && wire_encoding=="bytes" && navigator.clipboard && navigator.clipboard.write) {
     3035                        ctx.log("clipboard", "png image received");
     3036                        var blob = new Blob(wire_data, {type: dtype});
     3037                        ctx.log("clipboard", "created blob", blob);
     3038                        var item = new ClipboardItem({"image/png": blob});
     3039                        ctx.log("clipboard", "created ClipboardItem", item);
     3040                        var items = [item];
     3041                        ctx.log("clipboard", "created ClipboardItem list", items);
     3042                        navigator.clipboard.write(items).then(function() {
     3043                                ctx.log("clipboard", "copied png image to clipboard");
     3044                        })
     3045                        .catch(function(err) {
     3046                                ctx.log("clipboard", "failed to set png image", err);
     3047                        });
     3048                }
    30173049        }
     3050
    30183051        if (navigator.clipboard && navigator.clipboard.writeText && selection=="CLIPBOARD") {
    30193052                //always claim the CLIPBOARD again,
    30203053                //so we will be asked for contents
     
    30383071                selection = packet[2];
    30393072                //target = packet[3];
    30403073
    3041         if (navigator.clipboard && navigator.clipboard.readText) {
    3042                 navigator.clipboard.readText().then(text => {
    3043                         ctx.cdebug("clipboard", "clipboard request via readText() text=", text);
    3044                         if (selection=="CLIPBOARD" && text==ctx.clipboard_server_buffers["PRIMARY"]) {
    3045                                 //we set the clipboard contents to the PRIMARY selection
    3046                                 //and the server is asking for the CLIPBOARD selection
    3047                                 ctx.cdebug("clipboard request: using backup value");
    3048                                 text = ctx.clipboard_server_buffers["CLIPBOARD"] || "";
    3049                         }
    3050                         ctx.send_clipboard_contents(request_id, selection, text);
    3051                 })
    3052                 .catch(err => {
    3053                         ctx.cdebug("clipboard", "readText() failed:", err);
    3054                         //send last server buffer instead:
    3055                         ctx.send_clipboard_contents(request_id, selection, ctx.clipboard_server_buffer);
    3056                 });
     3074        ctx.debug("clipboard", selection+" request");
     3075
     3076        //we only handle CLIPBOARD requests,
     3077        //PRIMARY is used read-only
     3078        if (selection!="CLIPBOARD") {
     3079                ctx.send_clipboard_string(request_id, selection, "");
    30573080                return;
    30583081        }
    3059         var clipboard_buffer = ctx.get_clipboard_buffer();
    3060         ctx.send_clipboard_contents(request_id, selection, clipboard_buffer);
     3082
     3083        if (navigator.clipboard) {
     3084                if (navigator.clipboard.read) {
     3085                        ctx.debug("clipboard", "request using read()");
     3086                        navigator.clipboard.read().then(data => {
     3087                                var item = null;
     3088                                var itemtype = null;
     3089                                ctx.debug("clipboard", "request via read() data=", data);
     3090                                for (var i = 0; i < data.length; i++) {
     3091                                        item = data[i];
     3092                                        ctx.debug("clipboard", "item", i, "types:", item.types);
     3093                                        for (var j = 0; j < item.types.length; j++) {
     3094                                                itemtype = item.types[j];
     3095                                                if (itemtype == "text/plain") {
     3096                                                        item.getType(itemtype).then(blob => {
     3097                                                                var fileReader = new FileReader();
     3098                                                                fileReader.onload = function(event) {
     3099                                                                        ctx.send_clipboard_string(request_id, selection, event.target.result);
     3100                                                                };
     3101                                                                fileReader.readAsText(blob);
     3102                                                        })
     3103                                                        .catch(err => {
     3104                                                                ctx.debug("clipboard", "getType('"+itemtype+"') failed", err);
     3105                                                                //send last server buffer instead:
     3106                                                                ctx.resend_clipboard_server_buffer();
     3107                                                        });
     3108                                                        return;
     3109                                                }
     3110                                                else if (itemtype == "image/png") {
     3111                                                        item.getType(itemtype).then(blob => {
     3112                                                                var fileReader = new FileReader();
     3113                                                                fileReader.onload = function(event) {
     3114                                                                        ctx.send_clipboard_contents(request_id, selection, itemtype, 8, "bytes", event.target.result);
     3115                                                                };
     3116                                                                fileReader.readAsText(blob);
     3117                                                        })
     3118                                                        .catch(err => {
     3119                                                                ctx.debug("clipboard", "getType('"+itemtype+"') failed", err);
     3120                                                                //send last server buffer instead:
     3121                                                                ctx.resend_clipboard_server_buffer();
     3122                                                        });
     3123                                                        return;
     3124                                                }
     3125                                        }
     3126                                }
     3127                        })
     3128                        .catch(err => {
     3129                                ctx.debug("clipboard", "read() failed:", err);
     3130                                //send last server buffer instead:
     3131                                ctx.resend_clipboard_server_buffer();
     3132                        });
     3133                        return;
     3134                }
     3135                else if (navigator.clipboard.readText) {
     3136                        ctx.debug("clipboard", "clipboard request using readText()");
     3137                        navigator.clipboard.readText().then(text => {
     3138                                ctx.debug("clipboard", "clipboard request via readText() text=", text);
     3139                                var primary_server_buffer = ctx.clipboard_server_buffers["PRIMARY"];
     3140                                if (primary_server_buffer && primary_server_buffer[2]==8 && primary_server_buffer[3]=="bytes" && text==primary_server_buffer[4]) {
     3141                                        //we have set the clipboard contents to the PRIMARY selection
     3142                                        //and the server is asking for the CLIPBOARD selection
     3143                                        //send it back the last value it gave us
     3144                                        ctx.debug("clipboard request: using backup value");
     3145                                        ctx.resend_clipboard_server_buffer();
     3146                                        return;
     3147                                }
     3148                                ctx.send_clipboard_string(request_id, selection, text);
     3149                        })
     3150                        .catch(err => {
     3151                                ctx.debug("clipboard", "readText() failed:", err);
     3152                                //send last server buffer instead:
     3153                                ctx.resend_clipboard_server_buffer();
     3154                        });
     3155                        return;
     3156                }
     3157        }
     3158        var clipboard_buffer = ctx.get_clipboard_buffer() || "";
     3159        ctx.send_clipboard_string(request_id, selection, clipboard_buffer, "UTF8_STRING");
    30613160}
    30623161
    3063 XpraClient.prototype.send_clipboard_contents = function(request_id, selection, clipboard_buffer) {
     3162XpraClient.prototype.resend_clipboard_server_buffer = function(request_id, selection) {
     3163        var server_buffer = this.clipboard_server_buffers["CLIPBOARD"];
     3164        if (!server_buffer) {
     3165                this.send_clipboard_string(request_id, selection, "", "UTF8_STRING");
     3166        }
     3167        var target = server_buffer[0];
     3168        var dtype = server_buffer[1];
     3169        var dformat = server_buffer[2];
     3170        var wire_encoding = server_buffer[3];
     3171        var wire_data = server_buffer[4];
     3172        this.send_clipboard_contents(request_id, selection, dtype, dformat, wire_encoding, wire_data);
     3173}
     3174
     3175XpraClient.prototype.send_clipboard_string = function(request_id, selection, clipboard_buffer, datatype) {
    30643176        var packet;
    30653177        if (clipboard_buffer == "") {
    30663178                packet = ["clipboard-contents-none", request_id, selection];
    30673179        } else {
    3068                 packet = ["clipboard-contents", request_id, selection, "UTF8_STRING", 8, "bytes", clipboard_buffer];
     3180                packet = ["clipboard-contents", request_id, selection, datatype || "UTF8_STRING", 8, "bytes", clipboard_buffer];
    30693181        }
     3182        this.log("send_clipboard_string: packet=", packet);
    30703183        this.send(packet);
    30713184}
    30723185
     3186XpraClient.prototype.send_clipboard_contents = function(request_id, selection, datatype, dformat, encoding, clipboard_buffer) {
     3187        var packet;
     3188        if (clipboard_buffer == "") {
     3189                packet = ["clipboard-contents-none", request_id, selection];
     3190        } else {
     3191                packet = ["clipboard-contents", request_id, selection, datatype, dformat || 8, encoding || "bytes", clipboard_buffer];
     3192        }
     3193        this.send(packet);
     3194}
     3195
    30733196/**
    30743197 * File transfers and printing
    30753198 */