| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (C) 2004, 2008, 2009, 2010 Apple Inc. All rights reserved. | 2 * Copyright (C) 2004, 2008, 2009, 2010 Apple Inc. All rights reserved. |
| 3 * | 3 * |
| 4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
| 5 * modification, are permitted provided that the following conditions | 5 * modification, are permitted provided that the following conditions |
| 6 * are met: | 6 * are met: |
| 7 * 1. Redistributions of source code must retain the above copyright | 7 * 1. Redistributions of source code must retain the above copyright |
| 8 * notice, this list of conditions and the following disclaimer. | 8 * notice, this list of conditions and the following disclaimer. |
| 9 * 2. Redistributions in binary form must reproduce the above copyright | 9 * 2. Redistributions in binary form must reproduce the above copyright |
| 10 * notice, this list of conditions and the following disclaimer in the | 10 * notice, this list of conditions and the following disclaimer in the |
| 11 * documentation and/or other materials provided with the distribution. | 11 * documentation and/or other materials provided with the distribution. |
| 12 * | 12 * |
| 13 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY | 13 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY |
| 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR | 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR |
| 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| 20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | 20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
| 21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 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. | 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 24 */ | 24 */ |
| 25 | 25 |
| 26 #include "config.h" | 26 #include "config.h" |
| 27 #include "core/editing/Caret.h" | 27 #include "core/editing/DragCaretController.h" |
| 28 | 28 |
| 29 #include "core/dom/Document.h" | |
| 30 #include "core/editing/VisibleUnits.h" | |
| 31 #include "core/editing/htmlediting.h" | 29 #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/layout/LayoutBlock.h" | |
| 36 #include "core/layout/LayoutView.h" | 30 #include "core/layout/LayoutView.h" |
| 37 #include "core/paint/DeprecatedPaintLayer.h" | 31 #include "core/paint/DeprecatedPaintLayer.h" |
| 38 #include "platform/graphics/GraphicsContext.h" | |
| 39 | 32 |
| 40 namespace blink { | 33 namespace blink { |
| 41 | 34 |
| 42 CaretBase::CaretBase(CaretVisibility visibility) | |
| 43 : m_caretPainter(nullptr) | |
| 44 , m_caretVisibility(visibility) | |
| 45 { | |
| 46 } | |
| 47 | |
| 48 DragCaretController::DragCaretController() | 35 DragCaretController::DragCaretController() |
| 49 : CaretBase(Visible) | 36 : CaretBase(Visible) |
| 50 { | 37 { |
| 51 } | 38 } |
| 52 | 39 |
| 53 PassOwnPtrWillBeRawPtr<DragCaretController> DragCaretController::create() | 40 PassOwnPtrWillBeRawPtr<DragCaretController> DragCaretController::create() |
| 54 { | 41 { |
| 55 return adoptPtrWillBeNoop(new DragCaretController); | 42 return adoptPtrWillBeNoop(new DragCaretController); |
| 56 } | 43 } |
| 57 | 44 |
| 58 bool DragCaretController::isContentRichlyEditable() const | 45 bool DragCaretController::isContentRichlyEditable() const |
| 59 { | 46 { |
| 60 return isRichlyEditablePosition(m_position.deepEquivalent()); | 47 return isRichlyEditablePosition(m_position.deepEquivalent()); |
| 61 } | 48 } |
| 62 | 49 |
| 63 void DragCaretController::setCaretPosition(const VisiblePosition& position) | 50 void DragCaretController::setCaretPosition(const VisiblePosition& position) |
| 64 { | 51 { |
| 65 // for querying Layer::compositingState() | 52 // for querying Layer::compositingState() |
| 66 // This code is probably correct, since it doesn't occur in a stack that inv
olves updating compositing state. | 53 // This code is probably correct, since it doesn't occur in a stack that |
| 54 // involves updating compositing state. |
| 67 DisableCompositingQueryAsserts disabler; | 55 DisableCompositingQueryAsserts disabler; |
| 68 | 56 |
| 69 if (Node* node = m_position.deepEquivalent().deprecatedNode()) | 57 if (Node* node = m_position.deepEquivalent().deprecatedNode()) |
| 70 invalidateCaretRect(node); | 58 invalidateCaretRect(node); |
| 71 m_position = position; | 59 m_position = position; |
| 72 Document* document = nullptr; | 60 Document* document = nullptr; |
| 73 if (Node* node = m_position.deepEquivalent().deprecatedNode()) { | 61 if (Node* node = m_position.deepEquivalent().deprecatedNode()) { |
| 74 invalidateCaretRect(node); | 62 invalidateCaretRect(node); |
| 75 document = &node->document(); | 63 document = &node->document(); |
| 76 } | 64 } |
| (...skipping 30 matching lines...) Expand all Loading... |
| 107 | 95 |
| 108 m_position.deepEquivalent().document()->layoutView()->clearSelection(); | 96 m_position.deepEquivalent().document()->layoutView()->clearSelection(); |
| 109 clear(); | 97 clear(); |
| 110 } | 98 } |
| 111 | 99 |
| 112 DEFINE_TRACE(DragCaretController) | 100 DEFINE_TRACE(DragCaretController) |
| 113 { | 101 { |
| 114 visitor->trace(m_position); | 102 visitor->trace(m_position); |
| 115 } | 103 } |
| 116 | 104 |
| 117 void CaretBase::clearCaretRect() | |
| 118 { | |
| 119 m_caretPainter = nullptr; | |
| 120 m_caretLocalRect = LayoutRect(); | |
| 121 } | |
| 122 | |
| 123 static inline bool caretRendersInsideNode(Node* node) | |
| 124 { | |
| 125 return node && !isRenderedTableElement(node) && !editingIgnoresContent(node)
; | |
| 126 } | |
| 127 | |
| 128 LayoutBlock* CaretBase::caretLayoutObject(Node* node) | |
| 129 { | |
| 130 if (!node) | |
| 131 return 0; | |
| 132 | |
| 133 LayoutObject* layoutObject = node->layoutObject(); | |
| 134 if (!layoutObject) | |
| 135 return 0; | |
| 136 | |
| 137 // if caretNode is a block and caret is inside it then caret should be paint
ed by that block | |
| 138 bool paintedByBlock = layoutObject->isLayoutBlock() && caretRendersInsideNod
e(node); | |
| 139 return paintedByBlock ? toLayoutBlock(layoutObject) : layoutObject->containi
ngBlock(); | |
| 140 } | |
| 141 | |
| 142 static void mapCaretRectToCaretPainter(LayoutObject* caretLayoutObject, LayoutBl
ock* 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 caretLayoutObject and the caretPainter. | |
| 147 | |
| 148 ASSERT(caretLayoutObject->isDescendantOf(caretPainter)); | |
| 149 | |
| 150 bool unrooted = false; | |
| 151 while (caretLayoutObject != caretPainter) { | |
| 152 LayoutObject* containerObject = caretLayoutObject->container(); | |
| 153 if (!containerObject) { | |
| 154 unrooted = true; | |
| 155 break; | |
| 156 } | |
| 157 caretRect.move(caretLayoutObject->offsetFromContainer(containerObject, c
aretRect.location())); | |
| 158 caretLayoutObject = containerObject; | |
| 159 } | |
| 160 | |
| 161 if (unrooted) | |
| 162 caretRect = LayoutRect(); | |
| 163 } | |
| 164 | |
| 165 bool CaretBase::updateCaretRect(Document* document, const PositionWithAffinity&
caretPosition) | |
| 166 { | |
| 167 m_caretPainter = nullptr; | |
| 168 m_caretLocalRect = LayoutRect(); | |
| 169 | |
| 170 if (caretPosition.position().isNull()) | |
| 171 return false; | |
| 172 | |
| 173 ASSERT(caretPosition.position().deprecatedNode()->layoutObject()); | |
| 174 | |
| 175 // First compute a rect local to the layoutObject at the selection start. | |
| 176 LayoutObject* layoutObject; | |
| 177 m_caretLocalRect = localCaretRectOfPosition(caretPosition, layoutObject); | |
| 178 | |
| 179 // Get the layoutObject that will be responsible for painting the caret | |
| 180 // (which is either the layoutObject we just found, or one of its containers
). | |
| 181 m_caretPainter = caretLayoutObject(caretPosition.position().deprecatedNode()
); | |
| 182 | |
| 183 mapCaretRectToCaretPainter(layoutObject, m_caretPainter, m_caretLocalRect); | |
| 184 | |
| 185 return true; | |
| 186 } | |
| 187 | |
| 188 bool CaretBase::updateCaretRect(Document* document, const VisiblePosition& caret
Position) | |
| 189 { | |
| 190 return updateCaretRect(document, PositionWithAffinity(caretPosition.deepEqui
valent(), caretPosition.affinity())); | |
| 191 } | |
| 192 | |
| 193 IntRect CaretBase::absoluteBoundsForLocalRect(Node* node, const LayoutRect& rect
) const | |
| 194 { | |
| 195 LayoutBlock* caretPainter = caretLayoutObject(node); | |
| 196 if (!caretPainter) | |
| 197 return IntRect(); | |
| 198 | |
| 199 LayoutRect localRect(rect); | |
| 200 caretPainter->flipForWritingMode(localRect); | |
| 201 return caretPainter->localToAbsoluteQuad(FloatRect(localRect)).enclosingBoun
dingBox(); | |
| 202 } | |
| 203 | |
| 204 void CaretBase::invalidateLocalCaretRect(Node* node, const LayoutRect& rect) | |
| 205 { | |
| 206 LayoutBlock* caretPainter = caretLayoutObject(node); | |
| 207 if (!caretPainter) | |
| 208 return; | |
| 209 | |
| 210 // FIXME: Need to over-paint 1 pixel to workaround some rounding problems. | |
| 211 // https://bugs.webkit.org/show_bug.cgi?id=108283 | |
| 212 LayoutRect inflatedRect = rect; | |
| 213 inflatedRect.inflate(1); | |
| 214 | |
| 215 // FIXME: We should use mapLocalToContainer() since we know we're not un-roo
ted. | |
| 216 mapCaretRectToCaretPainter(node->layoutObject(), caretPainter, inflatedRect)
; | |
| 217 | |
| 218 // FIXME: We should not allow paint invalidation out of paint invalidation s
tate. crbug.com/457415 | |
| 219 DisablePaintInvalidationStateAsserts disabler; | |
| 220 caretPainter->invalidatePaintRectangle(inflatedRect); | |
| 221 } | |
| 222 | |
| 223 bool CaretBase::shouldRepaintCaret(Node& node) const | |
| 224 { | |
| 225 // If PositionAnchorType::BeforeAnchor or PositionAnchorType::AfterAnchor, | |
| 226 // carets need to be repainted not only when the node is contentEditable but | |
| 227 // also when its parentNode() is contentEditable. | |
| 228 return node.isContentEditable() || (node.parentNode() && node.parentNode()->
isContentEditable()); | |
| 229 } | |
| 230 | |
| 231 bool CaretBase::shouldRepaintCaret(const LayoutView* view) const | |
| 232 { | |
| 233 ASSERT(view); | |
| 234 if (FrameView* frameView = view->frameView()) { | |
| 235 LocalFrame& frame = frameView->frame(); // The frame where the selection
started | |
| 236 return frame.settings() && frame.settings()->caretBrowsingEnabled(); | |
| 237 } | |
| 238 return false; | |
| 239 } | |
| 240 | |
| 241 void CaretBase::invalidateCaretRect(Node* node, bool caretRectChanged) | |
| 242 { | |
| 243 if (caretRectChanged) | |
| 244 return; | |
| 245 | |
| 246 if (LayoutView* view = node->document().layoutView()) { | |
| 247 if (node->isContentEditable(Node::UserSelectAllIsAlwaysNonEditable) || s
houldRepaintCaret(view)) | |
| 248 invalidateLocalCaretRect(node, localCaretRectWithoutUpdate()); | |
| 249 } | |
| 250 } | |
| 251 | |
| 252 void CaretBase::paintCaret(Node* node, GraphicsContext* context, const LayoutPoi
nt& paintOffset, const LayoutRect& clipRect) const | |
| 253 { | |
| 254 if (m_caretVisibility == Hidden) | |
| 255 return; | |
| 256 | |
| 257 LayoutRect drawingRect = localCaretRectWithoutUpdate(); | |
| 258 if (LayoutBlock* layoutObject = caretLayoutObject(node)) | |
| 259 layoutObject->flipForWritingMode(drawingRect); | |
| 260 drawingRect.moveBy(roundedIntPoint(paintOffset)); | |
| 261 LayoutRect caret = intersection(drawingRect, clipRect); | |
| 262 if (caret.isEmpty()) | |
| 263 return; | |
| 264 | |
| 265 Color caretColor = Color::black; | |
| 266 | |
| 267 Element* element; | |
| 268 if (node->isElementNode()) | |
| 269 element = toElement(node); | |
| 270 else | |
| 271 element = node->parentElement(); | |
| 272 | |
| 273 if (element && element->layoutObject()) | |
| 274 caretColor = element->layoutObject()->resolveColor(CSSPropertyColor); | |
| 275 | |
| 276 context->fillRect(caret, caretColor); | |
| 277 } | |
| 278 | |
| 279 void DragCaretController::paintDragCaret(LocalFrame* frame, GraphicsContext* p,
const LayoutPoint& paintOffset, const LayoutRect& clipRect) const | 105 void DragCaretController::paintDragCaret(LocalFrame* frame, GraphicsContext* p,
const LayoutPoint& paintOffset, const LayoutRect& clipRect) const |
| 280 { | 106 { |
| 281 if (m_position.deepEquivalent().deprecatedNode()->document().frame() == fram
e) | 107 if (m_position.deepEquivalent().deprecatedNode()->document().frame() == fram
e) |
| 282 paintCaret(m_position.deepEquivalent().deprecatedNode(), p, paintOffset,
clipRect); | 108 paintCaret(m_position.deepEquivalent().deprecatedNode(), p, paintOffset,
clipRect); |
| 283 } | 109 } |
| 284 | 110 |
| 285 } | 111 } // namespace blink |
| OLD | NEW |