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