how to get multifile from Clipboard ?

Talk about add-ons and extension development.
b337
Posts: 7
Joined: May 3rd, 2016, 10:02 pm

how to get multifile from Clipboard ?

Post by b337 »

hi,

i'm trying to develop extension for TB38/TB45 to add attachment with paste event, but i am only able to get one file from clipboard.

Code: Select all

Components.utils.import('resource://gre/modules/Services.jsm');

document.addEventListener("paste", function(e) { 
		var clip = Components.classes["@mozilla.org/widget/clipboard;1"].getService(Components.interfaces.nsIClipboard);
		if (!clip) return false;

		var trans = Components.classes["@mozilla.org/widget/transferable;1"].createInstance(Components.interfaces.nsITransferable);
		if (!trans) return false;
		
		trans.addDataFlavor("application/x-moz-file");
			
		clip.getData(trans,clip.kGlobalClipboard);

		var file = new Object();
		var strLength = new Object();
		var flavorObj = new Object();
		
		try {
			trans.getAnyTransferData(flavorObj,file,strLength);
		} catch(e){
			return false;
		}

		if (file) file = file.value.QueryInterface(Components.interfaces.nsILocalFile);
		
		if ( file.isFile()) {
			let fileHandler = Services.io.getProtocolHandler("file").QueryInterface(Components.interfaces.nsIFileProtocolHandler);
			let attachment = Components.classes["@mozilla.org/messengercompose/attachment;1"].createInstance(Components.interfaces.nsIMsgAttachment);
			attachment.url = fileHandler.getURLSpecFromFile(file);
			attachment.size = file.fileSize;
			AddAttachments([attachment]);
		}		
	}, true);
can someone help me to give example how to get multifile from clipboard ?

Thanks
Noitidart
Posts: 1168
Joined: September 16th, 2007, 8:01 am

Re: how to get multifile from Clipboard ?

Post by Noitidart »

Wow this is fun question.

I also am not able to get more then one file. I would guess that if there are multiple files on the clipboard it would be as nsISupportsArray but its not happening, its weird:

Code: Select all

var cs = Cc["@mozilla.org/widget/clipboard;1"].getService(Ci.nsIClipboard);
var flavs = ['application/x-moz-file'];
var hasFlavs = cs.hasDataMatchingFlavors(flavs, flavs.length, cs.kGlobalClipboard);

if (hasFlavs) {
  var trans = Cc['@mozilla.org/widget/transferable;1'].createInstance(Ci.nsITransferable);
  trans.addDataFlavor(flavs[0]);
  cs.getData(trans, cs.kGlobalClipboard);
  var flavor={}, data={}, length={};
  trans.getTransferData(flavs[0], data, length);
  console.log(flavor, data, length);
  try {
    var file = data.value.QueryInterface(Ci.nsIFile);
    console.log('just one file on clipboard:', file);
  } catch(ignore) {}
  try {
    var arr = data.value.QueryInterface(Ci.nsISupportsArray);
    console.log('multiple files:', arr)
  } catch(ignore) {}
} else {
  console.log('no files on clipboard');
}
b337
Posts: 7
Joined: May 3rd, 2016, 10:02 pm

Re: how to get multifile from Clipboard ?

Post by b337 »

return from event.clipboardData.files is same, only one file.

maybe the problem is from DataTransfer.cpp - https://github.com/mozilla/gecko-dev/bl ... ansfer.cpp

Code: Select all

  // Only the first item is valid for clipboard events
  if (aIndex > 0 &&
      (mEventMessage == eCut || mEventMessage == eCopy ||
       mEventMessage == ePaste)) {
    aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
    return nullptr;
  }

Code: Select all

  // Only the first item is valid for clipboard events
  if (aIndex > 0 &&
      (mEventMessage == eCut || mEventMessage == eCopy ||
       mEventMessage == ePaste)) {
    return NS_ERROR_DOM_INDEX_SIZE_ERR;
  }

Code: Select all

  // Only the first item is valid for clipboard events
  if (aIndex > 0 &&
      (mEventMessage == eCut || mEventMessage == eCopy ||
       mEventMessage == ePaste)) {
    aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
    return;
  }
Noitidart
Posts: 1168
Joined: September 16th, 2007, 8:01 am

Re: how to get multifile from Clipboard ?

Post by Noitidart »

@b33y this is a really interesting issue. I'll ask around to see if anyone can help out, I dug into it some more and could not get multiple files, way weird.

@The_8472 notified us of this bug - https://bugzilla.mozilla.org/show_bug.cgi?id=906420 - may be related although that seems HTML5ish and not the XPCOM method we're using here. However related. If we are limited by this bug though, we can resort to js-ctypes, which would be nice because we can run it from a worker. But I hear clipboard APIs on the os's are pretty yucky.
b337
Posts: 7
Joined: May 3rd, 2016, 10:02 pm

Re: how to get multifile from Clipboard ?

Post by b337 »

@Noitidart Thank You.

i hope that related, but aren't that bug about drag event?
Noitidart
Posts: 1168
Joined: September 16th, 2007, 8:01 am

Re: how to get multifile from Clipboard ?

Post by Noitidart »

Yep it's about dragging, but the underlying mechanism uses the same stuff I think.

If you want to start diving into the js-ctypes version let me know and I'll start with you, we can collaborate on it. Pretty much all the types for windows, mac, linux/unix are typed in this lib - https://github.com/Noitidart/ostypes

I can set up a playground for us based on - https://github.com/Noitidart/ostypes_playground/
b337
Posts: 7
Joined: May 3rd, 2016, 10:02 pm

Re: how to get multifile from Clipboard ?

Post by b337 »

i already read some js-ctypes document, and trying some example, for now i am already able to open clipboard and clear clipboard but still not able to get data, i got stuck in type like "HANDLE" and "UINT uFormat".

Code: Select all

Components.utils.import("resource://gre/modules/ctypes.jsm");
var lib = ctypes.open("user32.dll");
var openclip = lib.declare("OpenClipboard",
                         ctypes.winapi_abi,
                         ctypes.bool,
                         ctypes.int32_t);
var emptyclip = lib.declare("EmptyClipboard",
                         ctypes.winapi_abi,
                         ctypes.bool);
var closeclip = lib.declare("CloseClipboard",
                         ctypes.winapi_abi,
                         ctypes.bool);
openclip(0)
emptyclip()
closeclip()
how to call library in https://github.com/Noitidart/ostypes ?
i already try calling like
var worker = new Worker("chrome://test/content/ostypes_win.jsm");
and
Components.utils.import("chrome://test/content/ostypes_win.jsm");
but i got error message "ReferenceError: ctypes is not defined"
Noitidart
Posts: 1168
Joined: September 16th, 2007, 8:01 am

Re: how to get multifile from Clipboard ?

Post by Noitidart »

b337 wrote:i already read some js-ctypes document, and trying some example, for now i am already able to open clipboard and clear clipboard but still not able to get data, i got stuck in type like "HANDLE" and "UINT uFormat".

Code: Select all

Components.utils.import("resource://gre/modules/ctypes.jsm");
var lib = ctypes.open("user32.dll");
var openclip = lib.declare("OpenClipboard",
                         ctypes.winapi_abi,
                         ctypes.bool,
                         ctypes.int32_t);
var emptyclip = lib.declare("EmptyClipboard",
                         ctypes.winapi_abi,
                         ctypes.bool);
var closeclip = lib.declare("CloseClipboard",
                         ctypes.winapi_abi,
                         ctypes.bool);
openclip(0)
emptyclip()
closeclip()
how to call library in https://github.com/Noitidart/ostypes ?
i already try calling like
var worker = new Worker("chrome://test/content/ostypes_win.jsm");
and
Components.utils.import("chrome://test/content/ostypes_win.jsm");
but i got error message "ReferenceError: ctypes is not defined"
Oh you can't call the libraries or ctypes from ostypes. The only things exposed is what is available on the ostypes.* so ostypes.HELPERS, .API, .CONST, and .TYPE

In order to use those functions you have to first declare them in the ostypes repo like I did for you here:
https://github.com/Noitidart/ostypes/co ... 8...master

And then you update your ostypes submodule in whatever repository you use, I set it up here in ostyeps_playground#clipboard repo/branch:

https://github.com/Noitidart/ostypes_pl ... s#L52-L172

That right there lists all the datas currently on the clipboard, and the format names of it.

If you make an xpi out of that and run it you will see something like this in your browser console:

Image
Noitidart
Posts: 1168
Joined: September 16th, 2007, 8:01 am

Re: how to get multifile from Clipboard ?

Post by Noitidart »

This was real fun. Ok I wrote it up for windows, now we need to handle mac and linux:

https://github.com/Noitidart/ostypes_pl ... #L228-L229

That function will return to you null if no files on the clipboard, or an array of the full path to the files on the clipboard.

Awesome thing about this, is you can run it from a ChromeWorker, so that means off of the main thread, yahoo! It's recommended you work with OS.File on the file paths. Try to avoid nsIFile.

Another yahoo about this, is that it's not XPCOM so it shouldn't get deprecated. :)
Edit: Sadly, temporarily striked out the "no deprecation comment", as it is not for certain right now. I'm praying they don't deprecate it, and there is a comment saying there is a possibility for js-ctypes v2 here - https://groups.google.com/d/msg/mozilla ... W7qLwqBwAJ - so Im' hoping hard! haha
Last edited by Noitidart on May 18th, 2016, 2:50 am, edited 1 time in total.
Noitidart
Posts: 1168
Joined: September 16th, 2007, 8:01 am

Re: how to get multifile from Clipboard ?

Post by Noitidart »

Wow this was so fun. I went ahead and added mac support. Make an xpi out of that and run on mac and browser console will show you this:

Image
b337
Posts: 7
Joined: May 3rd, 2016, 10:02 pm

Re: how to get multifile from Clipboard ?

Post by b337 »

Wow, Thank you so much. :D

now i am able to add attachment with paste

Code: Select all

function load() {
	initOstypes();
	document.addEventListener("paste", function(e) {
		var file = getFilesOnClipboard();
		if ( file ) {
			for(var i=0;i<file.length;i++) {
				let j = i;
				promise = OS.File.stat(file[j].replace(/\\/g, "/"));
				promise = promise.then(
					function onSuccess(a) {
						if ( !a.isDir ) {
							let attachment = Components.classes["@mozilla.org/messengercompose/attachment;1"].createInstance(Components.interfaces.nsIMsgAttachment);
							attachment.url = toURL(a.path);
							attachment.size = a.size;
							AddAttachments([attachment]);
						}
					},function failure(reason) {
						console.log(reason);
					}
				);
			}
			if ( file.length > 0 ) {
				e.preventDefault();
				e.stopPropagation();
				return;
			}
		}
	}, true);
}

function toURL(e) {
	return "file:///" + e.replace(/%/g,'%25').replace(/ /g,'%20')
}

window.addEventListener("load", function() {
	document.getElementById("content-frame").addEventListener("load", load, true);
}, true);
now just to add for linux support, or do you want to add support for linux too :wink: hehhehe
and i want make it so i can copy mail and paste it as attachment
Noitidart
Posts: 1168
Joined: September 16th, 2007, 8:01 am

Re: how to get multifile from Clipboard ?

Post by Noitidart »

b337 wrote:Wow, Thank you so much. :D

now i am able to add attachment with paste

Code: Select all

function load() {
	initOstypes();
	document.addEventListener("paste", function(e) {
		var file = getFilesOnClipboard();
		if ( file ) {
			for(var i=0;i<file.length;i++) {
				let j = i;
				promise = OS.File.stat(file[j].replace(/\\/g, "/"));
				promise = promise.then(
					function onSuccess(a) {
						if ( !a.isDir ) {
							let attachment = Components.classes["@mozilla.org/messengercompose/attachment;1"].createInstance(Components.interfaces.nsIMsgAttachment);
							attachment.url = toURL(a.path);
							attachment.size = a.size;
							AddAttachments([attachment]);
						}
					},function failure(reason) {
						console.log(reason);
					}
				);
			}
			if ( file.length > 0 ) {
				e.preventDefault();
				e.stopPropagation();
				return;
			}
		}
	}, true);
}

function toURL(e) {
	return "file:///" + e.replace(/%/g,'%25').replace(/ /g,'%20')
}

window.addEventListener("load", function() {
	document.getElementById("content-frame").addEventListener("load", load, true);
}, true);
now just to add for linux support, or do you want to add support for linux too :wink: hehhehe
and i want make it so i can copy mail and paste it as attachment

I would love to help. We have to do it with XCB (well we dont have to, we can use x11 or gtk but they won't work from ChromeWorker). But I can't figure out how, I asked on stackoverflow:
http://stackoverflow.com/questions/3729 ... rd-and-xcb

You seem to be using ostypes on the main thread. While you can do that, its recommended you do it from a ChromeWorker. The more stuff you can do off the mainthread the better :)
b337
Posts: 7
Joined: May 3rd, 2016, 10:02 pm

Re: how to get multifile from Clipboard ?

Post by b337 »

Thanks you.

how to access compose window from ChromeWorker? i try with overlay but dosen't work
Noitidart
Posts: 1168
Joined: September 16th, 2007, 8:01 am

Re: how to get multifile from Clipboard ?

Post by Noitidart »

You actually can't access anything in DOM from ChromeWorker, workers are purely for calculations. You have to send messages back. There is this thing called promiseworker which works awesomely - https://developer.mozilla.org/en-US/doc ... miseWorker
Noitidart
Posts: 1168
Joined: September 16th, 2007, 8:01 am

Re: how to get multifile from Clipboard ?

Post by Noitidart »

Hey @b337, were you able to do some research to figure out the code to do this in XCB. If you find it please let me know I can convert it to jsctypes for you.
Post Reply