PendingStyles.h (12912B)
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/. */ 5 6 #ifndef mozilla_PendingStyles_h 7 #define mozilla_PendingStyles_h 8 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" 14 #include "nsAtom.h" 15 #include "nsCOMPtr.h" 16 #include "nsCycleCollectionParticipant.h" 17 #include "nsGkAtoms.h" 18 #include "nsISupportsImpl.h" 19 #include "nsString.h" 20 #include "nsTArray.h" 21 #include "nscore.h" 22 23 class nsINode; 24 25 namespace mozilla { 26 namespace dom { 27 class MouseEvent; 28 class Selection; 29 } // namespace dom 30 31 enum class SpecifiedStyle : uint8_t { Preserve, Discard }; 32 33 class PendingStyle final { 34 public: 35 PendingStyle() = delete; 36 PendingStyle(nsStaticAtom* aTag, nsAtom* aAttribute, const nsAString& aValue, 37 SpecifiedStyle aSpecifiedStyle = SpecifiedStyle::Preserve) 38 : mTag(aTag), 39 mAttribute(aAttribute != nsGkAtoms::_empty ? aAttribute : nullptr), 40 mAttributeValueOrCSSValue(aValue), 41 mSpecifiedStyle(aSpecifiedStyle) { 42 MOZ_COUNT_CTOR(PendingStyle); 43 } 44 MOZ_COUNTED_DTOR(PendingStyle) 45 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; 50 } 51 void UpdateAttributeValueOrCSSValue(const nsAString& aNewValue) { 52 mAttributeValueOrCSSValue = aNewValue; 53 } 54 SpecifiedStyle GetSpecifiedStyle() const { return mSpecifiedStyle; } 55 56 EditorInlineStyle ToInlineStyle() const; 57 EditorInlineStyleAndValue ToInlineStyleAndValue() const; 58 59 private: 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; 71 }; 72 73 class PendingStyleCache final { 74 public: 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, 84 nsAString&& aValue) 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)) {} 90 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; 95 } 96 97 EditorInlineStyle ToInlineStyle() const; 98 99 private: 100 MOZ_KNOWN_LIVE nsStaticAtom& mTag; 101 MOZ_KNOWN_LIVE nsStaticAtom* const mAttribute; 102 const nsString mAttributeValueOrCSSValue; 103 }; 104 105 class MOZ_STACK_CLASS AutoPendingStyleCacheArray final 106 : public AutoTArray<PendingStyleCache, 21> { 107 public: 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) { 114 return index; 115 } 116 } 117 return NoIndex; 118 } 119 120 [[nodiscard]] bool Contains(const nsStaticAtom& aTag, 121 const nsStaticAtom* aAttribute) const { 122 return IndexOf(aTag, aAttribute) != NoIndex; 123 } 124 }; 125 126 enum class PendingStyleState { 127 // Will be not changed in new content 128 NotUpdated, 129 // Will be applied to new content 130 BeingPreserved, 131 // Will be cleared from new content 132 BeingCleared, 133 }; 134 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 { 141 public: 142 NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(PendingStyles) 143 NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(PendingStyles) 144 145 PendingStyles() = default; 146 147 void Reset() { 148 mClearingStyles.Clear(); 149 mPreservingStyles.Clear(); 150 } 151 152 nsresult UpdateSelState(const HTMLEditor& aHTMLEditor); 153 154 /** 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()` 159 * call. 160 */ 161 void PreHandleMouseEvent(const dom::MouseEvent& aMouseDownOrUpEvent); 162 163 void PreHandleSelectionChangeCommand(Command aCommand); 164 void PostHandleSelectionChangeCommand(const HTMLEditor& aHTMLEditor, 165 Command aCommand); 166 167 void OnSelectionChange(const HTMLEditor& aHTMLEditor, int16_t aReason); 168 169 /** 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. 173 * 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. 182 */ 183 void PreserveStyle(nsStaticAtom& aHTMLProperty, nsAtom* aAttribute, 184 const nsAString& aAttributeValueOrCSSValue); 185 186 /** 187 * Preserve the styles with new values in aStylesToPreserve. 188 * See above for the detail. 189 */ 190 void PreserveStyles( 191 const nsTArray<EditorInlineStyleAndValue>& aStylesToPreserve); 192 193 /** 194 * Preserve the style with new value specified with aStyleToPreserve. 195 * See above for the detail. 196 */ 197 void PreserveStyle(const PendingStyleCache& aStyleToPreserve) { 198 PreserveStyle(aStyleToPreserve.TagRef(), aStyleToPreserve.GetAttribute(), 199 aStyleToPreserve.AttributeValueOrCSSValueRef()); 200 } 201 202 /** 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 206 * element. 207 * 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. 213 */ 214 void ClearStyle(nsStaticAtom& aHTMLProperty, nsAtom* aAttribute) { 215 ClearStyleInternal(&aHTMLProperty, aAttribute); 216 } 217 218 /** 219 * Clear all styles specified by aStylesToClear when next content is inserted. 220 * See above for the detail. 221 */ 222 void ClearStyles(const nsTArray<EditorInlineStyle>& aStylesToClear); 223 224 /** 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. 228 */ 229 void ClearAllStyles() { 230 // XXX Why don't we clear mClearingStyles first? 231 ClearStyleInternal(nullptr, nullptr); 232 } 233 234 /** 235 * Clear <a> element and discard styles which is applied by it. 236 */ 237 void ClearLinkAndItsSpecifiedStyle() { 238 ClearStyleInternal(nsGkAtoms::a, nullptr, SpecifiedStyle::Discard); 239 } 240 241 /** 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 244 * content. 245 */ 246 UniquePtr<PendingStyle> TakeClearingStyle() { 247 if (mClearingStyles.IsEmpty()) { 248 return nullptr; 249 } 250 return mClearingStyles.PopLastElement(); 251 } 252 253 /** 254 * TakeAllPreservedStyles() moves all preserved styles and values to 255 * aOutStylesAndValues. 256 */ 257 void TakeAllPreservedStyles( 258 nsTArray<EditorInlineStyleAndValue>& aOutStylesAndValues); 259 260 /** 261 * TakeRelativeFontSize() hands back relative font value, which is then 262 * cleared out. 263 */ 264 int32_t TakeRelativeFontSize(); 265 266 /** 267 * GetStyleState() returns whether the style will be applied to new content, 268 * removed from new content or not changed. 269 * 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. 278 */ 279 PendingStyleState GetStyleState( 280 nsStaticAtom& aHTMLProperty, nsAtom* aAttribute = nullptr, 281 nsString* aOutNewAttributeValueOrCSSValue = nullptr) const; 282 283 protected: 284 virtual ~PendingStyles() { Reset(); }; 285 286 void ClearStyleInternal( 287 nsStaticAtom* aHTMLProperty, nsAtom* aAttribute, 288 SpecifiedStyle aSpecifiedStyle = SpecifiedStyle::Preserve); 289 290 void CancelPreservingStyle(nsStaticAtom* aHTMLProperty, nsAtom* aAttribute); 291 void CancelClearingStyle(nsStaticAtom& aHTMLProperty, nsAtom* aAttribute); 292 293 Maybe<size_t> IndexOfPreservingStyle(nsStaticAtom& aHTMLProperty, 294 nsAtom* aAttribute, 295 nsAString* aOutValue = nullptr) const { 296 return IndexOfStyleInArray(&aHTMLProperty, aAttribute, aOutValue, 297 mPreservingStyles); 298 } 299 Maybe<size_t> IndexOfClearingStyle(nsStaticAtom* aHTMLProperty, 300 nsAtom* aAttribute) const { 301 return IndexOfStyleInArray(aHTMLProperty, aAttribute, nullptr, 302 mClearingStyles); 303 } 304 305 bool IsLinkStyleSet() const { 306 return IndexOfPreservingStyle(*nsGkAtoms::a, nullptr).isSome(); 307 } 308 bool IsExplicitlyLinkStyleCleared() const { 309 return IndexOfClearingStyle(nsGkAtoms::a, nullptr).isSome(); 310 } 311 bool IsOnlyLinkStyleCleared() const { 312 return mClearingStyles.Length() == 1 && IsExplicitlyLinkStyleCleared(); 313 } 314 bool IsStyleCleared(nsStaticAtom* aHTMLProperty, nsAtom* aAttribute) const { 315 return IndexOfClearingStyle(aHTMLProperty, aAttribute).isSome() || 316 AreAllStylesCleared(); 317 } 318 bool AreAllStylesCleared() const { 319 return IndexOfClearingStyle(nullptr, nullptr).isSome(); 320 } 321 bool AreSomeStylesSet() const { return !mPreservingStyles.IsEmpty(); } 322 bool AreSomeStylesCleared() const { return !mClearingStyles.IsEmpty(); } 323 324 static Maybe<size_t> IndexOfStyleInArray( 325 nsStaticAtom* aHTMLProperty, nsAtom* aAttribute, nsAString* aOutValue, 326 const nsTArray<UniquePtr<PendingStyle>>& aArray); 327 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; 335 }; 336 337 } // namespace mozilla 338 339 #endif // #ifndef mozilla_PendingStyles_h