[whatwg] Fixing undo on the Web - UndoManager and Transaction

Jonas Sicking jonas at sicking.cc
Fri Aug 5 17:06:38 PDT 2011

On Fri, Aug 5, 2011 at 2:40 PM, Ryosuke Niwa <rniwa at webkit.org> wrote:
> On Fri, Aug 5, 2011 at 1:59 PM, Jonas Sicking <jonas at sicking.cc> wrote:
>> > In the case of collaborative editing apps, reapply is different from
>> > apply because the backend server may have a tree of transaction history and
>> > may need to consult on demand in order to determine exactly what mutations
>> > have to happen.
>> Looking at your examples other emails in this thread I do understand
>> the idea better I think. However, it seems to me that apply is the
>> same as reapply plus some initialization. This leads me to two
>> questions:
> No. As far as I understand it, reapply needs to do more work because the
> undo stack is managed by the server in such cases. The script running on the
> client may not even know what to do.

Can you give an example where this is needed?

>> 2. If we do go with the init/apply/unapply approach, would it make
>> sense to skip init and just ask people to do it manually before
>> creating the transaction? Or at least make init optional?
> As it stands, apply, unapply, and reapply are optional because it's
> desirable to add some transactions that only do work in undo/redo in some
> cases.

But as things stand, if you don't supply a 'apply' function, then
nothing will happen when the transaction is first inserted, and if you
don't supply a 'reapply' function then nothing will happen when a
transaction is "redone". So effectively you'll have to supply both
even if the IDL says they are optional.

Though I guess you can always supply the same function for apply and
reapply, but that means you can't use the convenient inline syntax
that you've used in your examples.

Or does 'reapply' default to the 'apply' function if it's not provided?

>> > Without this constraint, however, the UA is required to keep track of
>> > all DOM mutations happening in the undo scope :(
>> ...but now you lost me :)
>> For the record, my vision was that the editor will keep track of the
>> mutations *it* does to the DOM and inserts managed transactions for
>> those into the UndoManager.
> Did you mean manual transactions?

No. What I mean by "editor" above is the UA code which responds to
keyboard events and modifies the DOM based on those. I.e. the code
implementing @contenteditable.

I hope that makes things more clear.

>> > Given this constraint, the apps that do use managed transactions such as
>> > collaborative editing apps need to replace the managed transactions inserted
>> > by the UA by the corresponding manual transaction when user types text.
>> The vision I had for how this is done is that the editor code should
>> fire events before it makes any modifications to the DOM. The website
>> should be able to cancel this event and instead perform it's own
>> modifications to the DOM.
> Yes, that'll be an option as well (although we need some event like
> beforeEditingAction).

Yes, that's exactly what I was thinking.

I think such an event (or set of events) is needed anyway in order to
let people implement their own tweaks on the @contenteditable
implementation. For example some pages might want the enter key to
interact with bullet lists differently or insert some other element
than a <b> when the user selects some text and presses ctrl-b (my
understanding is that it's a common request to be able to tweak how
@contenteditable reacts to certain editing operations).

Such an event would let them do this.

> But there are cases where the script wants to let the
> user agent modify the DOM, and then learn what it did instead of preventing
> the default action and manually simulating it because the latter involves a
> lot of work.

Can you give examples?

For example in the collaborative editing case I would think that the
page generally wants to get a semantic understanding of the editing
operations so that it can reapply them even if the DOM looks somewhat
different when an action is redone. I.e. even if someone else has
edited the page by the time the action is redone.

>> Until the various editor implementations are there, the website can
>> always undo the editor transaction and then create its own manual
>> transaction and insert in the UndoManager.
> Right, this use case is supported by my proposal.  But the point of
> "replace" is to leave the changes made by the user agent alone but replace
> the entry by your own manual transaction so that undo and redo will work.

*If* this is only needed until @contenteditable implementations
improves by firing events like 'beforeEditingAction', then I would
prefer not to add it.

If it's needed for other reasons too, then of course we should. But
I'd like to understand those other reasons.

>> Cool, that matches my understanding. Is there a reason that you're not
>> allowing manual transactions to be part of a transaction group though?
> I didn't intend to disallow that.  Are there some statements that imply
> this?  I'll definitely want manual transactions to be able to form a
> transaction group.  It defeats the whole point otherwise.

Ah, good, I misunderstood some messages in this thread.

>> I.e. understand that a transaction means "break current
>> text into two paragraphs" rather than "insert new element with name
>> 'p'. Remove text from other element. Add text to newly inserted
>> element".
> I think the command names passed to execCommand is a good starting point.
>  If we define standarized names for user editing actions (I mean I'm sure
> user agents are internally using InsertLineBreak, etc... when user presses
> the enter key), then we can have "name" or "commandName" property on the
> transaction to indicate the semantics.

I don't really have a sense for what events are needed, so I'll leave
that to people that understand editors better than me :)

/ Jonas

More information about the whatwg mailing list