1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 #ifndef mozilla_dom_LinkStyle_h 7 #define mozilla_dom_LinkStyle_h 8 9 #include "nsINode.h" 10 #include "mozilla/Attributes.h" 11 #include "mozilla/StyleSheet.h" 12 #include "mozilla/Result.h" 13 #include "mozilla/Unused.h" 14 #include "nsTArray.h" 15 16 class nsIContent; 17 class nsICSSLoaderObserver; 18 class nsIPrincipal; 19 class nsIURI; 20 21 namespace mozilla { 22 23 namespace dom { 24 25 class Document; 26 class ShadowRoot; 27 28 // https://drafts.csswg.org/cssom/#the-linkstyle-interface 29 class LinkStyle { 30 public: 31 enum class ForceUpdate : uint8_t { 32 No, 33 Yes, 34 }; 35 36 enum class Completed : uint8_t { 37 No, 38 Yes, 39 }; 40 41 enum class HasAlternateRel : uint8_t { 42 No, 43 Yes, 44 }; 45 46 enum class IsAlternate : uint8_t { 47 No, 48 Yes, 49 }; 50 51 enum class IsInline : uint8_t { 52 No, 53 Yes, 54 }; 55 56 enum class IsExplicitlyEnabled : uint8_t { 57 No, 58 Yes, 59 }; 60 61 enum class MediaMatched : uint8_t { 62 Yes, 63 No, 64 }; 65 66 struct Update { 67 private: 68 bool mWillNotify; 69 bool mIsAlternate; 70 bool mMediaMatched; 71 72 public: UpdateUpdate73 Update() : mWillNotify(false), mIsAlternate(false), mMediaMatched(false) {} 74 UpdateUpdate75 Update(Completed aCompleted, IsAlternate aIsAlternate, 76 MediaMatched aMediaMatched) 77 : mWillNotify(aCompleted == Completed::No), 78 mIsAlternate(aIsAlternate == IsAlternate::Yes), 79 mMediaMatched(aMediaMatched == MediaMatched::Yes) {} 80 WillNotifyUpdate81 bool WillNotify() const { return mWillNotify; } 82 ShouldBlockUpdate83 bool ShouldBlock() const { 84 if (!mWillNotify) { 85 return false; 86 } 87 88 return !mIsAlternate && mMediaMatched; 89 } 90 }; 91 FromNode(nsINode & aNode)92 static LinkStyle* FromNode(nsINode& aNode) { return aNode.AsLinkStyle(); } FromNode(const nsINode & aNode)93 static const LinkStyle* FromNode(const nsINode& aNode) { 94 return aNode.AsLinkStyle(); 95 } 96 FromNodeOrNull(nsINode * aNode)97 static LinkStyle* FromNodeOrNull(nsINode* aNode) { 98 return aNode ? FromNode(*aNode) : nullptr; 99 } 100 FromNodeOrNull(const nsINode * aNode)101 static const LinkStyle* FromNodeOrNull(const nsINode* aNode) { 102 return aNode ? FromNode(*aNode) : nullptr; 103 } 104 105 enum RelValue { 106 ePREFETCH = 0x00000001, 107 eDNS_PREFETCH = 0x00000002, 108 eSTYLESHEET = 0x00000004, 109 eNEXT = 0x00000008, 110 eALTERNATE = 0x00000010, 111 ePRECONNECT = 0x00000020, 112 // NOTE: 0x40 is unused 113 ePRELOAD = 0x00000080 114 }; 115 116 // The return value is a bitwise or of 0 or more RelValues. 117 static uint32_t ParseLinkTypes(const nsAString& aTypes); 118 UpdateStyleSheetInternal()119 void UpdateStyleSheetInternal() { 120 Unused << UpdateStyleSheetInternal(nullptr, nullptr); 121 } 122 123 struct MOZ_STACK_CLASS SheetInfo { 124 nsIContent* mContent; 125 // FIXME(emilio): do these really need to be strong refs? 126 nsCOMPtr<nsIURI> mURI; 127 128 // The principal of the scripted caller that initiated the load, if 129 // available. Otherwise null. 130 nsCOMPtr<nsIPrincipal> mTriggeringPrincipal; 131 nsCOMPtr<nsIReferrerInfo> mReferrerInfo; 132 mozilla::CORSMode mCORSMode; 133 nsString mTitle; 134 nsString mMedia; 135 nsString mIntegrity; 136 nsString mNonce; 137 138 bool mHasAlternateRel; 139 bool mIsInline; 140 IsExplicitlyEnabled mIsExplicitlyEnabled; 141 142 SheetInfo(const mozilla::dom::Document&, nsIContent*, 143 already_AddRefed<nsIURI> aURI, 144 already_AddRefed<nsIPrincipal> aTriggeringPrincipal, 145 already_AddRefed<nsIReferrerInfo> aReferrerInfo, 146 mozilla::CORSMode, const nsAString& aTitle, 147 const nsAString& aMedia, const nsAString& aIntegrity, 148 const nsAString& aNonce, HasAlternateRel, IsInline, 149 IsExplicitlyEnabled); 150 151 ~SheetInfo(); 152 }; 153 154 virtual nsIContent& AsContent() = 0; 155 virtual Maybe<SheetInfo> GetStyleSheetInfo() = 0; 156 157 /** 158 * Used to make the association between a style sheet and 159 * the element that linked it to the document. 160 * 161 * @param aStyleSheet the style sheet associated with this 162 * element. 163 */ 164 void SetStyleSheet(StyleSheet* aStyleSheet); 165 166 /** 167 * Tells this element to update the stylesheet. 168 * 169 * @param aObserver observer to notify once the stylesheet is loaded. 170 * This will be passed to the CSSLoader 171 */ 172 Result<Update, nsresult> UpdateStyleSheet(nsICSSLoaderObserver*); 173 174 /** 175 * Tells this element whether to update the stylesheet when the 176 * element's properties change. 177 * 178 * @param aEnableUpdates update on changes or not. 179 */ SetEnableUpdates(bool aEnableUpdates)180 void SetEnableUpdates(bool aEnableUpdates) { 181 mUpdatesEnabled = aEnableUpdates; 182 } 183 184 /** 185 * Gets the charset that the element claims the style sheet is in. 186 * Can return empty string to indicate that we have no charset 187 * information. 188 * 189 * @param aCharset the charset 190 */ 191 virtual void GetCharset(nsAString& aCharset); 192 193 // This doesn't entirely belong here since they only make sense for 194 // some types of linking elements, but it's a better place than 195 // anywhere else. SetLineNumber(uint32_t aLineNumber)196 void SetLineNumber(uint32_t aLineNumber) { mLineNumber = aLineNumber; } 197 198 /** 199 * Get the line number, as previously set by SetLineNumber. 200 * 201 * @return the line number of this element; or 1 if no line number 202 * was set 203 */ GetLineNumber()204 uint32_t GetLineNumber() const { return mLineNumber; } 205 206 // This doesn't entirely belong here since they only make sense for 207 // some types of linking elements, but it's a better place than 208 // anywhere else. SetColumnNumber(uint32_t aColumnNumber)209 void SetColumnNumber(uint32_t aColumnNumber) { 210 mColumnNumber = aColumnNumber; 211 } 212 213 /** 214 * Get the column number, as previously set by SetColumnNumber. 215 * 216 * @return the column number of this element; or 1 if no column number 217 * was set 218 */ GetColumnNumber()219 uint32_t GetColumnNumber() const { return mColumnNumber; } 220 GetSheet()221 StyleSheet* GetSheet() const { return mStyleSheet; } 222 223 /** JS can only observe the sheet once fully loaded */ 224 StyleSheet* GetSheetForBindings() const; 225 226 protected: 227 LinkStyle(); 228 virtual ~LinkStyle(); 229 230 // Gets a suitable title and media for SheetInfo out of an element, which 231 // needs to be `this`. 232 // 233 // NOTE(emilio): Needs nsString instead of nsAString because of 234 // CompressWhitespace. 235 static void GetTitleAndMediaForElement(const mozilla::dom::Element&, 236 nsString& aTitle, nsString& aMedia); 237 238 // Returns whether the type attribute specifies the text/css type for style 239 // elements. 240 static bool IsCSSMimeTypeAttributeForStyleElement(const Element&); 241 242 // CC methods 243 void Unlink(); 244 void Traverse(nsCycleCollectionTraversalCallback& cb); 245 246 /** 247 * @param aOldDocument should be non-null only if we're updating because we 248 * removed the node from the document. 249 * @param aOldShadowRoot should be non-null only if we're updating because we 250 * removed the node from a shadow tree. 251 * @param aForceUpdate true will force the update even if the URI has not 252 * changed. This should be used in cases when something 253 * about the content that affects the resulting sheet 254 * changed but the URI may not have changed. 255 * 256 * TODO(emilio): Should probably pass a single DocumentOrShadowRoot. 257 */ 258 Result<Update, nsresult> UpdateStyleSheetInternal( 259 Document* aOldDocument, ShadowRoot* aOldShadowRoot, 260 ForceUpdate = ForceUpdate::No); 261 262 /** 263 * @param aOldDocument should be non-null only if we're updating because we 264 * removed the node from the document. 265 * @param aOldShadowRoot The ShadowRoot that used to contain the style. 266 * Passed as a parameter because on an update, the node 267 * is removed from the tree before the sheet is removed 268 * from the ShadowRoot. 269 * @param aForceUpdate true will force the update even if the URI has not 270 * changed. This should be used in cases when something 271 * about the content that affects the resulting sheet 272 * changed but the URI may not have changed. 273 */ 274 Result<Update, nsresult> DoUpdateStyleSheet(Document* aOldDocument, 275 ShadowRoot* aOldShadowRoot, 276 nsICSSLoaderObserver*, 277 ForceUpdate); 278 279 RefPtr<mozilla::StyleSheet> mStyleSheet; 280 nsCOMPtr<nsIPrincipal> mTriggeringPrincipal; 281 bool mUpdatesEnabled; 282 uint32_t mLineNumber; 283 uint32_t mColumnNumber; 284 }; 285 286 } // namespace dom 287 288 } // namespace mozilla 289 290 #endif // mozilla_dom_LinkStyle_h 291