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