/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include"PendingStyles.h"
#include <stddef.h>
#include"EditAction.h" #include"EditorBase.h" #include"HTMLEditHelpers.h"// for EditorInlineStyle, EditorInlineStyleAndValue #include"HTMLEditor.h" #include"HTMLEditUtils.h"
mLastSelectionPoint =
aHTMLEditor.GetFirstSelectionStartPoint<EditorDOMPoint>(); if (!mLastSelectionPoint.IsSet()) { return NS_ERROR_FAILURE;
} // We need to store only offset because referring child may be removed by // we'll check the point later.
AutoEditorDOMPointChildInvalidator saveOnlyOffset(mLastSelectionPoint); return NS_OK;
}
void PendingStyles::PreHandleMouseEvent(const MouseEvent& aMouseDownOrUpEvent) {
MOZ_ASSERT(aMouseDownOrUpEvent.WidgetEventPtr()->mMessage == eMouseDown ||
aMouseDownOrUpEvent.WidgetEventPtr()->mMessage == eMouseUp); bool& eventFiredInLinkElement =
aMouseDownOrUpEvent.WidgetEventPtr()->mMessage == eMouseDown
? mMouseDownFiredInLinkElement
: mMouseUpFiredInLinkElement;
eventFiredInLinkElement = false; if (aMouseDownOrUpEvent.DefaultPrevented()) { return;
} // If mouse button is down or up in a link element, we shouldn't unlink // it when we get a notification of selection change.
EventTarget* target = aMouseDownOrUpEvent.GetExplicitOriginalTarget(); if (NS_WARN_IF(!target)) { return;
}
nsIContent* targetContent = nsIContent::FromEventTarget(target); if (NS_WARN_IF(!targetContent)) { return;
}
eventFiredInLinkElement =
HTMLEditUtils::IsContentInclusiveDescendantOfLink(*targetContent);
}
// If `OnSelectionChange()` hasn't been called for `mLastSelectionCommand`, // it means that it didn't cause selection change. if (!aHTMLEditor.SelectionRef().IsCollapsed() ||
!aHTMLEditor.SelectionRef().RangeCount()) { return;
}
constauto caretPoint =
aHTMLEditor.GetFirstSelectionStartPoint<EditorRawDOMPoint>(); if (NS_WARN_IF(!caretPoint.IsSet())) { return;
}
if (!HTMLEditUtils::IsPointAtEdgeOfLink(caretPoint)) { return;
}
// If all styles are cleared or link style is explicitly set, we // shouldn't reset them without caret move. if (AreAllStylesCleared() || IsLinkStyleSet()) { return;
} // And if non-link styles are cleared or some styles are set, we // shouldn't reset them too, but we may need to change the link // style. if (AreSomeStylesSet() ||
(AreSomeStylesCleared() && !IsOnlyLinkStyleCleared())) {
ClearLinkAndItsSpecifiedStyle(); return;
}
Reset();
ClearLinkAndItsSpecifiedStyle();
}
void PendingStyles::OnSelectionChange(const HTMLEditor& aHTMLEditor,
int16_t aReason) { // XXX: Selection currently generates bogus selection changed notifications // XXX: (bug 140303). It can notify us when the selection hasn't actually // XXX: changed, and it notifies us more than once for the same change. // XXX: // XXX: The following code attempts to work around the bogus notifications, // XXX: and should probably be removed once bug 140303 is fixed. // XXX: // XXX: This code temporarily fixes the problem where clicking the mouse in // XXX: the same location clears the type-in-state.
if (mLastSelectionPoint == selectionStartPoint) { // If all styles are cleared or link style is explicitly set, we // shouldn't reset them without caret move. if (AreAllStylesCleared() || IsLinkStyleSet()) { return;
} // And if non-link styles are cleared or some styles are set, we // shouldn't reset them too, but we may need to change the link // style. if (AreSomeStylesSet() ||
(AreSomeStylesCleared() && !IsOnlyLinkStyleCleared())) {
resetAllStyles = false;
}
}
RefPtr<Element> linkElement; if (HTMLEditUtils::IsPointAtEdgeOfLink(selectionStartPoint,
getter_AddRefs(linkElement))) { // If caret comes from outside of <a href> element, we should clear "link" // style after reset. if (causedByFrameSelectionMoveCaret) {
MOZ_ASSERT(!(aReason & (nsISelectionListener::MOUSEDOWN_REASON |
nsISelectionListener::MOUSEUP_REASON))); // If caret is moves in a link per character, we should keep inserting // new text to the link because user may want to keep extending the link // text. Otherwise, e.g., using `End` or `Home` key. we should insert // new text outside the link because it should be possible to user // choose it, and this is similar to the other browsers. switch (lastSelectionCommand) { case Command::CharNext: case Command::CharPrevious: case Command::MoveLeft: case Command::MoveLeft2: case Command::MoveRight: case Command::MoveRight2: // If selection becomes collapsed, we should unlink new text. if (!mLastSelectionPoint.IsSet()) {
unlink = true; break;
} // Special case, if selection isn't moved, it means that caret is // positioned at start or end of an editing host. In this case, // we can unlink it even with arrow key press. // TODO: This does not work as expected for `ArrowLeft` key press // at start of an editing host. if (mLastSelectionPoint == selectionStartPoint) {
unlink = true; break;
} // Otherwise, if selection is moved in a link element, we should // keep inserting new text into the link. Note that this is our // traditional behavior, but different from the other browsers. // If this breaks some web apps, we should change our behavior, // but let's wait a report because our traditional behavior allows // user to type text into start/end of a link only when user // moves caret inside the link with arrow keys.
unlink =
!mLastSelectionPoint.GetContainer()->IsInclusiveDescendantOf(
linkElement); break; default: // If selection is moved without arrow keys, e.g., `Home` and // `End`, we should not insert new text into the link element. // This is important for web-compat especially when the link is // the last content in the block.
unlink = true; break;
}
} elseif (aReason & (nsISelectionListener::MOUSEDOWN_REASON |
nsISelectionListener::MOUSEUP_REASON)) { // If the corresponding mouse event is fired in a link element, // we should keep treating inputting content as content in the link, // but otherwise, i.e., clicked outside the link, we should stop // treating inputting content as content in the link.
unlink = !mouseEventFiredInLinkElement;
} elseif (aReason & nsISelectionListener::JS_REASON) { // If this is caused by a call of Selection API or something similar // API, we should not contain new inserting content to the link.
unlink = true;
} else { switch (aHTMLEditor.GetEditAction()) { case EditAction::eDeleteBackward: case EditAction::eDeleteForward: case EditAction::eDeleteSelection: case EditAction::eDeleteToBeginningOfSoftLine: case EditAction::eDeleteToEndOfSoftLine: case EditAction::eDeleteWordBackward: case EditAction::eDeleteWordForward: // This selection change is caused by the editor and the edit // action is deleting content at edge of a link, we shouldn't // keep the link style for new inserted content.
unlink = true; break; default: break;
}
}
} elseif (mLastSelectionPoint == selectionStartPoint) { return;
}
mLastSelectionPoint = selectionStartPoint; // We need to store only offset because referring child may be removed by // we'll check the point later.
AutoEditorDOMPointChildInvalidator saveOnlyOffset(mLastSelectionPoint);
} else { if (aHTMLEditor.SelectionRef().RangeCount()) { // If selection starts from a link, we shouldn't preserve the link style // unless the range is entirely in the link.
EditorRawDOMRange firstRange(*aHTMLEditor.SelectionRef().GetRangeAt(0)); if (firstRange.StartRef().IsInContentNode() &&
HTMLEditUtils::IsContentInclusiveDescendantOfLink(
*firstRange.StartRef().ContainerAs<nsIContent>())) {
unlink = !HTMLEditUtils::IsRangeEntirelyInLink(firstRange);
}
}
mLastSelectionPoint.Clear();
}
if (resetAllStyles) {
Reset(); if (unlink) {
ClearLinkAndItsSpecifiedStyle();
} return;
}
if (unlink == IsExplicitlyLinkStyleCleared()) { return;
}
// Even if we shouldn't touch existing style, we need to set/clear only link // style in some cases. if (unlink) {
ClearLinkAndItsSpecifiedStyle(); return;
}
CancelClearingStyle(*nsGkAtoms::a, nullptr);
}
void PendingStyles::PreserveStyle(nsStaticAtom& aHTMLProperty,
nsAtom* aAttribute, const nsAString& aAttributeValueOrCSSValue) { // special case for big/small, these nest if (nsGkAtoms::big == &aHTMLProperty) {
mRelativeFontSize++; return;
} if (nsGkAtoms::small == &aHTMLProperty) {
mRelativeFontSize--; return;
}
Maybe<size_t> index = IndexOfPreservingStyle(aHTMLProperty, aAttribute); if (index.isSome()) { // If it's already set, update the value
mPreservingStyles[index.value()]->UpdateAttributeValueOrCSSValue(
aAttributeValueOrCSSValue); return;
}
// font-size and font-family need to be applied outer-most because height of // outer inline elements of them are computed without these styles. E.g., // background-color may be applied bottom-half of the text. Therefore, we // need to apply the font styles first.
UniquePtr<PendingStyle> style = MakeUnique<PendingStyle>(
&aHTMLProperty, aAttribute, aAttributeValueOrCSSValue); if (&aHTMLProperty == nsGkAtoms::font && aAttribute != nsGkAtoms::bgcolor) {
MOZ_ASSERT(aAttribute == nsGkAtoms::color ||
aAttribute == nsGkAtoms::face || aAttribute == nsGkAtoms::size);
mPreservingStyles.InsertElementAt(0, std::move(style));
} else {
mPreservingStyles.AppendElement(std::move(style));
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.