Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(95)

Side by Side Diff: Source/core/editing/InputMethodController.cpp

Issue 1325563002: Avoid style clobbering in setCompositionFromExistingText. (2nd land) (Closed) Base URL: https://chromium.googlesource.com/chromium/blink.git@master
Patch Set: Fix end to refer to endPosition Created 5 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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/events/CompositionEvent.h" 36 #include "core/events/CompositionEvent.h"
37 #include "core/frame/LocalFrame.h" 37 #include "core/frame/LocalFrame.h"
38 #include "core/html/HTMLTextAreaElement.h" 38 #include "core/html/HTMLTextAreaElement.h"
39 #include "core/input/EventHandler.h" 39 #include "core/input/EventHandler.h"
40 #include "core/layout/LayoutObject.h" 40 #include "core/layout/LayoutObject.h"
41 #include "core/page/ChromeClient.h" 41 #include "core/page/ChromeClient.h"
42 42
43 namespace blink { 43 namespace blink {
(...skipping 11 matching lines...) Expand all
55 55
56 // ---------------------------- 56 // ----------------------------
57 57
58 PassOwnPtrWillBeRawPtr<InputMethodController> InputMethodController::create(Loca lFrame& frame) 58 PassOwnPtrWillBeRawPtr<InputMethodController> InputMethodController::create(Loca lFrame& frame)
59 { 59 {
60 return adoptPtrWillBeNoop(new InputMethodController(frame)); 60 return adoptPtrWillBeNoop(new InputMethodController(frame));
61 } 61 }
62 62
63 InputMethodController::InputMethodController(LocalFrame& frame) 63 InputMethodController::InputMethodController(LocalFrame& frame)
64 : m_frame(&frame) 64 : m_frame(&frame)
65 , m_compositionStart(0) 65 , m_isExistingText(true)
yosin_UTC9 2015/08/31 01:35:40 Can we use inverse logic? It is unclear why just i
aelias_OOO_until_Jul13 2015/09/04 04:03:24 Done.
66 , m_compositionEnd(0)
67 { 66 {
68 } 67 }
69 68
70 InputMethodController::~InputMethodController() 69 InputMethodController::~InputMethodController()
71 { 70 {
72 } 71 }
73 72
74 bool InputMethodController::hasComposition() const 73 bool InputMethodController::hasComposition() const
75 { 74 {
76 return m_compositionNode && m_compositionNode->isContentEditable(); 75 return m_compositionRange.get() && m_compositionRange->startContainer() && m _compositionRange->startContainer()->isContentEditable();
77 } 76 }
78 77
79 inline Editor& InputMethodController::editor() const 78 inline Editor& InputMethodController::editor() const
80 { 79 {
81 return frame().editor(); 80 return frame().editor();
82 } 81 }
83 82
84 void InputMethodController::clear() 83 void InputMethodController::clear()
85 { 84 {
86 m_compositionNode = nullptr; 85 m_compositionRange = nullptr;
87 m_customCompositionUnderlines.clear(); 86 m_customCompositionUnderlines.clear();
87 m_isExistingText = true;
88 } 88 }
89 89
90 bool InputMethodController::insertTextForConfirmedComposition(const String& text ) 90 bool InputMethodController::insertTextForConfirmedComposition(const String& text )
91 { 91 {
92 return frame().eventHandler().handleTextInputEvent(text, 0, TextEventInputCo mposition); 92 return frame().eventHandler().handleTextInputEvent(text, 0, TextEventInputCo mposition);
93 } 93 }
94 94
95 void InputMethodController::selectComposition() const 95 void InputMethodController::selectComposition() const
96 { 96 {
97 const EphemeralRange range = compositionEphemeralRange(); 97 const EphemeralRange range = compositionEphemeralRange();
98 if (range.isNull()) 98 if (range.isNull())
99 return; 99 return;
100 100
101 // 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.
102 // See <http://bugs.webkit.org/show_bug.cgi?id=15781> 102 // See <http://bugs.webkit.org/show_bug.cgi?id=15781>
103 VisibleSelection selection; 103 VisibleSelection selection;
104 selection.setWithoutValidation(range.startPosition(), range.endPosition()); 104 selection.setWithoutValidation(range.startPosition(), range.endPosition());
105 frame().selection().setSelection(selection, 0); 105 frame().selection().setSelection(selection, 0);
106 } 106 }
107 107
108 bool InputMethodController::confirmComposition() 108 bool InputMethodController::confirmComposition()
109 { 109 {
110 if (!hasComposition()) 110 if (!hasComposition())
111 return false; 111 return false;
112 return finishComposition(m_compositionNode->data().substring(m_compositionSt art, m_compositionEnd - m_compositionStart), ConfirmComposition); 112 return confirmComposition(plainText(compositionEphemeralRange()));
113 } 113 }
114 114
115 bool InputMethodController::confirmComposition(const String& text) 115 bool InputMethodController::confirmComposition(const String& text)
116 { 116 {
117 return finishComposition(text, ConfirmComposition); 117 return finishComposition(text, ConfirmComposition);
118 } 118 }
119 119
120 bool InputMethodController::confirmCompositionOrInsertText(const String& text, C onfirmCompositionBehavior confirmBehavior) 120 bool InputMethodController::confirmCompositionOrInsertText(const String& text, C onfirmCompositionBehavior confirmBehavior)
121 { 121 {
122 if (!hasComposition()) { 122 if (!hasComposition()) {
(...skipping 19 matching lines...) Expand all
142 { 142 {
143 finishComposition(emptyString(), CancelComposition); 143 finishComposition(emptyString(), CancelComposition);
144 } 144 }
145 145
146 void InputMethodController::cancelCompositionIfSelectionIsInvalid() 146 void InputMethodController::cancelCompositionIfSelectionIsInvalid()
147 { 147 {
148 if (!hasComposition() || editor().preventRevealSelection()) 148 if (!hasComposition() || editor().preventRevealSelection())
149 return; 149 return;
150 150
151 // Check if selection start and selection end are valid. 151 // Check if selection start and selection end are valid.
152 Position start = frame().selection().start(); 152 FrameSelection& selection = frame().selection();
153 Position end = frame().selection().end(); 153 if (!selection.isNone()) {
154 if (start.computeContainerNode() == m_compositionNode 154 Position start = selection.start();
155 && end.computeContainerNode() == m_compositionNode 155 Position end = selection.end();
156 && static_cast<unsigned>(start.computeOffsetInContainerNode()) >= m_comp ositionStart 156 if (start.anchorNode() && end.anchorNode()) {
157 && static_cast<unsigned>(end.computeOffsetInContainerNode()) <= m_compos itionEnd) 157 if (m_compositionRange->isPointInRange(start.computeContainerNode(), start.computeOffsetInContainerNode(), IGNORE_EXCEPTION)
158 return; 158 && m_compositionRange->isPointInRange(end.computeContainerNode() , end.computeOffsetInContainerNode(), IGNORE_EXCEPTION))
159 return;
160 }
161 }
159 162
160 cancelComposition(); 163 cancelComposition();
161 frame().chromeClient().didCancelCompositionOnSelectionChange(); 164 frame().chromeClient().didCancelCompositionOnSelectionChange();
162 } 165 }
163 166
164 bool InputMethodController::finishComposition(const String& text, FinishComposit ionMode mode) 167 bool InputMethodController::finishComposition(const String& text, FinishComposit ionMode mode)
165 { 168 {
166 if (!hasComposition()) 169 if (!hasComposition())
167 return false; 170 return false;
168 171
(...skipping 10 matching lines...) Expand all
179 return false; 182 return false;
180 183
181 // Dispatch a compositionend event to the focused node. 184 // 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 185 // 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. 186 // the DOM Event specification.
184 if (Element* target = frame().document()->focusedElement()) { 187 if (Element* target = frame().document()->focusedElement()) {
185 RefPtrWillBeRawPtr<CompositionEvent> event = CompositionEvent::create(Ev entTypeNames::compositionend, frame().domWindow(), text); 188 RefPtrWillBeRawPtr<CompositionEvent> event = CompositionEvent::create(Ev entTypeNames::compositionend, frame().domWindow(), text);
186 target->dispatchEvent(event); 189 target->dispatchEvent(event);
187 } 190 }
188 191
192 bool existingText = m_isExistingText;
193
189 // If text is empty, then delete the old composition here. If text is non-em pty, InsertTextCommand::input 194 // 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. 195 // will delete the old composition with an optimized replace operation.
191 if (text.isEmpty() && mode != CancelComposition) { 196 if (text.isEmpty() && mode != CancelComposition && !existingText) {
192 ASSERT(frame().document()); 197 ASSERT(frame().document());
193 TypingCommand::deleteSelection(*frame().document(), 0); 198 TypingCommand::deleteSelection(*frame().document(), 0);
194 } 199 }
195 200
196 m_compositionNode = nullptr; 201 clear();
197 m_customCompositionUnderlines.clear();
198 202
199 insertTextForConfirmedComposition(text); 203 if (!existingText)
204 insertTextForConfirmedComposition(text);
200 205
201 if (mode == CancelComposition) { 206 if (mode == CancelComposition) {
202 // An open typing command that disagrees about current selection would c ause issues with typing later on. 207 // An open typing command that disagrees about current selection would c ause issues with typing later on.
203 TypingCommand::closeTyping(m_frame); 208 TypingCommand::closeTyping(m_frame);
204 } 209 }
205 210
206 return true; 211 return true;
207 } 212 }
208 213
209 void InputMethodController::setComposition(const String& text, const Vector<Comp ositionUnderline>& underlines, unsigned selectionStart, unsigned selectionEnd) 214 void InputMethodController::setComposition(const String& text, const Vector<Comp ositionUnderline>& underlines, unsigned selectionStart, unsigned selectionEnd)
(...skipping 10 matching lines...) Expand all
220 if (frame().selection().isNone()) 225 if (frame().selection().isNone())
221 return; 226 return;
222 227
223 if (Element* target = frame().document()->focusedElement()) { 228 if (Element* target = frame().document()->focusedElement()) {
224 // Dispatch an appropriate composition event to the focused node. 229 // Dispatch an appropriate composition event to the focused node.
225 // We check the composition status and choose an appropriate composition event since this 230 // We check the composition status and choose an appropriate composition event since this
226 // function is used for three purposes: 231 // function is used for three purposes:
227 // 1. Starting a new composition. 232 // 1. Starting a new composition.
228 // Send a compositionstart and a compositionupdate event when this fu nction creates 233 // Send a compositionstart and a compositionupdate event when this fu nction creates
229 // a new composition node, i.e. 234 // a new composition node, i.e.
230 // m_compositionNode == 0 && !text.isEmpty(). 235 // !hasComposition() && !text.isEmpty().
231 // Sending a compositionupdate event at this time ensures that at lea st one 236 // Sending a compositionupdate event at this time ensures that at lea st one
232 // compositionupdate event is dispatched. 237 // compositionupdate event is dispatched.
233 // 2. Updating the existing composition node. 238 // 2. Updating the existing composition node.
234 // Send a compositionupdate event when this function updates the exis ting composition 239 // Send a compositionupdate event when this function updates the exis ting composition
235 // node, i.e. m_compositionNode != 0 && !text.isEmpty(). 240 // node, i.e. hasComposition() && !text.isEmpty().
236 // 3. Canceling the ongoing composition. 241 // 3. Canceling the ongoing composition.
237 // Send a compositionend event when function deletes the existing com position node, i.e. 242 // Send a compositionend event when function deletes the existing com position node, i.e.
238 // m_compositionNode != 0 && test.isEmpty(). 243 // !hasComposition() && test.isEmpty().
239 RefPtrWillBeRawPtr<CompositionEvent> event = nullptr; 244 RefPtrWillBeRawPtr<CompositionEvent> event = nullptr;
240 if (!hasComposition()) { 245 if (!hasComposition()) {
241 // We should send a compositionstart event only when the given text is not empty because this 246 // 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 . 247 // function doesn't create a composition node when the text is empty .
243 if (!text.isEmpty()) { 248 if (!text.isEmpty()) {
244 target->dispatchEvent(CompositionEvent::create(EventTypeNames::c ompositionstart, frame().domWindow(), frame().selectedText())); 249 target->dispatchEvent(CompositionEvent::create(EventTypeNames::c ompositionstart, frame().domWindow(), frame().selectedText()));
245 event = CompositionEvent::create(EventTypeNames::compositionupda te, frame().domWindow(), text); 250 event = CompositionEvent::create(EventTypeNames::compositionupda te, frame().domWindow(), text);
246 } 251 }
247 } else { 252 } else {
248 if (!text.isEmpty()) 253 if (!text.isEmpty())
249 event = CompositionEvent::create(EventTypeNames::compositionupda te, frame().domWindow(), text); 254 event = CompositionEvent::create(EventTypeNames::compositionupda te, frame().domWindow(), text);
250 else 255 else
251 event = CompositionEvent::create(EventTypeNames::compositionend, frame().domWindow(), text); 256 event = CompositionEvent::create(EventTypeNames::compositionend, frame().domWindow(), text);
252 } 257 }
253 if (event.get()) 258 if (event.get())
254 target->dispatchEvent(event); 259 target->dispatchEvent(event);
255 } 260 }
256 261
257 // If text is empty, then delete the old composition here. If text is non-em pty, InsertTextCommand::input 262 // 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. 263 // will delete the old composition with an optimized replace operation.
259 if (text.isEmpty()) { 264 if (text.isEmpty()) {
260 ASSERT(frame().document()); 265 ASSERT(frame().document());
261 TypingCommand::deleteSelection(*frame().document(), TypingCommand::Preve ntSpellChecking); 266 TypingCommand::deleteSelection(*frame().document(), TypingCommand::Preve ntSpellChecking);
262 } 267 }
263 268
264 m_compositionNode = nullptr; 269 clear();
265 m_customCompositionUnderlines.clear(); 270
271 m_isExistingText = false;
266 272
267 if (text.isEmpty()) 273 if (text.isEmpty())
268 return; 274 return;
269 ASSERT(frame().document()); 275 ASSERT(frame().document());
270 TypingCommand::insertText(*frame().document(), text, TypingCommand::SelectIn sertedText | TypingCommand::PreventSpellChecking, TypingCommand::TextComposition Update); 276 TypingCommand::insertText(*frame().document(), text, TypingCommand::SelectIn sertedText | TypingCommand::PreventSpellChecking, TypingCommand::TextComposition Update);
271 277
272 // Find out what node has the composition now. 278 // Find out what node has the composition now.
273 Position base = mostForwardCaretPosition(frame().selection().base()); 279 Position base = mostForwardCaretPosition(frame().selection().base());
274 Node* baseNode = base.anchorNode(); 280 Node* baseNode = base.anchorNode();
275 if (!baseNode || !baseNode->isTextNode()) 281 if (!baseNode || !baseNode->isTextNode())
276 return; 282 return;
277 283
278 Position extent = frame().selection().extent(); 284 Position extent = frame().selection().extent();
279 Node* extentNode = extent.anchorNode(); 285 Node* extentNode = extent.anchorNode();
280 if (baseNode != extentNode) 286 if (baseNode != extentNode)
281 return; 287 return;
282 288
283 unsigned extentOffset = extent.computeOffsetInContainerNode(); 289 unsigned extentOffset = extent.computeOffsetInContainerNode();
284 unsigned baseOffset = base.computeOffsetInContainerNode(); 290 unsigned baseOffset = base.computeOffsetInContainerNode();
285 if (baseOffset + text.length() != extentOffset) 291 if (baseOffset + text.length() != extentOffset)
286 return; 292 return;
287 293
288 m_compositionNode = toText(baseNode); 294 m_compositionRange = Range::create(baseNode->document(), baseNode, baseOffse t, baseNode, extentOffset);
289 m_compositionStart = baseOffset; 295 NodeUnderlinesMap customCompositionUnderlines;
290 m_compositionEnd = extentOffset; 296 customCompositionUnderlines[baseNode].first = baseNode;
291 m_customCompositionUnderlines = underlines; 297 customCompositionUnderlines[baseNode].second = underlines;
292 for (auto& underline : m_customCompositionUnderlines) { 298 for (auto& underline : customCompositionUnderlines[baseNode].second) {
293 underline.startOffset += baseOffset; 299 underline.startOffset += baseOffset;
294 underline.endOffset += baseOffset; 300 underline.endOffset += baseOffset;
295 } 301 }
302
303 m_customCompositionUnderlines.swap(customCompositionUnderlines);
304
296 if (baseNode->layoutObject()) 305 if (baseNode->layoutObject())
297 baseNode->layoutObject()->setShouldDoFullPaintInvalidation(); 306 baseNode->layoutObject()->setShouldDoFullPaintInvalidation();
298 307
308 for (auto& nodeUnderlinesPair : customCompositionUnderlines) {
309 if (nodeUnderlinesPair.first->layoutObject() && nodeUnderlinesPair.first != baseNode)
310 nodeUnderlinesPair.first->layoutObject()->setShouldDoFullPaintInvali dation();
311 }
312
299 unsigned start = std::min(baseOffset + selectionStart, extentOffset); 313 unsigned start = std::min(baseOffset + selectionStart, extentOffset);
300 unsigned end = std::min(std::max(start, baseOffset + selectionEnd), extentOf fset); 314 unsigned end = std::min(std::max(start, baseOffset + selectionEnd), extentOf fset);
301 RefPtrWillBeRawPtr<Range> selectedRange = Range::create(baseNode->document() , baseNode, start, baseNode, end); 315 RefPtrWillBeRawPtr<Range> selectedRange = Range::create(baseNode->document() , baseNode, start, baseNode, end);
302 frame().selection().setSelectedRange(selectedRange.get(), TextAffinity::Down stream, FrameSelection::NonDirectional, NotUserTriggered); 316 frame().selection().setSelectedRange(selectedRange.get(), TextAffinity::Down stream, FrameSelection::NonDirectional, NotUserTriggered);
303 } 317 }
304 318
305 void InputMethodController::setCompositionFromExistingText(const Vector<Composit ionUnderline>& underlines, unsigned compositionStart, unsigned compositionEnd) 319 void InputMethodController::setCompositionFromExistingText(const Vector<Composit ionUnderline>& underlines, unsigned compositionStart, unsigned compositionEnd)
306 { 320 {
307 Element* editable = frame().selection().rootEditableElement(); 321 Element* editable = frame().selection().rootEditableElement();
308 Position base = mostForwardCaretPosition(frame().selection().base()); 322 if (!editable)
309 Node* baseNode = base.anchorNode(); 323 return;
310 if (baseNode && editable->firstChild() == baseNode && editable->lastChild() == baseNode && baseNode->isTextNode()) {
311 m_compositionNode = nullptr;
312 m_customCompositionUnderlines.clear();
313 324
314 if (!base.isOffsetInAnchor()) 325 EphemeralRange range = PlainTextRange(compositionStart, compositionEnd).crea teRange(*editable);
315 return; 326 if (range.isNull())
316 if (baseNode != frame().selection().extent().anchorNode()) 327 return;
317 return;
318 328
319 m_compositionNode = toText(baseNode); 329 Position start = range.startPosition();
320 const EphemeralRange range = PlainTextRange(compositionStart, compositio nEnd).createRange(*editable); 330 if (!start.anchorNode() || start.anchorNode()->rootEditableElement() != edit able)
321 if (range.isNull()) 331 return;
322 return;
323 332
324 m_compositionStart = range.startPosition().computeOffsetInContainerNode( ); 333 Position end = range.endPosition();
325 m_compositionEnd = range.endPosition().computeOffsetInContainerNode(); 334 if (!end.anchorNode() || end.anchorNode()->rootEditableElement() != editable )
326 m_customCompositionUnderlines = underlines; 335 return;
327 size_t numUnderlines = m_customCompositionUnderlines.size(); 336
328 for (size_t i = 0; i < numUnderlines; ++i) { 337 clear();
329 m_customCompositionUnderlines[i].startOffset += m_compositionStart; 338
330 m_customCompositionUnderlines[i].endOffset += m_compositionStart; 339 // customCompositionUnderlines has the same type as m_customCompositionUnder lines. So we can swap them later.
340 NodeUnderlinesMap customCompositionUnderlines;
341 CompositionUnderline underline;
342 EphemeralRange ephemeralLineRange;
343 RefPtrWillBeRawPtr<Range> underlineRange;
344 for (size_t i = 0; i < underlines.size(); ++i) {
345 // We separate each disjoint underline into node specific underlines and associate them with the node that they belong to.
346 ephemeralLineRange = PlainTextRange(compositionStart + underlines[i].sta rtOffset, compositionStart + underlines[i].endOffset).createRange(*editable);
347 if (ephemeralLineRange.isNull())
348 continue;
349 underlineRange = Range::create(ephemeralLineRange.document(), ephemeralL ineRange.startPosition(), ephemeralLineRange.endPosition());
350 Node* stopNode = underlineRange->pastLastNode();
351 for (Node* node = underlineRange->firstNode(); node && node != stopNode; node = NodeTraversal::next(*node)) {
352 if (node->isTextNode()) {
353 if (!customCompositionUnderlines[node].first)
354 customCompositionUnderlines[node].first = node;
355 underline = underlines[i];
356 underline.startOffset = node == underlineRange->startContainer() ? underlineRange->startOffset() : 0;
357 underline.endOffset = node == underlineRange->endContainer() ? u nderlineRange->endOffset() : toText(node)->length();
358 customCompositionUnderlines[node].second.append(underline);
359 }
331 } 360 }
332 if (baseNode->layoutObject())
333 baseNode->layoutObject()->setShouldDoFullPaintInvalidation();
334 return;
335 } 361 }
336 362
337 Editor::RevealSelectionScope revealSelectionScope(&editor()); 363 m_compositionRange = Range::create(range.document(), range.startPosition(), range.endPosition());
338 SelectionOffsetsScope selectionOffsetsScope(this); 364 m_customCompositionUnderlines.swap(customCompositionUnderlines);
339 setSelectionOffsets(PlainTextRange(compositionStart, compositionEnd)); 365
340 setComposition(frame().selectedText(), underlines, 0, 0); 366 // Invalidate affected composition nodes
367 for (auto& nodeUnderlinesPair : m_customCompositionUnderlines) {
368 if (nodeUnderlinesPair.first->layoutObject())
369 nodeUnderlinesPair.first->layoutObject()->setShouldDoFullPaintInvali dation();
370 NodeUnderlinesMap::iterator it = customCompositionUnderlines.find(nodeUn derlinesPair.first);
371 if (it != customCompositionUnderlines.end())
372 customCompositionUnderlines.erase(it);
373 }
374
375 for (auto& nodeUnderlinesPair : customCompositionUnderlines) {
376 if (nodeUnderlinesPair.first->layoutObject())
377 nodeUnderlinesPair.first->layoutObject()->setShouldDoFullPaintInvali dation();
378 }
341 } 379 }
342 380
343 EphemeralRange InputMethodController::compositionEphemeralRange() const 381 EphemeralRange InputMethodController::compositionEphemeralRange() const
344 { 382 {
345 if (!hasComposition()) 383 if (!hasComposition())
346 return EphemeralRange(); 384 return EphemeralRange();
347 unsigned length = m_compositionNode->length(); 385 return EphemeralRange(m_compositionRange->startPosition(), m_compositionRang e->endPosition());
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 } 386 }
354 387
355 PassRefPtrWillBeRawPtr<Range> InputMethodController::compositionRange() const 388 PassRefPtrWillBeRawPtr<Range> InputMethodController::compositionRange() const
356 { 389 {
357 return createRange(compositionEphemeralRange()); 390 return hasComposition() ? m_compositionRange : nullptr;
391 }
392
393 bool InputMethodController::isCompositionNode(const Node* node)
394 {
395 if (!node || m_customCompositionUnderlines.empty() || !node->isTextNode())
396 return false;
397
398 return m_customCompositionUnderlines.find(node) != m_customCompositionUnderl ines.end();
399 }
400
401 const Vector<CompositionUnderline>& InputMethodController::customCompositionUnde rlines() const
402 {
403 CR_DEFINE_STATIC_LOCAL(Vector<CompositionUnderline>, emptyUnderline, ());
404 return m_customCompositionUnderlines.empty() ? emptyUnderline : m_customComp ositionUnderlines.begin()->second.second;
405 }
406
407 const Vector<CompositionUnderline>* InputMethodController::customCompositionUnde rlines(const Node* node) const
408 {
409 if (!node || !node->isTextNode() || m_customCompositionUnderlines.empty())
410 return nullptr;
411
412 NodeUnderlinesMap::iterator it = m_customCompositionUnderlines.find(node);
413 return (it == m_customCompositionUnderlines.end() || m_customCompositionUnde rlines[node].second.isEmpty()) ?
414 nullptr : &(m_customCompositionUnderlines[node].second);
358 } 415 }
359 416
360 PlainTextRange InputMethodController::getSelectionOffsets() const 417 PlainTextRange InputMethodController::getSelectionOffsets() const
361 { 418 {
362 RefPtrWillBeRawPtr<Range> range = firstRangeOf(frame().selection().selection ()); 419 RefPtrWillBeRawPtr<Range> range = firstRangeOf(frame().selection().selection ());
363 if (!range) 420 if (!range)
364 return PlainTextRange(); 421 return PlainTextRange();
365 ContainerNode* editable = frame().selection().rootEditableElementOrTreeScope RootNode(); 422 ContainerNode* editable = frame().selection().rootEditableElementOrTreeScope RootNode();
366 ASSERT(editable); 423 ASSERT(editable);
367 return PlainTextRange::create(*editable, *range.get()); 424 return PlainTextRange::create(*editable, *range.get());
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
415 if (before == 0) 472 if (before == 0)
416 break; 473 break;
417 ++before; 474 ++before;
418 } while (frame().selection().start() == frame().selection().end() && before <= static_cast<int>(selectionOffsets.start())); 475 } while (frame().selection().start() == frame().selection().end() && before <= static_cast<int>(selectionOffsets.start()));
419 TypingCommand::deleteSelection(*frame().document()); 476 TypingCommand::deleteSelection(*frame().document());
420 } 477 }
421 478
422 DEFINE_TRACE(InputMethodController) 479 DEFINE_TRACE(InputMethodController)
423 { 480 {
424 visitor->trace(m_frame); 481 visitor->trace(m_frame);
425 visitor->trace(m_compositionNode); 482 visitor->trace(m_compositionRange);
426 } 483 }
427 484
428 } // namespace blink 485 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698