Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(424)

Unified Diff: third_party/WebKit/Source/web/WebFrameWidgetImpl.cpp

Issue 2029423003: OOPIF IME: Renderer Side Changes (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Addressing lfg@'s comments. Created 4 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: third_party/WebKit/Source/web/WebFrameWidgetImpl.cpp
diff --git a/third_party/WebKit/Source/web/WebFrameWidgetImpl.cpp b/third_party/WebKit/Source/web/WebFrameWidgetImpl.cpp
index 063029b1238e07c7c351984fd35326c5874baa40..2ff772538b969c387606a2670355f7342c6365f1 100644
--- a/third_party/WebKit/Source/web/WebFrameWidgetImpl.cpp
+++ b/third_party/WebKit/Source/web/WebFrameWidgetImpl.cpp
@@ -30,6 +30,7 @@
#include "web/WebFrameWidgetImpl.h"
+#include "core/InputTypeNames.h"
#include "core/editing/EditingUtilities.h"
#include "core/editing/Editor.h"
#include "core/editing/FrameSelection.h"
@@ -40,6 +41,8 @@
#include "core/frame/RemoteFrame.h"
#include "core/frame/Settings.h"
#include "core/frame/VisualViewport.h"
+#include "core/html/HTMLInputElement.h"
+#include "core/html/HTMLTextAreaElement.h"
#include "core/input/EventHandler.h"
#include "core/layout/LayoutView.h"
#include "core/layout/api/LayoutViewItem.h"
@@ -51,7 +54,10 @@
#include "platform/KeyboardCodes.h"
#include "platform/graphics/CompositorMutatorClient.h"
#include "public/platform/WebFrameScheduler.h"
+#include "public/web/WebPlugin.h"
+#include "public/web/WebRange.h"
#include "public/web/WebWidgetClient.h"
+#include "web/CompositionUnderlineVectorBuilder.h"
#include "web/CompositorMutatorImpl.h"
#include "web/CompositorProxyClientImpl.h"
#include "web/ContextMenuAllowedScope.h"
@@ -104,6 +110,7 @@ WebFrameWidgetImpl::WebFrameWidgetImpl(WebWidgetClient* client, WebLocalFrame* l
, m_suppressNextKeypressEvent(false)
, m_ignoreInputEvents(false)
, m_isTransparent(false)
+ , m_imeAcceptEvents(true)
lfg 2016/07/07 15:13:31 Should this be initialized to false?
EhsanK 2016/07/07 20:25:08 WebViewImpl initializes this to true. But I guess
lfg 2016/07/07 20:58:27 Yes, indeed.
, m_selfKeepAlive(this)
{
DCHECK(m_localRoot->frame()->isLocalRoot());
@@ -439,6 +446,7 @@ void WebFrameWidgetImpl::mouseCaptureLost()
void WebFrameWidgetImpl::setFocus(bool enable)
{
+ m_imeAcceptEvents = enable;
lfg 2016/07/07 15:13:31 Is it possible for the widget to have focus, but m
EhsanK 2016/07/07 20:25:08 m_imeAcceptEvents is apparent tracking page level
lfg 2016/07/07 20:58:27 That's right, can you dig a bit more to see if we
EhsanK 2016/07/11 20:29:53 I have explained what might happen in WebViewImpl:
page()->focusController().setFocused(enable);
if (enable) {
page()->focusController().setActive(true);
@@ -470,42 +478,239 @@ bool WebFrameWidgetImpl::setComposition(
int selectionStart,
int selectionEnd)
{
- // FIXME: To be implemented.
- return false;
+ if (!m_imeAcceptEvents)
+ return false;
+
+ LocalFrame* focused = focusedLocalFrameInWidget();
+
+ if (!focused)
+ return false;
+
+ if (WebPlugin* plugin = focusedPluginIfInputMethodSupported(focused))
+ return plugin->setComposition(text, underlines, selectionStart, selectionEnd);
+
+ // The input focus has been moved to another WebWidget object.
+ // We should use this |editor| object only to complete the ongoing
+ // composition.
+ InputMethodController& inputMethodController = focused->inputMethodController();
+ if (!focused->editor().canEdit() && !inputMethodController.hasComposition())
+ return false;
+
+ // We should verify the parent node of this IME composition node are
+ // editable because JavaScript may delete a parent node of the composition
+ // node. In this case, WebKit crashes while deleting texts from the parent
+ // node, which doesn't exist any longer.
+ const EphemeralRange range = inputMethodController.compositionEphemeralRange();
+ if (range.isNotNull()) {
+ Node* node = range.startPosition().computeContainerNode();
+ if (!node || !node->isContentEditable())
+ return false;
+ }
+
+ // A keypress event is canceled. If an ongoing composition exists, then the
+ // keydown event should have arisen from a handled key (e.g., backspace).
+ // In this case we ignore the cancellation and continue; otherwise (no
+ // ongoing composition) we exit and signal success only for attempts to
+ // clear the composition.
+ if (m_suppressNextKeypressEvent && !inputMethodController.hasComposition())
+ return text.isEmpty();
+
+ UserGestureIndicator gestureIndicator(DefinitelyProcessingNewUserGesture);
+
+ // When the range of composition underlines overlap with the range between
+ // selectionStart and selectionEnd, WebKit somehow won't paint the selection
+ // at all (see InlineTextBox::paint() function in InlineTextBox.cpp).
+ // But the selection range actually takes effect.
+ inputMethodController.setComposition(String(text),
+ CompositionUnderlineVectorBuilder(underlines),
+ selectionStart, selectionEnd);
+
+ return text.isEmpty() || inputMethodController.hasComposition();
}
bool WebFrameWidgetImpl::confirmComposition()
{
- // FIXME: To be implemented.
- return false;
+ return confirmComposition(DoNotKeepSelection);
}
bool WebFrameWidgetImpl::confirmComposition(ConfirmCompositionBehavior selectionBehavior)
{
- // FIXME: To be implemented.
- return false;
+ return confirmComposition(WebString(), selectionBehavior);
}
bool WebFrameWidgetImpl::confirmComposition(const WebString& text)
{
- // FIXME: To be implemented.
- return false;
+ UserGestureIndicator gestureIndicator(DefinitelyProcessingNewUserGesture);
+ return confirmComposition(text, DoNotKeepSelection);
+}
+
+bool WebFrameWidgetImpl::confirmComposition(const WebString& text, ConfirmCompositionBehavior selectionBehavior) const
+{
+ if (!m_imeAcceptEvents)
+ return false;
+
+ LocalFrame* focused = focusedLocalFrameInWidget();
+ if (!focused)
+ return false;
+
+ if (WebPlugin* plugin = focusedPluginIfInputMethodSupported(focused))
+ return plugin->confirmComposition(text, selectionBehavior);
+
+ return focused->inputMethodController().confirmCompositionOrInsertText(text, selectionBehavior == KeepSelection ? InputMethodController::KeepSelection : InputMethodController::DoNotKeepSelection);
}
bool WebFrameWidgetImpl::compositionRange(size_t* location, size_t* length)
{
- // FIXME: To be implemented.
- return false;
+ if (!m_imeAcceptEvents)
+ return false;
+
+ LocalFrame* focused = focusedLocalFrameInWidget();
+ if (!focused)
+ return false;
+
+ const EphemeralRange range = focused->inputMethodController().compositionEphemeralRange();
+ if (range.isNull())
+ return false;
+
+ Element* editable = focused->selection().rootEditableElementOrDocumentElement();
+ DCHECK(editable);
+ PlainTextRange plainTextRange(PlainTextRange::create(*editable, range));
+ if (plainTextRange.isNull())
+ return false;
+ *location = plainTextRange.start();
+ *length = plainTextRange.length();
+ return true;
}
WebTextInputInfo WebFrameWidgetImpl::textInputInfo()
{
- return view()->textInputInfo();
+ WebTextInputInfo info;
+
+ LocalFrame* focused = focusedLocalFrameInWidget();
+ if (!focused)
+ return info;
+
+ FrameSelection& selection = focused->selection();
+ if (!selection.isAvailable()) {
+ // plugins/mouse-capture-inside-shadow.html reaches here.
+ return info;
+ }
+ Element* element = selection.selection().rootEditableElement();
+ if (!element)
+ return info;
+
+ info.inputMode = inputModeOfFocusedElement();
+
+ info.type = textInputType();
+ info.flags = textInputFlags();
+ if (info.type == WebTextInputTypeNone)
+ return info;
+
+ if (!focused->editor().canEdit())
+ return info;
+
+ // Emits an object replacement character for each replaced element so that
+ // it is exposed to IME and thus could be deleted by IME on android.
+ info.value = plainText(EphemeralRange::rangeOfContents(*element), TextIteratorEmitsObjectReplacementCharacter);
+
+ if (info.value.isEmpty())
+ return info;
+
+ EphemeralRange firstRange = firstEphemeralRangeOf(selection.selection());
+ if (firstRange.isNotNull()) {
+ PlainTextRange plainTextRange(PlainTextRange::create(*element, firstRange));
+ if (plainTextRange.isNotNull()) {
+ info.selectionStart = plainTextRange.start();
+ info.selectionEnd = plainTextRange.end();
+ }
+ }
+
+ EphemeralRange range = focused->inputMethodController().compositionEphemeralRange();
+ if (range.isNotNull()) {
+ PlainTextRange plainTextRange(PlainTextRange::create(*element, range));
+ if (plainTextRange.isNotNull()) {
+ info.compositionStart = plainTextRange.start();
+ info.compositionEnd = plainTextRange.end();
+ }
+ }
+
+ return info;
}
WebTextInputType WebFrameWidgetImpl::textInputType()
{
- return view()->textInputType();
+ LocalFrame* focusedFrame = focusedLocalFrameInWidget();
+ if (!focusedFrame)
+ return WebTextInputTypeNone;
+
+ if (!focusedFrame->selection().isAvailable()) {
+ // "mouse-capture-inside-shadow.html" reaches here.
+ return WebTextInputTypeNone;
+ }
+
+ // It's important to preserve the equivalence of textInputInfo().type and textInputType(),
+ // so perform the same rootEditableElement() existence check here for consistency.
+ if (!focusedFrame->selection().selection().rootEditableElement())
+ return WebTextInputTypeNone;
+
+ Document* document = focusedFrame->document();
+ if (!document)
+ return WebTextInputTypeNone;
+
+ Element* element = document->focusedElement();
+ if (!element)
+ return WebTextInputTypeNone;
+
+ if (isHTMLInputElement(*element)) {
+ HTMLInputElement& input = toHTMLInputElement(*element);
+ const AtomicString& type = input.type();
+
+ if (input.isDisabledOrReadOnly())
+ return WebTextInputTypeNone;
+
+ if (type == InputTypeNames::password)
+ return WebTextInputTypePassword;
+ if (type == InputTypeNames::search)
+ return WebTextInputTypeSearch;
+ if (type == InputTypeNames::email)
+ return WebTextInputTypeEmail;
+ if (type == InputTypeNames::number)
+ return WebTextInputTypeNumber;
+ if (type == InputTypeNames::tel)
+ return WebTextInputTypeTelephone;
+ if (type == InputTypeNames::url)
+ return WebTextInputTypeURL;
+ if (type == InputTypeNames::date)
+ return WebTextInputTypeDate;
+ if (type == InputTypeNames::datetime_local)
+ return WebTextInputTypeDateTimeLocal;
+ if (type == InputTypeNames::month)
+ return WebTextInputTypeMonth;
+ if (type == InputTypeNames::time)
+ return WebTextInputTypeTime;
+ if (type == InputTypeNames::week)
+ return WebTextInputTypeWeek;
+ if (type == InputTypeNames::text)
+ return WebTextInputTypeText;
+
+ return WebTextInputTypeNone;
+ }
+
+ if (isHTMLTextAreaElement(*element)) {
+ if (toHTMLTextAreaElement(*element).isDisabledOrReadOnly())
+ return WebTextInputTypeNone;
+ return WebTextInputTypeTextArea;
+ }
+
+ if (element->isHTMLElement()) {
+ if (toHTMLElement(element)->isDateTimeFieldElement())
+ return WebTextInputTypeDateTimeField;
+ }
+
+ if (element->isContentEditable(Node::UserSelectAllIsAlwaysNonEditable))
+ return WebTextInputTypeContentEditable;
+
+ return WebTextInputTypeNone;
}
WebColor WebFrameWidgetImpl::backgroundColor() const
@@ -664,6 +869,44 @@ void WebFrameWidgetImpl::didLosePointerLock()
page()->pointerLockController().didLosePointerLock();
}
+bool WebFrameWidgetImpl::getCompositionCharacterBounds(WebVector<WebRect>& bounds)
+{
+ size_t offset = 0;
+ size_t characterCount = 0;
+ if (!compositionRange(&offset, &characterCount))
+ return false;
+
+ if (characterCount == 0)
+ return false;
+
+ LocalFrame* frame = focusedLocalFrameInWidget();
+ if (!frame)
+ return false;
+
+ WebLocalFrameImpl* webLocalFrame = WebLocalFrameImpl::fromFrame(frame);
+ WebVector<WebRect> result(characterCount);
+ WebRect webrect;
+ for (size_t i = 0; i < characterCount; ++i) {
+ if (!webLocalFrame->firstRectForCharacterRange(offset + i, 1, webrect)) {
+ DLOG(ERROR) << "Could not retrieve character rectangle at " << i;
+ return false;
+ }
+ result[i] = webrect;
+ }
+ bounds.swap(result);
+ return true;
+}
+
+void WebFrameWidgetImpl::applyReplacementRange(int start, int length)
+{
+ if (LocalFrame* frame = focusedLocalFrameInWidget()) {
+ WebLocalFrameImpl* webLocalFrame = WebLocalFrameImpl::fromFrame(frame);
+ WebRange webrange = WebRange::fromDocumentRange(webLocalFrame, start, length);
+ if (!webrange.isNull())
+ webLocalFrame->selectRange(webrange);
+ }
+}
+
void WebFrameWidgetImpl::handleMouseLeave(LocalFrame& mainFrame, const WebMouseEvent& event)
{
// FIXME: WebWidget doesn't have the method below.
@@ -1124,4 +1367,94 @@ HitTestResult WebFrameWidgetImpl::hitTestResultForRootFramePos(const IntPoint& p
return result;
}
+LocalFrame* WebFrameWidgetImpl::focusedLocalFrameInWidget() const
+{
+ LocalFrame* frame = page()->focusController().focusedFrame();
+ return (frame && frame->localFrameRoot() == m_localRoot->frame()) ? frame : nullptr;
+}
+
+WebPlugin* WebFrameWidgetImpl::focusedPluginIfInputMethodSupported(LocalFrame* frame) const
+{
+ WebPluginContainerImpl* container = WebLocalFrameImpl::currentPluginContainer(frame);
+ if (container && container->supportsInputMethod())
+ return container->plugin();
+ return nullptr;
+}
+
+WebString WebFrameWidgetImpl::inputModeOfFocusedElement() const
+{
+ if (!RuntimeEnabledFeatures::inputModeAttributeEnabled())
+ return WebString();
+
+ Element* element = focusedElement();
+ if (!element)
+ return WebString();
+
+ if (isHTMLInputElement(*element)) {
+ const HTMLInputElement& input = toHTMLInputElement(*element);
+ if (input.supportsInputModeAttribute())
+ return input.fastGetAttribute(HTMLNames::inputmodeAttr).lower();
+ return WebString();
+ }
+ if (isHTMLTextAreaElement(*element)) {
+ const HTMLTextAreaElement& textarea = toHTMLTextAreaElement(*element);
+ return textarea.fastGetAttribute(HTMLNames::inputmodeAttr).lower();
+ }
+
+ return WebString();
+}
+
+int WebFrameWidgetImpl::textInputFlags() const
+{
+ Element* element = focusedElement();
+ if (!element)
+ return WebTextInputFlagNone;
+
+ DEFINE_STATIC_LOCAL(AtomicString, autocompleteString, ("autocomplete"));
+ DEFINE_STATIC_LOCAL(AtomicString, autocorrectString, ("autocorrect"));
+ int flags = 0;
+
+ const AtomicString& autocomplete = element->getAttribute(autocompleteString);
+ if (autocomplete == "on")
+ flags |= WebTextInputFlagAutocompleteOn;
+ else if (autocomplete == "off")
+ flags |= WebTextInputFlagAutocompleteOff;
+
+ const AtomicString& autocorrect = element->getAttribute(autocorrectString);
+ if (autocorrect == "on")
+ flags |= WebTextInputFlagAutocorrectOn;
+ else if (autocorrect == "off")
+ flags |= WebTextInputFlagAutocorrectOff;
+
+ SpellcheckAttributeState spellcheck = element->spellcheckAttributeState();
+ if (spellcheck == SpellcheckAttributeTrue)
+ flags |= WebTextInputFlagSpellcheckOn;
+ else if (spellcheck == SpellcheckAttributeFalse)
+ flags |= WebTextInputFlagSpellcheckOff;
+
+ if (isHTMLTextFormControlElement(element)) {
+ HTMLTextFormControlElement* formElement = static_cast<HTMLTextFormControlElement*>(element);
+ if (formElement->supportsAutocapitalize()) {
+ DEFINE_STATIC_LOCAL(const AtomicString, none, ("none"));
+ DEFINE_STATIC_LOCAL(const AtomicString, characters, ("characters"));
+ DEFINE_STATIC_LOCAL(const AtomicString, words, ("words"));
+ DEFINE_STATIC_LOCAL(const AtomicString, sentences, ("sentences"));
+
+ const AtomicString& autocapitalize = formElement->autocapitalize();
+ if (autocapitalize == none)
+ flags |= WebTextInputFlagAutocapitalizeNone;
+ else if (autocapitalize == characters)
+ flags |= WebTextInputFlagAutocapitalizeCharacters;
+ else if (autocapitalize == words)
+ flags |= WebTextInputFlagAutocapitalizeWords;
+ else if (autocapitalize == sentences)
+ flags |= WebTextInputFlagAutocapitalizeSentences;
+ else
+ NOTREACHED();
+ }
+ }
+
+ return flags;
+}
+
} // namespace blink

Powered by Google App Engine
This is Rietveld 408576698