diff options
Diffstat (limited to 'editor/libeditor/PendingStyles.h')
-rw-r--r-- | editor/libeditor/PendingStyles.h | 339 |
1 files changed, 339 insertions, 0 deletions
diff --git a/editor/libeditor/PendingStyles.h b/editor/libeditor/PendingStyles.h new file mode 100644 index 0000000000..b41e383e80 --- /dev/null +++ b/editor/libeditor/PendingStyles.h @@ -0,0 +1,339 @@ +/* -*- 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/. */ + +#ifndef mozilla_PendingStyles_h +#define mozilla_PendingStyles_h + +#include "mozilla/EditorDOMPoint.h" +#include "mozilla/EditorForwards.h" +#include "mozilla/EventForwards.h" +#include "mozilla/Maybe.h" +#include "mozilla/UniquePtr.h" +#include "nsAtom.h" +#include "nsCOMPtr.h" +#include "nsCycleCollectionParticipant.h" +#include "nsGkAtoms.h" +#include "nsISupportsImpl.h" +#include "nsString.h" +#include "nsTArray.h" +#include "nscore.h" + +class nsINode; + +namespace mozilla { +namespace dom { +class MouseEvent; +class Selection; +} // namespace dom + +enum class SpecifiedStyle : uint8_t { Preserve, Discard }; + +class PendingStyle final { + public: + PendingStyle() = delete; + PendingStyle(nsStaticAtom* aTag, nsAtom* aAttribute, const nsAString& aValue, + SpecifiedStyle aSpecifiedStyle = SpecifiedStyle::Preserve) + : mTag(aTag), + mAttribute(aAttribute != nsGkAtoms::_empty ? aAttribute : nullptr), + mAttributeValueOrCSSValue(aValue), + mSpecifiedStyle(aSpecifiedStyle) { + MOZ_COUNT_CTOR(PendingStyle); + } + MOZ_COUNTED_DTOR(PendingStyle) + + MOZ_KNOWN_LIVE nsStaticAtom* GetTag() const { return mTag; } + MOZ_KNOWN_LIVE nsAtom* GetAttribute() const { return mAttribute; } + const nsString& AttributeValueOrCSSValueRef() const { + return mAttributeValueOrCSSValue; + } + void UpdateAttributeValueOrCSSValue(const nsAString& aNewValue) { + mAttributeValueOrCSSValue = aNewValue; + } + SpecifiedStyle GetSpecifiedStyle() const { return mSpecifiedStyle; } + + EditorInlineStyle ToInlineStyle() const; + EditorInlineStyleAndValue ToInlineStyleAndValue() const; + + private: + MOZ_KNOWN_LIVE nsStaticAtom* const mTag = nullptr; + // TODO: Once we stop using `HTMLEditor::SetInlinePropertiesAsSubAction` to + // add any attributes of <a href>, we can make this `nsStaticAtom*`. + MOZ_KNOWN_LIVE const RefPtr<nsAtom> mAttribute; + // If the editor is in CSS mode, this value is the property value. + // If the editor is in HTML mode, this value is not empty only when + // - mAttribute is not nullptr + // - mTag is CSS invertible style and "-moz-editor-invert-value" + nsString mAttributeValueOrCSSValue; + // Whether the class and style attribute should be preserved or discarded. + const SpecifiedStyle mSpecifiedStyle = SpecifiedStyle::Preserve; +}; + +class PendingStyleCache final { + public: + PendingStyleCache() = delete; + PendingStyleCache(const nsStaticAtom& aTag, const nsStaticAtom* aAttribute, + const nsAString& aValue) + // Needs const_cast hack here because the this class users may want + // non-const nsStaticAtom reference/pointer due to bug 1794954 + : mTag(const_cast<nsStaticAtom&>(aTag)), + mAttribute(const_cast<nsStaticAtom*>(aAttribute)), + mAttributeValueOrCSSValue(aValue) {} + PendingStyleCache(const nsStaticAtom& aTag, const nsStaticAtom* aAttribute, + nsAString&& aValue) + // Needs const_cast hack here because the this class users may want + // non-const nsStaticAtom reference/pointer due to bug 1794954 + : mTag(const_cast<nsStaticAtom&>(aTag)), + mAttribute(const_cast<nsStaticAtom*>(aAttribute)), + mAttributeValueOrCSSValue(std::move(aValue)) {} + + MOZ_KNOWN_LIVE nsStaticAtom& TagRef() const { return mTag; } + MOZ_KNOWN_LIVE nsStaticAtom* GetAttribute() const { return mAttribute; } + const nsString& AttributeValueOrCSSValueRef() const { + return mAttributeValueOrCSSValue; + } + + EditorInlineStyle ToInlineStyle() const; + + private: + MOZ_KNOWN_LIVE nsStaticAtom& mTag; + MOZ_KNOWN_LIVE nsStaticAtom* const mAttribute; + const nsString mAttributeValueOrCSSValue; +}; + +class MOZ_STACK_CLASS AutoPendingStyleCacheArray final + : public AutoTArray<PendingStyleCache, 21> { + public: + [[nodiscard]] index_type IndexOf(const nsStaticAtom& aTag, + const nsStaticAtom* aAttribute) const { + for (index_type index = 0; index < Length(); ++index) { + const PendingStyleCache& styleCache = ElementAt(index); + if (&styleCache.TagRef() == &aTag && + styleCache.GetAttribute() == aAttribute) { + return index; + } + } + return NoIndex; + } + + [[nodiscard]] bool Contains(const nsStaticAtom& aTag, + const nsStaticAtom* aAttribute) const { + return IndexOf(aTag, aAttribute) != NoIndex; + } +}; + +enum class PendingStyleState { + // Will be not changed in new content + NotUpdated, + // Will be applied to new content + BeingPreserved, + // Will be cleared from new content + BeingCleared, +}; + +/****************************************************************************** + * PendingStyles stores pending inline styles which WILL be applied to new + * content when it'll be inserted. I.e., updating styles of this class means + * that it does NOT update the DOM tree immediately. + ******************************************************************************/ +class PendingStyles final { + public: + NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(PendingStyles) + NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(PendingStyles) + + PendingStyles() = default; + + void Reset() { + mClearingStyles.Clear(); + mPreservingStyles.Clear(); + } + + nsresult UpdateSelState(const HTMLEditor& aHTMLEditor); + + /** + * PreHandleMouseEvent() is called when `HTMLEditorEventListener` receives + * "mousedown" and "mouseup" events. Note that `aMouseDownOrUpEvent` may not + * be acceptable event for the `HTMLEditor`, but this is called even in + * the case because the event may cause a following `OnSelectionChange()` + * call. + */ + void PreHandleMouseEvent(const dom::MouseEvent& aMouseDownOrUpEvent); + + void PreHandleSelectionChangeCommand(Command aCommand); + void PostHandleSelectionChangeCommand(const HTMLEditor& aHTMLEditor, + Command aCommand); + + void OnSelectionChange(const HTMLEditor& aHTMLEditor, int16_t aReason); + + /** + * Preserve the style for next inserting content. E.g., when user types next + * character, an inline element which provides the style will be inserted + * and the typing character will appear in it. + * + * @param aHTMLProperty The HTML tag name which represents the style. + * For example, nsGkAtoms::b for bold text. + * @param aAttribute nullptr or attribute name which represents the + * style with aHTMLProperty. E.g., nsGkAtoms::size + * for nsGkAtoms::font. + * @param aAttributeValueOrCSSValue + * New value of aAttribute or new CSS value if the + * editor is in the CSS mode. + */ + void PreserveStyle(nsStaticAtom& aHTMLProperty, nsAtom* aAttribute, + const nsAString& aAttributeValueOrCSSValue); + + /** + * Preserve the styles with new values in aStylesToPreserve. + * See above for the detail. + */ + void PreserveStyles( + const nsTArray<EditorInlineStyleAndValue>& aStylesToPreserve); + + /** + * Preserve the style with new value specified with aStyleToPreserve. + * See above for the detail. + */ + void PreserveStyle(const PendingStyleCache& aStyleToPreserve) { + PreserveStyle(aStyleToPreserve.TagRef(), aStyleToPreserve.GetAttribute(), + aStyleToPreserve.AttributeValueOrCSSValueRef()); + } + + /** + * Clear the style when next content is inserted. E.g., when user types next + * character, it'll will be inserted after parent element which provides the + * style and clones element which represents other styles in the parent + * element. + * + * @param aHTMLProperty The HTML tag name which represents the style. + * For example, nsGkAtoms::b for bold text. + * @param aAttribute nullptr or attribute name which represents the + * style with aHTMLProperty. E.g., nsGkAtoms::size + * for nsGkAtoms::font. + */ + void ClearStyle(nsStaticAtom& aHTMLProperty, nsAtom* aAttribute) { + ClearStyleInternal(&aHTMLProperty, aAttribute); + } + + /** + * Clear all styles specified by aStylesToClear when next content is inserted. + * See above for the detail. + */ + void ClearStyles(const nsTArray<EditorInlineStyle>& aStylesToClear); + + /** + * Clear all styles when next inserting content. E.g., when user types next + * character, it will be inserted outside any inline parents which provides + * current text style. + */ + void ClearAllStyles() { + // XXX Why don't we clear mClearingStyles first? + ClearStyleInternal(nullptr, nullptr); + } + + /** + * Clear <a> element and discard styles which is applied by it. + */ + void ClearLinkAndItsSpecifiedStyle() { + ClearStyleInternal(nsGkAtoms::a, nullptr, SpecifiedStyle::Discard); + } + + /** + * TakeClearingStyle() hands back next property item on the clearing styles. + * This must be used only for handling to clear the styles from inserting + * content. + */ + UniquePtr<PendingStyle> TakeClearingStyle() { + if (mClearingStyles.IsEmpty()) { + return nullptr; + } + return mClearingStyles.PopLastElement(); + } + + /** + * TakeAllPreservedStyles() moves all preserved styles and values to + * aOutStylesAndValues. + */ + void TakeAllPreservedStyles( + nsTArray<EditorInlineStyleAndValue>& aOutStylesAndValues); + + /** + * TakeRelativeFontSize() hands back relative font value, which is then + * cleared out. + */ + int32_t TakeRelativeFontSize(); + + /** + * GetStyleState() returns whether the style will be applied to new content, + * removed from new content or not changed. + * + * @param aHTMLProperty The HTML tag name which represents the style. + * For example, nsGkAtoms::b for bold text. + * @param aAttribute nullptr or attribute name which represents the + * style with aHTMLProperty. E.g., nsGkAtoms::size + * for nsGkAtoms::font. + * @param aOutNewAttributeValueOrCSSValue + * [out, optional] If applied to new content, this + * is set to the new value. + */ + PendingStyleState GetStyleState( + nsStaticAtom& aHTMLProperty, nsAtom* aAttribute = nullptr, + nsString* aOutNewAttributeValueOrCSSValue = nullptr) const; + + protected: + virtual ~PendingStyles() { Reset(); }; + + void ClearStyleInternal( + nsStaticAtom* aHTMLProperty, nsAtom* aAttribute, + SpecifiedStyle aSpecifiedStyle = SpecifiedStyle::Preserve); + + void CancelPreservingStyle(nsStaticAtom* aHTMLProperty, nsAtom* aAttribute); + void CancelClearingStyle(nsStaticAtom& aHTMLProperty, nsAtom* aAttribute); + + Maybe<size_t> IndexOfPreservingStyle(nsStaticAtom& aHTMLProperty, + nsAtom* aAttribute, + nsAString* aOutValue = nullptr) const { + return IndexOfStyleInArray(&aHTMLProperty, aAttribute, aOutValue, + mPreservingStyles); + } + Maybe<size_t> IndexOfClearingStyle(nsStaticAtom* aHTMLProperty, + nsAtom* aAttribute) const { + return IndexOfStyleInArray(aHTMLProperty, aAttribute, nullptr, + mClearingStyles); + } + + bool IsLinkStyleSet() const { + return IndexOfPreservingStyle(*nsGkAtoms::a, nullptr).isSome(); + } + bool IsExplicitlyLinkStyleCleared() const { + return IndexOfClearingStyle(nsGkAtoms::a, nullptr).isSome(); + } + bool IsOnlyLinkStyleCleared() const { + return mClearingStyles.Length() == 1 && IsExplicitlyLinkStyleCleared(); + } + bool IsStyleCleared(nsStaticAtom* aHTMLProperty, nsAtom* aAttribute) const { + return IndexOfClearingStyle(aHTMLProperty, aAttribute).isSome() || + AreAllStylesCleared(); + } + bool AreAllStylesCleared() const { + return IndexOfClearingStyle(nullptr, nullptr).isSome(); + } + bool AreSomeStylesSet() const { return !mPreservingStyles.IsEmpty(); } + bool AreSomeStylesCleared() const { return !mClearingStyles.IsEmpty(); } + + static Maybe<size_t> IndexOfStyleInArray( + nsStaticAtom* aHTMLProperty, nsAtom* aAttribute, nsAString* aOutValue, + const nsTArray<UniquePtr<PendingStyle>>& aArray); + + nsTArray<UniquePtr<PendingStyle>> mPreservingStyles; + nsTArray<UniquePtr<PendingStyle>> mClearingStyles; + EditorDOMPoint mLastSelectionPoint; + int32_t mRelativeFontSize = 0; + Command mLastSelectionCommand = Command::DoNothing; + bool mMouseDownFiredInLinkElement = false; + bool mMouseUpFiredInLinkElement = false; +}; + +} // namespace mozilla + +#endif // #ifndef mozilla_PendingStyles_h |