summaryrefslogtreecommitdiff
path: root/Source/WebCore/editing/EditorCommand.cpp
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
committerLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
commit1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch)
tree46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WebCore/editing/EditorCommand.cpp
parent32761a6cee1d0dee366b885b7b9c777e67885688 (diff)
downloadWebKitGtk-tarball-master.tar.gz
Diffstat (limited to 'Source/WebCore/editing/EditorCommand.cpp')
-rw-r--r--Source/WebCore/editing/EditorCommand.cpp369
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