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