0f6df5d430b116fbf3c5d563a7fb5d526e7ed21a
[WebKit-https.git] / WebCore / khtml / editing / htmlediting.cpp
1 /*
2  * Copyright (C) 2004 Apple Computer, 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 "htmlediting.h"
27
28 #include "css_computedstyle.h"
29 #include "css_value.h"
30 #include "css_valueimpl.h"
31 #include "cssparser.h"
32 #include "cssproperties.h"
33 #include "dom_docimpl.h"
34 #include "dom_elementimpl.h"
35 #include "dom_nodeimpl.h"
36 #include "dom_position.h"
37 #include "dom_stringimpl.h"
38 #include "dom_textimpl.h"
39 #include "dom2_range.h"
40 #include "dom2_rangeimpl.h"
41 #include "html_elementimpl.h"
42 #include "html_imageimpl.h"
43 #include "html_interchange.h"
44 #include "htmlnames.h"
45 #include "khtml_part.h"
46 #include "khtml_part.h"
47 #include "khtmlview.h"
48 #include "qcolor.h"
49 #include "qptrlist.h"
50 #include "render_object.h"
51 #include "render_style.h"
52 #include "render_text.h"
53 #include "visible_position.h"
54 #include "visible_text.h"
55 #include "visible_units.h"
56
57 using namespace DOM::HTMLNames;
58
59 using DOM::AttrImpl;
60 using DOM::CSSComputedStyleDeclarationImpl;
61 using DOM::CSSMutableStyleDeclarationImpl;
62 using DOM::CSSParser;
63 using DOM::CSSPrimitiveValue;
64 using DOM::CSSPrimitiveValueImpl;
65 using DOM::CSSProperty;
66 using DOM::CSSStyleDeclarationImpl;
67 using DOM::CSSValue;
68 using DOM::CSSValueImpl;
69 using DOM::DocumentFragmentImpl;
70 using DOM::DocumentImpl;
71 using DOM::DOMString;
72 using DOM::DOMStringImpl;
73 using DOM::DoNotUpdateLayout;
74 using DOM::EditingTextImpl;
75 using DOM::ElementImpl;
76 using DOM::HTMLElementImpl;
77 using DOM::HTMLImageElementImpl;
78 using DOM::NamedAttrMapImpl;
79 using DOM::NodeImpl;
80 using DOM::NodeListImpl;
81 using DOM::Position;
82 using DOM::RangeImpl;
83 using DOM::TextImpl;
84 using DOM::TreeWalkerImpl;
85
86 #if APPLE_CHANGES
87 #include <kxmlcore/Assertions.h>
88 #include "KWQLogging.h"
89 #include "KWQKHTMLPart.h"
90 #else
91 #define ASSERT(assertion) ((void)0)
92 #define ASSERT_WITH_MESSAGE(assertion, formatAndArgs...) ((void)0)
93 #define ASSERT_NOT_REACHED() ((void)0)
94 #define LOG(channel, formatAndArgs...) ((void)0)
95 #define ERROR(formatAndArgs...) ((void)0)
96 #define ASSERT(assertion) assert(assertion)
97 #endif
98
99 namespace khtml {
100
101 bool isTableStructureNode(const NodeImpl *node)
102 {
103     RenderObject *r = node->renderer();
104     return (r && (r->isTableCell() || r->isTableRow() || r->isTableSection() || r->isTableCol()));
105 }
106
107 DOMString &nonBreakingSpaceString()
108 {
109     static DOMString nonBreakingSpaceString = QString(QChar(NON_BREAKING_SPACE));
110     return nonBreakingSpaceString;
111 }
112
113 void derefNodesInList(QPtrList<NodeImpl> &list)
114 {
115     for (QPtrListIterator<NodeImpl> it(list); it.current(); ++it)
116         it.current()->deref();
117 }
118
119 static int maxRangeOffset(NodeImpl *n)
120 {
121     if (DOM::offsetInCharacters(n->nodeType()))
122         return n->maxOffset();
123
124     if (n->isElementNode())
125         return n->childNodeCount();
126
127     return 1;
128 }
129
130 bool isSpecialElement(const NodeImpl *n)
131 {
132     if (!n)
133         return false;
134         
135     if (!n->isHTMLElement())
136         return false;
137
138     if (n->isLink())
139         return true;
140
141     if (n->hasTagName(ulTag) || n->hasTagName(olTag) || n->hasTagName(dlTag))
142         return true;
143
144     RenderObject *renderer = n->renderer();
145     if (!renderer)
146         return false;
147         
148     if (renderer->style()->display() == TABLE || renderer->style()->display() == INLINE_TABLE)
149         return true;
150
151     if (renderer->style()->isFloating())
152         return true;
153
154     if (renderer->style()->position() != STATIC)
155         return true;
156         
157     return false;
158 }
159
160 bool isFirstVisiblePositionInSpecialElement(const Position& pos)
161 {
162     VisiblePosition vPos = VisiblePosition(pos, DOWNSTREAM);
163
164     for (NodeImpl *n = pos.node(); n; n = n->parentNode()) {
165         if (VisiblePosition(n, 0, DOWNSTREAM) != vPos)
166             return false;
167         if (n->rootEditableElement() == NULL)
168             return false;
169         if (isSpecialElement(n))
170             return true;
171     }
172
173     return false;
174 }
175
176 static Position positionBeforeNode(NodeImpl *node)
177 {
178     return Position(node->parentNode(), node->nodeIndex());
179 }
180
181 Position positionBeforeContainingSpecialElement(const Position& pos)
182 {
183     ASSERT(isFirstVisiblePositionInSpecialElement(pos));
184
185     VisiblePosition vPos = VisiblePosition(pos, DOWNSTREAM);
186     
187     NodeImpl *outermostSpecialElement = NULL;
188
189     for (NodeImpl *n = pos.node(); n; n = n->parentNode()) {
190         if (VisiblePosition(n, 0, DOWNSTREAM) != vPos)
191             break;
192         if (n->rootEditableElement() == NULL)
193             break;
194         if (isSpecialElement(n))
195             outermostSpecialElement = n;
196     }
197     
198     ASSERT(outermostSpecialElement);
199
200     Position result = positionBeforeNode(outermostSpecialElement);
201     if (result.isNull() || !result.node()->rootEditableElement())
202         return pos;
203     
204     return result;
205 }
206
207 bool isLastVisiblePositionInSpecialElement(const Position& pos)
208 {
209     // make sure to get a range-compliant version of the position
210     Position rangePos = VisiblePosition(pos, DOWNSTREAM).position();
211
212     VisiblePosition vPos = VisiblePosition(rangePos, DOWNSTREAM);
213
214     for (NodeImpl *n = rangePos.node(); n; n = n->parentNode()) {
215         if (VisiblePosition(n, maxRangeOffset(n), DOWNSTREAM) != vPos)
216             return false;
217         if (n->rootEditableElement() == NULL)
218             return false;
219         if (isSpecialElement(n))
220             return true;
221     }
222
223     return false;
224 }
225
226 static Position positionAfterNode(NodeImpl *node)
227 {
228     return Position(node->parentNode(), node->nodeIndex() + 1);
229 }
230
231 Position positionAfterContainingSpecialElement(const Position& pos)
232 {
233     ASSERT(isLastVisiblePositionInSpecialElement(pos));
234
235     // make sure to get a range-compliant version of the position
236     Position rangePos = VisiblePosition(pos, DOWNSTREAM).position();
237
238     VisiblePosition vPos = VisiblePosition(rangePos, DOWNSTREAM);
239
240     NodeImpl *outermostSpecialElement = NULL;
241
242     for (NodeImpl *n = rangePos.node(); n; n = n->parentNode()) {
243         if (VisiblePosition(n, maxRangeOffset(n), DOWNSTREAM) != vPos)
244             break;
245         if (n->rootEditableElement() == NULL)
246             break;
247         if (isSpecialElement(n))
248             outermostSpecialElement = n;
249     }
250     
251     ASSERT(outermostSpecialElement);
252
253     Position result = positionAfterNode(outermostSpecialElement);
254     if (result.isNull() || !result.node()->rootEditableElement())
255         return pos;
256     
257     return result;
258 }
259
260 Position positionOutsideContainingSpecialElement(const Position &pos)
261 {
262     if (isFirstVisiblePositionInSpecialElement(pos)) {
263         return positionBeforeContainingSpecialElement(pos);
264     } else if (isLastVisiblePositionInSpecialElement(pos)) {
265         return positionAfterContainingSpecialElement(pos);
266     }
267
268     return pos;
269 }
270
271 ElementImpl *createDefaultParagraphElement(DocumentImpl *document)
272 {
273     // We would need this margin-zeroing code back if we ever return to using <p> elements for default paragraphs.
274     // static const DOMString defaultParagraphStyle("margin-top: 0; margin-bottom: 0");    
275     int exceptionCode = 0;
276     ElementImpl *element = document->createElementNS(xhtmlNamespaceURI, "div", exceptionCode);
277     ASSERT(exceptionCode == 0);
278     return element;
279 }
280
281 ElementImpl *createBreakElement(DocumentImpl *document)
282 {
283     int exceptionCode = 0;
284     ElementImpl *breakNode = document->createElementNS(xhtmlNamespaceURI, "br", exceptionCode);
285     ASSERT(exceptionCode == 0);
286     return breakNode;
287 }
288
289 bool isTabSpanNode(const NodeImpl *node)
290 {
291     return (node && node->isElementNode() && static_cast<const ElementImpl *>(node)->getAttribute("class") == AppleTabSpanClass);
292 }
293
294 bool isTabSpanTextNode(const NodeImpl *node)
295 {
296     return (node && node->parentNode() && isTabSpanNode(node->parentNode()));
297 }
298
299 Position positionBeforeTabSpan(const Position& pos)
300 {
301     NodeImpl *node = pos.node();
302     if (isTabSpanTextNode(node))
303         node = node->parent();
304     else if (!isTabSpanNode(node))
305         return pos;
306     
307     return Position(node->parentNode(), node->nodeIndex());
308 }
309
310 ElementImpl *createTabSpanElement(DocumentImpl *document, NodeImpl *tabTextNode)
311 {
312     // make the span to hold the tab
313     int exceptionCode = 0;
314     ElementImpl *spanElement = document->createElementNS(xhtmlNamespaceURI, "span", exceptionCode);
315     assert(exceptionCode == 0);
316     spanElement->setAttribute(classAttr, AppleTabSpanClass);
317     spanElement->setAttribute(styleAttr, "white-space:pre");
318
319     // add tab text to that span
320     if (!tabTextNode)
321         tabTextNode = document->createEditingTextNode("\t");
322     spanElement->appendChild(tabTextNode, exceptionCode);
323     assert(exceptionCode == 0);
324
325     return spanElement;
326 }
327
328 ElementImpl *createTabSpanElement(DocumentImpl *document, QString *tabText)
329 {
330     return createTabSpanElement(document, document->createTextNode(*tabText));
331 }
332
333 bool isNodeRendered(const NodeImpl *node)
334 {
335     if (!node)
336         return false;
337
338     RenderObject *renderer = node->renderer();
339     if (!renderer)
340         return false;
341
342     return renderer->style()->visibility() == VISIBLE;
343 }
344
345 NodeImpl *nearestMailBlockquote(const NodeImpl *node)
346 {
347     for (NodeImpl *n = const_cast<NodeImpl *>(node); n; n = n->parentNode()) {
348         if (isMailBlockquote(n))
349             return n;
350     }
351     return 0;
352 }
353
354 bool isMailBlockquote(const NodeImpl *node)
355 {
356     if (!node || !node->renderer() || !node->isElementNode() && !node->hasTagName(blockquoteTag))
357         return false;
358         
359     return static_cast<const ElementImpl *>(node)->getAttribute("type") == "cite";
360 }
361
362 } // namespace khtml