When we launched Bespin people noticed that our copy and paste experience wasn’t the best. It wasn’t integrating with the system clipboard in most cases. An editor needs clipboard support. Needs it. Essential. 101.

It turns out that this can be a royal pain to work across various browsers and platforms. I thought it may be good to share our story, where we were, where we are, and where we want to be. I also am looking forward to hearing your ideas, as we may have missed something.

First, what are we talking about here?

With our canvas based editor we needed access to the system clipboard to be able to copy data into, cut, and paste from the outside clipboard.

The first version that we had, used Clipboard Copy which accesses the clipboard APIs available in Flash. Unfortunately, our parade was rained on. With Flash 10 Adobe changed the access model to these APIs (due to security issues). Now a user has to explicitly click on a SWF control to get the access. To help, zeroclipboard tricks Flash, allowing you to at least fake out the user and have them click on any element. zeroclipboard hides a translucent SWF control on top of the element so the user thinks they are clicking on the “copy” icon, or something like it. This is all well and good, but we obviously need to tie into the key combinations that we habitually know (Cmd/Ctrl C, X, and V).

With Flash not able to do what we need (as Flash 10 will be well supported soon, and it broke it) we needed to take a fresh look at the problem.

What APIs do we have available in the browsers? In typical fashion, each browser does things differently.

IE had an early API to give you access. I won’t go into detail on this puppy because we aren’t interested in supporting IE yes, until we can get Canvas/fillText running in a performant manner. Then we will care.

What about Firefox?

Firefox has a rich clipboard API in the XUL platform itself via the nsIClipboard interface. It has everything we could possibly need, and we can implement copy and paste easily:

Time to get subtle here. Our use case isn’t about being able to sneakily get access to the clipboard without the user knowing. All we want it to actually get access based on the user doing something. When they Cmd/Ctrl C, X, or V. When they click on a copy button, then do the action. This is very different than getting access to the clipboard at any time.

In the code above, notice what the exception cause is doing: clipdata = copytext; This is our worst case scenario. If we can’t get access to the clipboard then we keep an internal clipboard that only allows you to cut/copy/paste within the editor itself. As soon as you want to copy something from a web page and paste it into the editor? Outta luck. This isn’t good enough.

Now, let’s take a look at WebKit. WebKit does a decent job and implementing the initial Microsoft work in a nicer DOM way.

There are a set of DOM events that have pairings that tell you before* “getting ready to do the action so set things up if you need” and then the action itself.

beforecopy

copy

beforecut

cut

beforepaste

paste

In our world with the editor, we use the before events to set things up, and we have to do something pretty hacky to make it happen. The copy event itself only actually goes through if you are on an element that supports it. There are hacks around this too. For example, if you want to be able to get a copy event on a div, you need to turn on contentEdible and set the tab index to -1. Strange huh?

To get around all of this, we use a hidden text input, which can of course accept these events. Then, in the before event we focus over to that hidden element. Here is an example for beforecopy:

You will notice that we make a call to preventDefault. This tells the system that we are in control and are handling the copy ourselves. This turns out to be important is some subtle ways too. For example, The OS will often grey out a menu choice for “Copy” if it doesn’t see anything selected. Since we are in control of the selection, we need to override that behavior and continue to pass through the event. In fact, I noticed a few weird things happening if you don’t get this all just right. For example, in WebKit, if you select the “Edit” main menu, it would fire a beforecopy right away, and then once again when you selected copy. Strange.

But, now we have gotten beforecopy to focus us in the text input, we are ready to implement the copy event which will not get passed through. Here we need to do OUR magic to get the selection that we want to put into the clipboard, and for that we use e.clipboardData.setData(type, data). In our case the type used is just plain text (test/plain) but you could use other rich data formats if you need.

You will also notice at the bottom of the code, we focus back on the canvas to shoot us back to where we were. This completes the cycle:

We favour feature detection over browser detection of course, but here it is a little painful as the real test is to to see if the event in a copy/cut/paste/ has a clipboardData object associated with it. That’s a little too deep into the onion layers, so we punt.

We would love to use this standards approach with Firefox too, and we almost can. MDC has docs on support for this, such as paste but the rub is in this bug that states how onpaste doesn’t let you getData() back, which makes it useless for all bar signaling when the user wants o paste something. PPK does a good job talking abou the various quirks with these events in general.

We still have some problems though. We handle the copy/cut/paste menus and keyboard shortcuts perfectly, but our UI has our own icons that try to kick off these actions. This is where we need to get zeroclipboard going, and then we are done (in WebKit).

Phew. That turned out to be a long explanation. Fun times on the Open Web! What am I missing? What tricks have you run into? Inquiring minds want to know!

More articles from Bespin

We are learning a lot from Bespin and beyond. Here are posts from the team and community that you may find interesting:

26 Responses to “Supporting the system clipboard in your Web applications; What a pain!”

Fundamentally, the problem you’re running into is that being able to place text controlled by a web page into the system clipboard without the user selecting it and copying it is considered a security bug in Firefox.

So you need to make sure that whatever setup you use doesn’t involve doing that, or anything that appears to be that to Firefox.

I guess I haven’t heard a solid reason why canvas was used over more conventional HTML 4.01. Even over here where you justify it by saying: We need control. Control over what exactly? Font? Text Color? Background Color? Hover Tooltip? What else?

It seems like Google Docs does just fine with giving me an editable text document that is cross-browser, text which can be richly styled, text which the browser and system know is selectable, copyable, linkable, can be checked in a spell-checker, can be ubiq’ed, etc. And it’s a document that can be edited by multiple people simultaneously. It seems they do it using nothing but HTML 4.01 elements.

It seems like a lot to give up – and I’m not sure all the things you’re trying to gain.

You mention performance… I didn’t do the experiments but I have a hard time believing that a page-sized div of HTML elements (spans, etc) that is dynamically changing as you scroll wouldn’t be as performant as programmatically formatting, styling and drawing the text on a canvas.

Yes, getting clipboard access from a web application is a security hole – and the fact that you can still do it with Flash is troubling. If I were you I wouldn’t count too much on that hack, Flash 11 will likely break it again.

However, why can’t you use existing mechanisms? Since you draw everything in the canvas yourself, I don’t think you care too much about input focus. Why not have an invisible text field that takes focus instead of the canvas? Whenever the user selects something in the canvas you copy the selected text into the text field and select it there. This way when the user presses Ctrl+C it will work automatically. Cut and paste operations in the text field can be detected and you can easily get the pasted data out. I realize that this isn’t quite as simple as I made it sound but at least that would be a stable approach.

Copy and paste buttons on the toolbar are a different thing of course, for that you will have to work around existing security mechanisms – meaning that it might be better to consider some different UI approach.

I find myself constantly oscillating between thinking that this is a great and innovative project to being worried that the choice to snub *hackable* web technologies (with what appears to everyone like Mozilla’s blessing) will do more harm to the web than Microsoft’s neglect of IE6.

When I inspect BeSpin with Firebug, I see a div element with id=editor that contains a canvas element with id=canvas — and that’s it. I can’t use Firebug to manipulate the editor (like resize, move or delete elements). I can’t put CSS declarations in my user agent style sheet that will have any effect on the BeSpin text editing — and furthermore, I can’t use any extension that manipulates fonts, colors, etc. on my behalf. Even Firefox’s Zoom Page function has no effect on BeSpin. When I run a “Find on Page”, I can’t find any of what I’ve typed. Even the copy & paste problem you describe in this post arises partly because you’ve spun your own UI. What about testrunners? I use Selenium IDE to automate my website testing. Completely useless with BeSpin. The ScrapBook extension, which lets you save webpage clippings? Useless again with BeSpin. Spellcheckers? Useless. Context-sensitive searches? Useless. The list of browser features and extensions that this breaks just goes on and on.

And then there’s Ubiquity. Oh what irony that Ubiquity becomes completely useless on a fellow labs project given what the word ubiquity means!

BeSpin is still hackable in one very important regard: because it uses the browser’s Javascript engine, I can use something like GreaseMonkey to alter it’s behaviour. But BeSpin is its own system — I have to learn the BeSpin system in order to do any hacking on it.

What happens if everyone starts thinking this kind of thing is a good idea and developing their own BeSpin-like projects? We’ll have 10000 different websites with 10000 different systems that people have to learn in each new case to do something basic like change their font!

To be fair, you’ve proposed adding a custom DOM within the canvas. This may address some of my concerns, but unless you are proposing to make that custom DOM a wider standard, it really doesn’t address anything. You may as well just dump out plain text, because the tags are meaningless by virtue of having no common interpretation. (Because that’s what meaning is!)

I don’t wish to sound too critical. I realise this is an experiment and I think it’s a great exploration of what’s possible and is a very promising project in itself. But unless you address hackability, then I cannot support the project.

(Aside: I find myself constantly trying to defend web hackability. Whether it be because of Flash, Silverlight or Java, or information hiding and non-mutability in ES4, or security practices that haven’t been subjected to a proper cost/benefit analysis. Where is the FSF when I need them? :) )

We have done something similar Dion where we capture the ctrl + c keydown event on whatever DOM element you want then put whatever text you want to copy into a hidden textarea and focus the textarea with the text selected. The keyup event will cause a native copy to happen and the selected text will then be on the system clipboard. Paste works in a similar way.

@Voracity – good points. I agree that a part of the “open” in the Open Web is being lost here. Being able to poke, prod and tweak a thing in the browser, at runtime is very much a part of what gives the Open Web its value. No black boxes.

But that’s a digression from the subject of this post. The applet seems to be the way to go, if you cant do it all with a hidden field. The applet and js api you wrap around it just need to bridge the gap – put a facade up that smooths away the funkiness you describe here, until such time as sanity is brought to the subject. That would give you your IE compatability too (when you need it).

I know everyone has this knee-jerk reaction when it comes to java applets, but seriously, why not?