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 |
324 // Dispatch an appropriate composition event to the focused node. | 329 // Dispatch an appropriate composition event to the focused node. |
325 // We check the composition status and choose an appropriate composition eve
nt since this | 330 // We check the composition status and choose an appropriate composition eve
nt since this |
326 // function is used for three purposes: | 331 // function is used for three purposes: |
327 // 1. Starting a new composition. | 332 // 1. Starting a new composition. |
328 // Send a compositionstart and a compositionupdate event when this functi
on creates | 333 // Send a compositionstart and a compositionupdate event when this functi
on creates |
329 // a new composition node, i.e. | 334 // a new composition node, i.e. |
330 // !hasComposition() && !text.isEmpty(). | 335 // !hasComposition() && !text.isEmpty(). |
331 // Sending a compositionupdate event at this time ensures that at least o
ne | 336 // Sending a compositionupdate event at this time ensures that at least o
ne |
332 // compositionupdate event is dispatched. | 337 // compositionupdate event is dispatched. |
333 // 2. Updating the existing composition node. | 338 // 2. Updating the existing composition node. |
334 // Send a compositionupdate event when this function updates the existing
composition | 339 // Send a compositionupdate event when this function updates the existing
composition |
335 // node, i.e. hasComposition() && !text.isEmpty(). | 340 // node, i.e. hasComposition() && !text.isEmpty(). |
336 // 3. Canceling the ongoing composition. | 341 // 3. Canceling the ongoing composition. |
337 // Send a compositionend event when function deletes the existing composi
tion node, i.e. | 342 // Send a compositionend event when function deletes the existing composi
tion node, i.e. |
338 // !hasComposition() && test.isEmpty(). | 343 // !hasComposition() && test.isEmpty(). |
339 if (text.isEmpty()) { | 344 if (text.isEmpty()) { |
340 if (hasComposition()) { | 345 if (hasComposition()) { |
341 confirmComposition(emptyString()); | 346 confirmComposition(emptyString()); |
342 return; | 347 } else { |
| 348 // It's weird to call |setComposition()| with empty text outside com
position, however some IME |
| 349 // (e.g. Japanese IBus-Anthy) did this, so we simply delete selectio
n without sending extra events. |
| 350 TypingCommand::deleteSelection(*frame().document(), TypingCommand::P
reventSpellChecking); |
343 } | 351 } |
344 // It's weird to call |setComposition()| with empty text outside composi
tion, however some IME | 352 |
345 // (e.g. Japanese IBus-Anthy) did this, so we simply delete selection wi
thout sending extra events. | 353 setEditableSelectionOffsets(selectedRange); |
346 TypingCommand::deleteSelection(*frame().document(), TypingCommand::Preve
ntSpellChecking); | |
347 return; | 354 return; |
348 } | 355 } |
349 | 356 |
350 // We should send a 'compositionstart' event only when the given text is not
empty because this | 357 // 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. | 358 // function doesn't create a composition node when the text is empty. |
352 if (!hasComposition()) { | 359 if (!hasComposition()) { |
353 target->dispatchEvent(CompositionEvent::create(EventTypeNames::compositi
onstart, frame().domWindow(), frame().selectedText())); | 360 target->dispatchEvent(CompositionEvent::create(EventTypeNames::compositi
onstart, frame().domWindow(), frame().selectedText())); |
354 if (!frame().document()) | 361 if (!frame().document()) |
355 return; | 362 return; |
356 } | 363 } |
(...skipping 30 matching lines...) Expand all Loading... |
387 m_isDirty = true; | 394 m_isDirty = true; |
388 m_hasComposition = true; | 395 m_hasComposition = true; |
389 if (!m_compositionRange) | 396 if (!m_compositionRange) |
390 m_compositionRange = Range::create(baseNode->document()); | 397 m_compositionRange = Range::create(baseNode->document()); |
391 m_compositionRange->setStart(baseNode, baseOffset); | 398 m_compositionRange->setStart(baseNode, baseOffset); |
392 m_compositionRange->setEnd(baseNode, extentOffset); | 399 m_compositionRange->setEnd(baseNode, extentOffset); |
393 | 400 |
394 if (baseNode->layoutObject()) | 401 if (baseNode->layoutObject()) |
395 baseNode->layoutObject()->setShouldDoFullPaintInvalidation(); | 402 baseNode->layoutObject()->setShouldDoFullPaintInvalidation(); |
396 | 403 |
397 // In case of exceeding the left boundary. | 404 // We shouldn't close typing in the middle of setComposition. |
398 int selectionOffsetsStart = static_cast<int>(getSelectionOffsets().start()); | 405 setEditableSelectionOffsets(selectedRange, NotUserTriggered); |
399 int start = std::max(selectionOffsetsStart + selectionStart, 0); | |
400 int end = std::max(selectionOffsetsStart + selectionEnd, start); | |
401 | |
402 Element* rootEditableElement = frame().selection().rootEditableElement(); | |
403 if (!rootEditableElement) | |
404 return; | |
405 | |
406 // In case of exceeding the right boundary. | |
407 // If both |value1| and |value2| exceed right boundary, | |
408 // PlainTextRange(value1, value2)::createRange() will return a default | |
409 // value, which is [0,0]. In order to get the correct Position in that case, | |
410 // we should make sure |value1| is within range at least. | |
411 const EphemeralRange& startRange = PlainTextRange(0, start).createRange(*roo
tEditableElement); | |
412 const EphemeralRange& endRange = PlainTextRange(0, end).createRange(*rootEdi
tableElement); | |
413 | |
414 // TODO(yabinh): There should be a better way to create |startPosition| and | |
415 // |endPosition|. But for now, since we can't get |anchorNode| and |offset|, | |
416 // we can't create the 2 Position objects directly. So we use | |
417 // PlainTextRange::createRange as a workaround. | |
418 const Position& startPosition = startRange.endPosition(); | |
419 const Position& endPosition = endRange.endPosition(); | |
420 const EphemeralRange selectedRange(startPosition, endPosition); | |
421 frame().selection().setSelectedRange(selectedRange, TextAffinity::Downstream
, SelectionDirectionalMode::NonDirectional, NotUserTriggered); | |
422 | 406 |
423 if (underlines.isEmpty()) { | 407 if (underlines.isEmpty()) { |
424 frame().document()->markers().addCompositionMarker(m_compositionRange->s
tartPosition(), m_compositionRange->endPosition(), Color::black, false, LayoutTh
eme::theme().platformDefaultCompositionBackgroundColor()); | 408 frame().document()->markers().addCompositionMarker(m_compositionRange->s
tartPosition(), m_compositionRange->endPosition(), Color::black, false, LayoutTh
eme::theme().platformDefaultCompositionBackgroundColor()); |
425 return; | 409 return; |
426 } | 410 } |
427 for (const auto& underline : underlines) { | 411 for (const auto& underline : underlines) { |
428 unsigned underlineStart = baseOffset + underline.startOffset; | 412 unsigned underlineStart = baseOffset + underline.startOffset; |
429 unsigned underlineEnd = baseOffset + underline.endOffset; | 413 unsigned underlineEnd = baseOffset + underline.endOffset; |
430 EphemeralRange ephemeralLineRange = EphemeralRange(Position(baseNode, un
derlineStart), Position(baseNode, underlineEnd)); | 414 EphemeralRange ephemeralLineRange = EphemeralRange(Position(baseNode, un
derlineStart), Position(baseNode, underlineEnd)); |
431 if (ephemeralLineRange.isNull()) | 415 if (ephemeralLineRange.isNull()) |
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
494 PlainTextRange InputMethodController::getSelectionOffsets() const | 478 PlainTextRange InputMethodController::getSelectionOffsets() const |
495 { | 479 { |
496 EphemeralRange range = firstEphemeralRangeOf(frame().selection().selection()
); | 480 EphemeralRange range = firstEphemeralRangeOf(frame().selection().selection()
); |
497 if (range.isNull()) | 481 if (range.isNull()) |
498 return PlainTextRange(); | 482 return PlainTextRange(); |
499 ContainerNode* editable = frame().selection().rootEditableElementOrTreeScope
RootNode(); | 483 ContainerNode* editable = frame().selection().rootEditableElementOrTreeScope
RootNode(); |
500 DCHECK(editable); | 484 DCHECK(editable); |
501 return PlainTextRange::create(*editable, range); | 485 return PlainTextRange::create(*editable, range); |
502 } | 486 } |
503 | 487 |
504 bool InputMethodController::setSelectionOffsets(const PlainTextRange& selectionO
ffsets) | 488 bool InputMethodController::setSelectionOffsets(const PlainTextRange& selectionO
ffsets, FrameSelection::SetSelectionOptions options) |
505 { | 489 { |
506 if (selectionOffsets.isNull()) | 490 if (selectionOffsets.isNull()) |
507 return false; | 491 return false; |
508 Element* rootEditableElement = frame().selection().rootEditableElement(); | 492 Element* rootEditableElement = frame().selection().rootEditableElement(); |
509 if (!rootEditableElement) | 493 if (!rootEditableElement) |
510 return false; | 494 return false; |
511 | 495 |
512 // TODO(dglazkov): The use of updateStyleAndLayoutIgnorePendingStylesheets n
eeds to be audited. | 496 // TODO(dglazkov): The use of updateStyleAndLayoutIgnorePendingStylesheets n
eeds to be audited. |
513 // see http://crbug.com/590369 for more details. | 497 // see http://crbug.com/590369 for more details. |
514 rootEditableElement->document().updateStyleAndLayoutIgnorePendingStylesheets
(); | 498 rootEditableElement->document().updateStyleAndLayoutIgnorePendingStylesheets
(); |
515 | 499 |
516 const EphemeralRange range = selectionOffsets.createRange(*rootEditableEleme
nt); | 500 const EphemeralRange range = selectionOffsets.createRange(*rootEditableEleme
nt); |
517 if (range.isNull()) | 501 if (range.isNull()) |
518 return false; | 502 return false; |
519 | 503 |
520 return frame().selection().setSelectedRange(range, VP_DEFAULT_AFFINITY, Sele
ctionDirectionalMode::NonDirectional, FrameSelection::CloseTyping); | 504 return frame().selection().setSelectedRange(range, VP_DEFAULT_AFFINITY, Sele
ctionDirectionalMode::NonDirectional, options); |
521 } | 505 } |
522 | 506 |
523 bool InputMethodController::setEditableSelectionOffsets(const PlainTextRange& se
lectionOffsets) | 507 bool InputMethodController::setEditableSelectionOffsets(const PlainTextRange& se
lectionOffsets, FrameSelection::SetSelectionOptions options) |
524 { | 508 { |
525 if (!editor().canEdit()) | 509 if (!editor().canEdit()) |
526 return false; | 510 return false; |
527 return setSelectionOffsets(selectionOffsets); | 511 return setSelectionOffsets(selectionOffsets, options); |
| 512 } |
| 513 |
| 514 PlainTextRange InputMethodController::createRangeForSelection(int start, int end
, size_t textLength) const |
| 515 { |
| 516 // In case of exceeding the left boundary. |
| 517 start = std::max(start, 0); |
| 518 end = std::max(end, start); |
| 519 |
| 520 Element* rootEditableElement = frame().selection().rootEditableElement(); |
| 521 if (!rootEditableElement) |
| 522 return PlainTextRange(); |
| 523 const EphemeralRange& range = EphemeralRange::rangeOfContents(*rootEditableE
lement); |
| 524 if (range.isNull()) |
| 525 return PlainTextRange(); |
| 526 |
| 527 const TextIteratorBehaviorFlags behaviorFlags = TextIteratorEmitsObjectRepla
cementCharacter | TextIteratorEmitsCharactersBetweenAllVisiblePositions; |
| 528 TextIterator it(range.startPosition(), range.endPosition(), behaviorFlags); |
| 529 |
| 530 int rightBoundary = 0; |
| 531 for (; !it.atEnd(); it.advance()) |
| 532 rightBoundary += it.length(); |
| 533 |
| 534 if (hasComposition()) |
| 535 rightBoundary -= compositionRange()->text().length(); |
| 536 |
| 537 rightBoundary += textLength; |
| 538 |
| 539 // In case of exceeding the right boundary. |
| 540 start = std::min(start, rightBoundary); |
| 541 end = std::min(end, rightBoundary); |
| 542 |
| 543 return PlainTextRange(start, end); |
528 } | 544 } |
529 | 545 |
530 void InputMethodController::extendSelectionAndDelete(int before, int after) | 546 void InputMethodController::extendSelectionAndDelete(int before, int after) |
531 { | 547 { |
532 if (!editor().canEdit()) | 548 if (!editor().canEdit()) |
533 return; | 549 return; |
534 PlainTextRange selectionOffsets(getSelectionOffsets()); | 550 PlainTextRange selectionOffsets(getSelectionOffsets()); |
535 if (selectionOffsets.isNull()) | 551 if (selectionOffsets.isNull()) |
536 return; | 552 return; |
537 | 553 |
(...skipping 21 matching lines...) Expand all Loading... |
559 TypingCommand::deleteSelection(*frame().document()); | 575 TypingCommand::deleteSelection(*frame().document()); |
560 } | 576 } |
561 | 577 |
562 DEFINE_TRACE(InputMethodController) | 578 DEFINE_TRACE(InputMethodController) |
563 { | 579 { |
564 visitor->trace(m_frame); | 580 visitor->trace(m_frame); |
565 visitor->trace(m_compositionRange); | 581 visitor->trace(m_compositionRange); |
566 } | 582 } |
567 | 583 |
568 } // namespace blink | 584 } // namespace blink |
OLD | NEW |