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