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

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: Using focusedCoreFrame() instead of focusController().focusedFrame() in WebViewImpl 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
« no previous file with comments | « third_party/WebKit/Source/web/WebFrameWidgetImpl.h ('k') | third_party/WebKit/Source/web/WebViewImpl.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 2195baa3385fcc84d41f62e1858d95c76c29779e..b5c1dda24e90c95843c812982ca998fafeec7b74 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,11 @@
#include "platform/KeyboardCodes.h"
#include "platform/graphics/CompositorMutatorClient.h"
#include "public/platform/WebFrameScheduler.h"
+#include "public/web/WebAutofillClient.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 +111,7 @@ WebFrameWidgetImpl::WebFrameWidgetImpl(WebWidgetClient* client, WebLocalFrame* l
, m_suppressNextKeypressEvent(false)
, m_ignoreInputEvents(false)
, m_isTransparent(false)
+ , m_imeAcceptEvents(true)
, m_selfKeepAlive(this)
{
DCHECK(m_localRoot->frame()->isLocalRoot());
@@ -461,6 +469,23 @@ void WebFrameWidgetImpl::setFocus(bool enable)
}
}
}
+ } else {
+ LocalFrame* focusedFrame = focusedLocalFrameInWidget();
+ if (focusedFrame) {
+ // Finish an ongoing composition to delete the composition node.
+ if (focusedFrame->inputMethodController().hasComposition()) {
+ WebAutofillClient* autofillClient = WebLocalFrameImpl::fromFrame(focusedFrame)->autofillClient();
+
+ if (autofillClient)
+ autofillClient->setIgnoreTextChanges(true);
+
+ focusedFrame->inputMethodController().confirmComposition();
+
+ if (autofillClient)
+ autofillClient->setIgnoreTextChanges(false);
+ }
+ m_imeAcceptEvents = false;
+ }
}
}
@@ -470,42 +495,229 @@ bool WebFrameWidgetImpl::setComposition(
int selectionStart,
int selectionEnd)
{
- // FIXME: To be implemented.
- return false;
+ LocalFrame* focused = focusedLocalFrameAvailableForIme();
+ 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
+{
+ LocalFrame* focused = focusedLocalFrameAvailableForIme();
+ 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;
+ LocalFrame* focused = focusedLocalFrameAvailableForIme();
+ 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())
+ return WebTextInputTypeContentEditable;
+
+ return WebTextInputTypeNone;
}
WebColor WebFrameWidgetImpl::backgroundColor() const
@@ -520,13 +732,10 @@ WebColor WebFrameWidgetImpl::backgroundColor() const
bool WebFrameWidgetImpl::selectionBounds(WebRect& anchor, WebRect& focus) const
{
- const Frame* frame = focusedCoreFrame();
- if (!frame || !frame->isLocalFrame())
- return false;
-
- const LocalFrame* localFrame = toLocalFrame(frame);
+ const LocalFrame* localFrame = focusedLocalFrameInWidget();
if (!localFrame)
return false;
+
FrameSelection& selection = localFrame->selection();
if (selection.isCaret()) {
@@ -553,11 +762,10 @@ bool WebFrameWidgetImpl::selectionBounds(WebRect& anchor, WebRect& focus) const
bool WebFrameWidgetImpl::selectionTextDirection(WebTextDirection& start, WebTextDirection& end) const
{
- if (!focusedCoreFrame()->isLocalFrame())
- return false;
- const LocalFrame* frame = toLocalFrame(focusedCoreFrame());
+ const LocalFrame* frame = focusedLocalFrameInWidget();
if (!frame)
return false;
+
FrameSelection& selection = frame->selection();
if (selection.selection().toNormalizedEphemeralRange().isNull())
return false;
@@ -568,18 +776,14 @@ bool WebFrameWidgetImpl::selectionTextDirection(WebTextDirection& start, WebText
bool WebFrameWidgetImpl::isSelectionAnchorFirst() const
{
- if (!focusedCoreFrame()->isLocalFrame())
- return false;
- if (const LocalFrame* frame = toLocalFrame(focusedCoreFrame()))
+ if (const LocalFrame* frame = focusedLocalFrameInWidget())
return frame->selection().selection().isBaseFirst();
return false;
}
bool WebFrameWidgetImpl::caretOrSelectionRange(size_t* location, size_t* length)
{
- if (!focusedCoreFrame()->isLocalFrame())
- return false;
- const LocalFrame* focused = toLocalFrame(focusedCoreFrame());
+ LocalFrame* focused = focusedLocalFrameInWidget();
if (!focused)
return false;
@@ -598,9 +802,7 @@ void WebFrameWidgetImpl::setTextDirection(WebTextDirection direction)
// the text direction of the selected node and updates its DOM "dir"
// attribute and its CSS "direction" property.
// So, we just call the function as Safari does.
- if (!focusedCoreFrame()->isLocalFrame())
- return;
- const LocalFrame* focused = toLocalFrame(focusedCoreFrame());
+ const LocalFrame* focused = focusedLocalFrameInWidget();
if (!focused)
return;
@@ -664,6 +866,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 +1364,101 @@ 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;
+}
+
+LocalFrame* WebFrameWidgetImpl::focusedLocalFrameAvailableForIme() const
+{
+ if (!m_imeAcceptEvents)
+ return nullptr;
+ return focusedLocalFrameInWidget();
+}
+
} // namespace blink
« no previous file with comments | « third_party/WebKit/Source/web/WebFrameWidgetImpl.h ('k') | third_party/WebKit/Source/web/WebViewImpl.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698