Chromium Code Reviews| 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/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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 |
| OLD | NEW |