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 303 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 314 | 314 |
| 315 selectComposition(); | 315 selectComposition(); |
| 316 | 316 |
| 317 if (frame().selection().isNone()) | 317 if (frame().selection().isNone()) |
| 318 return; | 318 return; |
| 319 | 319 |
| 320 Element* target = frame().document()->focusedElement(); | 320 Element* target = frame().document()->focusedElement(); |
| 321 if (!target) | 321 if (!target) |
| 322 return; | 322 return; |
| 323 | 323 |
| 324 int selectionOffsetsStart = static_cast<int>(getSelectionOffsets().start()); | |
| 325 int start = selectionOffsetsStart + selectionStart; | |
| 326 int end = selectionOffsetsStart + selectionEnd; | |
| 327 PlainTextRange selectedRange = createRangeForSelection(start, end, text.leng th()); | |
| 328 if (selectedRange.isNull()) | |
| 329 return; | |
| 330 | |
| 324 // Dispatch an appropriate composition event to the focused node. | 331 // Dispatch an appropriate composition event to the focused node. |
| 325 // We check the composition status and choose an appropriate composition eve nt since this | 332 // We check the composition status and choose an appropriate composition eve nt since this |
| 326 // function is used for three purposes: | 333 // function is used for three purposes: |
| 327 // 1. Starting a new composition. | 334 // 1. Starting a new composition. |
| 328 // Send a compositionstart and a compositionupdate event when this functi on creates | 335 // Send a compositionstart and a compositionupdate event when this functi on creates |
| 329 // a new composition node, i.e. | 336 // a new composition node, i.e. |
| 330 // !hasComposition() && !text.isEmpty(). | 337 // !hasComposition() && !text.isEmpty(). |
| 331 // Sending a compositionupdate event at this time ensures that at least o ne | 338 // Sending a compositionupdate event at this time ensures that at least o ne |
| 332 // compositionupdate event is dispatched. | 339 // compositionupdate event is dispatched. |
| 333 // 2. Updating the existing composition node. | 340 // 2. Updating the existing composition node. |
| 334 // Send a compositionupdate event when this function updates the existing composition | 341 // Send a compositionupdate event when this function updates the existing composition |
| 335 // node, i.e. hasComposition() && !text.isEmpty(). | 342 // node, i.e. hasComposition() && !text.isEmpty(). |
| 336 // 3. Canceling the ongoing composition. | 343 // 3. Canceling the ongoing composition. |
| 337 // Send a compositionend event when function deletes the existing composi tion node, i.e. | 344 // Send a compositionend event when function deletes the existing composi tion node, i.e. |
| 338 // !hasComposition() && test.isEmpty(). | 345 // !hasComposition() && test.isEmpty(). |
| 339 if (text.isEmpty()) { | 346 if (text.isEmpty()) { |
| 340 if (hasComposition()) { | 347 if (hasComposition()) { |
| 341 confirmComposition(emptyString()); | 348 confirmComposition(emptyString()); |
| 342 return; | 349 } else { |
| 350 // It's weird to call |setComposition()| with empty text outside com position, however some IME | |
| 351 // (e.g. Japanese IBus-Anthy) did this, so we simply delete selectio n without sending extra events. | |
| 352 TypingCommand::deleteSelection(*frame().document(), TypingCommand::P reventSpellChecking); | |
| 343 } | 353 } |
| 344 // It's weird to call |setComposition()| with empty text outside composi tion, however some IME | 354 |
| 345 // (e.g. Japanese IBus-Anthy) did this, so we simply delete selection wi thout sending extra events. | 355 setEditableSelectionOffsets(selectedRange); |
| 346 TypingCommand::deleteSelection(*frame().document(), TypingCommand::Preve ntSpellChecking); | |
| 347 return; | 356 return; |
| 348 } | 357 } |
| 349 | 358 |
| 350 // We should send a 'compositionstart' event only when the given text is not empty because this | 359 // We should send a 'compositionstart' event only when the given text is not empty because this |
| 351 // function doesn't create a composition node when the text is empty. | 360 // function doesn't create a composition node when the text is empty. |
| 352 if (!hasComposition()) { | 361 if (!hasComposition()) { |
| 353 target->dispatchEvent(CompositionEvent::create(EventTypeNames::compositi onstart, frame().domWindow(), frame().selectedText())); | 362 target->dispatchEvent(CompositionEvent::create(EventTypeNames::compositi onstart, frame().domWindow(), frame().selectedText())); |
| 354 if (!frame().document()) | 363 if (!frame().document()) |
| 355 return; | 364 return; |
| 356 } | 365 } |
| (...skipping 26 matching lines...) Expand all Loading... | |
| 383 m_isDirty = true; | 392 m_isDirty = true; |
| 384 m_hasComposition = true; | 393 m_hasComposition = true; |
| 385 if (!m_compositionRange) | 394 if (!m_compositionRange) |
| 386 m_compositionRange = Range::create(baseNode->document()); | 395 m_compositionRange = Range::create(baseNode->document()); |
| 387 m_compositionRange->setStart(baseNode, baseOffset); | 396 m_compositionRange->setStart(baseNode, baseOffset); |
| 388 m_compositionRange->setEnd(baseNode, extentOffset); | 397 m_compositionRange->setEnd(baseNode, extentOffset); |
| 389 | 398 |
| 390 if (baseNode->layoutObject()) | 399 if (baseNode->layoutObject()) |
| 391 baseNode->layoutObject()->setShouldDoFullPaintInvalidation(); | 400 baseNode->layoutObject()->setShouldDoFullPaintInvalidation(); |
| 392 | 401 |
| 393 // In case of exceeding the left boundary. | 402 setEditableSelectionOffsets(selectedRange); |
| 394 int selectionOffsetsStart = static_cast<int>(getSelectionOffsets().start()); | |
| 395 int start = std::max(selectionOffsetsStart + selectionStart, 0); | |
| 396 int end = std::max(selectionOffsetsStart + selectionEnd, start); | |
| 397 | |
| 398 Element* rootEditableElement = frame().selection().rootEditableElement(); | |
| 399 if (!rootEditableElement) | |
| 400 return; | |
| 401 | |
| 402 // In case of exceeding the right boundary. | |
| 403 // If both |value1| and |value2| exceed right boundary, | |
| 404 // PlainTextRange(value1, value2)::createRange() will return a default | |
| 405 // value, which is [0,0]. In order to get the correct Position in that case, | |
| 406 // we should make sure |value1| is within range at least. | |
| 407 const EphemeralRange& startRange = PlainTextRange(0, start).createRange(*roo tEditableElement); | |
| 408 const EphemeralRange& endRange = PlainTextRange(0, end).createRange(*rootEdi tableElement); | |
| 409 | |
| 410 // TODO(yabinh): There should be a better way to create |startPosition| and | |
| 411 // |endPosition|. But for now, since we can't get |anchorNode| and |offset|, | |
| 412 // we can't create the 2 Position objects directly. So we use | |
| 413 // PlainTextRange::createRange as a workaround. | |
| 414 const Position& startPosition = startRange.endPosition(); | |
| 415 const Position& endPosition = endRange.endPosition(); | |
| 416 const EphemeralRange selectedRange(startPosition, endPosition); | |
| 417 frame().selection().setSelectedRange(selectedRange, TextAffinity::Downstream , SelectionDirectionalMode::NonDirectional, NotUserTriggered); | |
| 418 | 403 |
| 419 if (underlines.isEmpty()) { | 404 if (underlines.isEmpty()) { |
| 420 frame().document()->markers().addCompositionMarker(m_compositionRange->s tartPosition(), m_compositionRange->endPosition(), Color::black, false, LayoutTh eme::theme().platformDefaultCompositionBackgroundColor()); | 405 frame().document()->markers().addCompositionMarker(m_compositionRange->s tartPosition(), m_compositionRange->endPosition(), Color::black, false, LayoutTh eme::theme().platformDefaultCompositionBackgroundColor()); |
| 421 return; | 406 return; |
| 422 } | 407 } |
| 423 for (const auto& underline : underlines) { | 408 for (const auto& underline : underlines) { |
| 424 unsigned underlineStart = baseOffset + underline.startOffset; | 409 unsigned underlineStart = baseOffset + underline.startOffset; |
| 425 unsigned underlineEnd = baseOffset + underline.endOffset; | 410 unsigned underlineEnd = baseOffset + underline.endOffset; |
| 426 EphemeralRange ephemeralLineRange = EphemeralRange(Position(baseNode, un derlineStart), Position(baseNode, underlineEnd)); | 411 EphemeralRange ephemeralLineRange = EphemeralRange(Position(baseNode, un derlineStart), Position(baseNode, underlineEnd)); |
| 427 if (ephemeralLineRange.isNull()) | 412 if (ephemeralLineRange.isNull()) |
| (...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 508 return frame().selection().setSelectedRange(range, VP_DEFAULT_AFFINITY, Sele ctionDirectionalMode::NonDirectional, FrameSelection::CloseTyping); | 493 return frame().selection().setSelectedRange(range, VP_DEFAULT_AFFINITY, Sele ctionDirectionalMode::NonDirectional, FrameSelection::CloseTyping); |
| 509 } | 494 } |
| 510 | 495 |
| 511 bool InputMethodController::setEditableSelectionOffsets(const PlainTextRange& se lectionOffsets) | 496 bool InputMethodController::setEditableSelectionOffsets(const PlainTextRange& se lectionOffsets) |
| 512 { | 497 { |
| 513 if (!editor().canEdit()) | 498 if (!editor().canEdit()) |
| 514 return false; | 499 return false; |
| 515 return setSelectionOffsets(selectionOffsets); | 500 return setSelectionOffsets(selectionOffsets); |
| 516 } | 501 } |
| 517 | 502 |
| 503 PlainTextRange InputMethodController::createRangeForSelection(int start, int end , size_t textLength) | |
| 504 { | |
| 505 // In case of exceeding the left boundary. | |
| 506 start = std::max(start, 0); | |
|
yosin_UTC9
2016/06/07 08:11:20
Adjusting |start| and |end| should be done in call
yabinh
2016/06/07 08:47:50
It's easy to handle the left boundary. But for the
| |
| 507 end = std::max(end, start); | |
| 508 | |
| 509 // In case of exceeding the right boundary. | |
| 510 Element* rootEditableElement = frame().selection().rootEditableElement(); | |
| 511 if (!rootEditableElement) | |
| 512 return PlainTextRange(); | |
| 513 const EphemeralRange& range = EphemeralRange::rangeOfContents(*rootEditableE lement); | |
| 514 | |
| 515 TextIteratorBehaviorFlags behaviorFlags = TextIteratorEmitsObjectReplacement Character; | |
|
yosin_UTC9
2016/06/07 08:11:20
nit: Should have |const|. We can express this in o
| |
| 516 behaviorFlags |= TextIteratorEmitsCharactersBetweenAllVisiblePositions; | |
| 517 TextIterator it(range.startPosition(), range.endPosition(), behaviorFlags); | |
| 518 | |
| 519 int rightBoundary = 0; | |
| 520 for (; !it.atEnd(); it.advance()) { | |
|
yosin_UTC9
2016/06/07 08:11:20
nit: No need to have {}
| |
| 521 rightBoundary += it.length(); | |
| 522 } | |
| 523 | |
| 524 if (hasComposition()) | |
| 525 rightBoundary -= (compositionRange()->endOffset() - compositionRange()-> startOffset()); | |
| 526 | |
| 527 rightBoundary += textLength; | |
| 528 | |
| 529 start = std::min(start, rightBoundary); | |
| 530 end = std::min(end, rightBoundary); | |
| 531 | |
| 532 return PlainTextRange(start, end); | |
| 533 } | |
| 534 | |
| 518 void InputMethodController::extendSelectionAndDelete(int before, int after) | 535 void InputMethodController::extendSelectionAndDelete(int before, int after) |
| 519 { | 536 { |
| 520 if (!editor().canEdit()) | 537 if (!editor().canEdit()) |
| 521 return; | 538 return; |
| 522 PlainTextRange selectionOffsets(getSelectionOffsets()); | 539 PlainTextRange selectionOffsets(getSelectionOffsets()); |
| 523 if (selectionOffsets.isNull()) | 540 if (selectionOffsets.isNull()) |
| 524 return; | 541 return; |
| 525 | 542 |
| 526 // A common call of before=1 and after=0 will fail if the last character | 543 // A common call of before=1 and after=0 will fail if the last character |
| 527 // is multi-code-word UTF-16, including both multi-16bit code-points and | 544 // is multi-code-word UTF-16, including both multi-16bit code-points and |
| (...skipping 20 matching lines...) Expand all Loading... | |
| 548 TypingCommand::deleteSelection(*frame().document()); | 565 TypingCommand::deleteSelection(*frame().document()); |
| 549 } | 566 } |
| 550 | 567 |
| 551 DEFINE_TRACE(InputMethodController) | 568 DEFINE_TRACE(InputMethodController) |
| 552 { | 569 { |
| 553 visitor->trace(m_frame); | 570 visitor->trace(m_frame); |
| 554 visitor->trace(m_compositionRange); | 571 visitor->trace(m_compositionRange); |
| 555 } | 572 } |
| 556 | 573 |
| 557 } // namespace blink | 574 } // namespace blink |
| OLD | NEW |