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