Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[beforeinput] PreventDefault() on InputType=cut differs from Clipboard API #144

Closed
chong-z opened this issue Aug 16, 2016 · 20 comments
Closed

Comments

@chong-z
Copy link

chong-z commented Aug 16, 2016

If I recall correctly the outcome from the F2F is:

  1. Cut and paste are effectively the same as beforeinput
  2. Canceling 'beforeinput' prevents clipboard update

But according to Clipboard API canceling cut does not prevents clipboard update. Instead it writes user-modified data into clipboard.

To solve the conflicts the possible solutions are (For InputType=cut):

a. Set 'beforeinput'.dataTransfer=NULL, preventing default will prevent everything.
b. 'beforeinput' shares the same DataTransfer object with Clipboard::Cut, and does the same thing
c. Set 'beforeinput'.dataTransfer=Readonly-Data-To-Be-Cut, preventing default will prevent everything.

Personally I'd perfer a) since JS can always get data from selection, and we can add dataTransfer later if it's really useful.

@johanneswilm
Copy link
Contributor

johanneswilm commented Aug 16, 2016

While I can see several solutions working, I think consistency should be important. Without consistency, this will be increasingly difficult to use.

For the case where the JS author really wants to cancel the cut, there needs to be a way to do that. And if we don't want JS authors to intercept keydown combinations, and we cannot pass any argument to preventDefault, it seems like preventDefaulting beforeinput cut has to cancel the clipboard update as well. So A and C seem reasonable from that perspective.

The only thing is that in either case is the problem of then getting the changed content into the clipboard. If I recall correctly, the only way of getting something into the clipboard is by adding it to the DOM, then selecting it, then calling document.execCommand('copy') and removing it from the DOM again (unless one wants it to stay there). Is this still the case? That seems a little bit less than ideal.

@chong-z
Copy link
Author

chong-z commented Aug 16, 2016

While I can see several solutions working, I think consistency should be important. Without consistency, this will be increasingly difficult to use.

Yes, but current Clipboard API has a weird default action, where it's like a switch to choose data source.

...The only thing is that in either case is the problem of then getting the changed content into the clipboard. If I recall correctly, the only way of getting something into the clipboard is by adding it to the DOM, then selecting it, then calling document.execCommand('copy') and removing it from the DOM again (unless one wants it to stay there). Is this still the case? That seems a little bit less than ideal.

I'm not sure if I understand correctly, but:

  1. If JS wants to write back to the clipboard they can listen to Clipboard API - copy and cut.

    document.addEventListener('copy', function(e){
        e.clipboardData.setData('text/plain', 'Hello, world!');
        e.clipboardData.setData('text/html', '<b>Hello, world!</b>');
        e.preventDefault(); // We want our data, not data from any selection, to be written to the clipboard
    });
  2. Otherwise they can use 'beforeinput' to cancel everything and implement it's own single-site clipboard.

@johanneswilm
Copy link
Contributor

While I can see several solutions working, I think consistency should be important. Without consistency, this will be increasingly difficult to use.

Yes, but current Clipboard API has a weird default action, where it's like a switch to choose data source.

Indeed. I was thinking more about internal consistency so that beforeinput behaves as similar as possible independently of the inputtype.

Otherwise they can use 'beforeinput' to cancel everything and implement it's own single-site clipboard.

Only having a single site site clipboard doesn't sound like a good idea. I don't think anyone does that. What some have been doing is create the single site clipboard in addition to putting things into the system wide clipboard.

How would it look if one wants to add things to the global clipboard? Would that be as I outlined above?

Of course, the cut/copy/paste events don't disappear. So one can still use those as an alternative. I am a little concerned that the beforeinput event for cut will be slightly unusable if it's too difficult to add custom content to the clipboard that way.

@johanneswilm
Copy link
Contributor

One example: I create an editor where I added a lot of custom tags and attributes to my content that are there only for internal purposes. Also, I have a datamodel that holds the content of the document I am working on.

The user cuts, and I want to make sure that:

  • The part that was selected is being removed both from the DOM and from the datamodel. The order will need to be: remove from datamodel -> rerender document in the DOM.
  • For the case that the user pastes the cut content somewhere else on the web, I want the global clipboard to contain HTML similar to what was selected, but I want my custom attributes and tags removed from the version that goes into the global clipboard.
  • For the case that the user pastes the cut content back into my webapp, I need to copy the selected text as it is represented in my datamodel internally so that I can use this internal copy which holds additional data beyond what is available in the DOM.

Does that make sense?

I guess I can simply use the cut event for all of that. But what is then the point of the beforeinput event? Or I could use the beforeinput event, but if we choose your model A or C, then I will have a ahrd time getting content into the global clipboard, right?

@chong-z
Copy link
Author

chong-z commented Aug 16, 2016

Yes, choosing A or C requires additional work for global clipboard.

I cannot remember how we solved this issue during F2F, but the outcome is to prevent clipboard update. I just want to confirm is this still what we want?

@johanneswilm
Copy link
Contributor

Yes, choosing A or C requires additional work for global clipboard.

I cannot remember how we solved this issue during F2F, but the outcome is to prevent clipboard update. I just want to confirm is this still what we want?

I think the way we solved it was that we all forgot how difficult it is to add content to the global clipboard, so we didn't think this was an issue. :)

I think I agree with you that it's easier to do A now and then possible switch to C later on.

Additionally, we should file an issue with the Clipboard API (who is maintaining that now?) that there needs to be a added a method to add things to the clipboard without having to rely on execCommand and adding things to the DOM. This has been requested before, and here we have a good use case for this.

@chong-z
Copy link
Author

chong-z commented Aug 17, 2016

After more thinking, I guess we are making 'beforeinput' too complex to include Clipboard.

If we consider Clipboard part of DOM we should also add 'copy', otherwise 'beforeinput' shouldn't care about Clipboard.

Here is what I propose:

  1. 'beforeinput' doesn't care about Clipboard
  2. Rename InputType::cut to InputType::deleteByCut, so it becomes part of the delete series, and the default action is delete selection
  3. Rename InputType::paste to InputType::insertFromPaste or similar
  4. Event order for cut:
    • Default action: Clipboard::cut->'ClipboardUpdate'->beforeinput::deleteByCut->'DomUpdate'->input::deleteByCut
    • Prevent default on Clipboard::cut: Clipboard::cut->'JS handles DOM update'->'Pass custom data'->'ClipboardUpdate'
  5. Similar event order for paste

Use Cases:

  1. If JS really want to prevent Clipboard update, they should just disable cut

    • It's weird to allow deleting text but not writing anything to Clipboard
  2. If JS wants to write custom data to Clipboard, they should use Clipboard API

  3. If JS wants to do custom deletion but doesn't care about Clipboard, they should use 'beforeinput'

    document.addEventListener('beforeinput', e => {
        if (e.inputType.indexOf('delete') == 0) {
            const ranges = e.getTargetRanges() || getSelection();
            strikeThrough(ranges);
            e.preventDefault();
        }
    })

Note that 'use case 3' is hard to do with Clipboard API as you have to manage Clipboard if you call preventDefault().

I'm not sure but does it look useful?

@johanneswilm
Copy link
Contributor

This looks interesting. The only thing is, if I understood you correct yesterday, preventDefaulting cut doesn't really prevent it from adding things to the clipboard, according to the spec. Correct?

@johanneswilm
Copy link
Contributor

johanneswilm commented Aug 17, 2016

Looking at the Clipboard API, this specific usage of preventDefault seems to be true for both copy and cut. So the way to stop things from appearing on the clipboard would be to intercept CTRL+C and CTRL+X? If we require that, we are back to before we had the beforeinput event.

Maybe we should take this in two parts -- first do here what you proposed with beforeinput, and then afterward take a look at the clipboard API and adjust it as well.

As for paste: it seems that the datatransfer data arriving at this stage cannot be changed any longer. So then paste and beforeinput insertFromPaste will be the same, right?

@chong-z
Copy link
Author

chong-z commented Aug 17, 2016

Looking at the Clipboard API, this specific usage of preventDefault seems to be true for both copy and cut. So the way to stop things from appearing on the clipboard would be to intercept CTRL+C and CTRL+V? If we require that, we are back to before we had the beforeinput event.

Actually I was thinking we should use the Opt-in/opt-out to control whether cut is enabled, if it's enabled CTRL+X and CTRL+C should always write something to the Clipboard (the data will be decided by Clipboard API).

To stop things from appearing on the Clipboard JS should disable cut or copy through Opt-in/opt-out.

As for paste: it seems that the datatransfer data arriving at this stage cannot be changed any longer. So then paste and beforeinput insertFromPaste will be the same, right?

Yes, just the order.

@johanneswilm
Copy link
Contributor

Actually I was thinking we should use the Opt-in/opt-out to control whether cut is enabled, if it's enabled CTRL+X and CTRL+C should always write something to the Clipboard (the data will be decided by Clipboard API).

OK, well given that we haven't yet figured out how to define this opt-in/out mechanism, that might work. We need to make sure this is an option then.

I has been thinking we may end up with a simple additional attribute for contenteditable for Safari to provide an editor with the same menu options as all the other browsers (that means: spell checker, clipboard actions, but no formatting options). That seems to be the main and possible currently only real-world usecase for this feature that JS editors have been asking for so far. With the exception of the usecase you just mentioned.

Another issue with using opt-in/out may be that it will be set for the entire editor. What if there is a little note below the editor: "You can copy the text, but you are not allowed to copy the photos." and a JS mechanism which prevents the cut/copy whenever someone tries to copy/cut a selection which includes images? Maybe not a super common usecase.

To stop things from appearing on the Clipboard JS should disable cut or copy through Opt-in/opt-out.

Yes, maybe this could work.

@chong-z
Copy link
Author

chong-z commented Aug 17, 2016

OK, well given that we haven't yet figured out how to define this opt-in/out mechanism, that might work. We need to make sure this is an option then.

I has been thinking we may end up with a simple additional attribute for contenteditable for Safari to provide an editor with the same menu options as all the other browsers (that means: spell checker, clipboard actions, but no formatting options). That seems to be the main and possible currently only real-world usecase for this feature that JS editors have been asking for so far. With the exception of the usecase you just mentioned.

Another issue with using opt-in/out may be that it will be set for the entire editor. What if there is a little note below the editor: "You can copy the text, but you are not allowed to copy the photos." and a JS mechanism which prevents the cut/copy whenever someone tries to copy/cut a selection which includes images? Maybe not a super common usecase.

I see, it seems not to be a good idea to rely on opt-in/out in this stage. Maybe it will be better to do #144 (comment) for now.

Anyways thanks for the input!

@gked
Copy link

gked commented Aug 17, 2016

@johanneswilm:

Additionally, we should file an issue with the Clipboard API (who is maintaining that now?) that there needs to be a added a method to add things to the clipboard without having to rely on execCommand and adding things to the DOM. This has been requested before, and here we have a good use case for this.

Gary and I are taking over Clipboard Spec. Also, could you please clarify your request?

@johanneswilm
Copy link
Contributor

johanneswilm commented Aug 18, 2016

Gary and I are taking over Clipboard Spec.

Great!

Also, could you please clarify your request?

Something like: Create a method whereby one can add arbitrary, valid HTML content to the clipboard within user-initiated JS functions.

This should have the same level of security there currently is, where execCommand('copy') can only be triggered in user-initiated functions, but it should mean that one can replace this procedure:

  1. Add arbitrary content to DOM.
  2. Select arbitrary content.
  3. call execCommand('copy').
  4. Remove arbitrary content from DOM.

With:

  1. Add arbitrary content to clipboard.

@johanneswilm
Copy link
Contributor

I see, it seems not to be a good idea to rely on opt-in/out in this stage. Maybe it will be better to do #144 (comment) for now.

Anyways thanks for the input!

I wasn't trying to kill your idea. It doesn't sound bad, really. Any idea will create some potential issues. I was just trying to find what could be problems with this proposal so that we would know what we need to discuss.

Whether your proposal from yesterday or today is easier to work with really seems to depend on what is possible to do within the area of the clipboard API, etc. . Maybe it would be a point to discuss this with @gked and @garykac at the TPAC editing taskforce meeting.

@ojanvafai
Copy link

I was arguing for having preventDefault stop the clipboard operations at the F2F, but choniong's proposal in #144 (comment) seems like a more elegant solution to me. It gets rid of the expectation that the beforeInput even controls the clipboard access and focuses on just the DOM modification part of it, which seems desirable to me.

I wonder if a similar logic would apply to undo/redo, i.e. make preventDefault not prevent the items from getting popped from the undo stack? The complicated thing there is coming up with the name since undo/redo can both do deletes and insert. undoModification and redoModification maybe? That's wordy, but I think it at least would make it clear that the event is for the DOM modification, not the other parts of the user action.

I think this might also help address gked's concern at the F2F of us pouring too much into this API and other concerns about this becoming some catch-all event.

As to making it easier to putting things on the clipboard, I agree that the best way to handle that is to file an issue on the clipboard API.

We should not ad global opt-in/opt-outs IMO since they kill composability. That's one of the problems with the existing execCommands that change state that we should avoid.

@johanneswilm
Copy link
Contributor

johanneswilm commented Aug 19, 2016

I was arguing for having preventDefault stop the clipboard operations at the F2F, but choniong's proposal in #144 (comment) seems like a more elegant solution to me. It gets rid of the expectation that the beforeInput even controls the clipboard access and focuses on just the DOM modification part of it, which seems desirable to me.

ok. If we can agree on this here, and then take the remaining issues for JS editors in the clipboard API, I think that should work.

I wonder if a similar logic would apply to undo/redo, i.e. make preventDefault not prevent the items from getting popped from the undo stack? The complicated thing there is coming up with the name since undo/redo can both do deletes and insert. undoModification and redoModification maybe? That's wordy, but I think it at least would make it clear that the event is for the DOM modification, not the other parts of the user action.

So far, undo doesn't tell us what it wants to change at all (no target ranges), but that can be changed of course. The issue here is also that if we do any editing at all in JS space, we end up not being able to use the browser's undo stack, right? So the question is then: Under what circumstances will someone create a JS editor that listens to the beforeinput event but never modifies the DOM by means of JS? If it doesn't take a lot of time to implement this undo event in the browser, it may make sense to have for reasons of consistency. If it makes things really complex in the browser, then we should instead find out a way to just disable all undo/redo handling by the browser under certain circumstances.

@garykac
Copy link
Member

garykac commented Aug 19, 2016

Something like: Create a method whereby one can add arbitrary, valid HTML content to the clipboard within user-initiated JS functions.

Have you seen the async clipboard api proposal? I believe it addresses your concerns (but with support for proper permissions rather than assuming that "a user initiated action" is the same as "user grants full permission to access clipboard").

Please add any comments you have to the WICG discourse. Especially if you think something is missing from the proposal.

@johanneswilm
Copy link
Contributor

Have you seen the async clipboard api proposal? I believe it addresses your concerns (but with support for proper permissions rather than assuming that "a user initiated action" is the same as "user grants full permission to access clipboard").

@garykac I had none seen that proposal and it is exactly what I was asking for! The part I proposed about not changing the security model was just so that the discussion about creating such a method being unsafe could be minimized (this had been mentioned when this came up previously). I will leave all further commentary on that on the WICG page.

This takes care of one issue with the clipboard. If we now agree on going with #144 (comment) then there is still one remaining issue with the clipboard: How can the user prevent copying to the clipboard from happening altogether without intercepting keydown/up/press events? @garykac Do you think this is something you guys eventually could figure out to do within the clipboard api?

Unless anyone here disagrees with it, I would suggest I start updating the input-events draft accordingly once the FPWD proposal is through, which should be August 22.

@johanneswilm
Copy link
Contributor

This issue was moved to w3c/input-events#18

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants