660381e31754fd9628e329919fb98ce00a62bb66
[WebKit-https.git] / Source / WebCore / editing / htmlediting.cpp
1 /*
2  * Copyright (C) 2004, 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 "htmlediting.h"
28
29 #include "AXObjectCache.h"
30 #include "Document.h"
31 #include "Editor.h"
32 #include "ExceptionCodePlaceholder.h"
33 #include "Frame.h"
34 #include "HTMLBRElement.h"
35 #include "HTMLDivElement.h"
36 #include "HTMLElementFactory.h"
37 #include "HTMLInterchange.h"
38 #include "HTMLLIElement.h"
39 #include "HTMLNames.h"
40 #include "HTMLOListElement.h"
41 #include "HTMLObjectElement.h"
42 #include "HTMLParagraphElement.h"
43 #include "HTMLTableElement.h"
44 #include "HTMLTextFormControlElement.h"
45 #include "HTMLUListElement.h"
46 #include "NodeTraversal.h"
47 #include "PositionIterator.h"
48 #include "Range.h"
49 #include "RenderElement.h"
50 #include "ShadowRoot.h"
51 #include "Text.h"
52 #include "TextIterator.h"
53 #include "VisiblePosition.h"
54 #include "VisibleSelection.h"
55 #include "VisibleUnits.h"
56 #include <wtf/Assertions.h>
57 #include <wtf/StdLibExtras.h>
58 #include <wtf/unicode/CharacterNames.h>
59
60 using namespace std;
61
62 namespace WebCore {
63
64 using namespace HTMLNames;
65
66 // Atomic means that the node has no children, or has children which are ignored for the
67 // purposes of editing.
68 bool isAtomicNode(const Node *node)
69 {
70     return node && (!node->hasChildNodes() || editingIgnoresContent(node));
71 }
72
73 // Compare two positions, taking into account the possibility that one or both
74 // could be inside a shadow tree. Only works for non-null values.
75 int comparePositions(const Position& a, const Position& b)
76 {
77     TreeScope* commonScope = commonTreeScope(a.containerNode(), b.containerNode());
78
79     ASSERT(commonScope);
80     if (!commonScope)
81         return 0;
82
83     Node* nodeA = commonScope->ancestorInThisScope(a.containerNode());
84     ASSERT(nodeA);
85     bool hasDescendentA = nodeA != a.containerNode();
86     int offsetA = hasDescendentA ? 0 : a.computeOffsetInContainerNode();
87
88     Node* nodeB = commonScope->ancestorInThisScope(b.containerNode());
89     ASSERT(nodeB);
90     bool hasDescendentB = nodeB != b.containerNode();
91     int offsetB = hasDescendentB ? 0 : b.computeOffsetInContainerNode();
92
93     int bias = 0;
94     if (nodeA == nodeB) {
95         if (hasDescendentA)
96             bias = -1;
97         else if (hasDescendentB)
98             bias = 1;
99     }
100
101     int result = Range::compareBoundaryPoints(nodeA, offsetA, nodeB, offsetB, IGNORE_EXCEPTION);
102     return result ? result : bias;
103 }
104
105 int comparePositions(const VisiblePosition& a, const VisiblePosition& b)
106 {
107     return comparePositions(a.deepEquivalent(), b.deepEquivalent());
108 }
109
110 Node* highestEditableRoot(const Position& position, EditableType editableType)
111 {
112     Node* node = position.deprecatedNode();
113     if (!node)
114         return 0;
115
116     Node* highestEditableRoot = editableRootForPosition(position, editableType);
117     if (!highestEditableRoot)
118         return 0;
119
120     node = highestEditableRoot;
121     while (!node->hasTagName(bodyTag)) {
122         node = node->parentNode();
123         if (!node)
124             break;
125         if (node->rendererIsEditable(editableType))
126             highestEditableRoot = node;
127     }
128
129     return highestEditableRoot;
130 }
131
132 Node* lowestEditableAncestor(Node* node)
133 {
134     if (!node)
135         return 0;
136     
137     while (node) {
138         if (node->rendererIsEditable())
139             return node->rootEditableElement();
140         if (node->hasTagName(bodyTag))
141             break;
142         node = node->parentNode();
143     }
144     
145     return 0;
146 }
147
148 bool isEditablePosition(const Position& p, EditableType editableType, EUpdateStyle updateStyle)
149 {
150     Node* node = p.deprecatedNode();
151     if (!node)
152         return false;
153     if (updateStyle == UpdateStyle)
154         node->document().updateLayoutIgnorePendingStylesheets();
155     else
156         ASSERT(updateStyle == DoNotUpdateStyle);
157
158     if (node->renderer() && node->renderer()->isTable())
159         node = node->parentNode();
160     
161     return node->rendererIsEditable(editableType);
162 }
163
164 bool isAtUnsplittableElement(const Position& pos)
165 {
166     Node* node = pos.deprecatedNode();
167     return (node == editableRootForPosition(pos) || node == enclosingNodeOfType(pos, &isTableCell));
168 }
169     
170     
171 bool isRichlyEditablePosition(const Position& p, EditableType editableType)
172 {
173     Node* node = p.deprecatedNode();
174     if (!node)
175         return false;
176         
177     if (node->renderer() && node->renderer()->isTable())
178         node = node->parentNode();
179     
180     return node->rendererIsRichlyEditable(editableType);
181 }
182
183 Element* editableRootForPosition(const Position& p, EditableType editableType)
184 {
185     Node* node = p.containerNode();
186     if (!node)
187         return 0;
188         
189     if (node->renderer() && node->renderer()->isTable())
190         node = node->parentNode();
191     
192     return node->rootEditableElement(editableType);
193 }
194
195 // Finds the enclosing element until which the tree can be split.
196 // When a user hits ENTER, he/she won't expect this element to be split into two.
197 // You may pass it as the second argument of splitTreeToNode.
198 Element* unsplittableElementForPosition(const Position& p)
199 {
200     // Since enclosingNodeOfType won't search beyond the highest root editable node,
201     // this code works even if the closest table cell was outside of the root editable node.
202     Element* enclosingCell = toElement(enclosingNodeOfType(p, &isTableCell));
203     if (enclosingCell)
204         return enclosingCell;
205
206     return editableRootForPosition(p);
207 }
208
209 Position nextCandidate(const Position& position)
210 {
211     PositionIterator p = position;
212     while (!p.atEnd()) {
213         p.increment();
214         if (p.isCandidate())
215             return p;
216     }
217     return Position();
218 }
219
220 Position nextVisuallyDistinctCandidate(const Position& position)
221 {
222     Position p = position;
223     Position downstreamStart = p.downstream();
224     while (!p.atEndOfTree()) {
225         p = p.next(Character);
226         if (p.isCandidate() && p.downstream() != downstreamStart)
227             return p;
228     }
229     return Position();
230 }
231
232 Position previousCandidate(const Position& position)
233 {
234     PositionIterator p = position;
235     while (!p.atStart()) {
236         p.decrement();
237         if (p.isCandidate())
238             return p;
239     }
240     return Position();
241 }
242
243 Position previousVisuallyDistinctCandidate(const Position& position)
244 {
245     Position p = position;
246     Position downstreamStart = p.downstream();
247     while (!p.atStartOfTree()) {
248         p = p.previous(Character);
249         if (p.isCandidate() && p.downstream() != downstreamStart)
250             return p;
251     }
252     return Position();
253 }
254
255 VisiblePosition firstEditablePositionAfterPositionInRoot(const Position& position, Node* highestRoot)
256 {
257     // position falls before highestRoot.
258     if (comparePositions(position, firstPositionInNode(highestRoot)) == -1 && highestRoot->rendererIsEditable())
259         return firstPositionInNode(highestRoot);
260
261     Position p = position;
262
263     if (&position.deprecatedNode()->treeScope() != &highestRoot->treeScope()) {
264         Node* shadowAncestor = highestRoot->treeScope().ancestorInThisScope(p.deprecatedNode());
265         if (!shadowAncestor)
266             return VisiblePosition();
267
268         p = positionAfterNode(shadowAncestor);
269     }
270
271     while (p.deprecatedNode() && !isEditablePosition(p) && p.deprecatedNode()->isDescendantOf(highestRoot))
272         p = isAtomicNode(p.deprecatedNode()) ? positionInParentAfterNode(p.deprecatedNode()) : nextVisuallyDistinctCandidate(p);
273     
274     if (p.deprecatedNode() && p.deprecatedNode() != highestRoot && !p.deprecatedNode()->isDescendantOf(highestRoot))
275         return VisiblePosition();
276     
277     return VisiblePosition(p);
278 }
279
280 VisiblePosition lastEditablePositionBeforePositionInRoot(const Position& position, Node* highestRoot)
281 {
282     // When position falls after highestRoot, the result is easy to compute.
283     if (comparePositions(position, lastPositionInNode(highestRoot)) == 1)
284         return lastPositionInNode(highestRoot);
285
286     Position p = position;
287
288     if (&position.deprecatedNode()->treeScope() != &highestRoot->treeScope()) {
289         Node* shadowAncestor = highestRoot->treeScope().ancestorInThisScope(p.deprecatedNode());
290         if (!shadowAncestor)
291             return VisiblePosition();
292
293         p = firstPositionInOrBeforeNode(shadowAncestor);
294     }
295     
296     while (p.deprecatedNode() && !isEditablePosition(p) && p.deprecatedNode()->isDescendantOf(highestRoot))
297         p = isAtomicNode(p.deprecatedNode()) ? positionInParentBeforeNode(p.deprecatedNode()) : previousVisuallyDistinctCandidate(p);
298     
299     if (p.deprecatedNode() && p.deprecatedNode() != highestRoot && !p.deprecatedNode()->isDescendantOf(highestRoot))
300         return VisiblePosition();
301     
302     return VisiblePosition(p);
303 }
304
305 // FIXME: The method name, comment, and code say three different things here!
306 // Whether or not content before and after this node will collapse onto the same line as it.
307 bool isBlock(const Node* node)
308 {
309     return node && node->renderer() && !node->renderer()->isInline() && !node->renderer()->isRubyText();
310 }
311
312 bool isInline(const Node* node)
313 {
314     return node && node->renderer() && node->renderer()->isInline();
315 }
316
317 // FIXME: Deploy this in all of the places where enclosingBlockFlow/enclosingBlockFlowOrTableElement are used.
318 // FIXME: Pass a position to this function. The enclosing block of [table, x] for example, should be the 
319 // block that contains the table and not the table, and this function should be the only one responsible for 
320 // knowing about these kinds of special cases.
321 Element* enclosingBlock(Node* node, EditingBoundaryCrossingRule rule)
322 {
323     Node* enclosingNode = enclosingNodeOfType(firstPositionInOrBeforeNode(node), isBlock, rule);
324     return enclosingNode && enclosingNode->isElementNode() ? toElement(enclosingNode) : 0;
325 }
326
327 TextDirection directionOfEnclosingBlock(const Position& position)
328 {
329     auto block = enclosingBlock(position.containerNode());
330     if (!block)
331         return LTR;
332     auto renderer = block->renderer();
333     if (!renderer)
334         return LTR;
335     return renderer->style()->direction();
336 }
337
338 // This method is used to create positions in the DOM. It returns the maximum valid offset
339 // in a node. It returns 1 for some elements even though they do not have children, which
340 // creates technically invalid DOM Positions. Be sure to call parentAnchoredEquivalent
341 // on a Position before using it to create a DOM Range, or an exception will be thrown.
342 int lastOffsetForEditing(const Node* node)
343 {
344     ASSERT(node);
345     if (!node)
346         return 0;
347     if (node->offsetInCharacters())
348         return node->maxCharacterOffset();
349
350     if (node->hasChildNodes())
351         return node->childNodeCount();
352
353     // NOTE: This should preempt the childNodeCount for, e.g., select nodes
354     if (editingIgnoresContent(node))
355         return 1;
356
357     return 0;
358 }
359
360 String stringWithRebalancedWhitespace(const String& string, bool startIsStartOfParagraph, bool endIsEndOfParagraph)
361 {
362     Vector<UChar> rebalancedString;
363     append(rebalancedString, string);
364
365     bool previousCharacterWasSpace = false;
366     for (size_t i = 0; i < rebalancedString.size(); i++) {
367         if (!isWhitespace(rebalancedString[i])) {
368             previousCharacterWasSpace = false;
369             continue;
370         }
371
372         if (previousCharacterWasSpace || (!i && startIsStartOfParagraph) || (i + 1 == rebalancedString.size() && endIsEndOfParagraph)) {
373             rebalancedString[i] = noBreakSpace;
374             previousCharacterWasSpace = false;
375         } else {
376             rebalancedString[i] = ' ';
377             previousCharacterWasSpace = true;
378         }
379             
380     }
381
382     return String::adopt(rebalancedString);
383 }
384
385 bool isTableStructureNode(const Node *node)
386 {
387     RenderObject* renderer = node->renderer();
388     return (renderer && (renderer->isTableCell() || renderer->isTableRow() || renderer->isTableSection() || renderer->isRenderTableCol()));
389 }
390
391 const String& nonBreakingSpaceString()
392 {
393     DEFINE_STATIC_LOCAL(String, nonBreakingSpaceString, (&noBreakSpace, 1));
394     return nonBreakingSpaceString;
395 }
396
397 // FIXME: need to dump this
398 bool isSpecialElement(const Node *n)
399 {
400     if (!n)
401         return false;
402         
403     if (!n->isHTMLElement())
404         return false;
405
406     if (n->isLink())
407         return true;
408
409     RenderObject* renderer = n->renderer();
410     if (!renderer)
411         return false;
412         
413     if (renderer->style()->display() == TABLE || renderer->style()->display() == INLINE_TABLE)
414         return true;
415
416     if (renderer->style()->isFloating())
417         return true;
418
419     if (renderer->style()->position() != StaticPosition)
420         return true;
421         
422     return false;
423 }
424
425 static Node* firstInSpecialElement(const Position& pos)
426 {
427     Node* rootEditableElement = pos.containerNode()->rootEditableElement();
428     for (Node* n = pos.deprecatedNode(); n && n->rootEditableElement() == rootEditableElement; n = n->parentNode())
429         if (isSpecialElement(n)) {
430             VisiblePosition vPos = VisiblePosition(pos, DOWNSTREAM);
431             VisiblePosition firstInElement = VisiblePosition(firstPositionInOrBeforeNode(n), DOWNSTREAM);
432             if (isTableElement(n) && vPos == firstInElement.next())
433                 return n;
434             if (vPos == firstInElement)
435                 return n;
436         }
437     return 0;
438 }
439
440 static Node* lastInSpecialElement(const Position& pos)
441 {
442     Node* rootEditableElement = pos.containerNode()->rootEditableElement();
443     for (Node* n = pos.deprecatedNode(); n && n->rootEditableElement() == rootEditableElement; n = n->parentNode())
444         if (isSpecialElement(n)) {
445             VisiblePosition vPos = VisiblePosition(pos, DOWNSTREAM);
446             VisiblePosition lastInElement = VisiblePosition(lastPositionInOrAfterNode(n), DOWNSTREAM);
447             if (isTableElement(n) && vPos == lastInElement.previous())
448                 return n;
449             if (vPos == lastInElement)
450                 return n;
451         }
452     return 0;
453 }
454
455 bool isFirstVisiblePositionInSpecialElement(const Position& pos)
456 {
457     return firstInSpecialElement(pos);
458 }
459
460 Position positionBeforeContainingSpecialElement(const Position& pos, Node** containingSpecialElement)
461 {
462     Node* n = firstInSpecialElement(pos);
463     if (!n)
464         return pos;
465     Position result = positionInParentBeforeNode(n);
466     if (result.isNull() || result.deprecatedNode()->rootEditableElement() != pos.deprecatedNode()->rootEditableElement())
467         return pos;
468     if (containingSpecialElement)
469         *containingSpecialElement = n;
470     return result;
471 }
472
473 bool isLastVisiblePositionInSpecialElement(const Position& pos)
474 {
475     return lastInSpecialElement(pos);
476 }
477
478 Position positionAfterContainingSpecialElement(const Position& pos, Node **containingSpecialElement)
479 {
480     Node* n = lastInSpecialElement(pos);
481     if (!n)
482         return pos;
483     Position result = positionInParentAfterNode(n);
484     if (result.isNull() || result.deprecatedNode()->rootEditableElement() != pos.deprecatedNode()->rootEditableElement())
485         return pos;
486     if (containingSpecialElement)
487         *containingSpecialElement = n;
488     return result;
489 }
490
491 Position positionOutsideContainingSpecialElement(const Position &pos, Node **containingSpecialElement)
492 {
493     if (isFirstVisiblePositionInSpecialElement(pos))
494         return positionBeforeContainingSpecialElement(pos, containingSpecialElement);
495     if (isLastVisiblePositionInSpecialElement(pos))
496         return positionAfterContainingSpecialElement(pos, containingSpecialElement);
497     return pos;
498 }
499
500 Node* isFirstPositionAfterTable(const VisiblePosition& visiblePosition)
501 {
502     Position upstream(visiblePosition.deepEquivalent().upstream());
503     if (upstream.deprecatedNode() && upstream.deprecatedNode()->renderer() && upstream.deprecatedNode()->renderer()->isTable() && upstream.atLastEditingPositionForNode())
504         return upstream.deprecatedNode();
505     
506     return 0;
507 }
508
509 Node* isLastPositionBeforeTable(const VisiblePosition& visiblePosition)
510 {
511     Position downstream(visiblePosition.deepEquivalent().downstream());
512     if (downstream.deprecatedNode() && downstream.deprecatedNode()->renderer() && downstream.deprecatedNode()->renderer()->isTable() && downstream.atFirstEditingPositionForNode())
513         return downstream.deprecatedNode();
514     
515     return 0;
516 }
517
518 // Returns the visible position at the beginning of a node
519 VisiblePosition visiblePositionBeforeNode(Node* node)
520 {
521     ASSERT(node);
522     if (node->childNodeCount())
523         return VisiblePosition(firstPositionInOrBeforeNode(node), DOWNSTREAM);
524     ASSERT(node->parentNode());
525     ASSERT(!node->parentNode()->isShadowRoot());
526     return positionInParentBeforeNode(node);
527 }
528
529 // Returns the visible position at the ending of a node
530 VisiblePosition visiblePositionAfterNode(Node* node)
531 {
532     ASSERT(node);
533     if (node->childNodeCount())
534         return VisiblePosition(lastPositionInOrAfterNode(node), DOWNSTREAM);
535     ASSERT(node->parentNode());
536     ASSERT(!node->parentNode()->isShadowRoot());
537     return positionInParentAfterNode(node);
538 }
539
540 // Create a range object with two visible positions, start and end.
541 // create(PassRefPtr<Document>, const Position&, const Position&); will use deprecatedEditingOffset
542 // Use this function instead of create a regular range object (avoiding editing offset).
543 PassRefPtr<Range> createRange(PassRefPtr<Document> document, const VisiblePosition& start, const VisiblePosition& end, ExceptionCode& ec)
544 {
545     ec = 0;
546     RefPtr<Range> selectedRange = Range::create(document);
547     selectedRange->setStart(start.deepEquivalent().containerNode(), start.deepEquivalent().computeOffsetInContainerNode(), ec);
548     if (!ec)
549         selectedRange->setEnd(end.deepEquivalent().containerNode(), end.deepEquivalent().computeOffsetInContainerNode(), ec);
550     return selectedRange.release();
551 }
552
553 // Extend rangeToExtend to include nodes that wraps range and visibly starts and ends inside or at the boudnaries of maximumRange
554 // e.g. if the original range spaned "hello" in <div>hello</div>, then this function extends the range to contain div's around it.
555 // Call this function before copying / moving paragraphs to contain all wrapping nodes.
556 // This function stops extending the range immediately below rootNode; i.e. the extended range can contain a child node of rootNode
557 // but it can never contain rootNode itself.
558 PassRefPtr<Range> extendRangeToWrappingNodes(PassRefPtr<Range> range, const Range* maximumRange, const Node* rootNode)
559 {
560     ASSERT(range);
561     ASSERT(maximumRange);
562
563     Node* ancestor = range->commonAncestorContainer(IGNORE_EXCEPTION); // Find the closest common ancestor.
564     Node* highestNode = 0;
565     // traverse through ancestors as long as they are contained within the range, content-editable, and below rootNode (could be =0).
566     while (ancestor && ancestor->rendererIsEditable() && isNodeVisiblyContainedWithin(ancestor, maximumRange) && ancestor != rootNode) {
567         highestNode = ancestor;
568         ancestor = ancestor->parentNode();
569     }
570
571     if (!highestNode)
572         return range;
573
574     // Create new range with the highest editable node contained within the range
575     RefPtr<Range> extendedRange = Range::create(&range->ownerDocument());
576     extendedRange->selectNode(highestNode, IGNORE_EXCEPTION);
577     return extendedRange.release();
578 }
579
580 bool isListElement(Node *n)
581 {
582     return (n && (n->hasTagName(ulTag) || n->hasTagName(olTag) || n->hasTagName(dlTag)));
583 }
584
585 bool isListItem(const Node *n)
586 {
587     return n && n->renderer() && n->renderer()->isListItem();
588 }
589
590 Node* enclosingNodeWithTag(const Position& p, const QualifiedName& tagName)
591 {
592     if (p.isNull())
593         return 0;
594         
595     Node* root = highestEditableRoot(p);
596     for (Node* n = p.deprecatedNode(); n; n = n->parentNode()) {
597         if (root && !n->rendererIsEditable())
598             continue;
599         if (n->hasTagName(tagName))
600             return n;
601         if (n == root)
602             return 0;
603     }
604     
605     return 0;
606 }
607
608 Node* enclosingNodeOfType(const Position& p, bool (*nodeIsOfType)(const Node*), EditingBoundaryCrossingRule rule)
609 {
610     // FIXME: support CanSkipCrossEditingBoundary
611     ASSERT(rule == CanCrossEditingBoundary || rule == CannotCrossEditingBoundary);
612     if (p.isNull())
613         return 0;
614         
615     Node* root = rule == CannotCrossEditingBoundary ? highestEditableRoot(p) : 0;
616     for (Node* n = p.deprecatedNode(); n; n = n->parentNode()) {
617         // Don't return a non-editable node if the input position was editable, since
618         // the callers from editing will no doubt want to perform editing inside the returned node.
619         if (root && !n->rendererIsEditable())
620             continue;
621         if (nodeIsOfType(n))
622             return n;
623         if (n == root)
624             return 0;
625     }
626     
627     return 0;
628 }
629
630 Node* highestEnclosingNodeOfType(const Position& p, bool (*nodeIsOfType)(const Node*), EditingBoundaryCrossingRule rule, Node* stayWithin)
631 {
632     Node* highest = 0;
633     Node* root = rule == CannotCrossEditingBoundary ? highestEditableRoot(p) : 0;
634     for (Node* n = p.containerNode(); n && n != stayWithin; n = n->parentNode()) {
635         if (root && !n->rendererIsEditable())
636             continue;
637         if (nodeIsOfType(n))
638             highest = n;
639         if (n == root)
640             break;
641     }
642     
643     return highest;
644 }
645
646 static bool hasARenderedDescendant(Node* node, Node* excludedNode)
647 {
648     for (Node* n = node->firstChild(); n;) {
649         if (n == excludedNode) {
650             n = NodeTraversal::nextSkippingChildren(n, node);
651             continue;
652         }
653         if (n->renderer())
654             return true;
655         n = NodeTraversal::next(n, node);
656     }
657     return false;
658 }
659
660 Node* highestNodeToRemoveInPruning(Node* node)
661 {
662     Node* previousNode = 0;
663     Node* rootEditableElement = node ? node->rootEditableElement() : 0;
664     for (; node; node = node->parentNode()) {
665         if (RenderObject* renderer = node->renderer()) {
666             if (!renderer->canHaveChildren() || hasARenderedDescendant(node, previousNode) || rootEditableElement == node)
667                 return previousNode;
668         }
669         previousNode = node;
670     }
671     return 0;
672 }
673
674 Node* enclosingTableCell(const Position& p)
675 {
676     return toElement(enclosingNodeOfType(p, isTableCell));
677 }
678
679 Element* enclosingAnchorElement(const Position& p)
680 {
681     if (p.isNull())
682         return nullptr;
683
684     for (Node* node = p.deprecatedNode(); node; node = node->parentNode()) {
685         if (node->isElementNode() && node->isLink())
686             return toElement(node);
687     }
688     return nullptr;
689 }
690
691 HTMLElement* enclosingList(Node* node)
692 {
693     if (!node)
694         return 0;
695         
696     Node* root = highestEditableRoot(firstPositionInOrBeforeNode(node));
697     
698     for (ContainerNode* n = node->parentNode(); n; n = n->parentNode()) {
699         if (n->hasTagName(ulTag) || n->hasTagName(olTag))
700             return toHTMLElement(n);
701         if (n == root)
702             return 0;
703     }
704     
705     return 0;
706 }
707
708 Node* enclosingListChild(Node *node)
709 {
710     if (!node)
711         return 0;
712     // Check for a list item element, or for a node whose parent is a list element. Such a node
713     // will appear visually as a list item (but without a list marker)
714     Node* root = highestEditableRoot(firstPositionInOrBeforeNode(node));
715     
716     // FIXME: This function is inappropriately named if it starts with node instead of node->parentNode()
717     for (Node* n = node; n && n->parentNode(); n = n->parentNode()) {
718         if (n->hasTagName(liTag) || (isListElement(n->parentNode()) && n != root))
719             return n;
720         if (n == root || isTableCell(n))
721             return 0;
722     }
723     
724     return 0;
725 }
726
727 static HTMLElement* embeddedSublist(Node* listItem)
728 {
729     // Check the DOM so that we'll find collapsed sublists without renderers.
730     for (Node* n = listItem->firstChild(); n; n = n->nextSibling()) {
731         if (isListElement(n))
732             return toHTMLElement(n);
733     }
734     
735     return 0;
736 }
737
738 static Node* appendedSublist(Node* listItem)
739 {
740     // Check the DOM so that we'll find collapsed sublists without renderers.
741     for (Node* n = listItem->nextSibling(); n; n = n->nextSibling()) {
742         if (isListElement(n))
743             return toHTMLElement(n);
744         if (isListItem(listItem))
745             return 0;
746     }
747     
748     return 0;
749 }
750
751 // FIXME: This method should not need to call isStartOfParagraph/isEndOfParagraph
752 Node* enclosingEmptyListItem(const VisiblePosition& visiblePos)
753 {
754     // Check that position is on a line by itself inside a list item
755     Node* listChildNode = enclosingListChild(visiblePos.deepEquivalent().deprecatedNode());
756     if (!listChildNode || !isStartOfParagraph(visiblePos) || !isEndOfParagraph(visiblePos))
757         return 0;
758
759     VisiblePosition firstInListChild(firstPositionInOrBeforeNode(listChildNode));
760     VisiblePosition lastInListChild(lastPositionInOrAfterNode(listChildNode));
761
762     if (firstInListChild != visiblePos || lastInListChild != visiblePos)
763         return 0;
764     
765     if (embeddedSublist(listChildNode) || appendedSublist(listChildNode))
766         return 0;
767         
768     return listChildNode;
769 }
770
771 HTMLElement* outermostEnclosingList(Node* node, Node* rootList)
772 {
773     HTMLElement* list = enclosingList(node);
774     if (!list)
775         return 0;
776
777     while (HTMLElement* nextList = enclosingList(list)) {
778         if (nextList == rootList)
779             break;
780         list = nextList;
781     }
782
783     return list;
784 }
785
786 bool canMergeLists(Element* firstList, Element* secondList)
787 {
788     if (!firstList || !secondList || !firstList->isHTMLElement() || !secondList->isHTMLElement())
789         return false;
790
791     return firstList->hasTagName(secondList->tagQName()) // make sure the list types match (ol vs. ul)
792     && firstList->rendererIsEditable() && secondList->rendererIsEditable() // both lists are editable
793     && firstList->rootEditableElement() == secondList->rootEditableElement() // don't cross editing boundaries
794     && isVisiblyAdjacent(positionInParentAfterNode(firstList), positionInParentBeforeNode(secondList));
795     // Make sure there is no visible content between this li and the previous list
796 }
797
798 Node* highestAncestor(Node* node)
799 {
800     ASSERT(node);
801     Node* parent = node;
802     while ((node = node->parentNode()))
803         parent = node;
804     return parent;
805 }
806
807 static Node* previousNodeConsideringAtomicNodes(const Node* node)
808 {
809     if (node->previousSibling()) {
810         Node* n = node->previousSibling();
811         while (!isAtomicNode(n) && n->lastChild())
812             n = n->lastChild();
813         return n;
814     }
815     if (node->parentNode())
816         return node->parentNode();
817     return 0;
818 }
819
820 static Node* nextNodeConsideringAtomicNodes(const Node* node)
821 {
822     if (!isAtomicNode(node) && node->firstChild())
823         return node->firstChild();
824     if (node->nextSibling())
825         return node->nextSibling();
826     const Node* n = node;
827     while (n && !n->nextSibling())
828         n = n->parentNode();
829     if (n)
830         return n->nextSibling();
831     return 0;
832 }
833
834 Node* previousLeafNode(const Node* node)
835 {
836     Node* n = previousNodeConsideringAtomicNodes(node);
837     while (n) {
838         if (isAtomicNode(n))
839             return n;
840         n = previousNodeConsideringAtomicNodes(n);
841     }
842     return 0;
843 }
844
845 Node* nextLeafNode(const Node* node)
846 {
847     Node* n = nextNodeConsideringAtomicNodes(node);
848     while (n) {
849         if (isAtomicNode(n))
850             return n;
851         n = nextNodeConsideringAtomicNodes(n);
852     }
853     return 0;
854 }
855
856 // FIXME: do not require renderer, so that this can be used within fragments, or rename to isRenderedTable()
857 bool isTableElement(Node* n)
858 {
859     if (!n || !n->isElementNode())
860         return false;
861
862     RenderObject* renderer = n->renderer();
863     return (renderer && (renderer->style()->display() == TABLE || renderer->style()->display() == INLINE_TABLE));
864 }
865
866 bool isTableCell(const Node* node)
867 {
868     RenderObject* r = node->renderer();
869     if (!r)
870         return node->hasTagName(tdTag) || node->hasTagName(thTag);
871     
872     return r->isTableCell();
873 }
874
875 bool isEmptyTableCell(const Node* node)
876 {
877     // Returns true IFF the passed in node is one of:
878     //   .) a table cell with no children,
879     //   .) a table cell with a single BR child, and which has no other child renderers, including :before and :after renderers
880     //   .) the BR child of such a table cell
881
882     // Find rendered node
883     while (node && !node->renderer())
884         node = node->parentNode();
885     if (!node)
886         return false;
887
888     // Make sure the rendered node is a table cell or <br>.
889     // If it's a <br>, then the parent node has to be a table cell.
890     RenderObject* renderer = node->renderer();
891     if (renderer->isBR()) {
892         renderer = renderer->parent();
893         if (!renderer)
894             return false;
895     }
896     if (!renderer->isTableCell())
897         return false;
898
899     // Check that the table cell contains no child renderers except for perhaps a single <br>.
900     RenderObject* childRenderer = toRenderElement(renderer)->firstChild();
901     if (!childRenderer)
902         return true;
903     if (!childRenderer->isBR())
904         return false;
905     return !childRenderer->nextSibling();
906 }
907
908 PassRefPtr<HTMLElement> createDefaultParagraphElement(Document& document)
909 {
910     switch (document.frame()->editor().defaultParagraphSeparator()) {
911     case EditorParagraphSeparatorIsDiv:
912         return HTMLDivElement::create(document);
913     case EditorParagraphSeparatorIsP:
914         return HTMLParagraphElement::create(document);
915     }
916
917     ASSERT_NOT_REACHED();
918     return 0;
919 }
920
921 PassRefPtr<HTMLElement> createBreakElement(Document& document)
922 {
923     return HTMLBRElement::create(document);
924 }
925
926 PassRefPtr<HTMLElement> createOrderedListElement(Document& document)
927 {
928     return HTMLOListElement::create(document);
929 }
930
931 PassRefPtr<HTMLElement> createUnorderedListElement(Document& document)
932 {
933     return HTMLUListElement::create(document);
934 }
935
936 PassRefPtr<HTMLElement> createListItemElement(Document& document)
937 {
938     return HTMLLIElement::create(document);
939 }
940
941 PassRefPtr<HTMLElement> createHTMLElement(Document& document, const QualifiedName& name)
942 {
943     return HTMLElementFactory::createElement(name, document);
944 }
945
946 PassRefPtr<HTMLElement> createHTMLElement(Document& document, const AtomicString& tagName)
947 {
948     return createHTMLElement(document, QualifiedName(nullAtom, tagName, xhtmlNamespaceURI));
949 }
950
951 bool isTabSpanNode(const Node *node)
952 {
953     return node && node->hasTagName(spanTag) && node->isElementNode() && static_cast<const Element *>(node)->getAttribute(classAttr) == AppleTabSpanClass;
954 }
955
956 bool isTabSpanTextNode(const Node *node)
957 {
958     return node && node->isTextNode() && node->parentNode() && isTabSpanNode(node->parentNode());
959 }
960
961 Node* tabSpanNode(const Node *node)
962 {
963     return isTabSpanTextNode(node) ? node->parentNode() : 0;
964 }
965     
966 Position positionOutsideTabSpan(const Position& pos)
967 {
968     Node* node = pos.containerNode();
969     if (isTabSpanTextNode(node))
970         node = tabSpanNode(node);
971     else if (!isTabSpanNode(node))
972         return pos;
973
974     if (node && VisiblePosition(pos) == lastPositionInNode(node))
975         return positionInParentAfterNode(node);
976
977     return positionInParentBeforeNode(node);
978 }
979
980 PassRefPtr<Element> createTabSpanElement(Document& document, PassRefPtr<Node> prpTabTextNode)
981 {
982     RefPtr<Node> tabTextNode = prpTabTextNode;
983
984     // Make the span to hold the tab.
985     RefPtr<Element> spanElement = document.createElement(spanTag, false);
986     spanElement->setAttribute(classAttr, AppleTabSpanClass);
987     spanElement->setAttribute(styleAttr, "white-space:pre");
988
989     // Add tab text to that span.
990     if (!tabTextNode)
991         tabTextNode = document.createEditingTextNode("\t");
992
993     spanElement->appendChild(tabTextNode.release(), ASSERT_NO_EXCEPTION);
994
995     return spanElement.release();
996 }
997
998 PassRefPtr<Element> createTabSpanElement(Document& document, const String& tabText)
999 {
1000     return createTabSpanElement(document, document.createTextNode(tabText));
1001 }
1002
1003 PassRefPtr<Element> createTabSpanElement(Document& document)
1004 {
1005     return createTabSpanElement(document, PassRefPtr<Node>());
1006 }
1007
1008 bool isNodeRendered(const Node* node)
1009 {
1010     if (!node)
1011         return false;
1012
1013     RenderObject* renderer = node->renderer();
1014     if (!renderer)
1015         return false;
1016
1017     return renderer->style()->visibility() == VISIBLE;
1018 }
1019
1020 unsigned numEnclosingMailBlockquotes(const Position& p)
1021 {
1022     unsigned num = 0;
1023     for (Node* n = p.deprecatedNode(); n; n = n->parentNode())
1024         if (isMailBlockquote(n))
1025             num++;
1026     
1027     return num;
1028 }
1029
1030 void updatePositionForNodeRemoval(Position& position, Node* node)
1031 {
1032     if (position.isNull())
1033         return;
1034     switch (position.anchorType()) {
1035     case Position::PositionIsBeforeChildren:
1036         if (position.containerNode() == node)
1037             position = positionInParentBeforeNode(node);
1038         break;
1039     case Position::PositionIsAfterChildren:
1040         if (position.containerNode() == node)
1041             position = positionInParentAfterNode(node);
1042         break;
1043     case Position::PositionIsOffsetInAnchor:
1044         if (position.containerNode() == node->parentNode() && static_cast<unsigned>(position.offsetInContainerNode()) > node->nodeIndex())
1045             position.moveToOffset(position.offsetInContainerNode() - 1);
1046         else if (node->containsIncludingShadowDOM(position.containerNode()))
1047             position = positionInParentBeforeNode(node);
1048         break;
1049     case Position::PositionIsAfterAnchor:
1050         if (node->containsIncludingShadowDOM(position.anchorNode()))
1051             position = positionInParentAfterNode(node);
1052         break;
1053     case Position::PositionIsBeforeAnchor:
1054         if (node->containsIncludingShadowDOM(position.anchorNode()))
1055             position = positionInParentBeforeNode(node);
1056         break;
1057     }
1058 }
1059
1060 bool isMailBlockquote(const Node *node)
1061 {
1062     if (!node || !node->hasTagName(blockquoteTag))
1063         return false;
1064         
1065     return static_cast<const Element *>(node)->getAttribute("type") == "cite";
1066 }
1067
1068 int caretMinOffset(const Node* n)
1069 {
1070     RenderObject* r = n->renderer();
1071     ASSERT(!n->isCharacterDataNode() || !r || r->isText()); // FIXME: This was a runtime check that seemingly couldn't fail; changed it to an assertion for now.
1072     return r ? r->caretMinOffset() : 0;
1073 }
1074
1075 // If a node can contain candidates for VisiblePositions, return the offset of the last candidate, otherwise 
1076 // return the number of children for container nodes and the length for unrendered text nodes.
1077 int caretMaxOffset(const Node* n)
1078 {
1079     // For rendered text nodes, return the last position that a caret could occupy.
1080     if (n->isTextNode() && n->renderer())
1081         return n->renderer()->caretMaxOffset();
1082     // For containers return the number of children. For others do the same as above.
1083     return lastOffsetForEditing(n);
1084 }
1085
1086 bool lineBreakExistsAtVisiblePosition(const VisiblePosition& visiblePosition)
1087 {
1088     return lineBreakExistsAtPosition(visiblePosition.deepEquivalent().downstream());
1089 }
1090
1091 bool lineBreakExistsAtPosition(const Position& position)
1092 {
1093     if (position.isNull())
1094         return false;
1095     
1096     if (position.anchorNode()->hasTagName(brTag) && position.atFirstEditingPositionForNode())
1097         return true;
1098     
1099     if (!position.anchorNode()->renderer())
1100         return false;
1101     
1102     if (!position.anchorNode()->isTextNode() || !position.anchorNode()->renderer()->style()->preserveNewline())
1103         return false;
1104     
1105     Text* textNode = toText(position.anchorNode());
1106     unsigned offset = position.offsetInContainerNode();
1107     return offset < textNode->length() && textNode->data()[offset] == '\n';
1108 }
1109
1110 // Modifies selections that have an end point at the edge of a table
1111 // that contains the other endpoint so that they don't confuse
1112 // code that iterates over selected paragraphs.
1113 VisibleSelection selectionForParagraphIteration(const VisibleSelection& original)
1114 {
1115     VisibleSelection newSelection(original);
1116     VisiblePosition startOfSelection(newSelection.visibleStart());
1117     VisiblePosition endOfSelection(newSelection.visibleEnd());
1118     
1119     // If the end of the selection to modify is just after a table, and
1120     // if the start of the selection is inside that table, then the last paragraph
1121     // that we'll want modify is the last one inside the table, not the table itself
1122     // (a table is itself a paragraph).
1123     if (Node* table = isFirstPositionAfterTable(endOfSelection))
1124         if (startOfSelection.deepEquivalent().deprecatedNode()->isDescendantOf(table))
1125             newSelection = VisibleSelection(startOfSelection, endOfSelection.previous(CannotCrossEditingBoundary));
1126     
1127     // If the start of the selection to modify is just before a table,
1128     // and if the end of the selection is inside that table, then the first paragraph
1129     // we'll want to modify is the first one inside the table, not the paragraph
1130     // containing the table itself.
1131     if (Node* table = isLastPositionBeforeTable(startOfSelection))
1132         if (endOfSelection.deepEquivalent().deprecatedNode()->isDescendantOf(table))
1133             newSelection = VisibleSelection(startOfSelection.next(CannotCrossEditingBoundary), endOfSelection);
1134     
1135     return newSelection;
1136 }
1137
1138 // FIXME: indexForVisiblePosition and visiblePositionForIndex use TextIterators to convert between 
1139 // VisiblePositions and indices. But TextIterator iteration using TextIteratorEmitsCharactersBetweenAllVisiblePositions 
1140 // does not exactly match VisiblePosition iteration, so using them to preserve a selection during an editing 
1141 // opertion is unreliable. TextIterator's TextIteratorEmitsCharactersBetweenAllVisiblePositions mode needs to be fixed, 
1142 // or these functions need to be changed to iterate using actual VisiblePositions.
1143 // FIXME: Deploy these functions everywhere that TextIterators are used to convert between VisiblePositions and indices.
1144 int indexForVisiblePosition(const VisiblePosition& visiblePosition, RefPtr<ContainerNode>& scope)
1145 {
1146     if (visiblePosition.isNull())
1147         return 0;
1148
1149     Position p(visiblePosition.deepEquivalent());
1150     Document& document = p.anchorNode()->document();
1151     ShadowRoot* shadowRoot = p.anchorNode()->containingShadowRoot();
1152
1153     if (shadowRoot)
1154         scope = shadowRoot;
1155     else
1156         scope = document.documentElement();
1157
1158     RefPtr<Range> range = Range::create(&document, firstPositionInNode(scope.get()), p.parentAnchoredEquivalent());
1159
1160     return TextIterator::rangeLength(range.get(), true);
1161 }
1162
1163 // FIXME: Merge these two functions.
1164 int indexForVisiblePosition(Node* node, const VisiblePosition& visiblePosition, bool forSelectionPreservation)
1165 {
1166     ASSERT(node);
1167     RefPtr<Range> range = Range::create(&node->document(), firstPositionInNode(node), visiblePosition.deepEquivalent().parentAnchoredEquivalent());
1168     return TextIterator::rangeLength(range.get(), forSelectionPreservation);
1169 }
1170
1171 VisiblePosition visiblePositionForIndex(int index, ContainerNode* scope)
1172 {
1173     RefPtr<Range> range = TextIterator::rangeFromLocationAndLength(scope, index, 0, true);
1174     // Check for an invalid index. Certain editing operations invalidate indices because 
1175     // of problems with TextIteratorEmitsCharactersBetweenAllVisiblePositions.
1176     if (!range)
1177         return VisiblePosition();
1178     return VisiblePosition(range->startPosition());
1179 }
1180
1181 VisiblePosition visiblePositionForIndexUsingCharacterIterator(Node* node, int index)
1182 {
1183     ASSERT(node);
1184     if (index <= 0)
1185         return VisiblePosition(firstPositionInOrBeforeNode(node), DOWNSTREAM);
1186
1187     RefPtr<Range> range = Range::create(&node->document());
1188     range->selectNodeContents(node, IGNORE_EXCEPTION);
1189     CharacterIterator it(range.get());
1190     it.advance(index - 1);
1191
1192     return VisiblePosition(Position(it.range()->endContainer(), it.range()->endOffset(), Position::PositionIsOffsetInAnchor), UPSTREAM);
1193 }
1194
1195 // Determines whether two positions are visibly next to each other (first then second)
1196 // while ignoring whitespaces and unrendered nodes
1197 bool isVisiblyAdjacent(const Position& first, const Position& second)
1198 {
1199     return VisiblePosition(first) == VisiblePosition(second.upstream());
1200 }
1201
1202 // Determines whether a node is inside a range or visibly starts and ends at the boundaries of the range.
1203 // Call this function to determine whether a node is visibly fit inside selectedRange
1204 bool isNodeVisiblyContainedWithin(Node* node, const Range* selectedRange)
1205 {
1206     ASSERT(node);
1207     ASSERT(selectedRange);
1208     // If the node is inside the range, then it surely is contained within
1209     if (selectedRange->compareNode(node, IGNORE_EXCEPTION) == Range::NODE_INSIDE)
1210         return true;
1211
1212     bool startIsVisuallySame = visiblePositionBeforeNode(node) == selectedRange->startPosition();
1213     if (startIsVisuallySame && comparePositions(positionInParentAfterNode(node), selectedRange->endPosition()) < 0)
1214         return true;
1215
1216     bool endIsVisuallySame = visiblePositionAfterNode(node) == selectedRange->endPosition();
1217     if (endIsVisuallySame && comparePositions(selectedRange->startPosition(), positionInParentBeforeNode(node)) < 0)
1218         return true;
1219
1220     return startIsVisuallySame && endIsVisuallySame;
1221 }
1222
1223 bool isRenderedAsNonInlineTableImageOrHR(const Node* node)
1224 {
1225     if (!node)
1226         return false;
1227     RenderObject* renderer = node->renderer();
1228     return renderer && ((renderer->isTable() && !renderer->isInline()) || (renderer->isImage() && !renderer->isInline()) || renderer->isHR());
1229 }
1230
1231 bool areIdenticalElements(const Node* first, const Node* second)
1232 {
1233     if (!first->isElementNode() || !second->isElementNode())
1234         return false;
1235
1236     const Element* firstElement = toElement(first);
1237     const Element* secondElement = toElement(second);
1238     if (!firstElement->hasTagName(secondElement->tagQName()))
1239         return false;
1240
1241     return firstElement->hasEquivalentAttributes(secondElement);
1242 }
1243
1244 bool isNonTableCellHTMLBlockElement(const Node* node)
1245 {
1246     if (!node->isElementNode())
1247         return false;
1248
1249     const Element* element = toElement(node);
1250     return element->hasTagName(listingTag)
1251         || element->hasTagName(olTag)
1252         || element->hasTagName(preTag)
1253         || isHTMLTableElement(element)
1254         || element->hasTagName(ulTag)
1255         || element->hasTagName(xmpTag)
1256         || element->hasTagName(h1Tag)
1257         || element->hasTagName(h2Tag)
1258         || element->hasTagName(h3Tag)
1259         || element->hasTagName(h4Tag)
1260         || element->hasTagName(h5Tag);
1261 }
1262
1263 Position adjustedSelectionStartForStyleComputation(const VisibleSelection& selection)
1264 {
1265     // This function is used by range style computations to avoid bugs like:
1266     // <rdar://problem/4017641> REGRESSION (Mail): you can only bold/unbold a selection starting from end of line once
1267     // It is important to skip certain irrelevant content at the start of the selection, so we do not wind up 
1268     // with a spurious "mixed" style.
1269
1270     VisiblePosition visiblePosition = selection.start();
1271     if (visiblePosition.isNull())
1272         return Position();
1273
1274     // if the selection is a caret, just return the position, since the style
1275     // behind us is relevant
1276     if (selection.isCaret())
1277         return visiblePosition.deepEquivalent();
1278
1279     // if the selection starts just before a paragraph break, skip over it
1280     if (isEndOfParagraph(visiblePosition))
1281         return visiblePosition.next().deepEquivalent().downstream();
1282
1283     // otherwise, make sure to be at the start of the first selected node,
1284     // instead of possibly at the end of the last node before the selection
1285     return visiblePosition.deepEquivalent().downstream();
1286 }
1287
1288 // FIXME: Should this be deprecated like deprecatedEnclosingBlockFlowElement is?
1289 bool isBlockFlowElement(const Node* node)
1290 {
1291     if (!node->isElementNode())
1292         return false;
1293     RenderObject* renderer = node->renderer();
1294     return renderer && renderer->isRenderBlockFlow();
1295 }
1296
1297 Element* deprecatedEnclosingBlockFlowElement(Node* node)
1298 {
1299     if (!node)
1300         return 0;
1301     if (isBlockFlowElement(node))
1302         return toElement(node);
1303     while ((node = node->parentNode())) {
1304         if (isBlockFlowElement(node) || node->hasTagName(bodyTag))
1305             return toElement(node);
1306     }
1307     return 0;
1308 }
1309
1310 } // namespace WebCore