| 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 876 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 924 const size_t diff = computeDistanceToRightGraphemeBoundary(position); | 926 const size_t diff = computeDistanceToRightGraphemeBoundary(position); |
| 925 const int adjustedEnd = end + static_cast<int>(diff); | 927 const int adjustedEnd = end + static_cast<int>(diff); |
| 926 if (!setSelectionOffsets(PlainTextRange(selectionEnd, adjustedEnd))) | 928 if (!setSelectionOffsets(PlainTextRange(selectionEnd, adjustedEnd))) |
| 927 return; | 929 return; |
| 928 TypingCommand::deleteSelection(*frame().document()); | 930 TypingCommand::deleteSelection(*frame().document()); |
| 929 } | 931 } |
| 930 | 932 |
| 931 setSelectionOffsets(PlainTextRange(selectionStart, selectionEnd)); | 933 setSelectionOffsets(PlainTextRange(selectionStart, selectionEnd)); |
| 932 } | 934 } |
| 933 | 935 |
| 936 WebTextInputInfo InputMethodController::textInputInfo() const { |
| 937 WebTextInputInfo info; |
| 938 if (!frame().document()) |
| 939 return info; |
| 940 |
| 941 if (!frame().selection().isAvailable()) { |
| 942 // plugins/mouse-capture-inside-shadow.html reaches here. |
| 943 return info; |
| 944 } |
| 945 Element* element = frame().selection().rootEditableElement(); |
| 946 if (!element) |
| 947 return info; |
| 948 |
| 949 info.inputMode = inputModeOfFocusedElement(); |
| 950 info.type = textInputType(); |
| 951 info.flags = textInputFlags(); |
| 952 if (info.type == WebTextInputTypeNone) |
| 953 return info; |
| 954 |
| 955 if (!frame().editor().canEdit()) |
| 956 return info; |
| 957 |
| 958 // TODO(dglazkov): The use of updateStyleAndLayoutIgnorePendingStylesheets |
| 959 // needs to be audited. see http://crbug.com/590369 for more details. |
| 960 frame().document()->updateStyleAndLayoutIgnorePendingStylesheets(); |
| 961 |
| 962 DocumentLifecycle::DisallowTransitionScope disallowTransition( |
| 963 frame().document()->lifecycle()); |
| 964 |
| 965 // Emits an object replacement character for each replaced element so that |
| 966 // it is exposed to IME and thus could be deleted by IME on android. |
| 967 info.value = plainText(EphemeralRange::rangeOfContents(*element), |
| 968 TextIteratorEmitsObjectReplacementCharacter); |
| 969 |
| 970 if (info.value.isEmpty()) |
| 971 return info; |
| 972 |
| 973 EphemeralRange firstRange = |
| 974 firstEphemeralRangeOf(frame().selection().selection()); |
| 975 if (firstRange.isNotNull()) { |
| 976 PlainTextRange plainTextRange(PlainTextRange::create(*element, firstRange)); |
| 977 if (plainTextRange.isNotNull()) { |
| 978 info.selectionStart = plainTextRange.start(); |
| 979 info.selectionEnd = plainTextRange.end(); |
| 980 } |
| 981 } |
| 982 |
| 983 EphemeralRange range = compositionEphemeralRange(); |
| 984 if (range.isNotNull()) { |
| 985 PlainTextRange plainTextRange(PlainTextRange::create(*element, range)); |
| 986 if (plainTextRange.isNotNull()) { |
| 987 info.compositionStart = plainTextRange.start(); |
| 988 info.compositionEnd = plainTextRange.end(); |
| 989 } |
| 990 } |
| 991 |
| 992 return info; |
| 993 } |
| 994 |
| 995 int InputMethodController::textInputFlags() const { |
| 996 Element* element = frame().document()->focusedElement(); |
| 997 if (!element) |
| 998 return WebTextInputFlagNone; |
| 999 |
| 1000 int flags = 0; |
| 1001 |
| 1002 const AtomicString& autocomplete = |
| 1003 element->getAttribute(HTMLNames::autocompleteAttr); |
| 1004 if (autocomplete == "on") |
| 1005 flags |= WebTextInputFlagAutocompleteOn; |
| 1006 else if (autocomplete == "off") |
| 1007 flags |= WebTextInputFlagAutocompleteOff; |
| 1008 |
| 1009 const AtomicString& autocorrect = |
| 1010 element->getAttribute(HTMLNames::autocorrectAttr); |
| 1011 if (autocorrect == "on") |
| 1012 flags |= WebTextInputFlagAutocorrectOn; |
| 1013 else if (autocorrect == "off") |
| 1014 flags |= WebTextInputFlagAutocorrectOff; |
| 1015 |
| 1016 SpellcheckAttributeState spellcheck = element->spellcheckAttributeState(); |
| 1017 if (spellcheck == SpellcheckAttributeTrue) |
| 1018 flags |= WebTextInputFlagSpellcheckOn; |
| 1019 else if (spellcheck == SpellcheckAttributeFalse) |
| 1020 flags |= WebTextInputFlagSpellcheckOff; |
| 1021 |
| 1022 if (isHTMLTextFormControlElement(element)) { |
| 1023 HTMLTextFormControlElement* formElement = |
| 1024 static_cast<HTMLTextFormControlElement*>(element); |
| 1025 if (formElement->supportsAutocapitalize()) { |
| 1026 DEFINE_STATIC_LOCAL(const AtomicString, none, ("none")); |
| 1027 DEFINE_STATIC_LOCAL(const AtomicString, characters, ("characters")); |
| 1028 DEFINE_STATIC_LOCAL(const AtomicString, words, ("words")); |
| 1029 DEFINE_STATIC_LOCAL(const AtomicString, sentences, ("sentences")); |
| 1030 |
| 1031 const AtomicString& autocapitalize = formElement->autocapitalize(); |
| 1032 if (autocapitalize == none) |
| 1033 flags |= WebTextInputFlagAutocapitalizeNone; |
| 1034 else if (autocapitalize == characters) |
| 1035 flags |= WebTextInputFlagAutocapitalizeCharacters; |
| 1036 else if (autocapitalize == words) |
| 1037 flags |= WebTextInputFlagAutocapitalizeWords; |
| 1038 else if (autocapitalize == sentences) |
| 1039 flags |= WebTextInputFlagAutocapitalizeSentences; |
| 1040 else |
| 1041 NOTREACHED(); |
| 1042 } |
| 1043 } |
| 1044 |
| 1045 return flags; |
| 1046 } |
| 1047 |
| 1048 String InputMethodController::inputModeOfFocusedElement() const { |
| 1049 if (!RuntimeEnabledFeatures::inputModeAttributeEnabled()) |
| 1050 return String(); |
| 1051 |
| 1052 Element* element = frame().document()->focusedElement(); |
| 1053 if (!element) |
| 1054 return String(); |
| 1055 |
| 1056 if (isHTMLInputElement(*element)) { |
| 1057 const HTMLInputElement& input = toHTMLInputElement(*element); |
| 1058 if (input.supportsInputModeAttribute()) |
| 1059 return input.fastGetAttribute(HTMLNames::inputmodeAttr).lower(); |
| 1060 return String(); |
| 1061 } |
| 1062 if (isHTMLTextAreaElement(*element)) { |
| 1063 const HTMLTextAreaElement& textarea = toHTMLTextAreaElement(*element); |
| 1064 return textarea.fastGetAttribute(HTMLNames::inputmodeAttr).lower(); |
| 1065 } |
| 1066 |
| 1067 return String(); |
| 1068 } |
| 1069 |
| 1070 WebTextInputType InputMethodController::textInputType() const { |
| 1071 if (!frame().selection().isAvailable()) { |
| 1072 // "mouse-capture-inside-shadow.html" reaches here. |
| 1073 return WebTextInputTypeNone; |
| 1074 } |
| 1075 |
| 1076 // It's important to preserve the equivalence of textInputInfo().type and |
| 1077 // textInputType(), so perform the same rootEditableElement() existence check |
| 1078 // here for consistency. |
| 1079 if (!frame().selection().selection().rootEditableElement()) |
| 1080 return WebTextInputTypeNone; |
| 1081 |
| 1082 Document* document = frame().document(); |
| 1083 if (!document) |
| 1084 return WebTextInputTypeNone; |
| 1085 |
| 1086 Element* element = document->focusedElement(); |
| 1087 if (!element) |
| 1088 return WebTextInputTypeNone; |
| 1089 |
| 1090 if (isHTMLInputElement(*element)) { |
| 1091 HTMLInputElement& input = toHTMLInputElement(*element); |
| 1092 const AtomicString& type = input.type(); |
| 1093 |
| 1094 if (input.isDisabledOrReadOnly()) |
| 1095 return WebTextInputTypeNone; |
| 1096 |
| 1097 if (type == InputTypeNames::password) |
| 1098 return WebTextInputTypePassword; |
| 1099 if (type == InputTypeNames::search) |
| 1100 return WebTextInputTypeSearch; |
| 1101 if (type == InputTypeNames::email) |
| 1102 return WebTextInputTypeEmail; |
| 1103 if (type == InputTypeNames::number) |
| 1104 return WebTextInputTypeNumber; |
| 1105 if (type == InputTypeNames::tel) |
| 1106 return WebTextInputTypeTelephone; |
| 1107 if (type == InputTypeNames::url) |
| 1108 return WebTextInputTypeURL; |
| 1109 if (type == InputTypeNames::text) |
| 1110 return WebTextInputTypeText; |
| 1111 |
| 1112 return WebTextInputTypeNone; |
| 1113 } |
| 1114 |
| 1115 if (isHTMLTextAreaElement(*element)) { |
| 1116 if (toHTMLTextAreaElement(*element).isDisabledOrReadOnly()) |
| 1117 return WebTextInputTypeNone; |
| 1118 return WebTextInputTypeTextArea; |
| 1119 } |
| 1120 |
| 1121 if (element->isHTMLElement()) { |
| 1122 if (toHTMLElement(element)->isDateTimeFieldElement()) |
| 1123 return WebTextInputTypeDateTimeField; |
| 1124 } |
| 1125 |
| 1126 document->updateStyleAndLayoutTree(); |
| 1127 if (hasEditableStyle(*element)) |
| 1128 return WebTextInputTypeContentEditable; |
| 1129 |
| 1130 return WebTextInputTypeNone; |
| 1131 } |
| 1132 |
| 934 DEFINE_TRACE(InputMethodController) { | 1133 DEFINE_TRACE(InputMethodController) { |
| 935 visitor->trace(m_frame); | 1134 visitor->trace(m_frame); |
| 936 visitor->trace(m_compositionRange); | 1135 visitor->trace(m_compositionRange); |
| 937 } | 1136 } |
| 938 | 1137 |
| 939 } // namespace blink | 1138 } // namespace blink |
| OLD | NEW |