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 |