#include "config.h"
#include "Editor.h"
#include "AXObjectCache.h"
#include "ApplyStyleCommand.h"
#include "CSSComputedStyleDeclaration.h"
#include "CSSProperty.h"
#include "CSSPropertyNames.h"
#include "CharacterData.h"
#include "Clipboard.h"
#include "ClipboardEvent.h"
#include "DeleteButtonController.h"
#include "DeleteSelectionCommand.h"
#include "DocLoader.h"
#include "Document.h"
#include "DocumentFragment.h"
#include "EditCommand.h"
#include "Editor.h"
#include "EditorClient.h"
#include "Event.h"
#include "EventHandler.h"
#include "EventNames.h"
#include "FocusController.h"
#include "FrameView.h"
#include "HTMLElement.h"
#include "HTMLInputElement.h"
#include "HTMLNames.h"
#include "HTMLTextAreaElement.h"
#include "HitTestResult.h"
#include "IndentOutdentCommand.h"
#include "InsertListCommand.h"
#include "KeyboardEvent.h"
#include "KURL.h"
#include "ModifySelectionListLevel.h"
#include "Page.h"
#include "Pasteboard.h"
#include "Range.h"
#include "ReplaceSelectionCommand.h"
#include "SelectionController.h"
#include "Sound.h"
#include "Text.h"
#include "TextIterator.h"
#include "TypingCommand.h"
#include "htmlediting.h"
#include "markup.h"
#include "visible_units.h"
namespace WebCore {
class FontData;
using namespace std;
using namespace EventNames;
using namespace HTMLNames;
static Selection selectionForEvent(Frame* frame, Event* event)
{
Page* page = frame->page();
if (!page)
return Selection();
Selection selection = page->selection();
if (!event)
return selection;
Node* target = event->target()->toNode();
Node* selectionStart = selection.start().node();
if (target && (!selectionStart || target->shadowAncestorNode() != selectionStart->shadowAncestorNode())) {
if (target->hasTagName(inputTag) && static_cast<HTMLInputElement*>(target)->isTextField())
return static_cast<HTMLInputElement*>(target)->selection();
if (target->hasTagName(textareaTag))
return static_cast<HTMLTextAreaElement*>(target)->selection();
}
return selection;
}
EditorClient* Editor::client() const
{
if (Page* page = m_frame->page())
return page->editorClient();
return 0;
}
void Editor::handleKeypress(KeyboardEvent* event)
{
if (EditorClient* c = client())
if (selectionForEvent(m_frame, event).isContentEditable())
c->handleKeypress(event);
}
void Editor::handleInputMethodKeypress(KeyboardEvent* event)
{
if (EditorClient* c = client())
if (selectionForEvent(m_frame, event).isContentEditable())
c->handleInputMethodKeypress(event);
}
bool Editor::canEdit() const
{
return m_frame->selectionController()->isContentEditable();
}
bool Editor::canEditRichly() const
{
return m_frame->selectionController()->isContentRichlyEditable();
}
bool Editor::canDHTMLCut()
{
return !m_frame->selectionController()->isInPasswordField() && !dispatchCPPEvent(beforecutEvent, ClipboardNumb);
}
bool Editor::canDHTMLCopy()
{
return !m_frame->selectionController()->isInPasswordField() && !dispatchCPPEvent(beforecopyEvent, ClipboardNumb);
}
bool Editor::canDHTMLPaste()
{
return !dispatchCPPEvent(beforepasteEvent, ClipboardNumb);
}
bool Editor::canCut() const
{
return canCopy() && canDelete();
}
static Node* imageNodeFromImageDocument(Document* document)
{
if (!document)
return 0;
if (!document->isImageDocument())
return 0;
HTMLElement* body = document->body();
if (!body)
return 0;
Node* node = body->firstChild();
if (!node)
return 0;
if (!node->hasTagName(imgTag))
return 0;
return node;
}
bool Editor::canCopy() const
{
if (imageNodeFromImageDocument(m_frame->document()))
return true;
SelectionController* selectionController = m_frame->selectionController();
return selectionController->isRange() && !selectionController->isInPasswordField();
}
bool Editor::canPaste() const
{
return canEdit();
}
bool Editor::canDelete() const
{
SelectionController* selectionController = m_frame->selectionController();
return selectionController->isRange() && selectionController->isContentEditable();
}
bool Editor::canDeleteRange(Range* range) const
{
ExceptionCode ec = 0;
Node* startContainer = range->startContainer(ec);
Node* endContainer = range->endContainer(ec);
if (!startContainer || !endContainer)
return false;
if (!startContainer->isContentEditable() || !endContainer->isContentEditable())
return false;
if (range->collapsed(ec)) {
VisiblePosition start(startContainer, range->startOffset(ec), DOWNSTREAM);
VisiblePosition previous = start.previous();
if (previous.isNull() || previous.deepEquivalent().node()->rootEditableElement() != startContainer->rootEditableElement())
return false;
}
return true;
}
bool Editor::smartInsertDeleteEnabled()
{
return client() && client()->smartInsertDeleteEnabled();
}
bool Editor::canSmartCopyOrDelete()
{
return client() && client()->smartInsertDeleteEnabled() && m_frame->selectionGranularity() == WordGranularity;
}
void Editor::deleteRange(Range* range, bool killRing, bool prepend, bool smartDeleteOK, EditorDeleteAction deletionAction, TextGranularity granularity)
{
if (killRing)
addToKillRing(range, prepend);
ExceptionCode ec = 0;
SelectionController* selectionController = m_frame->selectionController();
bool smartDelete = smartDeleteOK && canSmartCopyOrDelete();
switch (deletionAction) {
case deleteSelectionAction:
selectionController->setSelectedRange(range, DOWNSTREAM, true, ec);
if (ec)
return;
deleteSelectionWithSmartDelete(smartDelete);
break;
case deleteKeyAction:
selectionController->setSelectedRange(range, DOWNSTREAM, (granularity != CharacterGranularity), ec);
if (ec)
return;
if (m_frame->document()) {
TypingCommand::deleteKeyPressed(m_frame->document(), smartDelete, granularity);
revealSelectionAfterEditingOperation();
}
break;
case forwardDeleteKeyAction:
selectionController->setSelectedRange(range, DOWNSTREAM, (granularity != CharacterGranularity), ec);
if (ec)
return;
if (m_frame->document()) {
TypingCommand::forwardDeleteKeyPressed(m_frame->document(), smartDelete, granularity);
revealSelectionAfterEditingOperation();
}
break;
}
if (killRing)
setStartNewKillRingSequence(false);
}
bool Editor::deleteWithDirection(SelectionController::EDirection direction, TextGranularity granularity, bool killRing, bool isTypingAction)
{
if (!canEdit())
return false;
RefPtr<Range> range;
EditorDeleteAction deletionAction = deleteSelectionAction;
bool smartDeleteOK = false;
if (m_frame->selectionController()->isRange()) {
range = selectedRange();
smartDeleteOK = true;
if (isTypingAction)
deletionAction = deleteKeyAction;
} else {
SelectionController selectionController;
selectionController.setSelection(m_frame->selectionController()->selection());
selectionController.modify(SelectionController::EXTEND, direction, granularity);
if (killRing && selectionController.isCaret() && granularity != CharacterGranularity)
selectionController.modify(SelectionController::EXTEND, direction, CharacterGranularity);
range = selectionController.toRange();
switch (direction) {
case SelectionController::FORWARD:
case SelectionController::RIGHT:
deletionAction = forwardDeleteKeyAction;
break;
case SelectionController::BACKWARD:
case SelectionController::LEFT:
deletionAction = deleteKeyAction;
break;
}
}
deleteRange(range.get(), killRing, false, smartDeleteOK, deletionAction, granularity);
return true;
}
void Editor::deleteSelectionWithSmartDelete()
{
deleteSelectionWithSmartDelete(canSmartCopyOrDelete());
}
void Editor::deleteSelectionWithSmartDelete(bool smartDelete)
{
if (m_frame->selectionController()->isNone())
return;
applyCommand(new DeleteSelectionCommand(m_frame->document(), smartDelete));
}
void Editor::pasteAsPlainTextWithPasteboard(Pasteboard* pasteboard)
{
String text = pasteboard->plainText(m_frame);
if (client() && client()->shouldInsertText(text, selectedRange().get(), EditorInsertActionPasted))
replaceSelectionWithText(text, false, canSmartReplaceWithPasteboard(pasteboard));
}
void Editor::pasteWithPasteboard(Pasteboard* pasteboard, bool allowPlainText)
{
RefPtr<Range> range = selectedRange();
bool chosePlainText;
RefPtr<DocumentFragment> fragment = pasteboard->documentFragment(m_frame, range, allowPlainText, chosePlainText);
if (fragment && shouldInsertFragment(fragment, range, EditorInsertActionPasted))
replaceSelectionWithFragment(fragment, false, canSmartReplaceWithPasteboard(pasteboard), chosePlainText);
}
bool Editor::canSmartReplaceWithPasteboard(Pasteboard* pasteboard)
{
return client() && client()->smartInsertDeleteEnabled() && pasteboard->canSmartReplace();
}
bool Editor::shouldInsertFragment(PassRefPtr<DocumentFragment> fragment, PassRefPtr<Range> replacingDOMRange, EditorInsertAction givenAction)
{
if (!client())
return false;
Node* child = fragment->firstChild();
if (child && fragment->lastChild() == child && child->isCharacterDataNode())
return client()->shouldInsertText(static_cast<CharacterData*>(child)->data(), replacingDOMRange.get(), givenAction);
return client()->shouldInsertNode(fragment.get(), replacingDOMRange.get(), givenAction);
}
void Editor::replaceSelectionWithFragment(PassRefPtr<DocumentFragment> fragment, bool selectReplacement, bool smartReplace, bool matchStyle)
{
if (m_frame->selectionController()->isNone() || !fragment)
return;
applyCommand(new ReplaceSelectionCommand(m_frame->document(), fragment, selectReplacement, smartReplace, matchStyle));
revealSelectionAfterEditingOperation();
}
void Editor::replaceSelectionWithText(const String& text, bool selectReplacement, bool smartReplace)
{
replaceSelectionWithFragment(createFragmentFromText(selectedRange().get(), text), selectReplacement, smartReplace, true);
}
PassRefPtr<Range> Editor::selectedRange()
{
if (!m_frame)
return 0;
return m_frame->selectionController()->toRange();
}
bool Editor::shouldDeleteRange(Range* range) const
{
ExceptionCode ec;
if (!range || range->collapsed(ec))
return false;
if (!canDeleteRange(range))
return false;
return client() && client()->shouldDeleteRange(range);
}
bool Editor::tryDHTMLCopy()
{
if (m_frame->selectionController()->isInPasswordField())
return false;
Pasteboard::generalPasteboard()->clear();
return !dispatchCPPEvent(copyEvent, ClipboardWritable);
}
bool Editor::tryDHTMLCut()
{
if (m_frame->selectionController()->isInPasswordField())
return false;
Pasteboard::generalPasteboard()->clear();
return !dispatchCPPEvent(cutEvent, ClipboardWritable);
}
bool Editor::tryDHTMLPaste()
{
return !dispatchCPPEvent(pasteEvent, ClipboardReadable);
}
void Editor::writeSelectionToPasteboard(Pasteboard* pasteboard)
{
pasteboard->writeSelection(selectedRange().get(), canSmartCopyOrDelete(), m_frame);
}
bool Editor::shouldInsertText(const String& text, Range* range, EditorInsertAction action) const
{
return client() && client()->shouldInsertText(text, range, action);
}
bool Editor::shouldShowDeleteInterface(HTMLElement* element) const
{
return client() && client()->shouldShowDeleteInterface(element);
}
void Editor::respondToChangedSelection(const Selection& oldSelection)
{
if (client())
client()->respondToChangedSelection();
m_deleteButtonController->respondToChangedSelection(oldSelection);
}
void Editor::respondToChangedContents(const Selection& endingSelection)
{
if (AXObjectCache::accessibilityEnabled()) {
Node* node = endingSelection.start().node();
if (node)
m_frame->renderer()->document()->axObjectCache()->postNotification(node->renderer(), "AXValueChanged");
}
if (client())
client()->respondToChangedContents();
}
const FontData* Editor::fontForSelection(bool& hasMultipleFonts) const
{
#if !PLATFORM(QT)
hasMultipleFonts = false;
if (!m_frame->selectionController()->isRange()) {
Node* nodeToRemove;
RenderStyle* style = m_frame->styleForSelectionStart(nodeToRemove);
const FontData* result = 0;
if (style)
result = style->font().primaryFont();
if (nodeToRemove) {
ExceptionCode ec;
nodeToRemove->remove(ec);
ASSERT(ec == 0);
}
return result;
}
const FontData* font = 0;
RefPtr<Range> range = m_frame->selectionController()->toRange();
Node* startNode = range->editingStartPosition().node();
if (startNode) {
Node* pastEnd = range->pastEndNode();
for (Node* n = startNode; n && n != pastEnd; n = n->traverseNextNode()) {
RenderObject *renderer = n->renderer();
if (!renderer)
continue;
const FontData* f = renderer->style()->font().primaryFont();
if (!font)
font = f;
else if (font != f) {
hasMultipleFonts = true;
break;
}
}
}
return font;
#else
return 0;
#endif
}
Frame::TriState Editor::selectionUnorderedListState() const
{
if (m_frame->selectionController()->isCaret()) {
Node* selectionNode = m_frame->selectionController()->selection().start().node();
if (enclosingNodeWithTag(selectionNode, ulTag))
return Frame::trueTriState;
} else if (m_frame->selectionController()->isRange()) {
Node* startNode = enclosingNodeWithTag(m_frame->selectionController()->selection().start().node(), ulTag);
Node* endNode = enclosingNodeWithTag(m_frame->selectionController()->selection().end().node(), ulTag);
if (startNode && endNode && startNode == endNode)
return Frame::trueTriState;
}
return Frame::falseTriState;
}
Frame::TriState Editor::selectionOrderedListState() const
{
if (m_frame->selectionController()->isCaret()) {
Node* selectionNode = m_frame->selectionController()->selection().start().node();
if (enclosingNodeWithTag(selectionNode, olTag))
return Frame::trueTriState;
} else if (m_frame->selectionController()->isRange()) {
Node* startNode = enclosingNodeWithTag(m_frame->selectionController()->selection().start().node(), olTag);
Node* endNode = enclosingNodeWithTag(m_frame->selectionController()->selection().end().node(), olTag);
if (startNode && endNode && startNode == endNode)
return Frame::trueTriState;
}
return Frame::falseTriState;
}
PassRefPtr<Node> Editor::insertOrderedList()
{
if (!canEditRichly())
return 0;
RefPtr<Node> newList = InsertListCommand::insertList(m_frame->document(), InsertListCommand::OrderedList);
revealSelectionAfterEditingOperation();
return newList;
}
PassRefPtr<Node> Editor::insertUnorderedList()
{
if (!canEditRichly())
return 0;
RefPtr<Node> newList = InsertListCommand::insertList(m_frame->document(), InsertListCommand::UnorderedList);
revealSelectionAfterEditingOperation();
return newList;
}
bool Editor::canIncreaseSelectionListLevel()
{
return canEditRichly() && IncreaseSelectionListLevelCommand::canIncreaseSelectionListLevel(m_frame->document());
}
bool Editor::canDecreaseSelectionListLevel()
{
return canEditRichly() && DecreaseSelectionListLevelCommand::canDecreaseSelectionListLevel(m_frame->document());
}
PassRefPtr<Node> Editor::increaseSelectionListLevel()
{
if (!canEditRichly() || m_frame->selectionController()->isNone())
return 0;
RefPtr<Node> newList = IncreaseSelectionListLevelCommand::increaseSelectionListLevel(m_frame->document());
revealSelectionAfterEditingOperation();
return newList;
}
PassRefPtr<Node> Editor::increaseSelectionListLevelOrdered()
{
if (!canEditRichly() || m_frame->selectionController()->isNone())
return 0;
PassRefPtr<Node> newList = IncreaseSelectionListLevelCommand::increaseSelectionListLevelOrdered(m_frame->document());
revealSelectionAfterEditingOperation();
return newList;
}
PassRefPtr<Node> Editor::increaseSelectionListLevelUnordered()
{
if (!canEditRichly() || m_frame->selectionController()->isNone())
return 0;
PassRefPtr<Node> newList = IncreaseSelectionListLevelCommand::increaseSelectionListLevelUnordered(m_frame->document());
revealSelectionAfterEditingOperation();
return newList;
}
void Editor::decreaseSelectionListLevel()
{
if (!canEditRichly() || m_frame->selectionController()->isNone())
return;
DecreaseSelectionListLevelCommand::decreaseSelectionListLevel(m_frame->document());
revealSelectionAfterEditingOperation();
}
void Editor::removeFormattingAndStyle()
{
Document* document = m_frame->document();
String string = m_frame->selectionController()->toString();
Node* root = m_frame->selectionController()->rootEditableElement();
RefPtr<CSSComputedStyleDeclaration> computedStyle = new CSSComputedStyleDeclaration(root);
RefPtr<CSSMutableStyleDeclaration> defaultStyle = computedStyle->copyInheritableProperties();
deleteSelectionWithSmartDelete(false);
setRemovedAnchor(0);
m_frame->setTypingStyle(defaultStyle.get());
TypingCommand::insertText(document, string, true);
}
void Editor::setLastEditCommand(PassRefPtr<EditCommand> lastEditCommand)
{
m_lastEditCommand = lastEditCommand;
}
bool Editor::dispatchCPPEvent(const AtomicString &eventType, ClipboardAccessPolicy policy)
{
Node* target = m_frame->selectionController()->start().element();
if (!target && m_frame->document())
target = m_frame->document()->body();
if (!target)
return true;
target = target->shadowAncestorNode();
RefPtr<Clipboard> clipboard = newGeneralClipboard(policy);
ExceptionCode ec = 0;
RefPtr<Event> evt = new ClipboardEvent(eventType, true, true, clipboard.get());
EventTargetNodeCast(target)->dispatchEvent(evt, ec, true);
bool noDefaultProcessing = evt->defaultPrevented();
clipboard->setAccessPolicy(ClipboardNumb);
return !noDefaultProcessing;
}
void Editor::applyStyle(CSSStyleDeclaration* style, EditAction editingAction)
{
switch (m_frame->selectionController()->state()) {
case Selection::NONE:
break;
case Selection::CARET: {
m_frame->computeAndSetTypingStyle(style, editingAction);
break;
}
case Selection::RANGE:
if (m_frame->document() && style)
applyCommand(new ApplyStyleCommand(m_frame->document(), style, editingAction));
break;
}
}
bool Editor::shouldApplyStyle(CSSStyleDeclaration* style, Range* range)
{
return client()->shouldApplyStyle(style, range);
}
void Editor::applyParagraphStyle(CSSStyleDeclaration* style, EditAction editingAction)
{
switch (m_frame->selectionController()->state()) {
case Selection::NONE:
break;
case Selection::CARET:
case Selection::RANGE:
if (m_frame->document() && style)
applyCommand(new ApplyStyleCommand(m_frame->document(), style, editingAction, ApplyStyleCommand::ForceBlockProperties));
break;
}
}
void Editor::applyStyleToSelection(CSSStyleDeclaration* style, EditAction editingAction)
{
if (!style || style->length() == 0 || !canEditRichly())
return;
if (client() && client()->shouldApplyStyle(style, m_frame->selectionController()->toRange().get()))
applyStyle(style, editingAction);
}
void Editor::applyParagraphStyleToSelection(CSSStyleDeclaration* style, EditAction editingAction)
{
if (!style || style->length() == 0 || !canEditRichly())
return;
if (client() && client()->shouldApplyStyle(style, m_frame->selectionController()->toRange().get()))
applyParagraphStyle(style, editingAction);
}
bool Editor::clientIsEditable() const
{
return client() && client()->isEditable();
}
bool Editor::selectionStartHasStyle(CSSStyleDeclaration* style) const
{
Node* nodeToRemove;
RefPtr<CSSStyleDeclaration> selectionStyle = m_frame->selectionComputedStyle(nodeToRemove);
if (!selectionStyle)
return false;
RefPtr<CSSMutableStyleDeclaration> mutableStyle = style->makeMutable();
bool match = true;
DeprecatedValueListConstIterator<CSSProperty> end;
for (DeprecatedValueListConstIterator<CSSProperty> it = mutableStyle->valuesIterator(); it != end; ++it) {
int propertyID = (*it).id();
if (!equalIgnoringCase(mutableStyle->getPropertyValue(propertyID), selectionStyle->getPropertyValue(propertyID))) {
match = false;
break;
}
}
if (nodeToRemove) {
ExceptionCode ec = 0;
nodeToRemove->remove(ec);
ASSERT(ec == 0);
}
return match;
}
void Editor::indent()
{
applyCommand(new IndentOutdentCommand(m_frame->document(), IndentOutdentCommand::Indent));
}
void Editor::outdent()
{
applyCommand(new IndentOutdentCommand(m_frame->document(), IndentOutdentCommand::Outdent));
}
static void dispatchEditableContentChangedEvents(const EditCommand& command)
{
Element* startRoot = command.startingRootEditableElement();
Element* endRoot = command.endingRootEditableElement();
ExceptionCode ec;
if (startRoot)
startRoot->dispatchEvent(new Event(webkitEditableContentChangedEvent, false, false), ec, true);
if (endRoot && endRoot != startRoot)
endRoot->dispatchEvent(new Event(webkitEditableContentChangedEvent, false, false), ec, true);
}
void Editor::appliedEditing(PassRefPtr<EditCommand> cmd)
{
dispatchEditableContentChangedEvents(*cmd);
RefPtr<Node> anchor = removedAnchor();
Selection newSelection(cmd->endingSelection());
if (newSelection == m_frame->selectionController()->selection() || m_frame->shouldChangeSelection(newSelection))
m_frame->selectionController()->setSelection(newSelection, false);
setRemovedAnchor(anchor);
if (cmd->typingStyle()) {
m_frame->setTypingStyle(cmd->typingStyle());
cmd->setTypingStyle(0);
}
if (m_lastEditCommand.get() == cmd)
ASSERT(cmd->isTypingCommand());
else {
m_lastEditCommand = cmd;
if (client())
client()->registerCommandForUndo(m_lastEditCommand);
}
respondToChangedContents(newSelection);
}
void Editor::unappliedEditing(PassRefPtr<EditCommand> cmd)
{
dispatchEditableContentChangedEvents(*cmd);
Selection newSelection(cmd->startingSelection());
if (newSelection == m_frame->selectionController()->selection() || m_frame->shouldChangeSelection(newSelection))
m_frame->selectionController()->setSelection(newSelection, true);
m_lastEditCommand = 0;
if (client())
client()->registerCommandForRedo(cmd);
respondToChangedContents(newSelection);
}
void Editor::reappliedEditing(PassRefPtr<EditCommand> cmd)
{
dispatchEditableContentChangedEvents(*cmd);
Selection newSelection(cmd->endingSelection());
if (newSelection == m_frame->selectionController()->selection() || m_frame->shouldChangeSelection(newSelection))
m_frame->selectionController()->setSelection(newSelection, true);
m_lastEditCommand = 0;
if (client())
client()->registerCommandForUndo(cmd);
respondToChangedContents(newSelection);
}
static bool execCopy(Frame* frame, Event*)
{
frame->editor()->copy();
return true;
}
static bool execCut(Frame* frame, Event*)
{
frame->editor()->cut();
return true;
}
static bool execDelete(Frame* frame, Event*)
{
frame->editor()->performDelete();
return true;
}
static bool execDeleteWordBackward(Frame* frame, Event*)
{
frame->editor()->deleteWithDirection(SelectionController::BACKWARD, WordGranularity, true, false);
return true;
}
static bool execDeleteWordForward(Frame* frame, Event*)
{
frame->editor()->deleteWithDirection(SelectionController::FORWARD, WordGranularity, true, false);
return true;
}
static bool execBackwardDelete(Frame* frame, Event*)
{
frame->editor()->deleteWithDirection(SelectionController::BACKWARD, CharacterGranularity, false, true);
return true;
}
static bool execForwardDelete(Frame* frame, Event*)
{
frame->editor()->deleteWithDirection(SelectionController::FORWARD, CharacterGranularity, false, true);
return true;
}
static bool execMoveBackward(Frame* frame, Event*)
{
frame->selectionController()->modify(SelectionController::MOVE, SelectionController::BACKWARD, CharacterGranularity, true);
return true;
}
static bool execMoveBackwardAndModifySelection(Frame* frame, Event*)
{
frame->selectionController()->modify(SelectionController::EXTEND, SelectionController::BACKWARD, CharacterGranularity, true);
return true;
}
static bool execMoveUpByPageAndModifyCaret(Frame* frame, Event*)
{
RenderObject* renderer = frame->document()->focusedNode()->renderer();
if (renderer->style()->overflowY() == OSCROLL
|| renderer->style()->overflowY() == OAUTO
|| renderer->isTextArea()) {
int height = -(frame->document()->focusedNode()->renderer()->clientHeight()-PAGE_KEEP);
bool handledScroll = renderer->scroll(ScrollUp, ScrollByPage);
bool handledCaretMove = frame->selectionController()->modify(SelectionController::MOVE, height);
return handledScroll || handledCaretMove;
}
return false;
}
static bool execMoveDown(Frame* frame, Event*)
{
frame->selectionController()->modify(SelectionController::MOVE, SelectionController::FORWARD, LineGranularity, true);
return true;
}
static bool execMoveDownAndModifySelection(Frame* frame, Event*)
{
frame->selectionController()->modify(SelectionController::EXTEND, SelectionController::FORWARD, LineGranularity, true);
return true;
}
static bool execMoveForward(Frame* frame, Event*)
{
frame->selectionController()->modify(SelectionController::MOVE, SelectionController::FORWARD, CharacterGranularity, true);
return true;
}
static bool execMoveForwardAndModifySelection(Frame* frame, Event*)
{
frame->selectionController()->modify(SelectionController::EXTEND, SelectionController::FORWARD, CharacterGranularity, true);
return true;
}
static bool execMoveDownByPageAndModifyCaret(Frame* frame, Event*)
{
RenderObject* renderer = frame->document()->focusedNode()->renderer();
if (renderer->style()->overflowY() == OSCROLL
|| renderer->style()->overflowY() == OAUTO
|| renderer->isTextArea()) {
int height = frame->document()->focusedNode()->renderer()->clientHeight()-PAGE_KEEP;
bool handledScroll = renderer->scroll(ScrollDown, ScrollByPage);
bool handledCaretMove = frame->selectionController()->modify(SelectionController::MOVE, height);
return handledScroll || handledCaretMove;
}
return false;
}
static bool execMoveLeft(Frame* frame, Event*)
{
frame->selectionController()->modify(SelectionController::MOVE, SelectionController::LEFT, CharacterGranularity, true);
return true;
}
static bool execMoveLeftAndModifySelection(Frame* frame, Event*)
{
frame->selectionController()->modify(SelectionController::EXTEND, SelectionController::LEFT, CharacterGranularity, true);
return true;
}
static bool execMoveRight(Frame* frame, Event*)
{
frame->selectionController()->modify(SelectionController::MOVE, SelectionController::RIGHT, CharacterGranularity, true);
return true;
}
static bool execMoveRightAndModifySelection(Frame* frame, Event*)
{
frame->selectionController()->modify(SelectionController::EXTEND, SelectionController::RIGHT, CharacterGranularity, true);
return true;
}
static bool execMoveToBeginningOfDocument(Frame* frame, Event*)
{
frame->selectionController()->modify(SelectionController::MOVE, SelectionController::BACKWARD, DocumentBoundary, true);
return true;
}
static bool execMoveToBeginningOfDocumentAndModifySelection(Frame* frame, Event*)
{
frame->selectionController()->modify(SelectionController::EXTEND, SelectionController::BACKWARD, DocumentBoundary, true);
return true;
}
static bool execMoveToBeginningOfSentence(Frame* frame, Event*)
{
frame->selectionController()->modify(SelectionController::MOVE, SelectionController::BACKWARD, SentenceBoundary, true);
return true;
}
static bool execMoveToBeginningOfSentenceAndModifySelection(Frame* frame, Event*)
{
frame->selectionController()->modify(SelectionController::EXTEND, SelectionController::BACKWARD, SentenceBoundary, true);
return true;
}
static bool execMoveToBeginningOfLine(Frame* frame, Event*)
{
frame->selectionController()->modify(SelectionController::MOVE, SelectionController::BACKWARD, LineBoundary, true);
return true;
}
static bool execMoveToBeginningOfLineAndModifySelection(Frame* frame, Event*)
{
frame->selectionController()->modify(SelectionController::EXTEND, SelectionController::BACKWARD, LineBoundary, true);
return true;
}
static bool execMoveToBeginningOfParagraph(Frame* frame, Event*)
{
frame->selectionController()->modify(SelectionController::MOVE, SelectionController::BACKWARD, ParagraphBoundary, true);
return true;
}
static bool execMoveToBeginningOfParagraphAndModifySelection(Frame* frame, Event*)
{
frame->selectionController()->modify(SelectionController::EXTEND, SelectionController::BACKWARD, ParagraphBoundary, true);
return true;
}
static bool execMoveToEndOfDocument(Frame* frame, Event*)
{
frame->selectionController()->modify(SelectionController::MOVE, SelectionController::FORWARD, DocumentBoundary, true);
return true;
}
static bool execMoveToEndOfDocumentAndModifySelection(Frame* frame, Event*)
{
frame->selectionController()->modify(SelectionController::EXTEND, SelectionController::FORWARD, DocumentBoundary, true);
return true;
}
static bool execMoveToEndOfSentence(Frame* frame, Event*)
{
frame->selectionController()->modify(SelectionController::MOVE, SelectionController::FORWARD, SentenceBoundary, true);
return true;
}
static bool execMoveToEndOfSentenceAndModifySelection(Frame* frame, Event*)
{
frame->selectionController()->modify(SelectionController::EXTEND, SelectionController::FORWARD, SentenceBoundary, true);
return true;
}
static bool execMoveToEndOfLine(Frame* frame, Event*)
{
frame->selectionController()->modify(SelectionController::MOVE, SelectionController::FORWARD, LineBoundary, true);
return true;
}
static bool execMoveToEndOfLineAndModifySelection(Frame* frame, Event*)
{
frame->selectionController()->modify(SelectionController::EXTEND, SelectionController::FORWARD, LineBoundary, true);
return true;
}
static bool execMoveToEndOfParagraph(Frame* frame, Event*)
{
frame->selectionController()->modify(SelectionController::MOVE, SelectionController::FORWARD, ParagraphBoundary, true);
return true;
}
static bool execMoveToEndOfParagraphAndModifySelection(Frame* frame, Event*)
{
frame->selectionController()->modify(SelectionController::EXTEND, SelectionController::FORWARD, ParagraphBoundary, true);
return true;
}
static bool execMoveParagraphBackwardAndModifySelection(Frame* frame, Event*)
{
frame->selectionController()->modify(SelectionController::EXTEND, SelectionController::BACKWARD, ParagraphGranularity, true);
return true;
}
static bool execMoveParagraphForwardAndModifySelection(Frame* frame, Event*)
{
frame->selectionController()->modify(SelectionController::EXTEND, SelectionController::FORWARD, ParagraphGranularity, true);
return true;
}
static bool execMoveUp(Frame* frame, Event*)
{
frame->selectionController()->modify(SelectionController::MOVE, SelectionController::BACKWARD, LineGranularity, true);
return true;
}
static bool execMoveUpAndModifySelection(Frame* frame, Event*)
{
frame->selectionController()->modify(SelectionController::EXTEND, SelectionController::BACKWARD, LineGranularity, true);
return true;
}
static bool execMoveWordBackward(Frame* frame, Event*)
{
frame->selectionController()->modify(SelectionController::MOVE, SelectionController::BACKWARD, WordGranularity, true);
return true;
}
static bool execMoveWordBackwardAndModifySelection(Frame* frame, Event*)
{
frame->selectionController()->modify(SelectionController::EXTEND, SelectionController::BACKWARD, WordGranularity, true);
return true;
}
static bool execMoveWordForward(Frame* frame, Event*)
{
frame->selectionController()->modify(SelectionController::MOVE, SelectionController::FORWARD, WordGranularity, true);
return true;
}
static bool execMoveWordForwardAndModifySelection(Frame* frame, Event*)
{
frame->selectionController()->modify(SelectionController::EXTEND, SelectionController::FORWARD, WordGranularity, true);
return true;
}
static bool execMoveWordLeft(Frame* frame, Event*)
{
frame->selectionController()->modify(SelectionController::MOVE, SelectionController::LEFT, WordGranularity, true);
return true;
}
static bool execMoveWordLeftAndModifySelection(Frame* frame, Event*)
{
frame->selectionController()->modify(SelectionController::EXTEND, SelectionController::LEFT, WordGranularity, true);
return true;
}
static bool execMoveWordRight(Frame* frame, Event*)
{
frame->selectionController()->modify(SelectionController::MOVE, SelectionController::RIGHT, WordGranularity, true);
return true;
}
static bool execMoveWordRightAndModifySelection(Frame* frame, Event*)
{
frame->selectionController()->modify(SelectionController::EXTEND, SelectionController::RIGHT, WordGranularity, true);
return true;
}
static bool execPaste(Frame* frame, Event*)
{
frame->editor()->paste();
return true;
}
static bool execSelectAll(Frame* frame, Event*)
{
frame->selectionController()->selectAll();
return true;
}
static bool execToggleBold(Frame* frame, Event*)
{
ExceptionCode ec;
RefPtr<CSSStyleDeclaration> style = frame->document()->createCSSStyleDeclaration();
style->setProperty(CSS_PROP_FONT_WEIGHT, "bold", false, ec);
if (frame->editor()->selectionStartHasStyle(style.get()))
style->setProperty(CSS_PROP_FONT_WEIGHT, "normal", false, ec);
frame->editor()->applyStyleToSelection(style.get(), EditActionSetFont);
return true;
}
static bool execToggleItalic(Frame* frame, Event*)
{
ExceptionCode ec;
RefPtr<CSSStyleDeclaration> style = frame->document()->createCSSStyleDeclaration();
style->setProperty(CSS_PROP_FONT_STYLE, "italic", false, ec);
if (frame->editor()->selectionStartHasStyle(style.get()))
style->setProperty(CSS_PROP_FONT_STYLE, "normal", false, ec);
frame->editor()->applyStyleToSelection(style.get(), EditActionSetFont);
return true;
}
static bool execRedo(Frame* frame, Event*)
{
frame->editor()->redo();
return true;
}
static bool execUndo(Frame* frame, Event*)
{
frame->editor()->undo();
return true;
}
static inline Frame* targetFrame(Frame* frame, Event* evt)
{
Node* node = evt ? evt->target()->toNode() : 0;
if (!node)
return frame;
return node->document()->frame();
}
static bool execInsertTab(Frame* frame, Event* evt)
{
return targetFrame(frame, evt)->eventHandler()->handleTextInputEvent("\t", evt, false, false);
}
static bool execInsertBacktab(Frame* frame, Event* evt)
{
return targetFrame(frame, evt)->eventHandler()->handleTextInputEvent("\t", evt, false, true);
}
static bool execInsertNewline(Frame* frame, Event* evt)
{
Frame* targetFrame = WebCore::targetFrame(frame, evt);
return targetFrame->eventHandler()->handleTextInputEvent("\n", evt, !targetFrame->editor()->canEditRichly());
}
static bool execInsertLineBreak(Frame* frame, Event* evt)
{
return targetFrame(frame, evt)->eventHandler()->handleTextInputEvent("\n", evt, true);
}
static bool enabled(Frame*, Event*)
{
return true;
}
static bool canPaste(Frame* frame, Event*)
{
return frame->editor()->canPaste();
}
static bool hasEditableSelection(Frame* frame, Event* evt)
{
if (evt)
return selectionForEvent(frame, evt).isContentEditable();
return frame->selectionController()->isContentEditable();
}
static bool hasEditableRangeSelection(Frame* frame, Event*)
{
return frame->selectionController()->isRange() && frame->selectionController()->isContentEditable();
}
static bool hasRangeSelection(Frame* frame, Event*)
{
return frame->selectionController()->isRange();
}
static bool hasRichlyEditableSelection(Frame* frame, Event*)
{
return frame->selectionController()->isCaretOrRange() && frame->selectionController()->isContentRichlyEditable();
}
static bool canRedo(Frame* frame, Event*)
{
return frame->editor()->canRedo();
}
static bool canUndo(Frame* frame, Event*)
{
return frame->editor()->canUndo();
}
struct Command {
bool (*enabled)(Frame*, Event*);
bool (*exec)(Frame*, Event*);
};
typedef HashMap<RefPtr<AtomicStringImpl>, const Command*> CommandMap;
static CommandMap* createCommandMap()
{
struct CommandEntry { const char* name; Command command; };
static const CommandEntry commands[] = {
{ "BackwardDelete", { hasEditableSelection, execBackwardDelete } },
{ "Copy", { hasRangeSelection, execCopy } },
{ "Cut", { hasEditableRangeSelection, execCut } },
{ "Delete", { hasEditableSelection, execDelete } },
{ "DeleteWordBackward", { hasEditableSelection, execDeleteWordBackward } },
{ "DeleteWordForward", { hasEditableSelection, execDeleteWordForward} },
{ "ForwardDelete", { hasEditableSelection, execForwardDelete } },
{ "InsertBacktab", { hasEditableSelection, execInsertBacktab } },
{ "InsertTab", { hasEditableSelection, execInsertTab } },
{ "InsertLineBreak", { hasEditableSelection, execInsertLineBreak } },
{ "InsertNewline", { hasEditableSelection, execInsertNewline } },
{ "MoveBackward", { hasEditableSelection, execMoveBackward } },
{ "MoveBackwardAndModifySelection", { hasEditableSelection, execMoveBackwardAndModifySelection } },
{ "MoveUpByPageAndModifyCaret", { hasEditableSelection, execMoveUpByPageAndModifyCaret } },
{ "MoveDown", { hasEditableSelection, execMoveDown } },
{ "MoveDownAndModifySelection", { hasEditableSelection, execMoveDownAndModifySelection } },
{ "MoveForward", { hasEditableSelection, execMoveForward } },
{ "MoveForwardAndModifySelection", { hasEditableSelection, execMoveForwardAndModifySelection } },
{ "MoveDownByPageAndModifyCaret", { hasEditableSelection, execMoveDownByPageAndModifyCaret } },
{ "MoveLeft", { hasEditableSelection, execMoveLeft } },
{ "MoveLeftAndModifySelection", { hasEditableSelection, execMoveLeftAndModifySelection } },
{ "MoveRight", { hasEditableSelection, execMoveRight } },
{ "MoveRightAndModifySelection", { hasEditableSelection, execMoveRightAndModifySelection } },
{ "MoveToBeginningOfDocument", { hasEditableSelection, execMoveToBeginningOfDocument } },
{ "MoveToBeginningOfDocumentAndModifySelection", { hasEditableSelection, execMoveToBeginningOfDocumentAndModifySelection } },
{ "MoveToBeginningOfSentence", { hasEditableSelection, execMoveToBeginningOfSentence } },
{ "MoveToBeginningOfSentenceAndModifySelection", { hasEditableSelection, execMoveToBeginningOfSentenceAndModifySelection } },
{ "MoveToBeginningOfLine", { hasEditableSelection, execMoveToBeginningOfLine } },
{ "MoveToBeginningOfLineAndModifySelection", { hasEditableSelection, execMoveToBeginningOfLineAndModifySelection } },
{ "MoveToBeginningOfParagraph", { hasEditableSelection, execMoveToBeginningOfParagraph } },
{ "MoveToBeginningOfParagraphAndModifySelection", { hasEditableSelection, execMoveToBeginningOfParagraphAndModifySelection } },
{ "MoveToEndOfDocument", { hasEditableSelection, execMoveToEndOfDocument } },
{ "MoveToEndOfDocumentAndModifySelection", { hasEditableSelection, execMoveToEndOfDocumentAndModifySelection } },
{ "MoveToEndOfSentence", { hasEditableSelection, execMoveToEndOfSentence } },
{ "MoveToEndOfSentenceAndModifySelection", { hasEditableSelection, execMoveToEndOfSentenceAndModifySelection } },
{ "MoveToEndOfLine", { hasEditableSelection, execMoveToEndOfLine } },
{ "MoveToEndOfLineAndModifySelection", { hasEditableSelection, execMoveToEndOfLineAndModifySelection } },
{ "MoveToEndOfParagraph", { hasEditableSelection, execMoveToEndOfParagraph } },
{ "MoveToEndOfParagraphAndModifySelection", { hasEditableSelection, execMoveToEndOfParagraphAndModifySelection } },
{ "MoveParagraphBackwardAndModifySelection", { hasEditableSelection, execMoveParagraphBackwardAndModifySelection } },
{ "MoveParagraphForwardAndModifySelection", { hasEditableSelection, execMoveParagraphForwardAndModifySelection } },
{ "MoveUp", { hasEditableSelection, execMoveUp } },
{ "MoveUpAndModifySelection", { hasEditableSelection, execMoveUpAndModifySelection } },
{ "MoveWordBackward", { hasEditableSelection, execMoveWordBackward } },
{ "MoveWordBackwardAndModifySelection", { hasEditableSelection, execMoveWordBackwardAndModifySelection } },
{ "MoveWordForward", { hasEditableSelection, execMoveWordForward } },
{ "MoveWordForwardAndModifySelection", { hasEditableSelection, execMoveWordForwardAndModifySelection } },
{ "MoveWordLeft", { hasEditableSelection, execMoveWordLeft } },
{ "MoveWordLeftAndModifySelection", { hasEditableSelection, execMoveWordLeftAndModifySelection } },
{ "MoveWordRight", { hasEditableSelection, execMoveWordRight } },
{ "MoveWordRightAndModifySelection", { hasEditableSelection, execMoveWordRightAndModifySelection } },
{ "Paste", { canPaste, execPaste } },
{ "Redo", { canRedo, execRedo } },
{ "SelectAll", { enabled, execSelectAll } },
{ "ToggleBold", { hasRichlyEditableSelection, execToggleBold } },
{ "ToggleItalic", { hasRichlyEditableSelection, execToggleItalic } },
{ "Undo", { canUndo, execUndo } }
};
CommandMap* commandMap = new CommandMap;
const unsigned numCommands = sizeof(commands) / sizeof(commands[0]);
for (unsigned i = 0; i < numCommands; i++)
commandMap->set(AtomicString(commands[i].name).impl(), &commands[i].command);
return commandMap;
}
Editor::Editor(Frame* frame)
: m_frame(frame)
, m_deleteButtonController(new DeleteButtonController(frame))
, m_ignoreCompositionSelectionChange(false)
{
}
Editor::~Editor()
{
}
void Editor::clear()
{
m_compositionNode = 0;
m_customCompositionUnderlines.clear();
}
bool Editor::execCommand(const AtomicString& command, Event* triggeringEvent)
{
if (!m_frame->document())
return false;
static CommandMap* commandMap;
if (!commandMap)
commandMap = createCommandMap();
const Command* c = commandMap->get(command.impl());
if (!c)
return false;
bool handled = false;
if (c->enabled(m_frame, triggeringEvent)) {
m_frame->document()->updateLayoutIgnorePendingStylesheets();
handled = c->exec(m_frame, triggeringEvent);
}
return handled;
}
bool Editor::insertText(const String& text, Event* triggeringEvent)
{
return m_frame->eventHandler()->handleTextInputEvent(text, triggeringEvent);
}
bool Editor::insertTextWithoutSendingTextEvent(const String& text, bool selectInsertedText, Event* triggeringEvent)
{
if (text.isEmpty())
return false;
Selection selection = selectionForEvent(m_frame, triggeringEvent);
if (!selection.isContentEditable())
return false;
RefPtr<Range> range = selection.toRange();
if (!shouldInsertText(text, range.get(), EditorInsertActionTyped))
return true;
selection = selectionForEvent(m_frame, triggeringEvent);
if (selection.isContentEditable()) {
if (Node* selectionStart = selection.start().node()) {
RefPtr<Document> document = selectionStart->document();
TypingCommand::insertText(document.get(), text, selection, selectInsertedText);
if (Frame* editedFrame = document->frame())
if (Page* page = editedFrame->page())
page->focusController()->focusedOrMainFrame()->revealSelection(RenderLayer::gAlignToEdgeIfNeeded);
}
}
return true;
}
bool Editor::insertLineBreak()
{
if (!canEdit())
return false;
if (!shouldInsertText("\n", m_frame->selectionController()->toRange().get(), EditorInsertActionTyped))
return true;
TypingCommand::insertLineBreak(m_frame->document());
revealSelectionAfterEditingOperation();
return true;
}
bool Editor::insertParagraphSeparator()
{
if (!canEdit())
return false;
if (!canEditRichly())
return insertLineBreak();
if (!shouldInsertText("\n", m_frame->selectionController()->toRange().get(), EditorInsertActionTyped))
return true;
TypingCommand::insertParagraphSeparator(m_frame->document());
revealSelectionAfterEditingOperation();
return true;
}
void Editor::cut()
{
if (tryDHTMLCut())
return; if (!canCut()) {
systemBeep();
return;
}
RefPtr<Range> selection = selectedRange();
if (shouldDeleteRange(selection.get())) {
Pasteboard::generalPasteboard()->writeSelection(selection.get(), canSmartCopyOrDelete(), m_frame);
didWriteSelectionToPasteboard();
deleteSelectionWithSmartDelete();
}
}
void Editor::copy()
{
if (tryDHTMLCopy())
return; if (!canCopy()) {
systemBeep();
return;
}
Document* document = m_frame->document();
if (Node* node = imageNodeFromImageDocument(document))
Pasteboard::generalPasteboard()->writeImage(node, document->URL(), document->title());
else
Pasteboard::generalPasteboard()->writeSelection(selectedRange().get(), canSmartCopyOrDelete(), m_frame);
didWriteSelectionToPasteboard();
}
void Editor::paste()
{
ASSERT(m_frame->document());
DocLoader* loader = m_frame->document()->docLoader();
#if PLATFORM(MAC)
loader->setAllowStaleResources(true);
m_frame->issuePasteCommand();
loader->setAllowStaleResources(false);
#else
if (tryDHTMLPaste())
return; if (!canPaste())
return;
loader->setAllowStaleResources(true);
if (m_frame->selectionController()->isContentRichlyEditable())
pasteWithPasteboard(Pasteboard::generalPasteboard(), true);
else
pasteAsPlainTextWithPasteboard(Pasteboard::generalPasteboard());
loader->setAllowStaleResources(false);
#endif
}
void Editor::pasteAsPlainText()
{
if (!canPaste())
return;
pasteAsPlainTextWithPasteboard(Pasteboard::generalPasteboard());
}
void Editor::performDelete()
{
if (!canDelete()) {
systemBeep();
return;
}
deleteRange(selectedRange().get(), true, false, true, deleteSelectionAction, CharacterGranularity);
}
void Editor::copyURL(const KURL& url, const String& title)
{
Pasteboard::generalPasteboard()->writeURL(url, title, m_frame);
}
void Editor::copyImage(const HitTestResult& result)
{
KURL url = result.absoluteLinkURL();
if (url.isEmpty())
url = result.absoluteImageURL();
Pasteboard::generalPasteboard()->writeImage(result.innerNonSharedNode(), url, result.altDisplayString());
}
bool Editor::isContinuousSpellCheckingEnabled()
{
return client() && client()->isContinuousSpellCheckingEnabled();
}
void Editor::toggleContinuousSpellChecking()
{
if (client())
client()->toggleContinuousSpellChecking();
}
bool Editor::isGrammarCheckingEnabled()
{
return client() && client()->isGrammarCheckingEnabled();
}
void Editor::toggleGrammarChecking()
{
if (client())
client()->toggleGrammarChecking();
}
int Editor::spellCheckerDocumentTag()
{
return client() ? client()->spellCheckerDocumentTag() : 0;
}
bool Editor::shouldEndEditing(Range* range)
{
return client() && client()->shouldEndEditing(range);
}
bool Editor::shouldBeginEditing(Range* range)
{
return client() && client()->shouldBeginEditing(range);
}
void Editor::clearUndoRedoOperations()
{
if (client())
client()->clearUndoRedoOperations();
}
bool Editor::canUndo()
{
return client() && client()->canUndo();
}
void Editor::undo()
{
if (client())
client()->undo();
}
bool Editor::canRedo()
{
return client() && client()->canRedo();
}
void Editor::redo()
{
if (client())
client()->redo();
}
void Editor::didBeginEditing()
{
if (client())
client()->didBeginEditing();
}
void Editor::didEndEditing()
{
if (client())
client()->didEndEditing();
}
void Editor::didWriteSelectionToPasteboard()
{
if (client())
client()->didWriteSelectionToPasteboard();
}
void Editor::toggleBold()
{
execToggleBold(frame(), 0);
}
void Editor::toggleUnderline()
{
ExceptionCode ec = 0;
RefPtr<CSSStyleDeclaration> style = frame()->document()->createCSSStyleDeclaration();
style->setProperty(CSS_PROP__WEBKIT_TEXT_DECORATIONS_IN_EFFECT, "underline", false, ec);
if (selectionStartHasStyle(style.get()))
style->setProperty(CSS_PROP__WEBKIT_TEXT_DECORATIONS_IN_EFFECT, "none", false, ec);
applyStyleToSelection(style.get(), EditActionUnderline);
}
void Editor::setBaseWritingDirection(String direction)
{
ExceptionCode ec = 0;
RefPtr<CSSStyleDeclaration> style = frame()->document()->createCSSStyleDeclaration();
style->setProperty(CSS_PROP_DIRECTION, direction, false, ec);
applyParagraphStyleToSelection(style.get(), EditActionSetWritingDirection);
}
void Editor::selectComposition()
{
RefPtr<Range> range = compositionRange();
if (!range)
return;
ExceptionCode ec = 0;
m_frame->selectionController()->setSelectedRange(range.get(), DOWNSTREAM, false, ec);
}
void Editor::confirmComposition()
{
if (!m_compositionNode)
return;
confirmComposition(m_compositionNode->data().substring(m_compositionStart, m_compositionEnd - m_compositionStart), false);
}
void Editor::confirmCompositionWithoutDisturbingSelection()
{
if (!m_compositionNode)
return;
confirmComposition(m_compositionNode->data().substring(m_compositionStart, m_compositionEnd - m_compositionStart), true);
}
void Editor::confirmComposition(const String& text)
{
confirmComposition(text, false);
}
void Editor::confirmComposition(const String& text, bool preserveSelection)
{
setIgnoreCompositionSelectionChange(true);
Selection oldSelection = m_frame->selectionController()->selection();
selectComposition();
if (m_frame->selectionController()->isNone()) {
setIgnoreCompositionSelectionChange(false);
return;
}
deleteSelectionWithSmartDelete(false);
m_compositionNode = 0;
m_customCompositionUnderlines.clear();
insertText(text, 0);
if (preserveSelection)
m_frame->selectionController()->setSelection(oldSelection, false, false);
setIgnoreCompositionSelectionChange(false);
}
void Editor::setComposition(const String& text, const Vector<CompositionUnderline>& underlines, unsigned selectionStart, unsigned selectionEnd)
{
setIgnoreCompositionSelectionChange(true);
selectComposition();
if (m_frame->selectionController()->isNone()) {
setIgnoreCompositionSelectionChange(false);
return;
}
deleteSelectionWithSmartDelete(false);
m_compositionNode = 0;
m_customCompositionUnderlines.clear();
if (!text.isEmpty()) {
TypingCommand::insertText(m_frame->document(), text, true, true);
Node* baseNode = m_frame->selectionController()->baseNode();
unsigned baseOffset = m_frame->selectionController()->base().offset();
Node* extentNode = m_frame->selectionController()->extentNode();
unsigned extentOffset = m_frame->selectionController()->extent().offset();
if (baseNode && baseNode == extentNode && baseNode->isTextNode() && baseOffset + text.length() == extentOffset) {
m_compositionNode = static_cast<Text*>(baseNode);
m_compositionStart = baseOffset;
m_compositionEnd = extentOffset;
m_customCompositionUnderlines = underlines;
size_t numUnderlines = m_customCompositionUnderlines.size();
for (size_t i = 0; i < numUnderlines; ++i) {
m_customCompositionUnderlines[i].startOffset += baseOffset;
m_customCompositionUnderlines[i].endOffset += baseOffset;
}
if (baseNode->renderer())
baseNode->renderer()->repaint();
unsigned start = min(baseOffset + selectionStart, extentOffset);
unsigned end = min(max(start, baseOffset + selectionEnd), extentOffset);
RefPtr<Range> selectedRange = new Range(baseNode->document(), baseNode, start, baseNode, end);
ExceptionCode ec = 0;
m_frame->selectionController()->setSelectedRange(selectedRange.get(), DOWNSTREAM, false, ec);
}
}
setIgnoreCompositionSelectionChange(false);
}
void Editor::ignoreSpelling()
{
if (!client())
return;
String text = frame()->selectedText();
ASSERT(text.length() != 0);
client()->ignoreWordInSpellDocument(text);
}
void Editor::learnSpelling()
{
if (!client())
return;
String text = frame()->selectedText();
ASSERT(text.length() != 0);
client()->learnWord(text);
}
static String findFirstMisspellingInRange(EditorClient* client, Range* searchRange, int& firstMisspellingOffset, bool markAll)
{
ASSERT_ARG(client, client);
ASSERT_ARG(searchRange, searchRange);
WordAwareIterator it(searchRange);
firstMisspellingOffset = 0;
String firstMisspelling;
int currentChunkOffset = 0;
while (!it.atEnd()) {
const UChar* chars = it.characters();
int len = it.length();
if (!(len == 1 && chars[0] == ' ')) {
int misspellingLocation = -1;
int misspellingLength = 0;
client->checkSpellingOfString(chars, len, &misspellingLocation, &misspellingLength);
ASSERT(misspellingLength >= 0);
ASSERT(misspellingLocation >= -1);
ASSERT(misspellingLength == 0 || misspellingLocation >= 0);
ASSERT(misspellingLocation < len);
ASSERT(misspellingLength <= len);
ASSERT(misspellingLocation + misspellingLength <= len);
if (misspellingLocation >= 0 && misspellingLength > 0 && misspellingLocation < len && misspellingLength <= len && misspellingLocation + misspellingLength <= len) {
if (!firstMisspelling) {
firstMisspellingOffset = currentChunkOffset + misspellingLocation;
firstMisspelling = String(chars + misspellingLocation, misspellingLength);
}
if (!markAll)
break;
RefPtr<Range> misspellingRange = TextIterator::subrange(searchRange, currentChunkOffset + misspellingLocation, misspellingLength);
ExceptionCode ec = 0;
misspellingRange->startContainer(ec)->document()->addMarker(misspellingRange.get(), DocumentMarker::Spelling);
ASSERT(ec == 0);
}
}
currentChunkOffset += len;
it.advance();
}
return firstMisspelling;
}
#ifndef BUILDING_ON_TIGER
static PassRefPtr<Range> paragraphAlignedRangeForRange(Range* arbitraryRange, int& offsetIntoParagraphAlignedRange, String& paragraphString)
{
ASSERT_ARG(arbitraryRange, arbitraryRange);
ExceptionCode ec = 0;
RefPtr<Range> paragraphRange = arbitraryRange->cloneRange(ec);
setStart(paragraphRange.get(), startOfParagraph(arbitraryRange->startPosition()));
setEnd(paragraphRange.get(), endOfParagraph(arbitraryRange->endPosition()));
RefPtr<Range> offsetAsRange = new Range(paragraphRange->startContainer(ec)->document(), paragraphRange->startPosition(), arbitraryRange->startPosition());
offsetIntoParagraphAlignedRange = TextIterator::rangeLength(offsetAsRange.get());
paragraphString = plainText(paragraphRange.get());
return paragraphRange;
}
static int findFirstGrammarDetailInRange(const Vector<GrammarDetail>& grammarDetails, int badGrammarPhraseLocation, int badGrammarPhraseLength, Range *searchRange, int startOffset, int endOffset, bool markAll)
{
int earliestDetailLocationSoFar = -1;
int earliestDetailIndex = -1;
for (unsigned i = 0; i < grammarDetails.size(); i++) {
const GrammarDetail* detail = &grammarDetails[i];
ASSERT(detail->length > 0 && detail->location >= 0);
int detailStartOffsetInParagraph = badGrammarPhraseLocation + detail->location;
if (detailStartOffsetInParagraph < startOffset)
continue;
if (detailStartOffsetInParagraph >= endOffset)
continue;
if (markAll) {
RefPtr<Range> badGrammarRange = TextIterator::subrange(searchRange, badGrammarPhraseLocation - startOffset + detail->location, detail->length);
ExceptionCode ec = 0;
badGrammarRange->startContainer(ec)->document()->addMarker(badGrammarRange.get(), DocumentMarker::Grammar, detail->userDescription);
ASSERT(ec == 0);
}
if (earliestDetailIndex < 0 || earliestDetailLocationSoFar > detail->location) {
earliestDetailIndex = i;
earliestDetailLocationSoFar = detail->location;
}
}
return earliestDetailIndex;
}
static String findFirstBadGrammarInRange(EditorClient* client, Range* searchRange, GrammarDetail& outGrammarDetail, int& outGrammarPhraseOffset, bool markAll)
{
ASSERT_ARG(client, client);
ASSERT_ARG(searchRange, searchRange);
outGrammarDetail.location = -1;
outGrammarDetail.length = 0;
outGrammarDetail.guesses.clear();
outGrammarDetail.userDescription = "";
outGrammarPhraseOffset = 0;
String firstBadGrammarPhrase;
int searchRangeStartOffset;
String paragraphString;
RefPtr<Range> paragraphRange = paragraphAlignedRangeForRange(searchRange, searchRangeStartOffset, paragraphString);
int searchRangeEndOffset = searchRangeStartOffset + TextIterator::rangeLength(searchRange);
int startOffset = 0;
while (startOffset < searchRangeEndOffset) {
Vector<GrammarDetail> grammarDetails;
int badGrammarPhraseLocation = -1;
int badGrammarPhraseLength = 0;
client->checkGrammarOfString(paragraphString.characters() + startOffset, paragraphString.length() - startOffset, grammarDetails, &badGrammarPhraseLocation, &badGrammarPhraseLength);
if (badGrammarPhraseLength == 0) {
ASSERT(badGrammarPhraseLocation == -1);
return String();
}
ASSERT(badGrammarPhraseLocation >= 0);
badGrammarPhraseLocation += startOffset;
int badGrammarIndex = findFirstGrammarDetailInRange(grammarDetails, badGrammarPhraseLocation, badGrammarPhraseLength, searchRange, searchRangeStartOffset, searchRangeEndOffset, markAll);
if (badGrammarIndex >= 0) {
ASSERT(static_cast<unsigned>(badGrammarIndex) < grammarDetails.size());
outGrammarDetail = grammarDetails[badGrammarIndex];
}
if (badGrammarIndex >= 0 && firstBadGrammarPhrase.isEmpty()) {
outGrammarPhraseOffset = badGrammarPhraseLocation - searchRangeStartOffset;
firstBadGrammarPhrase = paragraphString.substring(badGrammarPhraseLocation, badGrammarPhraseLength);
if (!markAll)
break;
}
startOffset = badGrammarPhraseLocation + badGrammarPhraseLength;
}
return firstBadGrammarPhrase;
}
#endif
void Editor::advanceToNextMisspelling(bool startBeforeSelection)
{
ExceptionCode ec = 0;
Selection selection(frame()->selectionController()->selection());
RefPtr<Range> spellingSearchRange(rangeOfContents(frame()->document()));
bool startedWithSelection = false;
if (selection.start().node()) {
startedWithSelection = true;
if (startBeforeSelection) {
VisiblePosition start(selection.visibleStart());
VisiblePosition oneBeforeStart = start.previous();
setStart(spellingSearchRange.get(), oneBeforeStart.isNotNull() ? oneBeforeStart : start);
} else
setStart(spellingSearchRange.get(), selection.visibleEnd());
}
Position position = spellingSearchRange->startPosition();
if (!isEditablePosition(position)) {
position = firstEditablePositionAfterPositionInRoot(position, frame()->document()->documentElement()).deepEquivalent();
if (position.isNull())
return;
spellingSearchRange->setStart(position.node(), position.offset(), ec);
startedWithSelection = false; }
Node* topNode = highestEditableRoot(position);
spellingSearchRange->setEnd(topNode, maxDeepOffset(topNode), ec);
if (startedWithSelection) {
VisiblePosition oneBeforeStart = startVisiblePosition(spellingSearchRange.get(), DOWNSTREAM).previous();
if (oneBeforeStart.isNotNull()) {
setStart(spellingSearchRange.get(), endOfWord(oneBeforeStart));
} }
if (spellingSearchRange->collapsed(ec))
return;
if (!client())
return;
Node *searchEndNodeAfterWrap = spellingSearchRange->endContainer(ec);
int searchEndOffsetAfterWrap = spellingSearchRange->endOffset(ec);
int misspellingOffset;
String misspelledWord = findFirstMisspellingInRange(client(), spellingSearchRange.get(), misspellingOffset, false);
String badGrammarPhrase;
#ifndef BUILDING_ON_TIGER
int grammarPhraseOffset = 0;
GrammarDetail grammarDetail;
RefPtr<Range> grammarSearchRange = spellingSearchRange->cloneRange(ec);
if (!misspelledWord.isEmpty()) {
CharacterIterator chars(grammarSearchRange.get());
chars.advance(misspellingOffset);
grammarSearchRange->setEnd(chars.range()->startContainer(ec), chars.range()->startOffset(ec), ec);
}
if (isGrammarCheckingEnabled())
badGrammarPhrase = findFirstBadGrammarInRange(client(), grammarSearchRange.get(), grammarDetail, grammarPhraseOffset, false);
#endif
if (startedWithSelection && !misspelledWord && !badGrammarPhrase) {
spellingSearchRange->setStart(topNode, 0, ec);
spellingSearchRange->setEnd(searchEndNodeAfterWrap, searchEndOffsetAfterWrap, ec);
misspelledWord = findFirstMisspellingInRange(client(), spellingSearchRange.get(), misspellingOffset, false);
#ifndef BUILDING_ON_TIGER
grammarSearchRange = spellingSearchRange->cloneRange(ec);
if (!misspelledWord.isEmpty()) {
CharacterIterator chars(grammarSearchRange.get());
chars.advance(misspellingOffset);
grammarSearchRange->setEnd(chars.range()->startContainer(ec), chars.range()->startOffset(ec), ec);
}
if (isGrammarCheckingEnabled())
badGrammarPhrase = findFirstBadGrammarInRange(client(), grammarSearchRange.get(), grammarDetail, grammarPhraseOffset, false);
#endif
}
if (!badGrammarPhrase.isEmpty()) {
#ifdef BUILDING_ON_TIGER
ASSERT_NOT_REACHED();
#else
ASSERT(badGrammarPhrase.length() > 0);
ASSERT(grammarDetail.location != -1 && grammarDetail.length > 0);
RefPtr<Range> badGrammarRange = TextIterator::subrange(grammarSearchRange.get(), grammarPhraseOffset + grammarDetail.location, grammarDetail.length);
frame()->selectionController()->setSelection(Selection(badGrammarRange.get(), SEL_DEFAULT_AFFINITY));
frame()->revealSelection();
client()->updateSpellingUIWithGrammarString(badGrammarPhrase, grammarDetail);
frame()->document()->addMarker(badGrammarRange.get(), DocumentMarker::Grammar, grammarDetail.userDescription);
#endif
} else if (!misspelledWord.isEmpty()) {
RefPtr<Range> misspellingRange = TextIterator::subrange(spellingSearchRange.get(), misspellingOffset, misspelledWord.length());
frame()->selectionController()->setSelection(Selection(misspellingRange.get(), DOWNSTREAM));
frame()->revealSelection();
client()->updateSpellingUIWithMisspelledWord(misspelledWord);
frame()->document()->addMarker(misspellingRange.get(), DocumentMarker::Spelling);
}
}
bool Editor::isSelectionMisspelled()
{
String selectedString = frame()->selectedText();
int length = selectedString.length();
if (length == 0)
return false;
if (!client())
return false;
int misspellingLocation = -1;
int misspellingLength = 0;
client()->checkSpellingOfString(selectedString.characters(), length, &misspellingLocation, &misspellingLength);
if (misspellingLength != length)
return false;
client()->updateSpellingUIWithMisspelledWord(selectedString);
return true;
}
#ifndef BUILDING_ON_TIGER
static bool isRangeUngrammatical(EditorClient* client, Range *range, Vector<String>& guessesVector)
{
if (!client)
return false;
ExceptionCode ec;
if (!range || range->collapsed(ec))
return false;
guessesVector.clear();
int grammarPhraseOffset;
GrammarDetail grammarDetail;
String badGrammarPhrase = findFirstBadGrammarInRange(client, range, grammarDetail, grammarPhraseOffset, false);
if (badGrammarPhrase.isEmpty())
return false;
if (grammarPhraseOffset > 0)
return false;
ASSERT(grammarDetail.location >= 0 && grammarDetail.length > 0);
if (grammarDetail.location + grammarPhraseOffset != 0)
return false;
if (grammarDetail.length != TextIterator::rangeLength(range))
return false;
client->updateSpellingUIWithGrammarString(badGrammarPhrase, grammarDetail);
return true;
}
#endif
bool Editor::isSelectionUngrammatical()
{
#ifdef BUILDING_ON_TIGER
return false;
#else
Vector<String> ignoredGuesses;
return isRangeUngrammatical(client(), frame()->selectionController()->toRange().get(), ignoredGuesses);
#endif
}
Vector<String> Editor::guessesForUngrammaticalSelection()
{
#ifdef BUILDING_ON_TIGER
return Vector<String>();
#else
Vector<String> guesses;
isRangeUngrammatical(client(), frame()->selectionController()->toRange().get(), guesses);
return guesses;
#endif
}
Vector<String> Editor::guessesForMisspelledSelection()
{
String selectedString = frame()->selectedText();
ASSERT(selectedString.length() != 0);
Vector<String> guesses;
if (client())
client()->getGuessesForWord(selectedString, guesses);
return guesses;
}
void Editor::showSpellingGuessPanel()
{
if (!client()) {
LOG_ERROR("No NSSpellChecker");
return;
}
#ifndef BUILDING_ON_TIGER
if (client()->spellingUIIsShowing()) {
client()->showSpellingUI(false);
return;
}
#endif
advanceToNextMisspelling(true);
client()->showSpellingUI(true);
}
bool Editor::spellingPanelIsShowing()
{
if (!client())
return false;
return client()->spellingUIIsShowing();
}
void Editor::markMisspellingsAfterTypingToPosition(const VisiblePosition &p)
{
if (!isContinuousSpellCheckingEnabled())
return;
markMisspellings(Selection(startOfWord(p, LeftWordIfOnBoundary), endOfWord(p, RightWordIfOnBoundary)));
if (!isGrammarCheckingEnabled())
return;
markBadGrammar(Selection(startOfSentence(p), endOfSentence(p)));
}
static void markAllMisspellingsInRange(EditorClient* client, Range* searchRange)
{
int ignoredOffset;
findFirstMisspellingInRange(client, searchRange, ignoredOffset, true);
}
#ifndef BUILDING_ON_TIGER
static void markAllBadGrammarInRange(EditorClient* client, Range* searchRange)
{
GrammarDetail ignoredGrammarDetail;
int ignoredOffset;
findFirstBadGrammarInRange(client, searchRange, ignoredGrammarDetail, ignoredOffset, true);
}
#endif
static void markMisspellingsOrBadGrammar(Editor* editor, const Selection& selection, bool checkSpelling)
{
if (!editor->isContinuousSpellCheckingEnabled())
return;
RefPtr<Range> searchRange(selection.toRange());
if (!searchRange || searchRange->isDetached())
return;
int exception = 0;
Node *editableNode = searchRange->startContainer(exception);
if (!editableNode->isContentEditable())
return;
if (!editor->client())
return;
if (checkSpelling)
markAllMisspellingsInRange(editor->client(), searchRange.get());
else {
#ifdef BUILDING_ON_TIGER
ASSERT_NOT_REACHED();
#else
if (editor->isGrammarCheckingEnabled())
markAllBadGrammarInRange(editor->client(), searchRange.get());
#endif
}
}
void Editor::markMisspellings(const Selection& selection)
{
markMisspellingsOrBadGrammar(this, selection, true);
}
void Editor::markBadGrammar(const Selection& selection)
{
#ifndef BUILDING_ON_TIGER
markMisspellingsOrBadGrammar(this, selection, false);
#endif
}
PassRefPtr<Range> Editor::rangeForPoint(const IntPoint& windowPoint)
{
Document* document = m_frame->documentAtPoint(windowPoint);
if (!document)
return 0;
Frame* frame = document->frame();
ASSERT(frame);
FrameView* frameView = frame->view();
if (!frameView)
return 0;
IntPoint framePoint = frameView->windowToContents(windowPoint);
Selection selection(frame->visiblePositionForPoint(framePoint));
return avoidIntersectionWithNode(selection.toRange().get(), deleteButtonController() ? deleteButtonController()->containerElement() : 0);
}
void Editor::revealSelectionAfterEditingOperation()
{
if (m_ignoreCompositionSelectionChange)
return;
m_frame->revealSelection(RenderLayer::gAlignToEdgeIfNeeded);
}
void Editor::setIgnoreCompositionSelectionChange(bool ignore)
{
if (m_ignoreCompositionSelectionChange == ignore)
return;
m_ignoreCompositionSelectionChange = ignore;
if (!ignore)
revealSelectionAfterEditingOperation();
}
PassRefPtr<Range> Editor::compositionRange() const
{
if (!m_compositionNode)
return 0;
unsigned length = m_compositionNode->length();
unsigned start = min(m_compositionStart, length);
unsigned end = min(max(start, m_compositionEnd), length);
if (start >= end)
return 0;
return new Range(m_compositionNode->document(), m_compositionNode.get(), start, m_compositionNode.get(), end);
}
bool Editor::getCompositionSelection(unsigned& selectionStart, unsigned& selectionEnd) const
{
if (!m_compositionNode)
return false;
Position start = m_frame->selectionController()->start();
if (start.node() != m_compositionNode)
return false;
Position end = m_frame->selectionController()->end();
if (end.node() != m_compositionNode)
return false;
if (static_cast<unsigned>(start.offset()) < m_compositionStart)
return false;
if (static_cast<unsigned>(end.offset()) > m_compositionEnd)
return false;
selectionStart = start.offset() - m_compositionStart;
selectionEnd = start.offset() - m_compositionEnd;
return true;
}
}