| 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 |
| 11 * notice, this list of conditions and the following disclaimer in the | 11 * notice, this list of conditions and the following disclaimer in the |
| 12 * documentation and/or other materials provided with the distribution. | 12 * documentation and/or other materials provided with the distribution. |
| 13 * | 13 * |
| 14 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY | 14 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY |
| 15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | 15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| 17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR | 17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR |
| 18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | 18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| 19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | 19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| 20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | 20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| 21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | 21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
| 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 "core/editing/InputMethodController.h" | 27 #include "core/editing/InputMethodController.h" |
| 28 | 28 |
| 29 #include "core/InputTypeNames.h" |
| 29 #include "core/dom/Document.h" | 30 #include "core/dom/Document.h" |
| 30 #include "core/dom/Element.h" | 31 #include "core/dom/Element.h" |
| 31 #include "core/dom/Text.h" | 32 #include "core/dom/Text.h" |
| 32 #include "core/editing/EditingUtilities.h" | 33 #include "core/editing/EditingUtilities.h" |
| 33 #include "core/editing/Editor.h" | 34 #include "core/editing/Editor.h" |
| 34 #include "core/editing/commands/TypingCommand.h" | 35 #include "core/editing/commands/TypingCommand.h" |
| 35 #include "core/editing/markers/DocumentMarkerController.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" |
| 39 #include "core/html/HTMLInputElement.h" |
| 38 #include "core/html/HTMLTextAreaElement.h" | 40 #include "core/html/HTMLTextAreaElement.h" |
| 39 #include "core/input/EventHandler.h" | 41 #include "core/input/EventHandler.h" |
| 40 #include "core/layout/LayoutObject.h" | 42 #include "core/layout/LayoutObject.h" |
| 41 #include "core/layout/LayoutTheme.h" | 43 #include "core/layout/LayoutTheme.h" |
| 42 #include "core/page/ChromeClient.h" | 44 #include "core/page/ChromeClient.h" |
| 43 | 45 |
| 44 namespace blink { | 46 namespace blink { |
| 45 | 47 |
| 46 namespace { | 48 namespace { |
| 47 | 49 |
| (...skipping 813 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 861 } while (frame().selection().start() == frame().selection().end() && | 863 } while (frame().selection().start() == frame().selection().end() && |
| 862 before <= static_cast<int>(selectionOffsets.start())); | 864 before <= static_cast<int>(selectionOffsets.start())); |
| 863 // TODO(chongz): Find a way to distinguish Forward and Backward. | 865 // TODO(chongz): Find a way to distinguish Forward and Backward. |
| 864 dispatchBeforeInputEditorCommand( | 866 dispatchBeforeInputEditorCommand( |
| 865 m_frame->document()->focusedElement(), | 867 m_frame->document()->focusedElement(), |
| 866 InputEvent::InputType::DeleteContentBackward, | 868 InputEvent::InputType::DeleteContentBackward, |
| 867 new RangeVector(1, m_frame->selection().firstRange())); | 869 new RangeVector(1, m_frame->selection().firstRange())); |
| 868 TypingCommand::deleteSelection(*frame().document()); | 870 TypingCommand::deleteSelection(*frame().document()); |
| 869 } | 871 } |
| 870 | 872 |
| 873 WebTextInputInfo InputMethodController::textInputInfo() const { |
| 874 WebTextInputInfo info; |
| 875 if (!frame().document()) |
| 876 return info; |
| 877 |
| 878 if (!frame().selection().isAvailable()) { |
| 879 // plugins/mouse-capture-inside-shadow.html reaches here. |
| 880 return info; |
| 881 } |
| 882 Element* element = frame().selection().rootEditableElement(); |
| 883 if (!element) |
| 884 return info; |
| 885 |
| 886 info.inputMode = inputModeOfFocusedElement(); |
| 887 info.type = textInputType(); |
| 888 info.flags = textInputFlags(); |
| 889 if (info.type == WebTextInputTypeNone) |
| 890 return info; |
| 891 |
| 892 if (!frame().editor().canEdit()) |
| 893 return info; |
| 894 |
| 895 // TODO(dglazkov): The use of updateStyleAndLayoutIgnorePendingStylesheets |
| 896 // needs to be audited. see http://crbug.com/590369 for more details. |
| 897 frame().document()->updateStyleAndLayoutIgnorePendingStylesheets(); |
| 898 |
| 899 DocumentLifecycle::DisallowTransitionScope disallowTransition( |
| 900 frame().document()->lifecycle()); |
| 901 |
| 902 // Emits an object replacement character for each replaced element so that |
| 903 // it is exposed to IME and thus could be deleted by IME on android. |
| 904 info.value = plainText(EphemeralRange::rangeOfContents(*element), |
| 905 TextIteratorEmitsObjectReplacementCharacter); |
| 906 |
| 907 if (info.value.isEmpty()) |
| 908 return info; |
| 909 |
| 910 EphemeralRange firstRange = |
| 911 firstEphemeralRangeOf(frame().selection().selection()); |
| 912 if (firstRange.isNotNull()) { |
| 913 PlainTextRange plainTextRange(PlainTextRange::create(*element, firstRange)); |
| 914 if (plainTextRange.isNotNull()) { |
| 915 info.selectionStart = plainTextRange.start(); |
| 916 info.selectionEnd = plainTextRange.end(); |
| 917 } |
| 918 } |
| 919 |
| 920 EphemeralRange range = compositionEphemeralRange(); |
| 921 if (range.isNotNull()) { |
| 922 PlainTextRange plainTextRange(PlainTextRange::create(*element, range)); |
| 923 if (plainTextRange.isNotNull()) { |
| 924 info.compositionStart = plainTextRange.start(); |
| 925 info.compositionEnd = plainTextRange.end(); |
| 926 } |
| 927 } |
| 928 |
| 929 return info; |
| 930 } |
| 931 |
| 932 int InputMethodController::textInputFlags() const { |
| 933 Element* element = frame().document()->focusedElement(); |
| 934 if (!element) |
| 935 return WebTextInputFlagNone; |
| 936 |
| 937 DEFINE_STATIC_LOCAL(AtomicString, autocompleteString, ("autocomplete")); |
| 938 DEFINE_STATIC_LOCAL(AtomicString, autocorrectString, ("autocorrect")); |
| 939 int flags = 0; |
| 940 |
| 941 const AtomicString& autocomplete = element->getAttribute(autocompleteString); |
| 942 if (autocomplete == "on") |
| 943 flags |= WebTextInputFlagAutocompleteOn; |
| 944 else if (autocomplete == "off") |
| 945 flags |= WebTextInputFlagAutocompleteOff; |
| 946 |
| 947 const AtomicString& autocorrect = element->getAttribute(autocorrectString); |
| 948 if (autocorrect == "on") |
| 949 flags |= WebTextInputFlagAutocorrectOn; |
| 950 else if (autocorrect == "off") |
| 951 flags |= WebTextInputFlagAutocorrectOff; |
| 952 |
| 953 SpellcheckAttributeState spellcheck = element->spellcheckAttributeState(); |
| 954 if (spellcheck == SpellcheckAttributeTrue) |
| 955 flags |= WebTextInputFlagSpellcheckOn; |
| 956 else if (spellcheck == SpellcheckAttributeFalse) |
| 957 flags |= WebTextInputFlagSpellcheckOff; |
| 958 |
| 959 if (isHTMLTextFormControlElement(element)) { |
| 960 HTMLTextFormControlElement* formElement = |
| 961 static_cast<HTMLTextFormControlElement*>(element); |
| 962 if (formElement->supportsAutocapitalize()) { |
| 963 DEFINE_STATIC_LOCAL(const AtomicString, none, ("none")); |
| 964 DEFINE_STATIC_LOCAL(const AtomicString, characters, ("characters")); |
| 965 DEFINE_STATIC_LOCAL(const AtomicString, words, ("words")); |
| 966 DEFINE_STATIC_LOCAL(const AtomicString, sentences, ("sentences")); |
| 967 |
| 968 const AtomicString& autocapitalize = formElement->autocapitalize(); |
| 969 if (autocapitalize == none) |
| 970 flags |= WebTextInputFlagAutocapitalizeNone; |
| 971 else if (autocapitalize == characters) |
| 972 flags |= WebTextInputFlagAutocapitalizeCharacters; |
| 973 else if (autocapitalize == words) |
| 974 flags |= WebTextInputFlagAutocapitalizeWords; |
| 975 else if (autocapitalize == sentences) |
| 976 flags |= WebTextInputFlagAutocapitalizeSentences; |
| 977 else |
| 978 NOTREACHED(); |
| 979 } |
| 980 } |
| 981 |
| 982 return flags; |
| 983 } |
| 984 |
| 985 WebString InputMethodController::inputModeOfFocusedElement() const { |
| 986 if (!RuntimeEnabledFeatures::inputModeAttributeEnabled()) |
| 987 return WebString(); |
| 988 |
| 989 Element* element = frame().document()->focusedElement(); |
| 990 if (!element) |
| 991 return WebString(); |
| 992 |
| 993 if (isHTMLInputElement(*element)) { |
| 994 const HTMLInputElement& input = toHTMLInputElement(*element); |
| 995 if (input.supportsInputModeAttribute()) |
| 996 return input.fastGetAttribute(HTMLNames::inputmodeAttr).lower(); |
| 997 return WebString(); |
| 998 } |
| 999 if (isHTMLTextAreaElement(*element)) { |
| 1000 const HTMLTextAreaElement& textarea = toHTMLTextAreaElement(*element); |
| 1001 return textarea.fastGetAttribute(HTMLNames::inputmodeAttr).lower(); |
| 1002 } |
| 1003 |
| 1004 return WebString(); |
| 1005 } |
| 1006 |
| 1007 WebTextInputType InputMethodController::textInputType() const { |
| 1008 if (!frame().selection().isAvailable()) { |
| 1009 // "mouse-capture-inside-shadow.html" reaches here. |
| 1010 return WebTextInputTypeNone; |
| 1011 } |
| 1012 |
| 1013 // It's important to preserve the equivalence of textInputInfo().type and |
| 1014 // textInputType(), so perform the same rootEditableElement() existence check |
| 1015 // here for consistency. |
| 1016 if (!frame().selection().selection().rootEditableElement()) |
| 1017 return WebTextInputTypeNone; |
| 1018 |
| 1019 Document* document = frame().document(); |
| 1020 if (!document) |
| 1021 return WebTextInputTypeNone; |
| 1022 |
| 1023 Element* element = document->focusedElement(); |
| 1024 if (!element) |
| 1025 return WebTextInputTypeNone; |
| 1026 |
| 1027 if (isHTMLInputElement(*element)) { |
| 1028 HTMLInputElement& input = toHTMLInputElement(*element); |
| 1029 const AtomicString& type = input.type(); |
| 1030 |
| 1031 if (input.isDisabledOrReadOnly()) |
| 1032 return WebTextInputTypeNone; |
| 1033 |
| 1034 if (type == InputTypeNames::password) |
| 1035 return WebTextInputTypePassword; |
| 1036 if (type == InputTypeNames::search) |
| 1037 return WebTextInputTypeSearch; |
| 1038 if (type == InputTypeNames::email) |
| 1039 return WebTextInputTypeEmail; |
| 1040 if (type == InputTypeNames::number) |
| 1041 return WebTextInputTypeNumber; |
| 1042 if (type == InputTypeNames::tel) |
| 1043 return WebTextInputTypeTelephone; |
| 1044 if (type == InputTypeNames::url) |
| 1045 return WebTextInputTypeURL; |
| 1046 if (type == InputTypeNames::text) |
| 1047 return WebTextInputTypeText; |
| 1048 |
| 1049 return WebTextInputTypeNone; |
| 1050 } |
| 1051 |
| 1052 if (isHTMLTextAreaElement(*element)) { |
| 1053 if (toHTMLTextAreaElement(*element).isDisabledOrReadOnly()) |
| 1054 return WebTextInputTypeNone; |
| 1055 return WebTextInputTypeTextArea; |
| 1056 } |
| 1057 |
| 1058 if (element->isHTMLElement()) { |
| 1059 if (toHTMLElement(element)->isDateTimeFieldElement()) |
| 1060 return WebTextInputTypeDateTimeField; |
| 1061 } |
| 1062 |
| 1063 document->updateStyleAndLayoutTree(); |
| 1064 if (hasEditableStyle(*element)) |
| 1065 return WebTextInputTypeContentEditable; |
| 1066 |
| 1067 return WebTextInputTypeNone; |
| 1068 } |
| 1069 |
| 871 DEFINE_TRACE(InputMethodController) { | 1070 DEFINE_TRACE(InputMethodController) { |
| 872 visitor->trace(m_frame); | 1071 visitor->trace(m_frame); |
| 873 visitor->trace(m_compositionRange); | 1072 visitor->trace(m_compositionRange); |
| 874 } | 1073 } |
| 875 | 1074 |
| 876 } // namespace blink | 1075 } // namespace blink |
| OLD | NEW |