Changeset 102357 in webkit


Ignore:
Timestamp:
Dec 8, 2011 11:32:17 AM (12 years ago)
Author:
rniwa@webkit.org
Message:

CompositeEditCommand should not be kept alive for undo and redo
https://bugs.webkit.org/show_bug.cgi?id=64414

Reviewed by Enrica Casucci.

This patch introduces EditCommandComposition that replaces CompositeEditCommand for
undo and redo purposes. Furthermore, we now keep a list of commands instead of a tree of commands
to unapply and reapply edit commands that composes an undoable action.

Each top-level CompositeEditCommand holds a ref-pointer to EditCommandComposition,
and applyCommandToComposite adds new SimpleEditCommands to the list.

  • editing/CompositeEditCommand.cpp:

(WebCore::EditCommandComposition::create):
(WebCore::EditCommandComposition::doApply): Never used.
(WebCore::EditCommandComposition::doUnapply):
(WebCore::EditCommandComposition::doReapply):
(WebCore::EditCommandComposition::append):
(WebCore::CompositeEditCommand::~CompositeEditCommand): Add an assertion to ensure we didn't create
a composition for CompositeEditCommands that have parents.
(WebCore::CompositeEditCommand::doUnapply): Never used.
(WebCore::CompositeEditCommand::doReapply): Never used.
(WebCore::CompositeEditCommand::ensureComposition): Creates and attaches a EditCommandComposition.
(WebCore::CompositeEditCommand::applyCommandToComposite): Append a SimpleEditCommand to the composition.

  • editing/CompositeEditCommand.h:

(WebCore::EditCommandComposition::EditCommandComposition):
(WebCore::CompositeEditCommand::composition):
(WebCore::toEditCommandComposition):
(WebCore::toCompositeEditCommand):

  • editing/DeleteButtonController.cpp: Wrap RemoveNodeCommand in RemoveTargetCommand since top level

commands are now required to be a CompositeEditCommand.
(WebCore::RemoveTargetCommand::create):
(WebCore::RemoveTargetCommand::RemoveTargetCommand):
(WebCore::RemoveTargetCommand::doApply):
(WebCore::DeleteButtonController::deleteTarget):

  • editing/EditCommand.cpp:

(WebCore::EditCommand::EditCommand): New constructor; used by EditCommandComposition.
(WebCore::EditCommand::apply): Create a composition for a top-level command.
(WebCore::EditCommand::unapply): Since we clear m_parent of SimpleEditCommand as soon as they are
added to EditCommandComposition, we can't use isTopLevelCommand() to differentiate EditCommandComposition
from SimpleEditCommand. Use isEditCommandComposition() instead.
(WebCore::EditCommand::reapply): Ditto.
(WebCore::compositionIfPossible):
(WebCore::EditCommand::setStartingSelection): Update the starting selection of EditCommandComposition.
(WebCore::EditCommand::setEndingSelection): Ditto.
(WebCore::EditCommand::setParent): Accepts a null pointer in order to avoid keeping a stale pointer in
m_parent inside SimpleEditCommand when CompositeEditCommand goes away.

  • editing/EditCommand.h:

(WebCore::EditCommand::isSimpleEditCommand):
(WebCore::EditCommand::isCompositeEditCommand):
(WebCore::EditCommand::isEditCommandComposition):
(WebCore::EditCommand::parent):
(WebCore::toSimpleEditCommand):

  • editing/Editor.cpp:

(WebCore::Editor::appliedEditing): Register a EditCommandComposition, instead of a CompositeEditCommand
to the undo stack.
(WebCore::Editor::unappliedEditing): Unapplied or reapplied commands are now always EditCommandComposition.
(WebCore::Editor::reappliedEditing):

  • editing/Editor.h:
Location:
trunk/Source/WebCore
Files:
8 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebCore/ChangeLog

    r102356 r102357  
     12011-12-08  Ryosuke Niwa  <rniwa@webkit.org>
     2
     3        CompositeEditCommand should not be kept alive for undo and redo
     4        https://bugs.webkit.org/show_bug.cgi?id=64414
     5
     6        Reviewed by Enrica Casucci.
     7
     8        This patch introduces EditCommandComposition that replaces CompositeEditCommand for
     9        undo and redo purposes. Furthermore, we now keep a list of commands instead of a tree of commands
     10        to unapply and reapply edit commands that composes an undoable action.
     11
     12        Each top-level CompositeEditCommand holds a ref-pointer to EditCommandComposition,
     13        and applyCommandToComposite adds new SimpleEditCommands to the list.
     14
     15        * editing/CompositeEditCommand.cpp:
     16        (WebCore::EditCommandComposition::create):
     17        (WebCore::EditCommandComposition::doApply): Never used.
     18        (WebCore::EditCommandComposition::doUnapply):
     19        (WebCore::EditCommandComposition::doReapply):
     20        (WebCore::EditCommandComposition::append):
     21        (WebCore::CompositeEditCommand::~CompositeEditCommand): Add an assertion to ensure we didn't create
     22        a composition for CompositeEditCommands that have parents.
     23        (WebCore::CompositeEditCommand::doUnapply): Never used.
     24        (WebCore::CompositeEditCommand::doReapply): Never used.
     25        (WebCore::CompositeEditCommand::ensureComposition): Creates and attaches a EditCommandComposition.
     26        (WebCore::CompositeEditCommand::applyCommandToComposite): Append a SimpleEditCommand to the composition.
     27
     28        * editing/CompositeEditCommand.h:
     29        (WebCore::EditCommandComposition::EditCommandComposition):
     30        (WebCore::CompositeEditCommand::composition):
     31        (WebCore::toEditCommandComposition):
     32        (WebCore::toCompositeEditCommand):
     33
     34        * editing/DeleteButtonController.cpp: Wrap RemoveNodeCommand in RemoveTargetCommand since top level
     35        commands are now required to be a CompositeEditCommand.
     36        (WebCore::RemoveTargetCommand::create):
     37        (WebCore::RemoveTargetCommand::RemoveTargetCommand):
     38        (WebCore::RemoveTargetCommand::doApply):
     39        (WebCore::DeleteButtonController::deleteTarget):
     40
     41        * editing/EditCommand.cpp:
     42        (WebCore::EditCommand::EditCommand): New constructor; used by EditCommandComposition.
     43        (WebCore::EditCommand::apply): Create a composition for a top-level command.
     44        (WebCore::EditCommand::unapply): Since we clear m_parent of SimpleEditCommand as soon as they are
     45        added to EditCommandComposition, we can't use isTopLevelCommand() to differentiate EditCommandComposition
     46        from SimpleEditCommand. Use isEditCommandComposition() instead.
     47        (WebCore::EditCommand::reapply): Ditto.
     48        (WebCore::compositionIfPossible):
     49        (WebCore::EditCommand::setStartingSelection): Update the starting selection of EditCommandComposition.
     50        (WebCore::EditCommand::setEndingSelection): Ditto.
     51        (WebCore::EditCommand::setParent): Accepts a null pointer in order to avoid keeping a stale pointer in
     52        m_parent inside SimpleEditCommand when CompositeEditCommand goes away.
     53
     54        * editing/EditCommand.h:
     55        (WebCore::EditCommand::isSimpleEditCommand):
     56        (WebCore::EditCommand::isCompositeEditCommand):
     57        (WebCore::EditCommand::isEditCommandComposition):
     58        (WebCore::EditCommand::parent):
     59        (WebCore::toSimpleEditCommand):
     60
     61        * editing/Editor.cpp:
     62        (WebCore::Editor::appliedEditing): Register a EditCommandComposition, instead of a CompositeEditCommand
     63        to the undo stack.
     64        (WebCore::Editor::unappliedEditing): Unapplied or reapplied commands are now always EditCommandComposition.
     65        (WebCore::Editor::reappliedEditing):
     66        * editing/Editor.h:
     67
    1682011-12-08  Stephen White  <senorblanco@chromium.org>
    269
  • trunk/Source/WebCore/editing/CompositeEditCommand.cpp

    r99594 r102357  
    7171using namespace HTMLNames;
    7272
    73 CompositeEditCommand::CompositeEditCommand(Document *document)
    74     : EditCommand(document)
    75 {
    76 }
    77 
    78 CompositeEditCommand::~CompositeEditCommand()
    79 {
    80 }
    81 
    82 void CompositeEditCommand::doUnapply()
     73PassRefPtr<EditCommandComposition> EditCommandComposition::create(Document* document,
     74    const VisibleSelection& startingSelection, const VisibleSelection endingSelection)
     75{
     76    return adoptRef(new EditCommandComposition(document, startingSelection, endingSelection));
     77}
     78
     79void EditCommandComposition::doApply()
     80{
     81    ASSERT_NOT_REACHED();
     82}
     83
     84void EditCommandComposition::doUnapply()
    8385{
    8486    size_t size = m_commands.size();
     
    8789}
    8890
    89 void CompositeEditCommand::doReapply()
     91void EditCommandComposition::doReapply()
    9092{
    9193    size_t size = m_commands.size();
     
    9496}
    9597
     98void EditCommandComposition::append(SimpleEditCommand* command)
     99{
     100    m_commands.append(command);
     101}
     102
     103CompositeEditCommand::CompositeEditCommand(Document *document)
     104    : EditCommand(document)
     105{
     106}
     107
     108CompositeEditCommand::~CompositeEditCommand()
     109{
     110    ASSERT(isTopLevelCommand() || !m_composition);
     111}
     112
     113void CompositeEditCommand::doUnapply()
     114{
     115    ASSERT_NOT_REACHED();
     116}
     117
     118void CompositeEditCommand::doReapply()
     119{
     120    ASSERT_NOT_REACHED();
     121}
     122
     123EditCommandComposition* CompositeEditCommand::ensureComposition()
     124{
     125    CompositeEditCommand* command = this;
     126    while (command && command->parent())
     127        command = command->parent();
     128    if (!command->m_composition)
     129        command->m_composition = EditCommandComposition::create(document(), startingSelection(), endingSelection());
     130    return command->m_composition.get();
     131}
     132
    96133//
    97134// sugary-sweet convenience functions to help create and apply edit commands in composite commands
    98135//
    99 void CompositeEditCommand::applyCommandToComposite(PassRefPtr<EditCommand> cmd)
    100 {
    101     cmd->setParent(this);
    102     cmd->apply();
    103     m_commands.append(cmd);
    104 }
    105 
    106 void CompositeEditCommand::applyCommandToComposite(PassRefPtr<CompositeEditCommand> command, const VisibleSelection& selection)
    107 {
     136void CompositeEditCommand::applyCommandToComposite(PassRefPtr<EditCommand> prpCommand)
     137{
     138    RefPtr<EditCommand> command = prpCommand;
     139    command->setParent(this);
     140    command->apply();
     141    if (command->isSimpleEditCommand()) {
     142        command->setParent(0);
     143        ensureComposition()->append(toSimpleEditCommand(command.get()));
     144    }
     145    m_commands.append(command.release());
     146}
     147
     148void CompositeEditCommand::applyCommandToComposite(PassRefPtr<CompositeEditCommand> prpCommand, const VisibleSelection& selection)
     149{
     150    RefPtr<CompositeEditCommand> command = prpCommand;
    108151    command->setParent(this);
    109152    if (selection != command->endingSelection()) {
     
    112155    }
    113156    command->apply();
    114     m_commands.append(command);
     157    if (command->isSimpleEditCommand()) {
     158        command->setParent(0);
     159        ensureComposition()->append(toSimpleEditCommand(command.get()));
     160    }
     161    m_commands.append(command.release());
    115162}
    116163
  • trunk/Source/WebCore/editing/CompositeEditCommand.h

    r90933 r102357  
    3838class Text;
    3939
     40class EditCommandComposition : public EditCommand {
     41public:
     42    static PassRefPtr<EditCommandComposition> create(Document*, const VisibleSelection&, const VisibleSelection);
     43
     44    virtual void doApply() OVERRIDE;
     45    virtual void doUnapply() OVERRIDE;
     46    virtual void doReapply() OVERRIDE;
     47    void append(SimpleEditCommand*);
     48
     49private:
     50    EditCommandComposition(Document* document, const VisibleSelection& startingSelection, const VisibleSelection& endingSelection)
     51        : EditCommand(document, startingSelection, endingSelection)
     52    { }
     53    virtual bool isEditCommandComposition() const OVERRIDE { return true; }
     54
     55    Vector<RefPtr<SimpleEditCommand> > m_commands;
     56};
     57
    4058class CompositeEditCommand : public EditCommand {
    4159public:
     
    4361
    4462    bool isFirstCommand(EditCommand* command) { return !m_commands.isEmpty() && m_commands.first() == command; }
     63    EditCommandComposition* composition() { return m_composition.get(); }
     64    EditCommandComposition* ensureComposition();
    4565
    4666protected:
     
    125145    virtual void doUnapply();
    126146    virtual void doReapply();
     147
     148    bool isCompositeEditCommand() const OVERRIDE { return true; }
     149
     150    RefPtr<EditCommandComposition> m_composition;
    127151};
     152
     153inline EditCommandComposition* toEditCommandComposition(EditCommand* command)
     154{
     155    ASSERT(command);
     156    ASSERT(command->isEditCommandComposition());
     157    return static_cast<EditCommandComposition*>(command);
     158}
     159
     160inline CompositeEditCommand* toCompositeEditCommand(EditCommand* command)
     161{
     162    ASSERT(command);
     163    ASSERT(command->isCompositeEditCommand());
     164    return static_cast<CompositeEditCommand*>(command);
     165}
    128166
    129167} // namespace WebCore
  • trunk/Source/WebCore/editing/DeleteButtonController.cpp

    r101707 r102357  
    3232#include "CSSPropertyNames.h"
    3333#include "CSSValueKeywords.h"
     34#include "CompositeEditCommand.h"
    3435#include "DeleteButton.h"
    3536#include "Document.h"
     
    361362}
    362363
     364class RemoveTargetCommand : public CompositeEditCommand {
     365public:
     366    static PassRefPtr<RemoveTargetCommand> create(Document* document, PassRefPtr<Node> target)
     367    {
     368        return adoptRef(new RemoveTargetCommand(document, target));
     369    }
     370
     371private:
     372    RemoveTargetCommand(Document* document, PassRefPtr<Node> target)
     373        : CompositeEditCommand(document)
     374        , m_target(target)
     375    { }
     376
     377    void doApply()
     378    {
     379        removeNode(m_target);
     380    }
     381
     382private:
     383    RefPtr<Node> m_target;
     384};
     385
    363386void DeleteButtonController::deleteTarget()
    364387{
     
    366389        return;
    367390
    368     RefPtr<Node> element = m_target;
    369391    hide();
    370392
     
    372394    // within the target, we unconditionally update the selection to be
    373395    // a caret where the target had been.
    374     Position pos = positionInParentBeforeNode(element.get());
    375     applyCommand(RemoveNodeCommand::create(element.release()));
     396    Position pos = positionInParentBeforeNode(m_target.get());
     397    applyCommand(RemoveTargetCommand::create(m_frame->document(), m_target));
    376398    m_frame->selection()->setSelection(VisiblePosition(pos));
    377399}
  • trunk/Source/WebCore/editing/EditCommand.cpp

    r90933 r102357  
    4141namespace WebCore {
    4242
    43 EditCommand::EditCommand(Document* document) 
     43EditCommand::EditCommand(Document* document)
    4444    : m_document(document)
    4545    , m_parent(0)
     
    5151}
    5252
     53EditCommand::EditCommand(Document* document, const VisibleSelection& startingSelection, const VisibleSelection& endingSelection)
     54    : m_document(document)
     55    , m_parent(0)
     56{
     57    ASSERT(m_document);
     58    ASSERT(m_document->frame());
     59    setStartingSelection(startingSelection);
     60    setEndingSelection(endingSelection);
     61}
     62
    5363EditCommand::~EditCommand()
    5464{
     
    6373   
    6474    if (isTopLevelCommand()) {
     75        ASSERT(isCompositeEditCommand());
    6576        if (!endingSelection().isContentRichlyEditable()) {
    6677            switch (editingAction()) {
     
    7788            }
    7889        }
     90        toCompositeEditCommand(this)->ensureComposition();
    7991    }
    8092   
     
    95107
    96108    if (isTopLevelCommand()) {
     109        ASSERT(isCompositeEditCommand());
    97110        // Only need to call appliedEditing for top-level commands, and TypingCommands do it on their
    98111        // own (see TypingCommand::typingAddedToOpenCommand).
    99112        if (!isTypingCommand())
    100             frame->editor()->appliedEditing(this);
     113            frame->editor()->appliedEditing(toCompositeEditCommand(this));
    101114    }
    102115
     
    123136    deleteButtonController->enable();
    124137
    125     if (isTopLevelCommand())
    126         frame->editor()->unappliedEditing(this);
     138    if (isEditCommandComposition())
     139        frame->editor()->unappliedEditing(toEditCommandComposition(this));
    127140}
    128141
     
    146159    deleteButtonController->enable();
    147160
    148     if (isTopLevelCommand())
    149         frame->editor()->reappliedEditing(this);
     161    if (isEditCommandComposition())
     162        frame->editor()->reappliedEditing(toEditCommandComposition(this));
    150163}
    151164
     
    158171{
    159172    return EditActionUnspecified;
     173}
     174
     175static inline EditCommandComposition* compositionIfPossible(EditCommand* command)
     176{
     177    if (!command->isCompositeEditCommand())
     178        return 0;
     179    return toCompositeEditCommand(command)->composition();
    160180}
    161181
     
    164184    Element* root = s.rootEditableElement();
    165185    for (EditCommand* cmd = this; ; cmd = cmd->m_parent) {
     186        if (EditCommandComposition* composition = compositionIfPossible(cmd)) {
     187            ASSERT(cmd->isTopLevelCommand());
     188            composition->m_startingSelection = s;
     189            composition->m_startingRootEditableElement = root;
     190        }
    166191        cmd->m_startingSelection = s;
    167192        cmd->m_startingRootEditableElement = root;
     
    175200    Element* root = s.rootEditableElement();
    176201    for (EditCommand* cmd = this; cmd; cmd = cmd->m_parent) {
     202        if (EditCommandComposition* composition = compositionIfPossible(cmd)) {
     203            ASSERT(cmd->isTopLevelCommand());
     204            composition->m_endingSelection = s;
     205            composition->m_endingRootEditableElement = root;
     206        }
    177207        cmd->m_endingSelection = s;
    178208        cmd->m_endingRootEditableElement = root;
     
    211241void EditCommand::setParent(CompositeEditCommand* parent)
    212242{
    213     ASSERT(parent);
    214     ASSERT(!m_parent);
     243    ASSERT((parent && !m_parent) || (!parent && m_parent));
     244    ASSERT(!parent || !isCompositeEditCommand() || !toCompositeEditCommand(this)->composition());
    215245    m_parent = parent;
    216     m_startingSelection = parent->m_endingSelection;
    217     m_endingSelection = parent->m_endingSelection;
    218     m_startingRootEditableElement = parent->m_endingRootEditableElement;
    219     m_endingRootEditableElement = parent->m_endingRootEditableElement;
     246    if (parent) {
     247        m_startingSelection = parent->m_endingSelection;
     248        m_endingSelection = parent->m_endingSelection;
     249        m_startingRootEditableElement = parent->m_endingRootEditableElement;
     250        m_endingRootEditableElement = parent->m_endingRootEditableElement;
     251    }
    220252}
    221253
  • trunk/Source/WebCore/editing/EditCommand.h

    r90933 r102357  
    5353    Element* startingRootEditableElement() const { return m_startingRootEditableElement.get(); }
    5454    Element* endingRootEditableElement() const { return m_endingRootEditableElement.get(); }
    55 
     55   
     56    virtual bool isSimpleEditCommand() const { return false; }
     57    virtual bool isCompositeEditCommand() const { return false; }
     58    virtual bool isEditCommandComposition() const { return false; }
    5659    virtual bool isTypingCommand() const;
    5760    virtual bool isCreateLinkCommand() const;
    58    
     61
    5962    virtual bool preservesTypingStyle() const;
    6063
     
    6669protected:
    6770    EditCommand(Document*);
     71    EditCommand(Document*, const VisibleSelection&, const VisibleSelection&);
    6872
    6973    Document* document() const { return m_document.get(); }
     74    CompositeEditCommand* parent() const { return m_parent; }
    7075
    7176    void setStartingSelection(const VisibleSelection&);
     
    9297protected:
    9398    SimpleEditCommand(Document* document) : EditCommand(document) { }
     99private:
     100    virtual bool isSimpleEditCommand() const OVERRIDE { return true; }
    94101};
     102
     103inline SimpleEditCommand* toSimpleEditCommand(EditCommand* command)
     104{
     105    ASSERT(command);
     106    ASSERT(command->isSimpleEditCommand());
     107    return static_cast<SimpleEditCommand*>(command);
     108}
    95109
    96110void applyCommand(PassRefPtr<EditCommand>);
  • trunk/Source/WebCore/editing/Editor.cpp

    r102328 r102357  
    866866}
    867867
    868 void Editor::appliedEditing(PassRefPtr<EditCommand> cmd)
     868void Editor::appliedEditing(PassRefPtr<CompositeEditCommand> cmd)
    869869{
    870870    m_frame->document()->updateLayout();
     
    889889        m_lastEditCommand = cmd;
    890890        if (client())
    891             client()->registerCommandForUndo(m_lastEditCommand);
     891            client()->registerCommandForUndo(toCompositeEditCommand(m_lastEditCommand.get())->ensureComposition());
    892892    }
    893893    respondToChangedContents(newSelection);
    894894}
    895895
    896 void Editor::unappliedEditing(PassRefPtr<EditCommand> cmd)
     896void Editor::unappliedEditing(PassRefPtr<EditCommandComposition> cmd)
    897897{
    898898    m_frame->document()->updateLayout();
     
    910910}
    911911
    912 void Editor::reappliedEditing(PassRefPtr<EditCommand> cmd)
     912void Editor::reappliedEditing(PassRefPtr<EditCommandComposition> cmd)
    913913{
    914914    m_frame->document()->updateLayout();
  • trunk/Source/WebCore/editing/Editor.h

    r102307 r102357  
    4949class CSSStyleDeclaration;
    5050class Clipboard;
    51 class SpellingCorrectionController;
     51class CompositeEditCommand;
    5252class DeleteButtonController;
    5353class EditCommand;
     54class EditCommandComposition;
    5455class EditorClient;
    5556class EditorInternalCommand;
     
    6162class SimpleFontData;
    6263class SpellChecker;
     64class SpellingCorrectionController;
    6365class Text;
    6466class TextCheckerClient;
     67class TextEvent;
    6568struct TextCheckingResult;
    66 class TextEvent;
    6769
    6870struct CompositionUnderline {
     
    165167    void applyParagraphStyleToSelection(CSSStyleDeclaration*, EditAction);
    166168
    167     void appliedEditing(PassRefPtr<EditCommand>);
    168     void unappliedEditing(PassRefPtr<EditCommand>);
    169     void reappliedEditing(PassRefPtr<EditCommand>);
     169    void appliedEditing(PassRefPtr<CompositeEditCommand>);
     170    void unappliedEditing(PassRefPtr<EditCommandComposition>);
     171    void reappliedEditing(PassRefPtr<EditCommandComposition>);
    170172    void unappliedSpellCorrection(const VisibleSelection& selectionOfCorrected, const String& corrected, const String& correction);
    171173
Note: See TracChangeset for help on using the changeset viewer.