blob: 1d83b09769f3007c757e2c642ac230d706e71ecf [file] [log] [blame]
/*
* Copyright (C) 2010-2017 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "WebEditorClient.h"
#include "EditorState.h"
#include "SharedBufferDataReference.h"
#include "UndoOrRedo.h"
#include "WKBundlePageEditorClient.h"
#include "WebCoreArgumentCoders.h"
#include "WebFrame.h"
#include "WebPage.h"
#include "WebPageProxy.h"
#include "WebPageProxyMessages.h"
#include "WebProcess.h"
#include "WebUndoStep.h"
#include <WebCore/ArchiveResource.h>
#include <WebCore/DOMPasteAccess.h>
#include <WebCore/DocumentFragment.h>
#include <WebCore/FocusController.h>
#include <WebCore/Frame.h>
#include <WebCore/FrameLoader.h>
#include <WebCore/FrameView.h>
#include <WebCore/HTMLInputElement.h>
#include <WebCore/HTMLNames.h>
#include <WebCore/HTMLTextAreaElement.h>
#include <WebCore/KeyboardEvent.h>
#include <WebCore/NotImplemented.h>
#include <WebCore/Page.h>
#include <WebCore/SerializedAttachmentData.h>
#include <WebCore/SpellChecker.h>
#include <WebCore/StyleProperties.h>
#include <WebCore/TextIterator.h>
#include <WebCore/UndoStep.h>
#include <WebCore/UserTypingGestureIndicator.h>
#include <WebCore/VisibleUnits.h>
#include <wtf/NeverDestroyed.h>
#include <wtf/text/StringView.h>
#if PLATFORM(GTK)
#include <WebCore/PlatformDisplay.h>
#endif
namespace WebKit {
using namespace WebCore;
using namespace HTMLNames;
static uint64_t generateTextCheckingRequestID()
{
static uint64_t uniqueTextCheckingRequestID = 1;
return uniqueTextCheckingRequestID++;
}
bool WebEditorClient::shouldDeleteRange(Range* range)
{
bool result = m_page->injectedBundleEditorClient().shouldDeleteRange(*m_page, range);
notImplemented();
return result;
}
bool WebEditorClient::smartInsertDeleteEnabled()
{
return m_page->isSmartInsertDeleteEnabled();
}
bool WebEditorClient::isSelectTrailingWhitespaceEnabled() const
{
return m_page->isSelectTrailingWhitespaceEnabled();
}
bool WebEditorClient::isContinuousSpellCheckingEnabled()
{
return WebProcess::singleton().textCheckerState().isContinuousSpellCheckingEnabled;
}
void WebEditorClient::toggleContinuousSpellChecking()
{
notImplemented();
}
bool WebEditorClient::isGrammarCheckingEnabled()
{
return WebProcess::singleton().textCheckerState().isGrammarCheckingEnabled;
}
void WebEditorClient::toggleGrammarChecking()
{
notImplemented();
}
int WebEditorClient::spellCheckerDocumentTag()
{
notImplemented();
return false;
}
bool WebEditorClient::shouldBeginEditing(Range* range)
{
bool result = m_page->injectedBundleEditorClient().shouldBeginEditing(*m_page, range);
notImplemented();
return result;
}
bool WebEditorClient::shouldEndEditing(Range* range)
{
bool result = m_page->injectedBundleEditorClient().shouldEndEditing(*m_page, range);
notImplemented();
return result;
}
bool WebEditorClient::shouldInsertNode(Node* node, Range* rangeToReplace, EditorInsertAction action)
{
bool result = m_page->injectedBundleEditorClient().shouldInsertNode(*m_page, node, rangeToReplace, action);
notImplemented();
return result;
}
bool WebEditorClient::shouldInsertText(const String& text, Range* rangeToReplace, EditorInsertAction action)
{
bool result = m_page->injectedBundleEditorClient().shouldInsertText(*m_page, text.impl(), rangeToReplace, action);
notImplemented();
return result;
}
bool WebEditorClient::shouldChangeSelectedRange(Range* fromRange, Range* toRange, EAffinity affinity, bool stillSelecting)
{
bool result = m_page->injectedBundleEditorClient().shouldChangeSelectedRange(*m_page, fromRange, toRange, affinity, stillSelecting);
notImplemented();
return result;
}
bool WebEditorClient::shouldApplyStyle(StyleProperties* style, Range* range)
{
Ref<MutableStyleProperties> mutableStyle(style->isMutable() ? Ref<MutableStyleProperties>(static_cast<MutableStyleProperties&>(*style)) : style->mutableCopy());
bool result = m_page->injectedBundleEditorClient().shouldApplyStyle(*m_page, &mutableStyle->ensureCSSStyleDeclaration(), range);
notImplemented();
return result;
}
#if ENABLE(ATTACHMENT_ELEMENT)
void WebEditorClient::registerAttachmentIdentifier(const String& identifier, const String& contentType, const String& preferredFileName, Ref<SharedBuffer>&& data)
{
m_page->send(Messages::WebPageProxy::RegisterAttachmentIdentifierFromData(identifier, contentType, preferredFileName, data.get()));
}
void WebEditorClient::registerAttachments(Vector<WebCore::SerializedAttachmentData>&& data)
{
m_page->send(Messages::WebPageProxy::registerAttachmentsFromSerializedData(WTFMove(data)));
}
void WebEditorClient::registerAttachmentIdentifier(const String& identifier, const String& contentType, const String& filePath)
{
m_page->send(Messages::WebPageProxy::RegisterAttachmentIdentifierFromFilePath(identifier, contentType, filePath));
}
void WebEditorClient::registerAttachmentIdentifier(const String& identifier)
{
m_page->send(Messages::WebPageProxy::RegisterAttachmentIdentifier(identifier));
}
void WebEditorClient::cloneAttachmentData(const String& fromIdentifier, const String& toIdentifier)
{
m_page->send(Messages::WebPageProxy::CloneAttachmentData(fromIdentifier, toIdentifier));
}
void WebEditorClient::didInsertAttachmentWithIdentifier(const String& identifier, const String& source, bool hasEnclosingImage)
{
m_page->send(Messages::WebPageProxy::DidInsertAttachmentWithIdentifier(identifier, source, hasEnclosingImage));
}
void WebEditorClient::didRemoveAttachmentWithIdentifier(const String& identifier)
{
m_page->send(Messages::WebPageProxy::DidRemoveAttachmentWithIdentifier(identifier));
}
Vector<SerializedAttachmentData> WebEditorClient::serializedAttachmentDataForIdentifiers(const Vector<String>& identifiers)
{
Vector<WebCore::SerializedAttachmentData> serializedData;
m_page->sendSync(Messages::WebPageProxy::SerializedAttachmentDataForIdentifiers(identifiers), Messages::WebPageProxy::SerializedAttachmentDataForIdentifiers::Reply(serializedData));
return serializedData;
}
#endif
void WebEditorClient::didApplyStyle()
{
m_page->didApplyStyle();
}
bool WebEditorClient::shouldMoveRangeAfterDelete(Range*, Range*)
{
notImplemented();
return true;
}
void WebEditorClient::didBeginEditing()
{
// FIXME: What good is a notification name, if it's always the same?
static NeverDestroyed<String> WebViewDidBeginEditingNotification(MAKE_STATIC_STRING_IMPL("WebViewDidBeginEditingNotification"));
m_page->injectedBundleEditorClient().didBeginEditing(*m_page, WebViewDidBeginEditingNotification.get().impl());
notImplemented();
}
void WebEditorClient::respondToChangedContents()
{
static NeverDestroyed<String> WebViewDidChangeNotification(MAKE_STATIC_STRING_IMPL("WebViewDidChangeNotification"));
m_page->injectedBundleEditorClient().didChange(*m_page, WebViewDidChangeNotification.get().impl());
m_page->didChangeContents();
}
void WebEditorClient::respondToChangedSelection(Frame* frame)
{
static NeverDestroyed<String> WebViewDidChangeSelectionNotification(MAKE_STATIC_STRING_IMPL("WebViewDidChangeSelectionNotification"));
m_page->injectedBundleEditorClient().didChangeSelection(*m_page, WebViewDidChangeSelectionNotification.get().impl());
if (!frame)
return;
m_page->didChangeSelection();
#if PLATFORM(GTK)
updateGlobalSelection(frame);
#endif
}
void WebEditorClient::didEndUserTriggeredSelectionChanges()
{
m_page->didEndUserTriggeredSelectionChanges();
}
void WebEditorClient::updateEditorStateAfterLayoutIfEditabilityChanged()
{
m_page->updateEditorStateAfterLayoutIfEditabilityChanged();
}
void WebEditorClient::didUpdateComposition()
{
m_page->didUpdateComposition();
}
void WebEditorClient::discardedComposition(Frame*)
{
m_page->discardedComposition();
}
void WebEditorClient::canceledComposition()
{
m_page->canceledComposition();
}
void WebEditorClient::didEndEditing()
{
static NeverDestroyed<String> WebViewDidEndEditingNotification(MAKE_STATIC_STRING_IMPL("WebViewDidEndEditingNotification"));
m_page->injectedBundleEditorClient().didEndEditing(*m_page, WebViewDidEndEditingNotification.get().impl());
notImplemented();
}
void WebEditorClient::didWriteSelectionToPasteboard()
{
m_page->injectedBundleEditorClient().didWriteToPasteboard(*m_page);
}
void WebEditorClient::willWriteSelectionToPasteboard(Range* range)
{
m_page->injectedBundleEditorClient().willWriteToPasteboard(*m_page, range);
}
void WebEditorClient::getClientPasteboardDataForRange(Range* range, Vector<String>& pasteboardTypes, Vector<RefPtr<SharedBuffer>>& pasteboardData)
{
m_page->injectedBundleEditorClient().getPasteboardDataForRange(*m_page, range, pasteboardTypes, pasteboardData);
}
bool WebEditorClient::performTwoStepDrop(DocumentFragment& fragment, Range& destination, bool isMove)
{
return m_page->injectedBundleEditorClient().performTwoStepDrop(*m_page, fragment, destination, isMove);
}
void WebEditorClient::registerUndoStep(UndoStep& step)
{
// FIXME: Add assertion that the command being reapplied is the same command that is
// being passed to us.
if (m_page->isInRedo())
return;
auto webStep = WebUndoStep::create(step);
auto stepID = webStep->stepID();
m_page->addWebUndoStep(stepID, WTFMove(webStep));
m_page->send(Messages::WebPageProxy::RegisterEditCommandForUndo(stepID, step.label()), IPC::SendOption::DispatchMessageEvenWhenWaitingForSyncReply);
}
void WebEditorClient::registerRedoStep(UndoStep&)
{
}
void WebEditorClient::clearUndoRedoOperations()
{
m_page->send(Messages::WebPageProxy::ClearAllEditCommands());
}
bool WebEditorClient::canCopyCut(Frame*, bool defaultValue) const
{
return defaultValue;
}
bool WebEditorClient::canPaste(Frame*, bool defaultValue) const
{
return defaultValue;
}
bool WebEditorClient::canUndo() const
{
bool result = false;
m_page->sendSync(Messages::WebPageProxy::CanUndoRedo(UndoOrRedo::Undo), Messages::WebPageProxy::CanUndoRedo::Reply(result));
return result;
}
bool WebEditorClient::canRedo() const
{
bool result = false;
m_page->sendSync(Messages::WebPageProxy::CanUndoRedo(UndoOrRedo::Redo), Messages::WebPageProxy::CanUndoRedo::Reply(result));
return result;
}
void WebEditorClient::undo()
{
m_page->sendSync(Messages::WebPageProxy::ExecuteUndoRedo(UndoOrRedo::Undo), Messages::WebPageProxy::ExecuteUndoRedo::Reply());
}
void WebEditorClient::redo()
{
m_page->sendSync(Messages::WebPageProxy::ExecuteUndoRedo(UndoOrRedo::Redo), Messages::WebPageProxy::ExecuteUndoRedo::Reply());
}
WebCore::DOMPasteAccessResponse WebEditorClient::requestDOMPasteAccess(const String& originIdentifier)
{
return m_page->requestDOMPasteAccess(originIdentifier);
}
#if PLATFORM(WIN)
void WebEditorClient::handleKeyboardEvent(KeyboardEvent& event)
{
if (m_page->handleEditingKeyboardEvent(event))
event.setDefaultHandled();
}
void WebEditorClient::handleInputMethodKeydown(KeyboardEvent&)
{
notImplemented();
}
#endif // PLATFORM(WIN)
void WebEditorClient::textFieldDidBeginEditing(Element* element)
{
if (!is<HTMLInputElement>(*element))
return;
WebFrame* webFrame = WebFrame::fromCoreFrame(*element->document().frame());
ASSERT(webFrame);
m_page->injectedBundleFormClient().textFieldDidBeginEditing(m_page, downcast<HTMLInputElement>(element), webFrame);
}
void WebEditorClient::textFieldDidEndEditing(Element* element)
{
if (!is<HTMLInputElement>(*element))
return;
WebFrame* webFrame = WebFrame::fromCoreFrame(*element->document().frame());
ASSERT(webFrame);
m_page->injectedBundleFormClient().textFieldDidEndEditing(m_page, downcast<HTMLInputElement>(element), webFrame);
}
void WebEditorClient::textDidChangeInTextField(Element* element)
{
if (!is<HTMLInputElement>(*element))
return;
bool initiatedByUserTyping = UserTypingGestureIndicator::processingUserTypingGesture() && UserTypingGestureIndicator::focusedElementAtGestureStart() == element;
WebFrame* webFrame = WebFrame::fromCoreFrame(*element->document().frame());
ASSERT(webFrame);
m_page->injectedBundleFormClient().textDidChangeInTextField(m_page, downcast<HTMLInputElement>(element), webFrame, initiatedByUserTyping);
}
void WebEditorClient::textDidChangeInTextArea(Element* element)
{
if (!is<HTMLTextAreaElement>(*element))
return;
WebFrame* webFrame = WebFrame::fromCoreFrame(*element->document().frame());
ASSERT(webFrame);
m_page->injectedBundleFormClient().textDidChangeInTextArea(m_page, downcast<HTMLTextAreaElement>(element), webFrame);
}
#if !PLATFORM(IOS_FAMILY)
void WebEditorClient::overflowScrollPositionChanged()
{
notImplemented();
}
void WebEditorClient::subFrameScrollPositionChanged()
{
notImplemented();
}
#endif
static bool getActionTypeForKeyEvent(KeyboardEvent* event, WKInputFieldActionType& type)
{
String key = event->keyIdentifier();
if (key == "Up")
type = WKInputFieldActionTypeMoveUp;
else if (key == "Down")
type = WKInputFieldActionTypeMoveDown;
else if (key == "U+001B")
type = WKInputFieldActionTypeCancel;
else if (key == "U+0009") {
if (event->shiftKey())
type = WKInputFieldActionTypeInsertBacktab;
else
type = WKInputFieldActionTypeInsertTab;
} else if (key == "Enter")
type = WKInputFieldActionTypeInsertNewline;
else
return false;
return true;
}
static API::InjectedBundle::FormClient::InputFieldAction toInputFieldAction(WKInputFieldActionType action)
{
switch (action) {
case WKInputFieldActionTypeMoveUp:
return API::InjectedBundle::FormClient::InputFieldAction::MoveUp;
case WKInputFieldActionTypeMoveDown:
return API::InjectedBundle::FormClient::InputFieldAction::MoveDown;
case WKInputFieldActionTypeCancel:
return API::InjectedBundle::FormClient::InputFieldAction::Cancel;
case WKInputFieldActionTypeInsertTab:
return API::InjectedBundle::FormClient::InputFieldAction::InsertTab;
case WKInputFieldActionTypeInsertNewline:
return API::InjectedBundle::FormClient::InputFieldAction::InsertNewline;
case WKInputFieldActionTypeInsertDelete:
return API::InjectedBundle::FormClient::InputFieldAction::InsertDelete;
case WKInputFieldActionTypeInsertBacktab:
return API::InjectedBundle::FormClient::InputFieldAction::InsertBacktab;
}
ASSERT_NOT_REACHED();
return API::InjectedBundle::FormClient::InputFieldAction::Cancel;
}
bool WebEditorClient::doTextFieldCommandFromEvent(Element* element, KeyboardEvent* event)
{
if (!is<HTMLInputElement>(*element))
return false;
WKInputFieldActionType actionType = static_cast<WKInputFieldActionType>(0);
if (!getActionTypeForKeyEvent(event, actionType))
return false;
WebFrame* webFrame = WebFrame::fromCoreFrame(*element->document().frame());
ASSERT(webFrame);
return m_page->injectedBundleFormClient().shouldPerformActionInTextField(m_page, downcast<HTMLInputElement>(element), toInputFieldAction(actionType), webFrame);
}
void WebEditorClient::textWillBeDeletedInTextField(Element* element)
{
if (!is<HTMLInputElement>(*element))
return;
WebFrame* webFrame = WebFrame::fromCoreFrame(*element->document().frame());
ASSERT(webFrame);
m_page->injectedBundleFormClient().shouldPerformActionInTextField(m_page, downcast<HTMLInputElement>(element), toInputFieldAction(WKInputFieldActionTypeInsertDelete), webFrame);
}
bool WebEditorClient::shouldEraseMarkersAfterChangeSelection(WebCore::TextCheckingType type) const
{
// This prevents erasing spelling markers on OS X Lion or later to match AppKit on these Mac OS X versions.
#if PLATFORM(COCOA)
return type != TextCheckingType::Spelling;
#else
UNUSED_PARAM(type);
return true;
#endif
}
void WebEditorClient::ignoreWordInSpellDocument(const String& word)
{
m_page->send(Messages::WebPageProxy::IgnoreWord(word));
}
void WebEditorClient::learnWord(const String& word)
{
m_page->send(Messages::WebPageProxy::LearnWord(word));
}
void WebEditorClient::checkSpellingOfString(StringView text, int* misspellingLocation, int* misspellingLength)
{
int32_t resultLocation = -1;
int32_t resultLength = 0;
m_page->sendSync(Messages::WebPageProxy::CheckSpellingOfString(text.toStringWithoutCopying()),
Messages::WebPageProxy::CheckSpellingOfString::Reply(resultLocation, resultLength));
*misspellingLocation = resultLocation;
*misspellingLength = resultLength;
}
String WebEditorClient::getAutoCorrectSuggestionForMisspelledWord(const String&)
{
notImplemented();
return String();
}
void WebEditorClient::checkGrammarOfString(StringView text, Vector<WebCore::GrammarDetail>& grammarDetails, int* badGrammarLocation, int* badGrammarLength)
{
int32_t resultLocation = -1;
int32_t resultLength = 0;
m_page->sendSync(Messages::WebPageProxy::CheckGrammarOfString(text.toStringWithoutCopying()),
Messages::WebPageProxy::CheckGrammarOfString::Reply(grammarDetails, resultLocation, resultLength));
*badGrammarLocation = resultLocation;
*badGrammarLength = resultLength;
}
static int32_t insertionPointFromCurrentSelection(const VisibleSelection& currentSelection)
{
VisiblePosition selectionStart = currentSelection.visibleStart();
VisiblePosition paragraphStart = startOfParagraph(selectionStart);
return TextIterator::rangeLength(makeRange(paragraphStart, selectionStart).get());
}
#if USE(UNIFIED_TEXT_CHECKING)
Vector<TextCheckingResult> WebEditorClient::checkTextOfParagraph(StringView stringView, OptionSet<WebCore::TextCheckingType> checkingTypes, const VisibleSelection& currentSelection)
{
Vector<TextCheckingResult> results;
m_page->sendSync(Messages::WebPageProxy::CheckTextOfParagraph(stringView.toStringWithoutCopying(), checkingTypes, insertionPointFromCurrentSelection(currentSelection)), Messages::WebPageProxy::CheckTextOfParagraph::Reply(results));
return results;
}
#endif
void WebEditorClient::updateSpellingUIWithGrammarString(const String& badGrammarPhrase, const GrammarDetail& grammarDetail)
{
m_page->send(Messages::WebPageProxy::UpdateSpellingUIWithGrammarString(badGrammarPhrase, grammarDetail));
}
void WebEditorClient::updateSpellingUIWithMisspelledWord(const String& misspelledWord)
{
m_page->send(Messages::WebPageProxy::UpdateSpellingUIWithMisspelledWord(misspelledWord));
}
void WebEditorClient::showSpellingUI(bool)
{
notImplemented();
}
bool WebEditorClient::spellingUIIsShowing()
{
bool isShowing = false;
m_page->sendSync(Messages::WebPageProxy::SpellingUIIsShowing(), Messages::WebPageProxy::SpellingUIIsShowing::Reply(isShowing));
return isShowing;
}
void WebEditorClient::getGuessesForWord(const String& word, const String& context, const VisibleSelection& currentSelection, Vector<String>& guesses)
{
m_page->sendSync(Messages::WebPageProxy::GetGuessesForWord(word, context, insertionPointFromCurrentSelection(currentSelection)), Messages::WebPageProxy::GetGuessesForWord::Reply(guesses));
}
void WebEditorClient::requestCheckingOfString(TextCheckingRequest& request, const WebCore::VisibleSelection& currentSelection)
{
uint64_t requestID = generateTextCheckingRequestID();
m_page->addTextCheckingRequest(requestID, request);
m_page->send(Messages::WebPageProxy::RequestCheckingOfString(requestID, request.data(), insertionPointFromCurrentSelection(currentSelection)));
}
void WebEditorClient::willSetInputMethodState()
{
}
void WebEditorClient::setInputMethodState(bool enabled)
{
#if PLATFORM(GTK)
m_page->setInputMethodState(enabled);
#else
notImplemented();
UNUSED_PARAM(enabled);
#endif
}
bool WebEditorClient::supportsGlobalSelection()
{
#if PLATFORM(GTK)
#if PLATFORM(X11)
if (PlatformDisplay::sharedDisplay().type() == PlatformDisplay::Type::X11)
return true;
#endif
#if PLATFORM(WAYLAND)
if (PlatformDisplay::sharedDisplay().type() == PlatformDisplay::Type::Wayland)
return true;
#endif
#endif
return false;
}
} // namespace WebKit