4507c5f1763121c9ec028273f9b40cf00a25c82b
[WebKit-https.git] / WebCore / editing / EditCommand.cpp
1 /*
2  * Copyright (C) 2005, 2006, 2007 Apple, Inc.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #include "config.h"
27 #include "EditCommand.h"
28
29 #include "CompositeEditCommand.h"
30 #include "CSSComputedStyleDeclaration.h"
31 #include "CSSMutableStyleDeclaration.h"
32 #include "DeleteButtonController.h"
33 #include "Document.h"
34 #include "Editor.h"
35 #include "Element.h"
36 #include "EventNames.h"
37 #include "Frame.h"
38 #include "SelectionController.h"
39 #include "VisiblePosition.h"
40 #include "htmlediting.h"
41
42 namespace WebCore {
43
44 EditCommand::EditCommand(Document* document) 
45     : m_document(document)
46     , m_parent(0)
47 {
48     ASSERT(m_document);
49     ASSERT(m_document->frame());
50     DeleteButtonController* deleteButton = m_document->frame()->editor()->deleteButtonController();
51     setStartingSelection(avoidIntersectionWithNode(m_document->frame()->selection()->selection(), deleteButton ? deleteButton->containerElement() : 0));
52     setEndingSelection(m_startingSelection);
53 }
54
55 EditCommand::~EditCommand()
56 {
57 }
58
59 void EditCommand::apply()
60 {
61     ASSERT(m_document);
62     ASSERT(m_document->frame());
63  
64     Frame* frame = m_document->frame();
65     
66     if (!m_parent) {
67         if (!endingSelection().isContentRichlyEditable()) {
68             switch (editingAction()) {
69                 case EditActionTyping:
70                 case EditActionPaste:
71                 case EditActionDrag:
72                 case EditActionSetWritingDirection:
73                 case EditActionCut:
74                 case EditActionUnspecified:
75                     break;
76                 default:
77                     ASSERT_NOT_REACHED();
78                     return;
79             }
80         }
81     }
82     
83     // Changes to the document may have been made since the last editing operation that 
84     // require a layout, as in <rdar://problem/5658603>.  Low level operations, like 
85     // RemoveNodeCommand, don't require a layout because the high level operations that 
86     // use them perform one if one is necessary (like for the creation of VisiblePositions).
87     if (!m_parent)
88         updateLayout();
89
90     DeleteButtonController* deleteButtonController = frame->editor()->deleteButtonController();
91     deleteButtonController->disable();
92     doApply();
93     deleteButtonController->enable();
94
95     if (!m_parent) {
96         updateLayout();
97         // Only need to call appliedEditing for top-level commands, and TypingCommands do it on their
98         // own (see TypingCommand::typingAddedToOpenCommand).
99         if (!isTypingCommand())
100             frame->editor()->appliedEditing(this);
101     }
102 }
103
104 void EditCommand::unapply()
105 {
106     ASSERT(m_document);
107     ASSERT(m_document->frame());
108  
109     Frame* frame = m_document->frame();
110     
111     // Changes to the document may have been made since the last editing operation that 
112     // require a layout, as in <rdar://problem/5658603>.  Low level operations, like 
113     // RemoveNodeCommand, don't require a layout because the high level operations that 
114     // use them perform one if one is necessary (like for the creation of VisiblePositions).
115     if (!m_parent)
116         updateLayout();
117     
118     DeleteButtonController* deleteButtonController = frame->editor()->deleteButtonController();
119     deleteButtonController->disable();
120     doUnapply();
121     deleteButtonController->enable();
122
123     if (!m_parent) {
124         updateLayout();
125         frame->editor()->unappliedEditing(this);
126     }
127 }
128
129 void EditCommand::reapply()
130 {
131     ASSERT(m_document);
132     ASSERT(m_document->frame());
133  
134     Frame* frame = m_document->frame();
135     
136     // Changes to the document may have been made since the last editing operation that 
137     // require a layout, as in <rdar://problem/5658603>.  Low level operations, like 
138     // RemoveNodeCommand, don't require a layout because the high level operations that 
139     // use them perform one if one is necessary (like for the creation of VisiblePositions).
140     if (!m_parent)
141         updateLayout();
142
143     DeleteButtonController* deleteButtonController = frame->editor()->deleteButtonController();
144     deleteButtonController->disable();
145     doReapply();
146     deleteButtonController->enable();
147
148     if (!m_parent) {
149         updateLayout();
150         frame->editor()->reappliedEditing(this);
151     }
152 }
153
154 void EditCommand::doReapply()
155 {
156     doApply();
157 }
158
159 EditAction EditCommand::editingAction() const
160 {
161     return EditActionUnspecified;
162 }
163
164 void EditCommand::setStartingSelection(const VisibleSelection& s)
165 {
166     Element* root = s.rootEditableElement();
167     for (EditCommand* cmd = this; ; cmd = cmd->m_parent) {
168         cmd->m_startingSelection = s;
169         cmd->m_startingRootEditableElement = root;
170         if (!cmd->m_parent || cmd->m_parent->isFirstCommand(cmd))
171             break;
172     }
173 }
174
175 void EditCommand::setEndingSelection(const VisibleSelection &s)
176 {
177     Element* root = s.rootEditableElement();
178     for (EditCommand* cmd = this; cmd; cmd = cmd->m_parent) {
179         cmd->m_endingSelection = s;
180         cmd->m_endingRootEditableElement = root;
181     }
182 }
183
184 bool EditCommand::preservesTypingStyle() const
185 {
186     return false;
187 }
188
189 bool EditCommand::isInsertTextCommand() const
190 {
191     return false;
192 }
193
194 bool EditCommand::isTypingCommand() const
195 {
196     return false;
197 }
198
199 PassRefPtr<CSSMutableStyleDeclaration> EditCommand::styleAtPosition(const Position &pos)
200 {
201     RefPtr<CSSMutableStyleDeclaration> style = positionBeforeTabSpan(pos).computedStyle()->copyInheritableProperties();
202  
203     // FIXME: It seems misleading to also include the typing style when returning the style at some arbitrary
204     // position in the document.
205     CSSMutableStyleDeclaration* typingStyle = document()->frame()->typingStyle();
206     if (typingStyle)
207         style->merge(typingStyle);
208
209     return style.release();
210 }
211
212 void EditCommand::updateLayout() const
213 {
214     document()->updateLayoutIgnorePendingStylesheets();
215 }
216
217 void EditCommand::setParent(CompositeEditCommand* parent)
218 {
219     ASSERT(parent);
220     ASSERT(!m_parent);
221     m_parent = parent;
222     m_startingSelection = parent->m_endingSelection;
223     m_endingSelection = parent->m_endingSelection;
224     m_startingRootEditableElement = parent->m_endingRootEditableElement;
225     m_endingRootEditableElement = parent->m_endingRootEditableElement;
226 }
227
228 void applyCommand(PassRefPtr<EditCommand> command)
229 {
230     command->apply();
231 }
232
233 } // namespace WebCore