OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 2006, 2007, 2008, 2011 Apple Inc. All rights reserved. | 2 * Copyright (C) 2006, 2007, 2008, 2011 Apple Inc. All rights reserved. |
3 * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) | 3 * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) |
4 * | 4 * |
5 * Redistribution and use in source and binary forms, with or without | 5 * Redistribution and use in source and binary forms, with or without |
6 * modification, are permitted provided that the following conditions | 6 * modification, are permitted provided that the following conditions |
7 * are met: | 7 * are met: |
8 * 1. Redistributions of source code must retain the above copyright | 8 * 1. Redistributions of source code must retain the above copyright |
9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
10 * 2. Redistributions in binary form must reproduce the above copyright | 10 * 2. Redistributions in binary form must reproduce the above copyright |
(...skipping 11 matching lines...) Expand all Loading... |
22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
25 */ | 25 */ |
26 | 26 |
27 #include "config.h" | 27 #include "config.h" |
28 #include "core/editing/InputMethodController.h" | 28 #include "core/editing/InputMethodController.h" |
29 | 29 |
30 #include "core/dom/Document.h" | 30 #include "core/dom/Document.h" |
31 #include "core/dom/Element.h" | 31 #include "core/dom/Element.h" |
| 32 #include "core/dom/Range.h" |
32 #include "core/dom/Text.h" | 33 #include "core/dom/Text.h" |
33 #include "core/editing/EditingUtilities.h" | |
34 #include "core/editing/Editor.h" | 34 #include "core/editing/Editor.h" |
35 #include "core/editing/commands/TypingCommand.h" | 35 #include "core/editing/commands/TypingCommand.h" |
36 #include "core/editing/markers/DocumentMarkerController.h" | |
37 #include "core/events/CompositionEvent.h" | 36 #include "core/events/CompositionEvent.h" |
38 #include "core/frame/LocalFrame.h" | 37 #include "core/frame/LocalFrame.h" |
39 #include "core/html/HTMLTextAreaElement.h" | 38 #include "core/html/HTMLTextAreaElement.h" |
40 #include "core/input/EventHandler.h" | 39 #include "core/input/EventHandler.h" |
41 #include "core/layout/LayoutObject.h" | 40 #include "core/layout/LayoutObject.h" |
42 #include "core/layout/LayoutTheme.h" | |
43 #include "core/page/ChromeClient.h" | 41 #include "core/page/ChromeClient.h" |
44 | 42 |
45 namespace blink { | 43 namespace blink { |
46 | 44 |
47 InputMethodController::SelectionOffsetsScope::SelectionOffsetsScope(InputMethodC
ontroller* inputMethodController) | 45 InputMethodController::SelectionOffsetsScope::SelectionOffsetsScope(InputMethodC
ontroller* inputMethodController) |
48 : m_inputMethodController(inputMethodController) | 46 : m_inputMethodController(inputMethodController) |
49 , m_offsets(inputMethodController->getSelectionOffsets()) | 47 , m_offsets(inputMethodController->getSelectionOffsets()) |
50 { | 48 { |
51 } | 49 } |
52 | 50 |
53 InputMethodController::SelectionOffsetsScope::~SelectionOffsetsScope() | 51 InputMethodController::SelectionOffsetsScope::~SelectionOffsetsScope() |
54 { | 52 { |
55 m_inputMethodController->setSelectionOffsets(m_offsets); | 53 m_inputMethodController->setSelectionOffsets(m_offsets); |
56 } | 54 } |
57 | 55 |
58 // ---------------------------- | 56 // ---------------------------- |
59 | 57 |
60 PassOwnPtrWillBeRawPtr<InputMethodController> InputMethodController::create(Loca
lFrame& frame) | 58 PassOwnPtrWillBeRawPtr<InputMethodController> InputMethodController::create(Loca
lFrame& frame) |
61 { | 59 { |
62 return adoptPtrWillBeNoop(new InputMethodController(frame)); | 60 return adoptPtrWillBeNoop(new InputMethodController(frame)); |
63 } | 61 } |
64 | 62 |
65 InputMethodController::InputMethodController(LocalFrame& frame) | 63 InputMethodController::InputMethodController(LocalFrame& frame) |
66 : m_frame(&frame) | 64 : m_frame(&frame) |
67 , m_isDirty(false) | 65 , m_compositionStart(0) |
68 , m_hasComposition(false) | 66 , m_compositionEnd(0) |
69 { | 67 { |
70 } | 68 } |
71 | 69 |
72 InputMethodController::~InputMethodController() | 70 InputMethodController::~InputMethodController() |
73 { | 71 { |
74 } | 72 } |
75 | 73 |
76 bool InputMethodController::hasComposition() const | 74 bool InputMethodController::hasComposition() const |
77 { | 75 { |
78 return m_hasComposition; | 76 return m_compositionNode && m_compositionNode->isContentEditable(); |
79 } | 77 } |
80 | 78 |
81 inline Editor& InputMethodController::editor() const | 79 inline Editor& InputMethodController::editor() const |
82 { | 80 { |
83 return frame().editor(); | 81 return frame().editor(); |
84 } | 82 } |
85 | 83 |
86 void InputMethodController::clear() | 84 void InputMethodController::clear() |
87 { | 85 { |
88 m_hasComposition = false; | 86 m_compositionNode = nullptr; |
89 if (m_compositionRange) { | 87 m_customCompositionUnderlines.clear(); |
90 m_compositionRange->setStart(frame().document(), 0); | |
91 m_compositionRange->collapse(true); | |
92 } | |
93 frame().document()->markers().removeMarkers(DocumentMarker::Composition); | |
94 m_isDirty = false; | |
95 } | 88 } |
96 | 89 |
97 bool InputMethodController::insertTextForConfirmedComposition(const String& text
) | 90 bool InputMethodController::insertTextForConfirmedComposition(const String& text
) |
98 { | 91 { |
99 return frame().eventHandler().handleTextInputEvent(text, 0, TextEventInputCo
mposition); | 92 return frame().eventHandler().handleTextInputEvent(text, 0, TextEventInputCo
mposition); |
100 } | 93 } |
101 | 94 |
102 void InputMethodController::selectComposition() const | 95 void InputMethodController::selectComposition() const |
103 { | 96 { |
104 const EphemeralRange range = compositionEphemeralRange(); | 97 const EphemeralRange range = compositionEphemeralRange(); |
105 if (range.isNull()) | 98 if (range.isNull()) |
106 return; | 99 return; |
107 | 100 |
108 // The composition can start inside a composed character sequence, so we hav
e to override checks. | 101 // The composition can start inside a composed character sequence, so we hav
e to override checks. |
109 // See <http://bugs.webkit.org/show_bug.cgi?id=15781> | 102 // See <http://bugs.webkit.org/show_bug.cgi?id=15781> |
110 VisibleSelection selection; | 103 VisibleSelection selection; |
111 selection.setWithoutValidation(range.startPosition(), range.endPosition()); | 104 selection.setWithoutValidation(range.startPosition(), range.endPosition()); |
112 frame().selection().setSelection(selection, 0); | 105 frame().selection().setSelection(selection, 0); |
113 } | 106 } |
114 | 107 |
115 bool InputMethodController::confirmComposition() | 108 bool InputMethodController::confirmComposition() |
116 { | 109 { |
117 if (!hasComposition()) | 110 if (!hasComposition()) |
118 return false; | 111 return false; |
119 return confirmComposition(plainText(compositionEphemeralRange())); | 112 return finishComposition(m_compositionNode->data().substring(m_compositionSt
art, m_compositionEnd - m_compositionStart), ConfirmComposition); |
120 } | 113 } |
121 | 114 |
122 bool InputMethodController::confirmComposition(const String& text) | 115 bool InputMethodController::confirmComposition(const String& text) |
123 { | 116 { |
124 return finishComposition(text, ConfirmComposition); | 117 return finishComposition(text, ConfirmComposition); |
125 } | 118 } |
126 | 119 |
127 bool InputMethodController::confirmCompositionOrInsertText(const String& text, C
onfirmCompositionBehavior confirmBehavior) | 120 bool InputMethodController::confirmCompositionOrInsertText(const String& text, C
onfirmCompositionBehavior confirmBehavior) |
128 { | 121 { |
129 if (!hasComposition()) { | 122 if (!hasComposition()) { |
(...skipping 19 matching lines...) Expand all Loading... |
149 { | 142 { |
150 finishComposition(emptyString(), CancelComposition); | 143 finishComposition(emptyString(), CancelComposition); |
151 } | 144 } |
152 | 145 |
153 void InputMethodController::cancelCompositionIfSelectionIsInvalid() | 146 void InputMethodController::cancelCompositionIfSelectionIsInvalid() |
154 { | 147 { |
155 if (!hasComposition() || editor().preventRevealSelection()) | 148 if (!hasComposition() || editor().preventRevealSelection()) |
156 return; | 149 return; |
157 | 150 |
158 // Check if selection start and selection end are valid. | 151 // Check if selection start and selection end are valid. |
159 FrameSelection& selection = frame().selection(); | 152 Position start = frame().selection().start(); |
160 if (!selection.isNone() && !m_compositionRange->collapsed()) { | 153 Position end = frame().selection().end(); |
161 Position start = selection.start(); | 154 if (start.computeContainerNode() == m_compositionNode |
162 Position end = selection.end(); | 155 && end.computeContainerNode() == m_compositionNode |
163 if (selection.start().compareTo(m_compositionRange->startPosition()) >=
0 | 156 && static_cast<unsigned>(start.computeOffsetInContainerNode()) >= m_comp
ositionStart |
164 && selection.end().compareTo(m_compositionRange->endPosition()) <= 0
) | 157 && static_cast<unsigned>(end.computeOffsetInContainerNode()) <= m_compos
itionEnd) |
165 return; | 158 return; |
166 } | |
167 | 159 |
168 cancelComposition(); | 160 cancelComposition(); |
169 frame().chromeClient().didCancelCompositionOnSelectionChange(); | 161 frame().chromeClient().didCancelCompositionOnSelectionChange(); |
170 } | 162 } |
171 | 163 |
172 bool InputMethodController::finishComposition(const String& text, FinishComposit
ionMode mode) | 164 bool InputMethodController::finishComposition(const String& text, FinishComposit
ionMode mode) |
173 { | 165 { |
174 if (!hasComposition()) | 166 if (!hasComposition()) |
175 return false; | 167 return false; |
176 | 168 |
(...skipping 10 matching lines...) Expand all Loading... |
187 return false; | 179 return false; |
188 | 180 |
189 // Dispatch a compositionend event to the focused node. | 181 // Dispatch a compositionend event to the focused node. |
190 // We should send this event before sending a TextEvent as written in Sectio
n 6.2.2 and 6.2.3 of | 182 // We should send this event before sending a TextEvent as written in Sectio
n 6.2.2 and 6.2.3 of |
191 // the DOM Event specification. | 183 // the DOM Event specification. |
192 if (Element* target = frame().document()->focusedElement()) { | 184 if (Element* target = frame().document()->focusedElement()) { |
193 RefPtrWillBeRawPtr<CompositionEvent> event = CompositionEvent::create(Ev
entTypeNames::compositionend, frame().domWindow(), text); | 185 RefPtrWillBeRawPtr<CompositionEvent> event = CompositionEvent::create(Ev
entTypeNames::compositionend, frame().domWindow(), text); |
194 target->dispatchEvent(event); | 186 target->dispatchEvent(event); |
195 } | 187 } |
196 | 188 |
197 bool dirty = m_isDirty || plainText(compositionEphemeralRange()) != text; | |
198 | |
199 // If text is empty, then delete the old composition here. If text is non-em
pty, InsertTextCommand::input | 189 // If text is empty, then delete the old composition here. If text is non-em
pty, InsertTextCommand::input |
200 // will delete the old composition with an optimized replace operation. | 190 // will delete the old composition with an optimized replace operation. |
201 if (text.isEmpty() && mode != CancelComposition && dirty) { | 191 if (text.isEmpty() && mode != CancelComposition) { |
202 ASSERT(frame().document()); | 192 ASSERT(frame().document()); |
203 TypingCommand::deleteSelection(*frame().document(), 0); | 193 TypingCommand::deleteSelection(*frame().document(), 0); |
204 } | 194 } |
205 | 195 |
206 clear(); | 196 m_compositionNode = nullptr; |
| 197 m_customCompositionUnderlines.clear(); |
207 | 198 |
208 if (dirty) | 199 insertTextForConfirmedComposition(text); |
209 insertTextForConfirmedComposition(text); | |
210 | 200 |
211 if (mode == CancelComposition) { | 201 if (mode == CancelComposition) { |
212 // An open typing command that disagrees about current selection would c
ause issues with typing later on. | 202 // An open typing command that disagrees about current selection would c
ause issues with typing later on. |
213 TypingCommand::closeTyping(m_frame); | 203 TypingCommand::closeTyping(m_frame); |
214 } | 204 } |
215 | 205 |
216 return true; | 206 return true; |
217 } | 207 } |
218 | 208 |
219 void InputMethodController::setComposition(const String& text, const Vector<Comp
ositionUnderline>& underlines, unsigned selectionStart, unsigned selectionEnd) | 209 void InputMethodController::setComposition(const String& text, const Vector<Comp
ositionUnderline>& underlines, unsigned selectionStart, unsigned selectionEnd) |
(...skipping 10 matching lines...) Expand all Loading... |
230 if (frame().selection().isNone()) | 220 if (frame().selection().isNone()) |
231 return; | 221 return; |
232 | 222 |
233 if (Element* target = frame().document()->focusedElement()) { | 223 if (Element* target = frame().document()->focusedElement()) { |
234 // Dispatch an appropriate composition event to the focused node. | 224 // Dispatch an appropriate composition event to the focused node. |
235 // We check the composition status and choose an appropriate composition
event since this | 225 // We check the composition status and choose an appropriate composition
event since this |
236 // function is used for three purposes: | 226 // function is used for three purposes: |
237 // 1. Starting a new composition. | 227 // 1. Starting a new composition. |
238 // Send a compositionstart and a compositionupdate event when this fu
nction creates | 228 // Send a compositionstart and a compositionupdate event when this fu
nction creates |
239 // a new composition node, i.e. | 229 // a new composition node, i.e. |
240 // !hasComposition() && !text.isEmpty(). | 230 // m_compositionNode == 0 && !text.isEmpty(). |
241 // Sending a compositionupdate event at this time ensures that at lea
st one | 231 // Sending a compositionupdate event at this time ensures that at lea
st one |
242 // compositionupdate event is dispatched. | 232 // compositionupdate event is dispatched. |
243 // 2. Updating the existing composition node. | 233 // 2. Updating the existing composition node. |
244 // Send a compositionupdate event when this function updates the exis
ting composition | 234 // Send a compositionupdate event when this function updates the exis
ting composition |
245 // node, i.e. hasComposition() && !text.isEmpty(). | 235 // node, i.e. m_compositionNode != 0 && !text.isEmpty(). |
246 // 3. Canceling the ongoing composition. | 236 // 3. Canceling the ongoing composition. |
247 // Send a compositionend event when function deletes the existing com
position node, i.e. | 237 // Send a compositionend event when function deletes the existing com
position node, i.e. |
248 // !hasComposition() && test.isEmpty(). | 238 // m_compositionNode != 0 && test.isEmpty(). |
249 RefPtrWillBeRawPtr<CompositionEvent> event = nullptr; | 239 RefPtrWillBeRawPtr<CompositionEvent> event = nullptr; |
250 if (!hasComposition()) { | 240 if (!hasComposition()) { |
251 // We should send a compositionstart event only when the given text
is not empty because this | 241 // We should send a compositionstart event only when the given text
is not empty because this |
252 // function doesn't create a composition node when the text is empty
. | 242 // function doesn't create a composition node when the text is empty
. |
253 if (!text.isEmpty()) { | 243 if (!text.isEmpty()) { |
254 target->dispatchEvent(CompositionEvent::create(EventTypeNames::c
ompositionstart, frame().domWindow(), frame().selectedText())); | 244 target->dispatchEvent(CompositionEvent::create(EventTypeNames::c
ompositionstart, frame().domWindow(), frame().selectedText())); |
255 event = CompositionEvent::create(EventTypeNames::compositionupda
te, frame().domWindow(), text); | 245 event = CompositionEvent::create(EventTypeNames::compositionupda
te, frame().domWindow(), text); |
256 } | 246 } |
257 } else { | 247 } else { |
258 if (!text.isEmpty()) | 248 if (!text.isEmpty()) |
259 event = CompositionEvent::create(EventTypeNames::compositionupda
te, frame().domWindow(), text); | 249 event = CompositionEvent::create(EventTypeNames::compositionupda
te, frame().domWindow(), text); |
260 else | 250 else |
261 event = CompositionEvent::create(EventTypeNames::compositionend,
frame().domWindow(), text); | 251 event = CompositionEvent::create(EventTypeNames::compositionend,
frame().domWindow(), text); |
262 } | 252 } |
263 if (event.get()) | 253 if (event.get()) |
264 target->dispatchEvent(event); | 254 target->dispatchEvent(event); |
265 } | 255 } |
266 | 256 |
267 // If text is empty, then delete the old composition here. If text is non-em
pty, InsertTextCommand::input | 257 // If text is empty, then delete the old composition here. If text is non-em
pty, InsertTextCommand::input |
268 // will delete the old composition with an optimized replace operation. | 258 // will delete the old composition with an optimized replace operation. |
269 if (text.isEmpty()) { | 259 if (text.isEmpty()) { |
270 ASSERT(frame().document()); | 260 ASSERT(frame().document()); |
271 TypingCommand::deleteSelection(*frame().document(), TypingCommand::Preve
ntSpellChecking); | 261 TypingCommand::deleteSelection(*frame().document(), TypingCommand::Preve
ntSpellChecking); |
272 } | 262 } |
273 | 263 |
274 clear(); | 264 m_compositionNode = nullptr; |
| 265 m_customCompositionUnderlines.clear(); |
275 | 266 |
276 if (text.isEmpty()) | 267 if (text.isEmpty()) |
277 return; | 268 return; |
278 ASSERT(frame().document()); | 269 ASSERT(frame().document()); |
279 TypingCommand::insertText(*frame().document(), text, TypingCommand::SelectIn
sertedText | TypingCommand::PreventSpellChecking, TypingCommand::TextComposition
Update); | 270 TypingCommand::insertText(*frame().document(), text, TypingCommand::SelectIn
sertedText | TypingCommand::PreventSpellChecking, TypingCommand::TextComposition
Update); |
280 | 271 |
281 // Find out what node has the composition now. | 272 // Find out what node has the composition now. |
282 Position base = mostForwardCaretPosition(frame().selection().base()); | 273 Position base = mostForwardCaretPosition(frame().selection().base()); |
283 Node* baseNode = base.anchorNode(); | 274 Node* baseNode = base.anchorNode(); |
284 if (!baseNode || !baseNode->isTextNode()) | 275 if (!baseNode || !baseNode->isTextNode()) |
285 return; | 276 return; |
286 | 277 |
287 Position extent = frame().selection().extent(); | 278 Position extent = frame().selection().extent(); |
288 Node* extentNode = extent.anchorNode(); | 279 Node* extentNode = extent.anchorNode(); |
289 if (baseNode != extentNode) | 280 if (baseNode != extentNode) |
290 return; | 281 return; |
291 | 282 |
292 unsigned extentOffset = extent.computeOffsetInContainerNode(); | 283 unsigned extentOffset = extent.computeOffsetInContainerNode(); |
293 unsigned baseOffset = base.computeOffsetInContainerNode(); | 284 unsigned baseOffset = base.computeOffsetInContainerNode(); |
294 if (baseOffset + text.length() != extentOffset) | 285 if (baseOffset + text.length() != extentOffset) |
295 return; | 286 return; |
296 | 287 |
297 m_isDirty = true; | 288 m_compositionNode = toText(baseNode); |
298 m_hasComposition = true; | 289 m_compositionStart = baseOffset; |
299 if (!m_compositionRange) | 290 m_compositionEnd = extentOffset; |
300 m_compositionRange = Range::create(baseNode->document()); | 291 m_customCompositionUnderlines = underlines; |
301 m_compositionRange->setStart(baseNode, baseOffset); | 292 for (auto& underline : m_customCompositionUnderlines) { |
302 m_compositionRange->setEnd(baseNode, extentOffset); | 293 underline.startOffset += baseOffset; |
303 | 294 underline.endOffset += baseOffset; |
| 295 } |
304 if (baseNode->layoutObject()) | 296 if (baseNode->layoutObject()) |
305 baseNode->layoutObject()->setShouldDoFullPaintInvalidation(); | 297 baseNode->layoutObject()->setShouldDoFullPaintInvalidation(); |
306 | 298 |
307 unsigned start = std::min(baseOffset + selectionStart, extentOffset); | 299 unsigned start = std::min(baseOffset + selectionStart, extentOffset); |
308 unsigned end = std::min(std::max(start, baseOffset + selectionEnd), extentOf
fset); | 300 unsigned end = std::min(std::max(start, baseOffset + selectionEnd), extentOf
fset); |
309 RefPtrWillBeRawPtr<Range> selectedRange = Range::create(baseNode->document()
, baseNode, start, baseNode, end); | 301 RefPtrWillBeRawPtr<Range> selectedRange = Range::create(baseNode->document()
, baseNode, start, baseNode, end); |
310 frame().selection().setSelectedRange(selectedRange.get(), TextAffinity::Down
stream, FrameSelection::NonDirectional, NotUserTriggered); | 302 frame().selection().setSelectedRange(selectedRange.get(), TextAffinity::Down
stream, FrameSelection::NonDirectional, NotUserTriggered); |
311 | |
312 if (underlines.isEmpty()) { | |
313 frame().document()->markers().addCompositionMarker(m_compositionRange->s
tartPosition(), m_compositionRange->endPosition(), Color::black, false, LayoutTh
eme::theme().platformDefaultCompositionBackgroundColor()); | |
314 return; | |
315 } | |
316 for (const auto& underline : underlines) { | |
317 unsigned underlineStart = baseOffset + underline.startOffset; | |
318 unsigned underlineEnd = baseOffset + underline.endOffset; | |
319 EphemeralRange ephemeralLineRange = EphemeralRange(Position(baseNode, un
derlineStart), Position(baseNode, underlineEnd)); | |
320 if (ephemeralLineRange.isNull()) | |
321 continue; | |
322 frame().document()->markers().addCompositionMarker(ephemeralLineRange.st
artPosition(), ephemeralLineRange.endPosition(), underline.color, underline.thic
k, underline.backgroundColor); | |
323 } | |
324 } | 303 } |
325 | 304 |
326 void InputMethodController::setCompositionFromExistingText(const Vector<Composit
ionUnderline>& underlines, unsigned compositionStart, unsigned compositionEnd) | 305 void InputMethodController::setCompositionFromExistingText(const Vector<Composit
ionUnderline>& underlines, unsigned compositionStart, unsigned compositionEnd) |
327 { | 306 { |
328 Element* editable = frame().selection().rootEditableElement(); | 307 Element* editable = frame().selection().rootEditableElement(); |
329 if (!editable) | 308 Position base = mostForwardCaretPosition(frame().selection().base()); |
| 309 Node* baseNode = base.anchorNode(); |
| 310 if (baseNode && editable->firstChild() == baseNode && editable->lastChild()
== baseNode && baseNode->isTextNode()) { |
| 311 m_compositionNode = nullptr; |
| 312 m_customCompositionUnderlines.clear(); |
| 313 |
| 314 if (!base.isOffsetInAnchor()) |
| 315 return; |
| 316 if (baseNode != frame().selection().extent().anchorNode()) |
| 317 return; |
| 318 |
| 319 m_compositionNode = toText(baseNode); |
| 320 const EphemeralRange range = PlainTextRange(compositionStart, compositio
nEnd).createRange(*editable); |
| 321 if (range.isNull()) |
| 322 return; |
| 323 |
| 324 m_compositionStart = range.startPosition().computeOffsetInContainerNode(
); |
| 325 m_compositionEnd = range.endPosition().computeOffsetInContainerNode(); |
| 326 m_customCompositionUnderlines = underlines; |
| 327 size_t numUnderlines = m_customCompositionUnderlines.size(); |
| 328 for (size_t i = 0; i < numUnderlines; ++i) { |
| 329 m_customCompositionUnderlines[i].startOffset += m_compositionStart; |
| 330 m_customCompositionUnderlines[i].endOffset += m_compositionStart; |
| 331 } |
| 332 if (baseNode->layoutObject()) |
| 333 baseNode->layoutObject()->setShouldDoFullPaintInvalidation(); |
330 return; | 334 return; |
331 | |
332 const EphemeralRange range = PlainTextRange(compositionStart, compositionEnd
).createRange(*editable); | |
333 if (range.isNull()) | |
334 return; | |
335 | |
336 const Position start = range.startPosition(); | |
337 if (editableRootForPosition(start) != editable) | |
338 return; | |
339 | |
340 const Position end = range.endPosition(); | |
341 if (editableRootForPosition(end) != editable) | |
342 return; | |
343 | |
344 clear(); | |
345 | |
346 for (const auto& underline : underlines) { | |
347 unsigned underlineStart = compositionStart + underline.startOffset; | |
348 unsigned underlineEnd = compositionStart + underline.endOffset; | |
349 EphemeralRange ephemeralLineRange = PlainTextRange(underlineStart, under
lineEnd).createRange(*editable); | |
350 if (ephemeralLineRange.isNull()) | |
351 continue; | |
352 frame().document()->markers().addCompositionMarker(ephemeralLineRange.st
artPosition(), ephemeralLineRange.endPosition(), underline.color, underline.thic
k, underline.backgroundColor); | |
353 } | 335 } |
354 | 336 |
355 m_hasComposition = true; | 337 Editor::RevealSelectionScope revealSelectionScope(&editor()); |
356 if (!m_compositionRange) | 338 SelectionOffsetsScope selectionOffsetsScope(this); |
357 m_compositionRange = Range::create(range.document()); | 339 setSelectionOffsets(PlainTextRange(compositionStart, compositionEnd)); |
358 m_compositionRange->setStart(range.startPosition()); | 340 setComposition(frame().selectedText(), underlines, 0, 0); |
359 m_compositionRange->setEnd(range.endPosition()); | |
360 } | 341 } |
361 | 342 |
362 EphemeralRange InputMethodController::compositionEphemeralRange() const | 343 EphemeralRange InputMethodController::compositionEphemeralRange() const |
363 { | 344 { |
364 if (!hasComposition()) | 345 if (!hasComposition()) |
365 return EphemeralRange(); | 346 return EphemeralRange(); |
366 return EphemeralRange(m_compositionRange.get()); | 347 unsigned length = m_compositionNode->length(); |
| 348 unsigned start = std::min(m_compositionStart, length); |
| 349 unsigned end = std::min(std::max(start, m_compositionEnd), length); |
| 350 if (start >= end) |
| 351 return EphemeralRange(); |
| 352 return EphemeralRange(Position(m_compositionNode.get(), start), Position(m_c
ompositionNode.get(), end)); |
367 } | 353 } |
368 | 354 |
369 PassRefPtrWillBeRawPtr<Range> InputMethodController::compositionRange() const | 355 PassRefPtrWillBeRawPtr<Range> InputMethodController::compositionRange() const |
370 { | 356 { |
371 return hasComposition() ? m_compositionRange : nullptr; | 357 return createRange(compositionEphemeralRange()); |
372 } | 358 } |
373 | 359 |
374 PlainTextRange InputMethodController::getSelectionOffsets() const | 360 PlainTextRange InputMethodController::getSelectionOffsets() const |
375 { | 361 { |
376 RefPtrWillBeRawPtr<Range> range = firstRangeOf(frame().selection().selection
()); | 362 RefPtrWillBeRawPtr<Range> range = firstRangeOf(frame().selection().selection
()); |
377 if (!range) | 363 if (!range) |
378 return PlainTextRange(); | 364 return PlainTextRange(); |
379 ContainerNode* editable = frame().selection().rootEditableElementOrTreeScope
RootNode(); | 365 ContainerNode* editable = frame().selection().rootEditableElementOrTreeScope
RootNode(); |
380 ASSERT(editable); | 366 ASSERT(editable); |
381 return PlainTextRange::create(*editable, *range.get()); | 367 return PlainTextRange::create(*editable, *range.get()); |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
429 if (before == 0) | 415 if (before == 0) |
430 break; | 416 break; |
431 ++before; | 417 ++before; |
432 } while (frame().selection().start() == frame().selection().end() && before
<= static_cast<int>(selectionOffsets.start())); | 418 } while (frame().selection().start() == frame().selection().end() && before
<= static_cast<int>(selectionOffsets.start())); |
433 TypingCommand::deleteSelection(*frame().document()); | 419 TypingCommand::deleteSelection(*frame().document()); |
434 } | 420 } |
435 | 421 |
436 DEFINE_TRACE(InputMethodController) | 422 DEFINE_TRACE(InputMethodController) |
437 { | 423 { |
438 visitor->trace(m_frame); | 424 visitor->trace(m_frame); |
439 visitor->trace(m_compositionRange); | 425 visitor->trace(m_compositionNode); |
440 } | 426 } |
441 | 427 |
442 } // namespace blink | 428 } // namespace blink |
OLD | NEW |