18e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project/*
28e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * Copyright (C) 2005, 2006 Apple Computer, Inc.  All rights reserved.
38e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project *
48e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * Redistribution and use in source and binary forms, with or without
58e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * modification, are permitted provided that the following conditions
68e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * are met:
78e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * 1. Redistributions of source code must retain the above copyright
88e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project *    notice, this list of conditions and the following disclaimer.
98e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * 2. Redistributions in binary form must reproduce the above copyright
108e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project *    notice, this list of conditions and the following disclaimer in the
118e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project *    documentation and/or other materials provided with the distribution.
128e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project *
138e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
148e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
158e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
168e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
178e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
188e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
198e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
208e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
218e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
228e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
238e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
248e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project */
258e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
268e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project#include "config.h"
278e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project#include "InsertLineBreakCommand.h"
288e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
298e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project#include "CSSMutableStyleDeclaration.h"
308e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project#include "Document.h"
318e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project#include "Frame.h"
325f1ab04193ad0130ca8204aadaceae083aca9881Feng Qian#include "HTMLElement.h"
338f72e70a9fd78eec56623b3a62e68f16b7b27e28Feng Qian#include "HTMLNames.h"
348f72e70a9fd78eec56623b3a62e68f16b7b27e28Feng Qian#include "Range.h"
358f72e70a9fd78eec56623b3a62e68f16b7b27e28Feng Qian#include "RenderObject.h"
368e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project#include "Text.h"
378e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project#include "VisiblePosition.h"
388e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project#include "htmlediting.h"
398e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project#include "visible_units.h"
408e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
418e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Projectnamespace WebCore {
428e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
438e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Projectusing namespace HTMLNames;
448e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
458e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source ProjectInsertLineBreakCommand::InsertLineBreakCommand(Document* document)
468e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    : CompositeEditCommand(document)
478e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project{
488e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project}
498e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
508e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Projectbool InsertLineBreakCommand::preservesTypingStyle() const
518e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project{
528e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    return true;
538e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project}
548e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
55635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Projectvoid InsertLineBreakCommand::insertNodeAfterPosition(Node* node, const Position& pos)
568e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project{
578e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    // Insert the BR after the caret position. In the case the
588e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    // position is a block, do an append. We don't want to insert
598e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    // the BR *after* the block.
60635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    Element* cb = pos.node()->enclosingBlockFlowElement();
618e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    if (cb == pos.node())
628e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        appendNode(node, cb);
638e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    else
648e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        insertNodeAfter(node, pos.node());
658e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project}
668e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
67635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Projectvoid InsertLineBreakCommand::insertNodeBeforePosition(Node* node, const Position& pos)
688e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project{
698e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    // Insert the BR after the caret position. In the case the
708e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    // position is a block, do an append. We don't want to insert
718e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    // the BR *before* the block.
72635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    Element* cb = pos.node()->enclosingBlockFlowElement();
738e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    if (cb == pos.node())
748e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        appendNode(node, cb);
758e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    else
768e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        insertNodeBefore(node, pos.node());
778e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project}
788e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
798e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project// Whether we should insert a break element or a '\n'.
808e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Projectbool InsertLineBreakCommand::shouldUseBreakElement(const Position& insertionPos)
818e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project{
828e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    // An editing position like [input, 0] actually refers to the position before
838e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    // the input element, and in that case we need to check the input element's
848e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    // parent's renderer.
858e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    Position p(rangeCompliantEquivalent(insertionPos));
868e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    return p.node()->renderer() && !p.node()->renderer()->style()->preserveNewline();
878e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project}
888e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
898e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Projectvoid InsertLineBreakCommand::doApply()
908e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project{
918e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    deleteSelection();
928f72e70a9fd78eec56623b3a62e68f16b7b27e28Feng Qian    VisibleSelection selection = endingSelection();
938e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    if (selection.isNone())
948e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        return;
958e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
968e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    VisiblePosition caret(selection.visibleStart());
978e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    Position pos(caret.deepEquivalent());
988e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
998e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    pos = positionAvoidingSpecialElementBoundary(pos);
1008e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
1018e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    pos = positionOutsideTabSpan(pos);
1028e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
1038e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    RefPtr<Node> nodeToInsert;
1048e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    if (shouldUseBreakElement(pos))
1058e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        nodeToInsert = createBreakElement(document());
1068e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    else
1078e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        nodeToInsert = document()->createTextNode("\n");
1088e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
1098e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    // FIXME: Need to merge text nodes when inserting just after or before text.
1108e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
1115f1ab04193ad0130ca8204aadaceae083aca9881Feng Qian    if (isEndOfParagraph(caret) && !lineBreakExistsAtVisiblePosition(caret)) {
1128e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        bool needExtraLineBreak = !pos.node()->hasTagName(hrTag) && !pos.node()->hasTagName(tableTag);
1138e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
1148e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        insertNodeAt(nodeToInsert.get(), pos);
1158e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
1168e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        if (needExtraLineBreak)
117635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project            insertNodeBefore(nodeToInsert->cloneNode(false), nodeToInsert);
1188e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
1198e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        VisiblePosition endingPosition(Position(nodeToInsert.get(), 0));
1208f72e70a9fd78eec56623b3a62e68f16b7b27e28Feng Qian        setEndingSelection(VisibleSelection(endingPosition));
1215f1ab04193ad0130ca8204aadaceae083aca9881Feng Qian    } else if (pos.deprecatedEditingOffset() <= caretMinOffset(pos.node())) {
1228e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        insertNodeAt(nodeToInsert.get(), pos);
1238e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
1248e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        // Insert an extra br or '\n' if the just inserted one collapsed.
1258e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        if (!isStartOfParagraph(VisiblePosition(Position(nodeToInsert.get(), 0))))
1268e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project            insertNodeBefore(nodeToInsert->cloneNode(false).get(), nodeToInsert.get());
1278e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
128231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block        setEndingSelection(VisibleSelection(positionInParentAfterNode(nodeToInsert.get()), DOWNSTREAM));
129635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    // If we're inserting after all of the rendered text in a text node, or into a non-text node,
130635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    // a simple insertion is sufficient.
1315f1ab04193ad0130ca8204aadaceae083aca9881Feng Qian    } else if (pos.deprecatedEditingOffset() >= caretMaxOffset(pos.node()) || !pos.node()->isTextNode()) {
1328e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        insertNodeAt(nodeToInsert.get(), pos);
133231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block        setEndingSelection(VisibleSelection(positionInParentAfterNode(nodeToInsert.get()), DOWNSTREAM));
134237faca5ff0377c7fda5c4dcc0f287e67fe66091Steve Block    } else if (pos.node()->isTextNode()) {
1358e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        // Split a text node
136635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project        Text* textNode = static_cast<Text*>(pos.node());
1375f1ab04193ad0130ca8204aadaceae083aca9881Feng Qian        splitTextNode(textNode, pos.deprecatedEditingOffset());
138635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project        insertNodeBefore(nodeToInsert, textNode);
1398e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        Position endingPosition = Position(textNode, 0);
1408e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
1418e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        // Handle whitespace that occurs after the split
1428e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        updateLayout();
1438e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        if (!endingPosition.isRenderedCharacter()) {
144231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block            Position positionBeforeTextNode(positionInParentBeforeNode(textNode));
1458e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project            // Clear out all whitespace and insert one non-breaking space
1468e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project            deleteInsignificantTextDownstream(endingPosition);
1478e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project            ASSERT(!textNode->renderer() || textNode->renderer()->style()->collapseWhiteSpace());
1488e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project            // Deleting insignificant whitespace will remove textNode if it contains nothing but insignificant whitespace.
1498e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project            if (textNode->inDocument())
1508e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project                insertTextIntoNode(textNode, 0, nonBreakingSpaceString());
1518e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project            else {
1528e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project                RefPtr<Text> nbspNode = document()->createTextNode(nonBreakingSpaceString());
1538e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project                insertNodeAt(nbspNode.get(), positionBeforeTextNode);
1548e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project                endingPosition = Position(nbspNode.get(), 0);
1558e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project            }
1568e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        }
1578e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
1588f72e70a9fd78eec56623b3a62e68f16b7b27e28Feng Qian        setEndingSelection(VisibleSelection(endingPosition, DOWNSTREAM));
1598e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    }
1608e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
1618e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    // Handle the case where there is a typing style.
1628e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
1638e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    CSSMutableStyleDeclaration* typingStyle = document()->frame()->typingStyle();
1648e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
1658e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    if (typingStyle && typingStyle->length() > 0) {
1668e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        // Apply the typing style to the inserted line break, so that if the selection
1678e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        // leaves and then comes back, new input will have the right style.
1688e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        // FIXME: We shouldn't always apply the typing style to the line break here,
1698e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        // see <rdar://problem/5794462>.
1708f72e70a9fd78eec56623b3a62e68f16b7b27e28Feng Qian        applyStyle(typingStyle, firstDeepEditingPositionForNode(nodeToInsert.get()), lastDeepEditingPositionForNode(nodeToInsert.get()));
1718e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        // Even though this applyStyle operates on a Range, it still sets an endingSelection().
1728f72e70a9fd78eec56623b3a62e68f16b7b27e28Feng Qian        // It tries to set a VisibleSelection around the content it operated on. So, that VisibleSelection
1738e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        // will either (a) select the line break we inserted, or it will (b) be a caret just
1748e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        // before the line break (if the line break is at the end of a block it isn't selectable).
1758e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        // So, this next call sets the endingSelection() to a caret just after the line break
1768e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        // that we inserted, or just before it if it's at the end of a block.
1778e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        setEndingSelection(endingSelection().visibleEnd());
1788e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    }
1798e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
1808e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    rebalanceWhitespace();
1818e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project}
1828e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
1838e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project}
184