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