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

Ryosuke Niwa rniwa at webkit.org
Fri Aug 5 14:40:50 PDT 2011


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.

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.

> 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?

The user agents will keep track of changes done to the DOM inside "apply"
method for *managed transactions*.  On the other hand, the user agent won't
keep track of changed for manual transactions.  In a sense, manual
transactions have the same effect as modifying DOM directly (without going
through UndoManager) except it can be hooked up with the user agent's undo
transaction history.

The reason we need a proper sequence of managed
transactions<http://rniwa.com/editing/undomanager.html#proper-sequence-of-managed-transactions>is
because the reference points (e.g. node, offset, etc...) stored by the
user agent for managed transactions to unapply or reapply them will be
stale when scripts directly modifies DOM or apply a manual transaction.

If we were to make this work (i.e. the user agents can always unapply or
reapply managed transactions regardless of what happens to the DOM state),
then the user agents must keep track of every single DOM change and update
the internal representation of the managed transactions as needed.

Any other mutations to the DOM the editor completely ignores. If the
> page makes other mutations to the DOM, it should use the UndoManager
> and create a managed transaction for it. If it doesn't, the page is on
> its own. If the UndoManager is asked to undo or redo a managed
> transaction and the DOM doesn't look the same as it had expected, then
> it should do the best it can. In some cases this means that it won't
> produce useful mutations, but this only happens if the page has done
> BadThings (tm).
>

Right. This is how managed transactions are used.

 > 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).  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.

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.

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.

> I admit this is more of stylistic issue rather than the technical
> limitation but given a transaction already has label and apply "properties",
> it seemed more natural for it to be an object.  That'll allow us to add more
> properties in the future.
> >
> > In particular, I really wanted to provide a mutation list as discussed on
> the mutation events replacement thread on public-webapps.  This allows apps
> to determine which transaction did what without having to listening to each
> mutation event, and makes it easier to replace managed transactions by
> manual transactions as described above.
>
> Hmm.. this is indeed an interesting idea. Though I would imagine that
> it's even more interesting for the page to know the "semantics" of the
> modification rather than knowing what exact DOM modifications are
> performed.


Totally agreed!


> 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.

Note: we still need a separate "label" property in that case because we
can't have "Undo - InsertLineBreak" on the edit menu.

My idea is that the event that editor fires before modifying the DOM
> would essentially describe "inserting paragraph break at cursor".


Right. If you look at my document, I have a place holder for this feature in
section 5 titled "Edit action event".  I'd love to add this feature since
almost all developers I've talked to told me they want it.

- Ryosuke



More information about the whatwg mailing list