1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #ifndef mozilla_PendingStyles_h
7 #define mozilla_PendingStyles_h
9 #include "mozilla/EditorDOMPoint.h"
10 #include "mozilla/EditorForwards.h"
11 #include "mozilla/EventForwards.h"
12 #include "mozilla/Maybe.h"
13 #include "mozilla/UniquePtr.h"
16 #include "nsCycleCollectionParticipant.h"
17 #include "nsGkAtoms.h"
18 #include "nsISupportsImpl.h"
31 enum class SpecifiedStyle
: uint8_t { Preserve
, Discard
};
33 class PendingStyle final
{
35 PendingStyle() = delete;
36 PendingStyle(nsStaticAtom
* aTag
, nsAtom
* aAttribute
, const nsAString
& aValue
,
37 SpecifiedStyle aSpecifiedStyle
= SpecifiedStyle::Preserve
)
39 mAttribute(aAttribute
!= nsGkAtoms::_empty
? aAttribute
: nullptr),
40 mAttributeValueOrCSSValue(aValue
),
41 mSpecifiedStyle(aSpecifiedStyle
) {
42 MOZ_COUNT_CTOR(PendingStyle
);
44 MOZ_COUNTED_DTOR(PendingStyle
)
46 MOZ_KNOWN_LIVE nsStaticAtom
* GetTag() const { return mTag
; }
47 MOZ_KNOWN_LIVE nsAtom
* GetAttribute() const { return mAttribute
; }
48 const nsString
& AttributeValueOrCSSValueRef() const {
49 return mAttributeValueOrCSSValue
;
51 void UpdateAttributeValueOrCSSValue(const nsAString
& aNewValue
) {
52 mAttributeValueOrCSSValue
= aNewValue
;
54 SpecifiedStyle
GetSpecifiedStyle() const { return mSpecifiedStyle
; }
56 EditorInlineStyle
ToInlineStyle() const;
57 EditorInlineStyleAndValue
ToInlineStyleAndValue() const;
60 MOZ_KNOWN_LIVE nsStaticAtom
* const mTag
= nullptr;
61 // TODO: Once we stop using `HTMLEditor::SetInlinePropertiesAsSubAction` to
62 // add any attributes of <a href>, we can make this `nsStaticAtom*`.
63 MOZ_KNOWN_LIVE
const RefPtr
<nsAtom
> mAttribute
;
64 // If the editor is in CSS mode, this value is the property value.
65 // If the editor is in HTML mode, this value is not empty only when
66 // - mAttribute is not nullptr
67 // - mTag is CSS invertible style and "-moz-editor-invert-value"
68 nsString mAttributeValueOrCSSValue
;
69 // Whether the class and style attribute should be preserved or discarded.
70 const SpecifiedStyle mSpecifiedStyle
= SpecifiedStyle::Preserve
;
73 class PendingStyleCache final
{
75 PendingStyleCache() = delete;
76 PendingStyleCache(const nsStaticAtom
& aTag
, const nsStaticAtom
* aAttribute
,
77 const nsAString
& aValue
)
78 // Needs const_cast hack here because the this class users may want
79 // non-const nsStaticAtom reference/pointer due to bug 1794954
80 : mTag(const_cast<nsStaticAtom
&>(aTag
)),
81 mAttribute(const_cast<nsStaticAtom
*>(aAttribute
)),
82 mAttributeValueOrCSSValue(aValue
) {}
83 PendingStyleCache(const nsStaticAtom
& aTag
, const nsStaticAtom
* aAttribute
,
85 // Needs const_cast hack here because the this class users may want
86 // non-const nsStaticAtom reference/pointer due to bug 1794954
87 : mTag(const_cast<nsStaticAtom
&>(aTag
)),
88 mAttribute(const_cast<nsStaticAtom
*>(aAttribute
)),
89 mAttributeValueOrCSSValue(std::move(aValue
)) {}
91 MOZ_KNOWN_LIVE nsStaticAtom
& TagRef() const { return mTag
; }
92 MOZ_KNOWN_LIVE nsStaticAtom
* GetAttribute() const { return mAttribute
; }
93 const nsString
& AttributeValueOrCSSValueRef() const {
94 return mAttributeValueOrCSSValue
;
97 EditorInlineStyle
ToInlineStyle() const;
100 MOZ_KNOWN_LIVE nsStaticAtom
& mTag
;
101 MOZ_KNOWN_LIVE nsStaticAtom
* const mAttribute
;
102 const nsString mAttributeValueOrCSSValue
;
105 class MOZ_STACK_CLASS AutoPendingStyleCacheArray final
106 : public AutoTArray
<PendingStyleCache
, 21> {
108 [[nodiscard
]] index_type
IndexOf(const nsStaticAtom
& aTag
,
109 const nsStaticAtom
* aAttribute
) const {
110 for (index_type index
= 0; index
< Length(); ++index
) {
111 const PendingStyleCache
& styleCache
= ElementAt(index
);
112 if (&styleCache
.TagRef() == &aTag
&&
113 styleCache
.GetAttribute() == aAttribute
) {
120 [[nodiscard
]] bool Contains(const nsStaticAtom
& aTag
,
121 const nsStaticAtom
* aAttribute
) const {
122 return IndexOf(aTag
, aAttribute
) != NoIndex
;
126 enum class PendingStyleState
{
127 // Will be not changed in new content
129 // Will be applied to new content
131 // Will be cleared from new content
135 /******************************************************************************
136 * PendingStyles stores pending inline styles which WILL be applied to new
137 * content when it'll be inserted. I.e., updating styles of this class means
138 * that it does NOT update the DOM tree immediately.
139 ******************************************************************************/
140 class PendingStyles final
{
142 NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(PendingStyles
)
143 NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(PendingStyles
)
145 PendingStyles() = default;
148 mClearingStyles
.Clear();
149 mPreservingStyles
.Clear();
152 nsresult
UpdateSelState(const HTMLEditor
& aHTMLEditor
);
155 * PreHandleMouseEvent() is called when `HTMLEditorEventListener` receives
156 * "mousedown" and "mouseup" events. Note that `aMouseDownOrUpEvent` may not
157 * be acceptable event for the `HTMLEditor`, but this is called even in
158 * the case because the event may cause a following `OnSelectionChange()`
161 void PreHandleMouseEvent(const dom::MouseEvent
& aMouseDownOrUpEvent
);
163 void PreHandleSelectionChangeCommand(Command aCommand
);
164 void PostHandleSelectionChangeCommand(const HTMLEditor
& aHTMLEditor
,
167 void OnSelectionChange(const HTMLEditor
& aHTMLEditor
, int16_t aReason
);
170 * Preserve the style for next inserting content. E.g., when user types next
171 * character, an inline element which provides the style will be inserted
172 * and the typing character will appear in it.
174 * @param aHTMLProperty The HTML tag name which represents the style.
175 * For example, nsGkAtoms::b for bold text.
176 * @param aAttribute nullptr or attribute name which represents the
177 * style with aHTMLProperty. E.g., nsGkAtoms::size
178 * for nsGkAtoms::font.
179 * @param aAttributeValueOrCSSValue
180 * New value of aAttribute or new CSS value if the
181 * editor is in the CSS mode.
183 void PreserveStyle(nsStaticAtom
& aHTMLProperty
, nsAtom
* aAttribute
,
184 const nsAString
& aAttributeValueOrCSSValue
);
187 * Preserve the styles with new values in aStylesToPreserve.
188 * See above for the detail.
191 const nsTArray
<EditorInlineStyleAndValue
>& aStylesToPreserve
);
194 * Preserve the style with new value specified with aStyleToPreserve.
195 * See above for the detail.
197 void PreserveStyle(const PendingStyleCache
& aStyleToPreserve
) {
198 PreserveStyle(aStyleToPreserve
.TagRef(), aStyleToPreserve
.GetAttribute(),
199 aStyleToPreserve
.AttributeValueOrCSSValueRef());
203 * Clear the style when next content is inserted. E.g., when user types next
204 * character, it'll will be inserted after parent element which provides the
205 * style and clones element which represents other styles in the parent
208 * @param aHTMLProperty The HTML tag name which represents the style.
209 * For example, nsGkAtoms::b for bold text.
210 * @param aAttribute nullptr or attribute name which represents the
211 * style with aHTMLProperty. E.g., nsGkAtoms::size
212 * for nsGkAtoms::font.
214 void ClearStyle(nsStaticAtom
& aHTMLProperty
, nsAtom
* aAttribute
) {
215 ClearStyleInternal(&aHTMLProperty
, aAttribute
);
219 * Clear all styles specified by aStylesToClear when next content is inserted.
220 * See above for the detail.
222 void ClearStyles(const nsTArray
<EditorInlineStyle
>& aStylesToClear
);
225 * Clear all styles when next inserting content. E.g., when user types next
226 * character, it will be inserted outside any inline parents which provides
227 * current text style.
229 void ClearAllStyles() {
230 // XXX Why don't we clear mClearingStyles first?
231 ClearStyleInternal(nullptr, nullptr);
235 * Clear <a> element and discard styles which is applied by it.
237 void ClearLinkAndItsSpecifiedStyle() {
238 ClearStyleInternal(nsGkAtoms::a
, nullptr, SpecifiedStyle::Discard
);
242 * TakeClearingStyle() hands back next property item on the clearing styles.
243 * This must be used only for handling to clear the styles from inserting
246 UniquePtr
<PendingStyle
> TakeClearingStyle() {
247 if (mClearingStyles
.IsEmpty()) {
250 return mClearingStyles
.PopLastElement();
254 * TakeAllPreservedStyles() moves all preserved styles and values to
255 * aOutStylesAndValues.
257 void TakeAllPreservedStyles(
258 nsTArray
<EditorInlineStyleAndValue
>& aOutStylesAndValues
);
261 * TakeRelativeFontSize() hands back relative font value, which is then
264 int32_t TakeRelativeFontSize();
267 * GetStyleState() returns whether the style will be applied to new content,
268 * removed from new content or not changed.
270 * @param aHTMLProperty The HTML tag name which represents the style.
271 * For example, nsGkAtoms::b for bold text.
272 * @param aAttribute nullptr or attribute name which represents the
273 * style with aHTMLProperty. E.g., nsGkAtoms::size
274 * for nsGkAtoms::font.
275 * @param aOutNewAttributeValueOrCSSValue
276 * [out, optional] If applied to new content, this
277 * is set to the new value.
279 PendingStyleState
GetStyleState(
280 nsStaticAtom
& aHTMLProperty
, nsAtom
* aAttribute
= nullptr,
281 nsString
* aOutNewAttributeValueOrCSSValue
= nullptr) const;
284 virtual ~PendingStyles() { Reset(); };
286 void ClearStyleInternal(
287 nsStaticAtom
* aHTMLProperty
, nsAtom
* aAttribute
,
288 SpecifiedStyle aSpecifiedStyle
= SpecifiedStyle::Preserve
);
290 void CancelPreservingStyle(nsStaticAtom
* aHTMLProperty
, nsAtom
* aAttribute
);
291 void CancelClearingStyle(nsStaticAtom
& aHTMLProperty
, nsAtom
* aAttribute
);
293 Maybe
<size_t> IndexOfPreservingStyle(nsStaticAtom
& aHTMLProperty
,
295 nsAString
* aOutValue
= nullptr) const {
296 return IndexOfStyleInArray(&aHTMLProperty
, aAttribute
, aOutValue
,
299 Maybe
<size_t> IndexOfClearingStyle(nsStaticAtom
* aHTMLProperty
,
300 nsAtom
* aAttribute
) const {
301 return IndexOfStyleInArray(aHTMLProperty
, aAttribute
, nullptr,
305 bool IsLinkStyleSet() const {
306 return IndexOfPreservingStyle(*nsGkAtoms::a
, nullptr).isSome();
308 bool IsExplicitlyLinkStyleCleared() const {
309 return IndexOfClearingStyle(nsGkAtoms::a
, nullptr).isSome();
311 bool IsOnlyLinkStyleCleared() const {
312 return mClearingStyles
.Length() == 1 && IsExplicitlyLinkStyleCleared();
314 bool IsStyleCleared(nsStaticAtom
* aHTMLProperty
, nsAtom
* aAttribute
) const {
315 return IndexOfClearingStyle(aHTMLProperty
, aAttribute
).isSome() ||
316 AreAllStylesCleared();
318 bool AreAllStylesCleared() const {
319 return IndexOfClearingStyle(nullptr, nullptr).isSome();
321 bool AreSomeStylesSet() const { return !mPreservingStyles
.IsEmpty(); }
322 bool AreSomeStylesCleared() const { return !mClearingStyles
.IsEmpty(); }
324 static Maybe
<size_t> IndexOfStyleInArray(
325 nsStaticAtom
* aHTMLProperty
, nsAtom
* aAttribute
, nsAString
* aOutValue
,
326 const nsTArray
<UniquePtr
<PendingStyle
>>& aArray
);
328 nsTArray
<UniquePtr
<PendingStyle
>> mPreservingStyles
;
329 nsTArray
<UniquePtr
<PendingStyle
>> mClearingStyles
;
330 EditorDOMPoint mLastSelectionPoint
;
331 int32_t mRelativeFontSize
= 0;
332 Command mLastSelectionCommand
= Command::DoNothing
;
333 bool mMouseDownFiredInLinkElement
= false;
334 bool mMouseUpFiredInLinkElement
= false;
337 } // namespace mozilla
339 #endif // #ifndef mozilla_PendingStyles_h