diff options
Diffstat (limited to 'Source/WebCore/editing/EditorCommand.cpp')
-rw-r--r-- | Source/WebCore/editing/EditorCommand.cpp | 369 |
1 files changed, 208 insertions, 161 deletions
diff --git a/Source/WebCore/editing/EditorCommand.cpp b/Source/WebCore/editing/EditorCommand.cpp index 5a916153b..06637d7dc 100644 --- a/Source/WebCore/editing/EditorCommand.cpp +++ b/Source/WebCore/editing/EditorCommand.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2006-2008, 2014, 2016 Apple Inc. All rights reserved. * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) * Copyright (C) 2009 Igalia S.L. * @@ -12,10 +12,10 @@ * 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 COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * 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 @@ -36,7 +36,6 @@ #include "EditorClient.h" #include "Event.h" #include "EventHandler.h" -#include "ExceptionCodePlaceholder.h" #include "FormatBlockCommand.h" #include "Frame.h" #include "FrameView.h" @@ -57,6 +56,7 @@ #include "StyleProperties.h" #include "TypingCommand.h" #include "UnlinkCommand.h" +#include "UserGestureIndicator.h" #include "UserTypingGestureIndicator.h" #include "htmlediting.h" #include "markup.h" @@ -74,17 +74,14 @@ public: TriState (*state)(Frame&, Event*); String (*value)(Frame&, Event*); bool isTextInsertion; - bool allowExecutionWhenDisabled; + bool (*allowExecutionWhenDisabled)(EditorCommandSource); }; -typedef HashMap<String, const EditorInternalCommand*, CaseFoldingHash> CommandMap; +typedef HashMap<String, const EditorInternalCommand*, ASCIICaseInsensitiveHash> CommandMap; static const bool notTextInsertion = false; static const bool isTextInsertion = true; -static const bool allowExecutionWhenDisabled = true; -static const bool doNotAllowExecutionWhenDisabled = false; - // Related to Editor::selectionForCommand. // Certain operations continue to use the target control's selection even if the event handler // already moved the selection outside of the text control. @@ -98,77 +95,46 @@ static Frame* targetFrame(Frame& frame, Event* event) return node->document().frame(); } -static bool applyCommandToFrame(Frame& frame, EditorCommandSource source, EditAction action, StyleProperties* style) +static bool applyCommandToFrame(Frame& frame, EditorCommandSource source, EditAction action, Ref<EditingStyle>&& style) { // FIXME: We don't call shouldApplyStyle when the source is DOM; is there a good reason for that? switch (source) { case CommandFromMenuOrKeyBinding: - frame.editor().applyStyleToSelection(style, action); + frame.editor().applyStyleToSelection(WTFMove(style), action); return true; case CommandFromDOM: case CommandFromDOMWithUserInterface: - frame.editor().applyStyle(style); + frame.editor().applyStyle(WTFMove(style), EditActionUnspecified); return true; } ASSERT_NOT_REACHED(); return false; } -static bool executeApplyStyle(Frame& frame, EditorCommandSource source, EditAction action, CSSPropertyID propertyID, const String& propertyValue) +static bool isStylePresent(Editor& editor, CSSPropertyID propertyID, const char* onValue) { - RefPtr<MutableStyleProperties> style = MutableStyleProperties::create(); - style->setProperty(propertyID, propertyValue); - return applyCommandToFrame(frame, source, action, style.get()); + // Style is considered present when + // Mac: present at the beginning of selection + // Windows: present throughout the selection + if (editor.behavior().shouldToggleStyleBasedOnStartOfSelection()) + return editor.selectionStartHasStyle(propertyID, onValue); + return editor.selectionHasStyle(propertyID, onValue) == TrueTriState; } -static bool executeApplyStyle(Frame& frame, EditorCommandSource source, EditAction action, CSSPropertyID propertyID, CSSValueID propertyValue) +static bool executeApplyStyle(Frame& frame, EditorCommandSource source, EditAction action, CSSPropertyID propertyID, const String& propertyValue) { - RefPtr<MutableStyleProperties> style = MutableStyleProperties::create(); - style->setProperty(propertyID, propertyValue); - return applyCommandToFrame(frame, source, action, style.get()); + return applyCommandToFrame(frame, source, action, EditingStyle::create(propertyID, propertyValue)); } -// FIXME: executeToggleStyleInList does not handle complicated cases such as <b><u>hello</u>world</b> properly. -// This function must use Editor::selectionHasStyle to determine the current style but we cannot fix this -// until https://bugs.webkit.org/show_bug.cgi?id=27818 is resolved. -static bool executeToggleStyleInList(Frame& frame, EditorCommandSource source, EditAction action, CSSPropertyID propertyID, CSSValue* value) +static bool executeApplyStyle(Frame& frame, EditorCommandSource source, EditAction action, CSSPropertyID propertyID, CSSValueID propertyValue) { - RefPtr<EditingStyle> selectionStyle = EditingStyle::styleAtSelectionStart(frame.selection().selection()); - if (!selectionStyle || !selectionStyle->style()) - return false; - - RefPtr<CSSValue> selectedCSSValue = selectionStyle->style()->getPropertyCSSValue(propertyID); - String newStyle = ASCIILiteral("none"); - if (selectedCSSValue->isValueList()) { - RefPtr<CSSValueList> selectedCSSValueList = toCSSValueList(selectedCSSValue.get()); - if (!selectedCSSValueList->removeAll(value)) - selectedCSSValueList->append(value); - if (selectedCSSValueList->length()) - newStyle = selectedCSSValueList->cssText(); - - } else if (selectedCSSValue->cssText() == "none") - newStyle = value->cssText(); - - // FIXME: We shouldn't be having to convert new style into text. We should have setPropertyCSSValue. - RefPtr<MutableStyleProperties> newMutableStyle = MutableStyleProperties::create(); - newMutableStyle->setProperty(propertyID, newStyle); - return applyCommandToFrame(frame, source, action, newMutableStyle.get()); + return applyCommandToFrame(frame, source, action, EditingStyle::create(propertyID, propertyValue)); } static bool executeToggleStyle(Frame& frame, EditorCommandSource source, EditAction action, CSSPropertyID propertyID, const char* offValue, const char* onValue) { - // Style is considered present when - // Mac: present at the beginning of selection - // other: present throughout the selection - - bool styleIsPresent; - if (frame.editor().behavior().shouldToggleStyleBasedOnStartOfSelection()) - styleIsPresent = frame.editor().selectionStartHasStyle(propertyID, onValue); - else - styleIsPresent = frame.editor().selectionHasStyle(propertyID, onValue) == TrueTriState; - - RefPtr<EditingStyle> style = EditingStyle::create(propertyID, styleIsPresent ? offValue : onValue); - return applyCommandToFrame(frame, source, action, style->style()); + bool styleIsPresent = isStylePresent(frame.editor(), propertyID, onValue); + return applyCommandToFrame(frame, source, action, EditingStyle::create(propertyID, styleIsPresent ? offValue : onValue)); } static bool executeApplyParagraphStyle(Frame& frame, EditorCommandSource source, EditAction action, CSSPropertyID propertyID, const String& propertyValue) @@ -192,18 +158,16 @@ static bool executeApplyParagraphStyle(Frame& frame, EditorCommandSource source, static bool executeInsertFragment(Frame& frame, PassRefPtr<DocumentFragment> fragment) { ASSERT(frame.document()); - applyCommand(ReplaceSelectionCommand::create(*frame.document(), fragment, ReplaceSelectionCommand::PreventNesting, EditActionUnspecified)); + applyCommand(ReplaceSelectionCommand::create(*frame.document(), fragment, ReplaceSelectionCommand::PreventNesting, EditActionInsert)); return true; } -static bool executeInsertNode(Frame& frame, PassRefPtr<Node> content) +static bool executeInsertNode(Frame& frame, Ref<Node>&& content) { - RefPtr<DocumentFragment> fragment = DocumentFragment::create(*frame.document()); - ExceptionCode ec = 0; - fragment->appendChild(content, ec); - if (ec) + auto fragment = DocumentFragment::create(*frame.document()); + if (fragment->appendChild(content).hasException()) return false; - return executeInsertFragment(frame, fragment.release()); + return executeInsertFragment(frame, WTFMove(fragment)); } static bool expandSelectionToGranularity(Frame& frame, TextGranularity granularity) @@ -213,10 +177,10 @@ static bool expandSelectionToGranularity(Frame& frame, TextGranularity granulari RefPtr<Range> newRange = selection.toNormalizedRange(); if (!newRange) return false; - if (newRange->collapsed(IGNORE_EXCEPTION)) + if (newRange->collapsed()) return false; - RefPtr<Range> oldRange = frame.selection().selection().toNormalizedRange(); - EAffinity affinity = frame.selection().affinity(); + RefPtr<Range> oldRange = selection.toNormalizedRange(); + EAffinity affinity = selection.affinity(); if (!frame.editor().client()->shouldChangeSelectedRange(oldRange.get(), newRange.get(), affinity, false)) return false; frame.selection().setSelectedRange(newRange.get(), affinity, true); @@ -251,22 +215,21 @@ static unsigned verticalScrollDistance(Frame& frame) Element* focusedElement = frame.document()->focusedElement(); if (!focusedElement) return 0; - auto renderer = focusedElement->renderer(); - if (!renderer || !renderer->isBox()) + auto* renderer = focusedElement->renderer(); + if (!is<RenderBox>(renderer)) return 0; const RenderStyle& style = renderer->style(); if (!(style.overflowY() == OSCROLL || style.overflowY() == OAUTO || focusedElement->hasEditableStyle())) return 0; - int height = std::min<int>(toRenderBox(renderer)->clientHeight(), frame.view()->visibleHeight()); - return static_cast<unsigned>(std::max(std::max<int>(height * Scrollbar::minFractionToStepWhenPaging(), height - Scrollbar::maxOverlapBetweenPages()), 1)); + int height = std::min<int>(downcast<RenderBox>(*renderer).clientHeight(), frame.view()->visibleHeight()); + return static_cast<unsigned>(Scrollbar::pageStep(height)); } -static RefPtr<Range> unionDOMRanges(Range* a, Range* b) +static RefPtr<Range> unionDOMRanges(Range& a, Range& b) { - Range* start = a->compareBoundaryPoints(Range::START_TO_START, b, ASSERT_NO_EXCEPTION) <= 0 ? a : b; - Range* end = a->compareBoundaryPoints(Range::END_TO_END, b, ASSERT_NO_EXCEPTION) <= 0 ? b : a; - - return Range::create(a->ownerDocument(), start->startContainer(), start->startOffset(), end->endContainer(), end->endOffset()); + Range& start = a.compareBoundaryPoints(Range::START_TO_START, b).releaseReturnValue() <= 0 ? a : b; + Range& end = a.compareBoundaryPoints(Range::END_TO_END, b).releaseReturnValue() <= 0 ? b : a; + return Range::create(a.ownerDocument(), &start.startContainer(), start.startOffset(), &end.endContainer(), end.endOffset()); } // Execute command functions @@ -302,19 +265,17 @@ static bool executeCut(Frame& frame, Event*, EditorCommandSource source, const S return true; } -#if PLATFORM(IOS) static bool executeClearText(Frame& frame, Event*, EditorCommandSource, const String&) { frame.editor().clearText(); return true; } -#endif static bool executeDefaultParagraphSeparator(Frame& frame, Event*, EditorCommandSource, const String& value) { - if (equalIgnoringCase(value, "div")) + if (equalLettersIgnoringASCIICase(value, "div")) frame.editor().setDefaultParagraphSeparator(EditorParagraphSeparatorIsDiv); - else if (equalIgnoringCase(value, "p")) + else if (equalLettersIgnoringASCIICase(value, "p")) frame.editor().setDefaultParagraphSeparator(EditorParagraphSeparatorIsP); return true; @@ -391,8 +352,8 @@ static bool executeDeleteToMark(Frame& frame, Event*, EditorCommandSource, const { RefPtr<Range> mark = frame.editor().mark().toNormalizedRange(); FrameSelection& selection = frame.selection(); - if (mark) { - bool selected = selection.setSelectedRange(unionDOMRanges(mark.get(), frame.editor().selectedRange().get()).get(), DOWNSTREAM, true); + if (mark && frame.editor().selectedRange()) { + bool selected = selection.setSelectedRange(unionDOMRanges(*mark, *frame.editor().selectedRange()).get(), DOWNSTREAM, true); ASSERT(selected); if (!selected) return false; @@ -416,7 +377,7 @@ static bool executeDeleteWordForward(Frame& frame, Event*, EditorCommandSource, static bool executeFindString(Frame& frame, Event*, EditorCommandSource, const String& value) { - return frame.editor().findString(value, CaseInsensitive | WrapAround); + return frame.editor().findString(value, CaseInsensitive | WrapAround | DoNotTraverseFlatTree); } static bool executeFontName(Frame& frame, Event*, EditorCommandSource source, const String& value) @@ -444,18 +405,17 @@ static bool executeForeColor(Frame& frame, Event*, EditorCommandSource source, c static bool executeFormatBlock(Frame& frame, Event*, EditorCommandSource, const String& value) { - String tagName = value.lower(); + String tagName = value.convertToASCIILowercase(); if (tagName[0] == '<' && tagName[tagName.length() - 1] == '>') tagName = tagName.substring(1, tagName.length() - 2); - String localName, prefix; - if (!Document::parseQualifiedName(tagName, prefix, localName, IGNORE_EXCEPTION)) + auto qualifiedTagName = Document::parseQualifiedName(xhtmlNamespaceURI, tagName); + if (qualifiedTagName.hasException()) return false; - QualifiedName qualifiedTagName(prefix, localName, xhtmlNamespaceURI); ASSERT(frame.document()); - RefPtr<FormatBlockCommand> command = FormatBlockCommand::create(*frame.document(), qualifiedTagName); - applyCommand(command); + auto command = FormatBlockCommand::create(*frame.document(), qualifiedTagName.releaseReturnValue()); + applyCommand(command.copyRef()); return command->didApply(); } @@ -492,35 +452,35 @@ static bool executeIndent(Frame& frame, Event*, EditorCommandSource, const Strin static bool executeInsertBacktab(Frame& frame, Event* event, EditorCommandSource, const String&) { - return targetFrame(frame, event)->eventHandler().handleTextInputEvent("\t", event, TextEventInputBackTab); + return targetFrame(frame, event)->eventHandler().handleTextInputEvent(ASCIILiteral("\t"), event, TextEventInputBackTab); } static bool executeInsertHorizontalRule(Frame& frame, Event*, EditorCommandSource, const String& value) { - RefPtr<HTMLHRElement> rule = HTMLHRElement::create(*frame.document()); + Ref<HTMLHRElement> rule = HTMLHRElement::create(*frame.document()); if (!value.isEmpty()) rule->setIdAttribute(value); - return executeInsertNode(frame, rule.release()); + return executeInsertNode(frame, WTFMove(rule)); } static bool executeInsertHTML(Frame& frame, Event*, EditorCommandSource, const String& value) { - return executeInsertFragment(frame, createFragmentFromMarkup(*frame.document(), value, "")); + return executeInsertFragment(frame, createFragmentFromMarkup(*frame.document(), value, emptyString())); } static bool executeInsertImage(Frame& frame, Event*, EditorCommandSource, const String& value) { // FIXME: If userInterface is true, we should display a dialog box and let the user choose a local image. - RefPtr<HTMLImageElement> image = HTMLImageElement::create(*frame.document()); + Ref<HTMLImageElement> image = HTMLImageElement::create(*frame.document()); image->setSrc(value); - return executeInsertNode(frame, image.release()); + return executeInsertNode(frame, WTFMove(image)); } static bool executeInsertLineBreak(Frame& frame, Event* event, EditorCommandSource source, const String&) { switch (source) { case CommandFromMenuOrKeyBinding: - return targetFrame(frame, event)->eventHandler().handleTextInputEvent("\n", event, TextEventInputLineBreak); + return targetFrame(frame, event)->eventHandler().handleTextInputEvent(ASCIILiteral("\n"), event, TextEventInputLineBreak); case CommandFromDOM: case CommandFromDOMWithUserInterface: // Doesn't scroll to make the selection visible, or modify the kill ring. @@ -536,7 +496,7 @@ static bool executeInsertLineBreak(Frame& frame, Event* event, EditorCommandSour static bool executeInsertNewline(Frame& frame, Event* event, EditorCommandSource, const String&) { Frame* targetFrame = WebCore::targetFrame(frame, event); - return targetFrame->eventHandler().handleTextInputEvent("\n", event, targetFrame->editor().canEditRichly() ? TextEventInputKeyboard : TextEventInputLineBreak); + return targetFrame->eventHandler().handleTextInputEvent(ASCIILiteral("\n"), event, targetFrame->editor().canEditRichly() ? TextEventInputKeyboard : TextEventInputLineBreak); } static bool executeInsertNewlineInQuotedContent(Frame& frame, Event*, EditorCommandSource, const String&) @@ -560,7 +520,7 @@ static bool executeInsertParagraph(Frame& frame, Event*, EditorCommandSource, co static bool executeInsertTab(Frame& frame, Event* event, EditorCommandSource, const String&) { - return targetFrame(frame, event)->eventHandler().handleTextInputEvent("\t", event); + return targetFrame(frame, event)->eventHandler().handleTextInputEvent(ASCIILiteral("\t"), event); } static bool executeInsertText(Frame& frame, Event*, EditorCommandSource, const String& value) @@ -578,22 +538,22 @@ static bool executeInsertUnorderedList(Frame& frame, Event*, EditorCommandSource static bool executeJustifyCenter(Frame& frame, Event*, EditorCommandSource source, const String&) { - return executeApplyParagraphStyle(frame, source, EditActionCenter, CSSPropertyTextAlign, "center"); + return executeApplyParagraphStyle(frame, source, EditActionCenter, CSSPropertyTextAlign, ASCIILiteral("center")); } static bool executeJustifyFull(Frame& frame, Event*, EditorCommandSource source, const String&) { - return executeApplyParagraphStyle(frame, source, EditActionJustify, CSSPropertyTextAlign, "justify"); + return executeApplyParagraphStyle(frame, source, EditActionJustify, CSSPropertyTextAlign, ASCIILiteral("justify")); } static bool executeJustifyLeft(Frame& frame, Event*, EditorCommandSource source, const String&) { - return executeApplyParagraphStyle(frame, source, EditActionAlignLeft, CSSPropertyTextAlign, "left"); + return executeApplyParagraphStyle(frame, source, EditActionAlignLeft, CSSPropertyTextAlign, ASCIILiteral("left")); } static bool executeJustifyRight(Frame& frame, Event*, EditorCommandSource source, const String&) { - return executeApplyParagraphStyle(frame, source, EditActionAlignRight, CSSPropertyTextAlign, "right"); + return executeApplyParagraphStyle(frame, source, EditActionAlignRight, CSSPropertyTextAlign, ASCIILiteral("right")); } static bool executeMakeTextWritingDirectionLeftToRight(Frame& frame, Event*, EditorCommandSource, const String&) @@ -970,7 +930,7 @@ static bool executePrint(Frame& frame, Event*, EditorCommandSource, const String Page* page = frame.page(); if (!page) return false; - page->chrome().print(&frame); + page->chrome().print(frame); return true; } @@ -1045,7 +1005,7 @@ static bool executeSelectToMark(Frame& frame, Event*, EditorCommandSource, const systemBeep(); return false; } - frame.selection().setSelectedRange(unionDOMRanges(mark.get(), selection.get()).get(), DOWNSTREAM, true); + frame.selection().setSelectedRange(unionDOMRanges(*mark, *selection).get(), DOWNSTREAM, true); return true; } @@ -1060,36 +1020,44 @@ static bool executeSetMark(Frame& frame, Event*, EditorCommandSource, const Stri return true; } +static TextDecorationChange textDecorationChangeForToggling(Editor& editor, CSSPropertyID propertyID, const char* onValue) +{ + return isStylePresent(editor, propertyID, onValue) ? TextDecorationChange::Remove : TextDecorationChange::Add; +} + static bool executeStrikethrough(Frame& frame, Event*, EditorCommandSource source, const String&) { - RefPtr<CSSPrimitiveValue> lineThrough = CSSPrimitiveValue::createIdentifier(CSSValueLineThrough); - return executeToggleStyleInList(frame, source, EditActionUnderline, CSSPropertyWebkitTextDecorationsInEffect, lineThrough.get()); + Ref<EditingStyle> style = EditingStyle::create(); + style->setStrikeThroughChange(textDecorationChangeForToggling(frame.editor(), CSSPropertyWebkitTextDecorationsInEffect, ASCIILiteral("line-through"))); + // FIXME: Needs a new EditAction! + return applyCommandToFrame(frame, source, EditActionUnderline, WTFMove(style)); } static bool executeStyleWithCSS(Frame& frame, Event*, EditorCommandSource, const String& value) { - frame.editor().setShouldStyleWithCSS(!equalIgnoringCase(value, "false")); + frame.editor().setShouldStyleWithCSS(!equalLettersIgnoringASCIICase(value, "false")); return true; } static bool executeUseCSS(Frame& frame, Event*, EditorCommandSource, const String& value) { - frame.editor().setShouldStyleWithCSS(equalIgnoringCase(value, "false")); + frame.editor().setShouldStyleWithCSS(equalLettersIgnoringASCIICase(value, "false")); return true; } static bool executeSubscript(Frame& frame, Event*, EditorCommandSource source, const String&) { - return executeToggleStyle(frame, source, EditActionSubscript, CSSPropertyVerticalAlign, "baseline", "sub"); + return executeToggleStyle(frame, source, EditActionSubscript, CSSPropertyVerticalAlign, ASCIILiteral("baseline"), ASCIILiteral("sub")); } static bool executeSuperscript(Frame& frame, Event*, EditorCommandSource source, const String&) { - return executeToggleStyle(frame, source, EditActionSuperscript, CSSPropertyVerticalAlign, "baseline", "super"); + return executeToggleStyle(frame, source, EditActionSuperscript, CSSPropertyVerticalAlign, ASCIILiteral("baseline"), ASCIILiteral("super")); } static bool executeSwapWithMark(Frame& frame, Event*, EditorCommandSource, const String&) { + Ref<Frame> protector(frame); const VisibleSelection& mark = frame.editor().mark(); const VisibleSelection& selection = frame.selection().selection(); if (mark.isNone() || selection.isNone()) { @@ -1101,7 +1069,7 @@ static bool executeSwapWithMark(Frame& frame, Event*, EditorCommandSource, const return true; } -#if PLATFORM(MAC) && !PLATFORM(IOS) +#if PLATFORM(MAC) static bool executeTakeFindStringFromSelection(Frame& frame, Event*, EditorCommandSource, const String&) { frame.editor().takeFindStringFromSelection(); @@ -1111,12 +1079,12 @@ static bool executeTakeFindStringFromSelection(Frame& frame, Event*, EditorComma static bool executeToggleBold(Frame& frame, Event*, EditorCommandSource source, const String&) { - return executeToggleStyle(frame, source, EditActionBold, CSSPropertyFontWeight, "normal", "bold"); + return executeToggleStyle(frame, source, EditActionBold, CSSPropertyFontWeight, ASCIILiteral("normal"), ASCIILiteral("bold")); } static bool executeToggleItalic(Frame& frame, Event*, EditorCommandSource source, const String&) { - return executeToggleStyle(frame, source, EditActionItalics, CSSPropertyFontStyle, "normal", "italic"); + return executeToggleStyle(frame, source, EditActionItalics, CSSPropertyFontStyle, ASCIILiteral("normal"), ASCIILiteral("italic")); } static bool executeTranspose(Frame& frame, Event*, EditorCommandSource, const String&) @@ -1127,8 +1095,10 @@ static bool executeTranspose(Frame& frame, Event*, EditorCommandSource, const St static bool executeUnderline(Frame& frame, Event*, EditorCommandSource source, const String&) { - RefPtr<CSSPrimitiveValue> underline = CSSPrimitiveValue::createIdentifier(CSSValueUnderline); - return executeToggleStyleInList(frame, source, EditActionUnderline, CSSPropertyWebkitTextDecorationsInEffect, underline.get()); + Ref<EditingStyle> style = EditingStyle::create(); + TextDecorationChange change = textDecorationChangeForToggling(frame.editor(), CSSPropertyWebkitTextDecorationsInEffect, ASCIILiteral("underline")); + style->setUnderlineChange(change); + return applyCommandToFrame(frame, source, EditActionUnderline, WTFMove(style)); } static bool executeUndo(Frame& frame, Event*, EditorCommandSource, const String&) @@ -1146,7 +1116,7 @@ static bool executeUnlink(Frame& frame, Event*, EditorCommandSource, const Strin static bool executeUnscript(Frame& frame, Event*, EditorCommandSource source, const String&) { - return executeApplyStyle(frame, source, EditActionUnscript, CSSPropertyVerticalAlign, "baseline"); + return executeApplyStyle(frame, source, EditActionUnscript, CSSPropertyVerticalAlign, ASCIILiteral("baseline")); } static bool executeUnselect(Frame& frame, Event*, EditorCommandSource, const String&) @@ -1181,12 +1151,30 @@ static bool supportedFromMenuOrKeyBinding(Frame*) return false; } +static bool defaultValueForSupportedCopyCut(Frame& frame) +{ + auto& settings = frame.settings(); + if (settings.javaScriptCanAccessClipboard()) + return true; + + switch (settings.clipboardAccessPolicy()) { + case ClipboardAccessPolicy::Allow: + case ClipboardAccessPolicy::RequiresUserGesture: + return true; + case ClipboardAccessPolicy::Deny: + return false; + } + + ASSERT_NOT_REACHED(); + return false; +} + static bool supportedCopyCut(Frame* frame) { if (!frame) return false; - bool defaultValue = frame->settings().javaScriptCanAccessClipboard(); + bool defaultValue = defaultValueForSupportedCopyCut(*frame); EditorClient* client = frame->editor().client(); return client ? client->canCopyCut(frame, defaultValue) : defaultValue; @@ -1243,31 +1231,56 @@ static bool enableCaretInEditableText(Frame& frame, Event* event, EditorCommandS return selection.isCaret() && selection.isContentEditable(); } -static bool enabledCopy(Frame& frame, Event*, EditorCommandSource) +static bool allowCopyCutFromDOM(Frame& frame) { -#if !PLATFORM(IOS) - return frame.editor().canDHTMLCopy() || frame.editor().canCopy(); -#else - return frame.editor().canCopy(); -#endif + auto& settings = frame.settings(); + if (settings.javaScriptCanAccessClipboard()) + return true; + + switch (settings.clipboardAccessPolicy()) { + case ClipboardAccessPolicy::Allow: + return true; + case ClipboardAccessPolicy::Deny: + return false; + case ClipboardAccessPolicy::RequiresUserGesture: + return UserGestureIndicator::processingUserGesture(); + } + + ASSERT_NOT_REACHED(); + return false; } -static bool enabledCut(Frame& frame, Event*, EditorCommandSource) +static bool enabledCopy(Frame& frame, Event*, EditorCommandSource source) { -#if !PLATFORM(IOS) - return frame.editor().canDHTMLCut() || frame.editor().canCut(); -#else - return frame.editor().canCut(); -#endif + switch (source) { + case CommandFromMenuOrKeyBinding: + return frame.editor().canDHTMLCopy() || frame.editor().canCopy(); + case CommandFromDOM: + case CommandFromDOMWithUserInterface: + return allowCopyCutFromDOM(frame) && (frame.editor().canDHTMLCopy() || frame.editor().canCopy()); + } + ASSERT_NOT_REACHED(); + return false; +} + +static bool enabledCut(Frame& frame, Event*, EditorCommandSource source) +{ + switch (source) { + case CommandFromMenuOrKeyBinding: + return frame.editor().canDHTMLCut() || frame.editor().canCut(); + case CommandFromDOM: + case CommandFromDOMWithUserInterface: + return allowCopyCutFromDOM(frame) && (frame.editor().canDHTMLCut() || frame.editor().canCut()); + } + ASSERT_NOT_REACHED(); + return false; } -#if PLATFORM(IOS) static bool enabledClearText(Frame& frame, Event*, EditorCommandSource) { UNUSED_PARAM(frame); return false; } -#endif static bool enabledInEditableText(Frame& frame, Event* event, EditorCommandSource) { @@ -1297,7 +1310,8 @@ static bool enabledInEditableTextOrCaretBrowsing(Frame& frame, Event* event, Edi static bool enabledInRichlyEditableText(Frame& frame, Event*, EditorCommandSource) { - return frame.selection().isCaretOrRange() && frame.selection().isContentRichlyEditable() && frame.selection().rootEditableElement(); + const VisibleSelection& selection = frame.selection().selection(); + return selection.isCaretOrRange() && selection.isContentRichlyEditable() && selection.rootEditableElement(); } static bool enabledPaste(Frame& frame, Event*, EditorCommandSource) @@ -1307,12 +1321,12 @@ static bool enabledPaste(Frame& frame, Event*, EditorCommandSource) static bool enabledRangeInEditableText(Frame& frame, Event*, EditorCommandSource) { - return frame.selection().isRange() && frame.selection().isContentEditable(); + return frame.selection().isRange() && frame.selection().selection().isContentEditable(); } static bool enabledRangeInRichlyEditableText(Frame& frame, Event*, EditorCommandSource) { - return frame.selection().isRange() && frame.selection().isContentRichlyEditable(); + return frame.selection().isRange() && frame.selection().selection().isContentRichlyEditable(); } static bool enabledRedo(Frame& frame, Event*, EditorCommandSource) @@ -1320,7 +1334,7 @@ static bool enabledRedo(Frame& frame, Event*, EditorCommandSource) return frame.editor().canRedo(); } -#if PLATFORM(MAC) && !PLATFORM(IOS) +#if PLATFORM(MAC) static bool enabledTakeFindStringFromSelection(Frame& frame, Event*, EditorCommandSource) { return frame.editor().canCopyExcludingStandaloneImages(); @@ -1341,12 +1355,12 @@ static TriState stateNone(Frame&, Event*) static TriState stateBold(Frame& frame, Event*) { - return stateStyle(frame, CSSPropertyFontWeight, "bold"); + return stateStyle(frame, CSSPropertyFontWeight, ASCIILiteral("bold")); } static TriState stateItalic(Frame& frame, Event*) { - return stateStyle(frame, CSSPropertyFontStyle, "italic"); + return stateStyle(frame, CSSPropertyFontStyle, ASCIILiteral("italic")); } static TriState stateOrderedList(Frame& frame, Event*) @@ -1356,7 +1370,7 @@ static TriState stateOrderedList(Frame& frame, Event*) static TriState stateStrikethrough(Frame& frame, Event*) { - return stateStyle(frame, CSSPropertyWebkitTextDecorationsInEffect, "line-through"); + return stateStyle(frame, CSSPropertyWebkitTextDecorationsInEffect, ASCIILiteral("line-through")); } static TriState stateStyleWithCSS(Frame& frame, Event*) @@ -1366,12 +1380,12 @@ static TriState stateStyleWithCSS(Frame& frame, Event*) static TriState stateSubscript(Frame& frame, Event*) { - return stateStyle(frame, CSSPropertyVerticalAlign, "sub"); + return stateStyle(frame, CSSPropertyVerticalAlign, ASCIILiteral("sub")); } static TriState stateSuperscript(Frame& frame, Event*) { - return stateStyle(frame, CSSPropertyVerticalAlign, "super"); + return stateStyle(frame, CSSPropertyVerticalAlign, ASCIILiteral("super")); } static TriState stateTextWritingDirectionLeftToRight(Frame& frame, Event*) @@ -1391,7 +1405,7 @@ static TriState stateTextWritingDirectionRightToLeft(Frame& frame, Event*) static TriState stateUnderline(Frame& frame, Event*) { - return stateStyle(frame, CSSPropertyWebkitTextDecorationsInEffect, "underline"); + return stateStyle(frame, CSSPropertyWebkitTextDecorationsInEffect, ASCIILiteral("underline")); } static TriState stateUnorderedList(Frame& frame, Event*) @@ -1401,22 +1415,22 @@ static TriState stateUnorderedList(Frame& frame, Event*) static TriState stateJustifyCenter(Frame& frame, Event*) { - return stateStyle(frame, CSSPropertyTextAlign, "center"); + return stateStyle(frame, CSSPropertyTextAlign, ASCIILiteral("center")); } static TriState stateJustifyFull(Frame& frame, Event*) { - return stateStyle(frame, CSSPropertyTextAlign, "justify"); + return stateStyle(frame, CSSPropertyTextAlign, ASCIILiteral("justify")); } static TriState stateJustifyLeft(Frame& frame, Event*) { - return stateStyle(frame, CSSPropertyTextAlign, "left"); + return stateStyle(frame, CSSPropertyTextAlign, ASCIILiteral("left")); } static TriState stateJustifyRight(Frame& frame, Event*) { - return stateStyle(frame, CSSPropertyTextAlign, "right"); + return stateStyle(frame, CSSPropertyTextAlign, ASCIILiteral("right")); } // Value functions @@ -1467,14 +1481,40 @@ static String valueForeColor(Frame& frame, Event*) static String valueFormatBlock(Frame& frame, Event*) { const VisibleSelection& selection = frame.selection().selection(); - if (!selection.isNonOrphanedCaretOrRange() || !selection.isContentEditable()) - return ""; + if (selection.isNoneOrOrphaned() || !selection.isContentEditable()) + return emptyString(); Element* formatBlockElement = FormatBlockCommand::elementForFormatBlockCommand(selection.firstRange().get()); if (!formatBlockElement) - return ""; + return emptyString(); return formatBlockElement->localName(); } +// allowExecutionWhenDisabled functions + +static bool allowExecutionWhenDisabled(EditorCommandSource) +{ + return true; +} + +static bool doNotAllowExecutionWhenDisabled(EditorCommandSource) +{ + return false; +} + +static bool allowExecutionWhenDisabledCopyCut(EditorCommandSource source) +{ + switch (source) { + case CommandFromMenuOrKeyBinding: + return true; + case CommandFromDOM: + case CommandFromDOMWithUserInterface: + return false; + } + + ASSERT_NOT_REACHED(); + return false; +} + // Map of functions struct CommandEntry { @@ -1490,11 +1530,11 @@ static const CommandMap& createCommandMap() { "AlignLeft", { executeJustifyLeft, supportedFromMenuOrKeyBinding, enabledInRichlyEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, { "AlignRight", { executeJustifyRight, supportedFromMenuOrKeyBinding, enabledInRichlyEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, { "BackColor", { executeBackColor, supported, enabledInRichlyEditableText, stateNone, valueBackColor, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "BackwardDelete", { executeDeleteBackward, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, // FIXME: remove BackwardDelete when Safari for Windows stops using it. { "Bold", { executeToggleBold, supported, enabledInRichlyEditableText, stateBold, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "Copy", { executeCopy, supportedCopyCut, enabledCopy, stateNone, valueNull, notTextInsertion, allowExecutionWhenDisabled } }, + { "ClearText", { executeClearText, supported, enabledClearText, stateNone, valueNull, notTextInsertion, allowExecutionWhenDisabled } }, + { "Copy", { executeCopy, supportedCopyCut, enabledCopy, stateNone, valueNull, notTextInsertion, allowExecutionWhenDisabledCopyCut } }, { "CreateLink", { executeCreateLink, supported, enabledInRichlyEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "Cut", { executeCut, supportedCopyCut, enabledCut, stateNone, valueNull, notTextInsertion, allowExecutionWhenDisabled } }, + { "Cut", { executeCut, supportedCopyCut, enabledCut, stateNone, valueNull, notTextInsertion, allowExecutionWhenDisabledCopyCut } }, { "DefaultParagraphSeparator", { executeDefaultParagraphSeparator, supported, enabled, stateNone, valueDefaultParagraphSeparator, notTextInsertion, doNotAllowExecutionWhenDisabled} }, { "Delete", { executeDelete, supported, enabledDelete, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, { "DeleteBackward", { executeDeleteBackward, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, @@ -1627,12 +1667,9 @@ static const CommandMap& createCommandMap() { "PasteGlobalSelection", { executePasteGlobalSelection, supportedFromMenuOrKeyBinding, enabledPaste, stateNone, valueNull, notTextInsertion, allowExecutionWhenDisabled } }, #endif -#if PLATFORM(MAC) && !PLATFORM(IOS) +#if PLATFORM(MAC) { "TakeFindStringFromSelection", { executeTakeFindStringFromSelection, supportedFromMenuOrKeyBinding, enabledTakeFindStringFromSelection, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, #endif -#if PLATFORM(IOS) - { "ClearText", { executeClearText, supported, enabledClearText, stateNone, valueNull, notTextInsertion, allowExecutionWhenDisabled } }, -#endif }; // These unsupported commands are listed here since they appear in the Microsoft @@ -1683,9 +1720,9 @@ static const CommandMap& createCommandMap() CommandMap& commandMap = *new CommandMap; - for (size_t i = 0; i < WTF_ARRAY_LENGTH(commands); ++i) { - ASSERT(!commandMap.get(commands[i].name)); - commandMap.set(commands[i].name, &commands[i].command); + for (auto& command : commands) { + ASSERT(!commandMap.get(command.name)); + commandMap.set(command.name, &command.command); } return commandMap; @@ -1713,7 +1750,6 @@ bool Editor::commandIsSupportedFromMenuOrKeyBinding(const String& commandName) } Editor::Command::Command() - : m_command(0) { } @@ -1733,10 +1769,14 @@ bool Editor::Command::execute(const String& parameter, Event* triggeringEvent) c { if (!isEnabled(triggeringEvent)) { // Let certain commands be executed when performed explicitly even if they are disabled. - if (!isSupported() || !m_frame || !m_command->allowExecutionWhenDisabled) + if (!allowExecutionWhenDisabled()) return false; } - m_frame->document()->updateLayoutIgnorePendingStylesheets(); + auto document = m_frame->document(); + document->updateLayoutIgnorePendingStylesheets(); + if (m_frame->document() != document) + return false; + return m_command->execute(*m_frame, triggeringEvent, m_source, parameter); } @@ -1779,7 +1819,7 @@ String Editor::Command::value(Event* triggeringEvent) const if (!isSupported() || !m_frame) return String(); if (m_command->value == valueNull && m_command->state != stateNone) - return m_command->state(*m_frame, triggeringEvent) == TrueTriState ? "true" : "false"; + return m_command->state(*m_frame, triggeringEvent) == TrueTriState ? ASCIILiteral("true") : ASCIILiteral("false"); return m_command->value(*m_frame, triggeringEvent); } @@ -1788,4 +1828,11 @@ bool Editor::Command::isTextInsertion() const return m_command && m_command->isTextInsertion; } +bool Editor::Command::allowExecutionWhenDisabled() const +{ + if (!isSupported() || !m_frame) + return false; + return m_command->allowExecutionWhenDisabled(m_source); +} + } // namespace WebCore |