Home | History | Annotate | Download | only in editing
      1 /*
      2  * Copyright (C) 2004, 2008, 2009, 2010 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 "core/editing/Caret.h"
     28 
     29 #include "core/dom/Document.h"
     30 #include "core/editing/htmlediting.h"
     31 #include "core/page/Frame.h"
     32 #include "core/page/Settings.h"
     33 #include "core/rendering/RenderBlock.h"
     34 #include "core/rendering/RenderView.h"
     35 
     36 namespace WebCore {
     37 
     38 static inline LayoutUnit NoXPosForVerticalArrowNavigation()
     39 {
     40     return LayoutUnit::min();
     41 }
     42 
     43 CaretBase::CaretBase(CaretVisibility visibility)
     44     : m_caretRectNeedsUpdate(true)
     45     , m_caretVisibility(visibility)
     46 {
     47 }
     48 
     49 DragCaretController::DragCaretController()
     50     : CaretBase(Visible)
     51 {
     52 }
     53 
     54 PassOwnPtr<DragCaretController> DragCaretController::create()
     55 {
     56     return adoptPtr(new DragCaretController);
     57 }
     58 
     59 bool DragCaretController::isContentRichlyEditable() const
     60 {
     61     return isRichlyEditablePosition(m_position.deepEquivalent());
     62 }
     63 
     64 void DragCaretController::setCaretPosition(const VisiblePosition& position)
     65 {
     66     if (Node* node = m_position.deepEquivalent().deprecatedNode())
     67         invalidateCaretRect(node);
     68     m_position = position;
     69     setCaretRectNeedsUpdate();
     70     Document* document = 0;
     71     if (Node* node = m_position.deepEquivalent().deprecatedNode()) {
     72         invalidateCaretRect(node);
     73         document = node->document();
     74     }
     75     if (m_position.isNull() || m_position.isOrphan())
     76         clearCaretRect();
     77     else
     78         updateCaretRect(document, m_position);
     79 }
     80 
     81 static bool removingNodeRemovesPosition(Node* node, const Position& position)
     82 {
     83     if (!position.anchorNode())
     84         return false;
     85 
     86     if (position.anchorNode() == node)
     87         return true;
     88 
     89     if (!node->isElementNode())
     90         return false;
     91 
     92     Element* element = toElement(node);
     93     return element->containsIncludingShadowDOM(position.anchorNode());
     94 }
     95 
     96 static void clearRenderViewSelection(const Position& position)
     97 {
     98     RefPtr<Document> document = position.anchorNode()->document();
     99     document->updateStyleIfNeeded();
    100     if (RenderView* view = document->renderView())
    101         view->clearSelection();
    102 }
    103 
    104 void DragCaretController::nodeWillBeRemoved(Node* node)
    105 {
    106     if (!hasCaret() || (node && !node->inDocument()))
    107         return;
    108 
    109     if (!removingNodeRemovesPosition(node, m_position.deepEquivalent()))
    110         return;
    111 
    112     clearRenderViewSelection(m_position.deepEquivalent());
    113     clear();
    114 }
    115 
    116 void CaretBase::clearCaretRect()
    117 {
    118     m_caretLocalRect = LayoutRect();
    119 }
    120 
    121 static inline bool caretRendersInsideNode(Node* node)
    122 {
    123     return node && !isTableElement(node) && !editingIgnoresContent(node);
    124 }
    125 
    126 RenderObject* CaretBase::caretRenderer(Node* node)
    127 {
    128     if (!node)
    129         return 0;
    130 
    131     RenderObject* renderer = node->renderer();
    132     if (!renderer)
    133         return 0;
    134 
    135     // if caretNode is a block and caret is inside it then caret should be painted by that block
    136     bool paintedByBlock = renderer->isBlockFlow() && caretRendersInsideNode(node);
    137     return paintedByBlock ? renderer : renderer->containingBlock();
    138 }
    139 
    140 bool CaretBase::updateCaretRect(Document* document, const VisiblePosition& caretPosition)
    141 {
    142     document->updateStyleIfNeeded();
    143     m_caretLocalRect = LayoutRect();
    144 
    145     m_caretRectNeedsUpdate = false;
    146 
    147     if (caretPosition.isNull())
    148         return false;
    149 
    150     ASSERT(caretPosition.deepEquivalent().deprecatedNode()->renderer());
    151 
    152     // First compute a rect local to the renderer at the selection start.
    153     RenderObject* renderer;
    154     LayoutRect localRect = caretPosition.localCaretRect(renderer);
    155 
    156     // Get the renderer that will be responsible for painting the caret
    157     // (which is either the renderer we just found, or one of its containers).
    158     RenderObject* caretPainter = caretRenderer(caretPosition.deepEquivalent().deprecatedNode());
    159 
    160     // Compute an offset between the renderer and the caretPainter.
    161     bool unrooted = false;
    162     while (renderer != caretPainter) {
    163         RenderObject* containerObject = renderer->container();
    164         if (!containerObject) {
    165             unrooted = true;
    166             break;
    167         }
    168         localRect.move(renderer->offsetFromContainer(containerObject, localRect.location()));
    169         renderer = containerObject;
    170     }
    171 
    172     if (!unrooted)
    173         m_caretLocalRect = localRect;
    174 
    175     return true;
    176 }
    177 
    178 RenderObject* DragCaretController::caretRenderer() const
    179 {
    180     return CaretBase::caretRenderer(m_position.deepEquivalent().deprecatedNode());
    181 }
    182 
    183 IntRect CaretBase::absoluteBoundsForLocalRect(Node* node, const LayoutRect& rect) const
    184 {
    185     RenderObject* caretPainter = caretRenderer(node);
    186     if (!caretPainter)
    187         return IntRect();
    188 
    189     LayoutRect localRect(rect);
    190     if (caretPainter->isBox())
    191         toRenderBox(caretPainter)->flipForWritingMode(localRect);
    192     return caretPainter->localToAbsoluteQuad(FloatRect(localRect)).enclosingBoundingBox();
    193 }
    194 
    195 void CaretBase::repaintCaretForLocalRect(Node* node, const LayoutRect& rect)
    196 {
    197     RenderObject* caretPainter = caretRenderer(node);
    198     if (!caretPainter)
    199         return;
    200 
    201     // FIXME: Need to over-paint 1 pixel to workaround some rounding problems.
    202     // https://bugs.webkit.org/show_bug.cgi?id=108283
    203     LayoutRect inflatedRect = rect;
    204     inflatedRect.inflate(1);
    205 
    206     caretPainter->repaintRectangle(inflatedRect);
    207 }
    208 
    209 bool CaretBase::shouldRepaintCaret(const RenderView* view, bool isContentEditable) const
    210 {
    211     ASSERT(view);
    212     Frame* frame = view->frameView() ? view->frameView()->frame() : 0; // The frame where the selection started.
    213     bool caretBrowsing = frame && frame->settings() && frame->settings()->caretBrowsingEnabled();
    214     return (caretBrowsing || isContentEditable);
    215 }
    216 
    217 void CaretBase::invalidateCaretRect(Node* node, bool caretRectChanged)
    218 {
    219     // EDIT FIXME: This is an unfortunate hack.
    220     // Basically, we can't trust this layout position since we
    221     // can't guarantee that the check to see if we are in unrendered
    222     // content will work at this point. We may have to wait for
    223     // a layout and re-render of the document to happen. So, resetting this
    224     // flag will cause another caret layout to happen the first time
    225     // that we try to paint the caret after this call. That one will work since
    226     // it happens after the document has accounted for any editing
    227     // changes which may have been done.
    228     // And, we need to leave this layout here so the caret moves right
    229     // away after clicking.
    230     m_caretRectNeedsUpdate = true;
    231 
    232     if (caretRectChanged)
    233         return;
    234 
    235     if (RenderView* view = node->document()->renderView()) {
    236         if (shouldRepaintCaret(view, node->isContentEditable(Node::UserSelectAllIsAlwaysNonEditable)))
    237             repaintCaretForLocalRect(node, localCaretRectWithoutUpdate());
    238     }
    239 }
    240 
    241 void CaretBase::paintCaret(Node* node, GraphicsContext* context, const LayoutPoint& paintOffset, const LayoutRect& clipRect) const
    242 {
    243     if (m_caretVisibility == Hidden)
    244         return;
    245 
    246     LayoutRect drawingRect = localCaretRectWithoutUpdate();
    247     RenderObject* renderer = caretRenderer(node);
    248     if (renderer && renderer->isBox())
    249         toRenderBox(renderer)->flipForWritingMode(drawingRect);
    250     drawingRect.moveBy(roundedIntPoint(paintOffset));
    251     LayoutRect caret = intersection(drawingRect, clipRect);
    252     if (caret.isEmpty())
    253         return;
    254 
    255     Color caretColor = Color::black;
    256 
    257     Element* element;
    258     if (node->isElementNode())
    259         element = toElement(node);
    260     else
    261         element = node->parentElement();
    262 
    263     if (element && element->renderer())
    264         caretColor = element->renderer()->resolveColor(CSSPropertyColor);
    265 
    266     context->fillRect(caret, caretColor);
    267 }
    268 
    269 void DragCaretController::paintDragCaret(Frame* frame, GraphicsContext* p, const LayoutPoint& paintOffset, const LayoutRect& clipRect) const
    270 {
    271     if (m_position.deepEquivalent().deprecatedNode()->document()->frame() == frame)
    272         paintCaret(m_position.deepEquivalent().deprecatedNode(), p, paintOffset, clipRect);
    273 }
    274 
    275 }
    276