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