nsHtml5SpeculativeLoad.h (17866B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 #ifndef nsHtml5SpeculativeLoad_h 6 #define nsHtml5SpeculativeLoad_h 7 8 #include "nsString.h" 9 #include "nsContentUtils.h" 10 #include "nsHtml5DocumentMode.h" 11 #include "nsHtml5String.h" 12 #include "ReferrerInfo.h" 13 14 class nsHtml5TreeOpExecutor; 15 16 enum eHtml5SpeculativeLoad { 17 eSpeculativeLoadUninitialized, 18 eSpeculativeLoadBase, 19 eSpeculativeLoadCSP, 20 eSpeculativeLoadMetaReferrer, 21 eSpeculativeLoadImage, 22 eSpeculativeLoadOpenPicture, 23 eSpeculativeLoadEndPicture, 24 eSpeculativeLoadPictureSource, 25 eSpeculativeLoadScript, 26 eSpeculativeLoadScriptFromHead, 27 eSpeculativeLoadStyle, 28 eSpeculativeLoadManifest, 29 eSpeculativeLoadSetDocumentCharset, 30 eSpeculativeLoadSetDocumentMode, 31 eSpeculativeLoadPreconnect, 32 eSpeculativeLoadFont, 33 eSpeculativeLoadFetch, 34 eSpeculativeLoadMaybeComplainAboutCharset 35 }; 36 37 class nsHtml5SpeculativeLoad { 38 using Encoding = mozilla::Encoding; 39 template <typename T> 40 using NotNull = mozilla::NotNull<T>; 41 42 public: 43 nsHtml5SpeculativeLoad(); 44 ~nsHtml5SpeculativeLoad(); 45 46 inline void InitBase(nsHtml5String aUrl) { 47 MOZ_ASSERT(mOpCode == eSpeculativeLoadUninitialized, 48 "Trying to reinitialize a speculative load!"); 49 mOpCode = eSpeculativeLoadBase; 50 aUrl.ToString(mUrlOrSizes); 51 } 52 53 inline void InitMetaCSP(nsHtml5String aCSP) { 54 MOZ_ASSERT(mOpCode == eSpeculativeLoadUninitialized, 55 "Trying to reinitialize a speculative load!"); 56 mOpCode = eSpeculativeLoadCSP; 57 nsString csp; // Not Auto, because using it to hold nsStringBuffer* 58 aCSP.ToString(csp); 59 mTypeOrCharsetSourceOrDocumentModeOrMetaCSPOrSizesOrIntegrity.Assign( 60 nsContentUtils::TrimWhitespace<nsContentUtils::IsHTMLWhitespace>(csp)); 61 } 62 63 inline void InitMetaReferrerPolicy(nsHtml5String aReferrerPolicy) { 64 MOZ_ASSERT(mOpCode == eSpeculativeLoadUninitialized, 65 "Trying to reinitialize a speculative load!"); 66 mOpCode = eSpeculativeLoadMetaReferrer; 67 nsString 68 referrerPolicy; // Not Auto, because using it to hold nsStringBuffer* 69 aReferrerPolicy.ToString(referrerPolicy); 70 mReferrerPolicyOrIntegrity.Assign( 71 nsContentUtils::TrimWhitespace<nsContentUtils::IsHTMLWhitespace>( 72 referrerPolicy)); 73 } 74 75 inline void InitImage(nsHtml5String aUrl, nsHtml5String aCrossOrigin, 76 nsHtml5String aMedia, nsHtml5String aReferrerPolicy, 77 nsHtml5String aSrcset, nsHtml5String aSizes, 78 bool aLinkPreload, nsHtml5String aFetchPriority, 79 nsHtml5String aType) { 80 MOZ_ASSERT(mOpCode == eSpeculativeLoadUninitialized, 81 "Trying to reinitialize a speculative load!"); 82 mOpCode = eSpeculativeLoadImage; 83 aUrl.ToString(mUrlOrSizes); 84 aCrossOrigin.ToString(mCrossOrigin); 85 aMedia.ToString(mMedia); 86 nsString 87 referrerPolicy; // Not Auto, because using it to hold nsStringBuffer* 88 aReferrerPolicy.ToString(referrerPolicy); 89 mReferrerPolicyOrIntegrity.Assign( 90 nsContentUtils::TrimWhitespace<nsContentUtils::IsHTMLWhitespace>( 91 referrerPolicy)); 92 aSrcset.ToString(mCharsetOrSrcset); 93 aSizes.ToString( 94 mTypeOrCharsetSourceOrDocumentModeOrMetaCSPOrSizesOrIntegrity); 95 mIsLinkPreload = aLinkPreload; 96 aFetchPriority.ToString(mFetchPriority); 97 aType.ToString(mNonceOrType); 98 } 99 100 inline void InitFont(nsHtml5String aUrl, nsHtml5String aCrossOrigin, 101 nsHtml5String aMedia, nsHtml5String aReferrerPolicy, 102 nsHtml5String aFetchPriority) { 103 MOZ_ASSERT(mOpCode == eSpeculativeLoadUninitialized, 104 "Trying to reinitialize a speculative load!"); 105 mOpCode = eSpeculativeLoadFont; 106 aUrl.ToString(mUrlOrSizes); 107 aCrossOrigin.ToString(mCrossOrigin); 108 aMedia.ToString(mMedia); 109 nsString 110 referrerPolicy; // Not Auto, because using it to hold nsStringBuffer* 111 aReferrerPolicy.ToString(referrerPolicy); 112 mReferrerPolicyOrIntegrity.Assign( 113 nsContentUtils::TrimWhitespace<nsContentUtils::IsHTMLWhitespace>( 114 referrerPolicy)); 115 aFetchPriority.ToString(mFetchPriority); 116 // This can be only triggered by <link rel=preload type=font> 117 mIsLinkPreload = true; 118 } 119 120 inline void InitFetch(nsHtml5String aUrl, nsHtml5String aCrossOrigin, 121 nsHtml5String aMedia, nsHtml5String aReferrerPolicy, 122 nsHtml5String aFetchPriority) { 123 MOZ_ASSERT(mOpCode == eSpeculativeLoadUninitialized, 124 "Trying to reinitialize a speculative load!"); 125 mOpCode = eSpeculativeLoadFetch; 126 aUrl.ToString(mUrlOrSizes); 127 aCrossOrigin.ToString(mCrossOrigin); 128 aMedia.ToString(mMedia); 129 nsString 130 referrerPolicy; // Not Auto, because using it to hold nsStringBuffer* 131 aReferrerPolicy.ToString(referrerPolicy); 132 mReferrerPolicyOrIntegrity.Assign( 133 nsContentUtils::TrimWhitespace<nsContentUtils::IsHTMLWhitespace>( 134 referrerPolicy)); 135 aFetchPriority.ToString(mFetchPriority); 136 137 // This method can be only be triggered by <link rel=preload type=fetch>, 138 // hence this operation is always a preload. 139 mIsLinkPreload = true; 140 } 141 142 // <picture> elements have multiple <source> nodes followed by an <img>, 143 // where we use the first valid source, which may be the img. Because we 144 // can't determine validity at this point without parsing CSS and getting 145 // main thread state, we push preload operations for picture pushed and 146 // popped, so that the target of the preload ops can determine what picture 147 // and nesting level each source/img from the main preloading code exists 148 // at. 149 inline void InitOpenPicture() { 150 MOZ_ASSERT(mOpCode == eSpeculativeLoadUninitialized, 151 "Trying to reinitialize a speculative load!"); 152 mOpCode = eSpeculativeLoadOpenPicture; 153 } 154 155 inline void InitEndPicture() { 156 MOZ_ASSERT(mOpCode == eSpeculativeLoadUninitialized, 157 "Trying to reinitialize a speculative load!"); 158 mOpCode = eSpeculativeLoadEndPicture; 159 } 160 161 inline void InitPictureSource(nsHtml5String aSrcset, nsHtml5String aSizes, 162 nsHtml5String aType, nsHtml5String aMedia) { 163 MOZ_ASSERT(mOpCode == eSpeculativeLoadUninitialized, 164 "Trying to reinitialize a speculative load!"); 165 mOpCode = eSpeculativeLoadPictureSource; 166 aSrcset.ToString(mCharsetOrSrcset); 167 aSizes.ToString(mUrlOrSizes); 168 aType.ToString( 169 mTypeOrCharsetSourceOrDocumentModeOrMetaCSPOrSizesOrIntegrity); 170 aMedia.ToString(mMedia); 171 } 172 173 inline void InitScript(nsHtml5String aUrl, nsHtml5String aCharset, 174 nsHtml5String aType, nsHtml5String aCrossOrigin, 175 nsHtml5String aMedia, nsHtml5String aNonce, 176 nsHtml5String aFetchPriority, nsHtml5String aIntegrity, 177 nsHtml5String aReferrerPolicy, bool aParserInHead, 178 bool aAsync, bool aDefer, bool aLinkPreload) { 179 MOZ_ASSERT(mOpCode == eSpeculativeLoadUninitialized, 180 "Trying to reinitialize a speculative load!"); 181 mOpCode = 182 aParserInHead ? eSpeculativeLoadScriptFromHead : eSpeculativeLoadScript; 183 aUrl.ToString(mUrlOrSizes); 184 aCharset.ToString(mCharsetOrSrcset); 185 aType.ToString( 186 mTypeOrCharsetSourceOrDocumentModeOrMetaCSPOrSizesOrIntegrity); 187 aCrossOrigin.ToString(mCrossOrigin); 188 aMedia.ToString(mMedia); 189 aNonce.ToString(mNonceOrType); 190 aFetchPriority.ToString(mFetchPriority); 191 aIntegrity.ToString(mReferrerPolicyOrIntegrity); 192 nsAutoString referrerPolicy; 193 aReferrerPolicy.ToString(referrerPolicy); 194 referrerPolicy = 195 nsContentUtils::TrimWhitespace<nsContentUtils::IsHTMLWhitespace>( 196 referrerPolicy); 197 mScriptReferrerPolicy = 198 mozilla::dom::ReferrerInfo::ReferrerPolicyAttributeFromString( 199 referrerPolicy); 200 201 mIsAsync = aAsync; 202 mIsDefer = aDefer; 203 mIsLinkPreload = aLinkPreload; 204 } 205 206 inline void InitImportStyle(nsString&& aUrl) { 207 MOZ_ASSERT(mOpCode == eSpeculativeLoadUninitialized, 208 "Trying to reinitialize a speculative load!"); 209 mOpCode = eSpeculativeLoadStyle; 210 mUrlOrSizes = std::move(aUrl); 211 mCharsetOrSrcset.SetIsVoid(true); 212 mCrossOrigin.SetIsVoid(true); 213 mMedia.SetIsVoid(true); 214 mReferrerPolicyOrIntegrity.SetIsVoid(true); 215 mNonceOrType.SetIsVoid(true); 216 mTypeOrCharsetSourceOrDocumentModeOrMetaCSPOrSizesOrIntegrity.SetIsVoid( 217 true); 218 } 219 220 inline void InitStyle(nsHtml5String aUrl, nsHtml5String aCharset, 221 nsHtml5String aCrossOrigin, nsHtml5String aMedia, 222 nsHtml5String aReferrerPolicy, nsHtml5String aNonce, 223 nsHtml5String aIntegrity, bool aLinkPreload, 224 nsHtml5String aFetchPriority) { 225 MOZ_ASSERT(mOpCode == eSpeculativeLoadUninitialized, 226 "Trying to reinitialize a speculative load!"); 227 mOpCode = eSpeculativeLoadStyle; 228 aUrl.ToString(mUrlOrSizes); 229 aCharset.ToString(mCharsetOrSrcset); 230 aCrossOrigin.ToString(mCrossOrigin); 231 aMedia.ToString(mMedia); 232 nsString 233 referrerPolicy; // Not Auto, because using it to hold nsStringBuffer* 234 aReferrerPolicy.ToString(referrerPolicy); 235 mReferrerPolicyOrIntegrity.Assign( 236 nsContentUtils::TrimWhitespace<nsContentUtils::IsHTMLWhitespace>( 237 referrerPolicy)); 238 aNonce.ToString(mNonceOrType); 239 aIntegrity.ToString( 240 mTypeOrCharsetSourceOrDocumentModeOrMetaCSPOrSizesOrIntegrity); 241 mIsLinkPreload = aLinkPreload; 242 aFetchPriority.ToString(mFetchPriority); 243 } 244 245 /** 246 * "Speculative" manifest loads aren't truly speculative--if a manifest 247 * gets loaded, we are committed to it. There can never be a <script> 248 * before the manifest, so the situation of having to undo a manifest due 249 * to document.write() never arises. The reason why a parser 250 * thread-discovered manifest gets loaded via the speculative load queue 251 * as opposed to tree operation queue is that the manifest must get 252 * processed before any actual speculative loads such as scripts. Thus, 253 * manifests seen by the parser thread have to maintain the queue order 254 * relative to true speculative loads. See bug 541079. 255 */ 256 inline void InitManifest(nsHtml5String aUrl) { 257 MOZ_ASSERT(mOpCode == eSpeculativeLoadUninitialized, 258 "Trying to reinitialize a speculative load!"); 259 mOpCode = eSpeculativeLoadManifest; 260 aUrl.ToString(mUrlOrSizes); 261 } 262 263 /** 264 * We communicate the encoding change via the speculative operation 265 * queue in order to act upon it as soon as possible and so as not to 266 * have speculative loads generated after an encoding change fail to 267 * make use of the encoding change. 268 */ 269 inline void InitSetDocumentCharset(NotNull<const Encoding*> aEncoding, 270 int32_t aCharsetSource, 271 bool aCommitEncodingSpeculation) { 272 MOZ_ASSERT(mOpCode == eSpeculativeLoadUninitialized, 273 "Trying to reinitialize a speculative load!"); 274 mOpCode = eSpeculativeLoadSetDocumentCharset; 275 mCharsetOrSrcset.~nsString(); 276 mEncoding = aEncoding; 277 mTypeOrCharsetSourceOrDocumentModeOrMetaCSPOrSizesOrIntegrity.Assign( 278 (char16_t)aCharsetSource); 279 mCommitEncodingSpeculation = aCommitEncodingSpeculation; 280 } 281 282 inline void InitMaybeComplainAboutCharset(const char* aMsgId, bool aError, 283 int32_t aLineNumber) { 284 MOZ_ASSERT(mOpCode == eSpeculativeLoadUninitialized, 285 "Trying to reinitialize a speculative load!"); 286 mOpCode = eSpeculativeLoadMaybeComplainAboutCharset; 287 mCharsetOrSrcset.~nsString(); 288 mMsgId = aMsgId; 289 mIsError = aError; 290 // Transport a 32-bit integer as two 16-bit code units of a string 291 // in order to avoid adding an integer field to the object. 292 // See https://bugzilla.mozilla.org/show_bug.cgi?id=1733043 for a better 293 // eventual approach. 294 char16_t high = (char16_t)(((uint32_t)aLineNumber) >> 16); 295 char16_t low = (char16_t)(((uint32_t)aLineNumber) & 0xFFFF); 296 mTypeOrCharsetSourceOrDocumentModeOrMetaCSPOrSizesOrIntegrity.Assign(high); 297 mTypeOrCharsetSourceOrDocumentModeOrMetaCSPOrSizesOrIntegrity.Append(low); 298 } 299 300 /** 301 * Speculative document mode setting isn't really speculative. Once it 302 * happens, we are committed to it. However, this information needs to 303 * travel in the speculation queue in order to have this information 304 * available before parsing the speculatively loaded style sheets. 305 */ 306 inline void InitSetDocumentMode(nsHtml5DocumentMode aMode) { 307 MOZ_ASSERT(mOpCode == eSpeculativeLoadUninitialized, 308 "Trying to reinitialize a speculative load!"); 309 mOpCode = eSpeculativeLoadSetDocumentMode; 310 mTypeOrCharsetSourceOrDocumentModeOrMetaCSPOrSizesOrIntegrity.Assign( 311 (char16_t)aMode); 312 } 313 314 inline void InitPreconnect(nsHtml5String aUrl, nsHtml5String aCrossOrigin) { 315 MOZ_ASSERT(mOpCode == eSpeculativeLoadUninitialized, 316 "Trying to reinitialize a speculative load!"); 317 mOpCode = eSpeculativeLoadPreconnect; 318 aUrl.ToString(mUrlOrSizes); 319 aCrossOrigin.ToString(mCrossOrigin); 320 } 321 322 void Perform(nsHtml5TreeOpExecutor* aExecutor); 323 324 private: 325 nsHtml5SpeculativeLoad(const nsHtml5SpeculativeLoad&) = delete; 326 nsHtml5SpeculativeLoad& operator=(const nsHtml5SpeculativeLoad&) = delete; 327 328 eHtml5SpeculativeLoad mOpCode; 329 330 /** 331 * Whether the refering element has async attribute. 332 */ 333 bool mIsAsync; 334 335 /** 336 * Whether the refering element has defer attribute. 337 */ 338 bool mIsDefer; 339 340 /** 341 * True if and only if this is a speculative load initiated by <link 342 * rel="preload"> or <link rel="modulepreload"> tag encounter. Passed to the 343 * handling loader as an indication to raise the priority. 344 */ 345 bool mIsLinkPreload; 346 347 /** 348 * Whether the charset complaint is an error. 349 */ 350 bool mIsError; 351 352 /** 353 * Whether setting document encoding involves also committing to an encoding 354 * speculation. 355 */ 356 bool mCommitEncodingSpeculation; 357 358 /* If mOpCode is eSpeculativeLoadPictureSource, this is the value of the 359 * "sizes" attribute. If the attribute is not set, this will be a void 360 * string. Otherwise it empty or the value of the url. 361 */ 362 nsString mUrlOrSizes; 363 /** 364 * If mOpCode is eSpeculativeLoadScript[FromHead], this is the value of the 365 * "integrity" attribute. If the attribute is not set, this will be a void 366 * string. Otherwise it is empty or the value of the referrer policy. 367 */ 368 nsString mReferrerPolicyOrIntegrity; 369 /** 370 * If mOpCode is eSpeculativeLoadStyle or eSpeculativeLoadScript[FromHead] 371 * then this is the value of the "charset" attribute. For 372 * eSpeculativeLoadSetDocumentCharset it is the charset that the 373 * document's charset is being set to. If mOpCode is eSpeculativeLoadImage 374 * or eSpeculativeLoadPictureSource, this is the value of the "srcset" 375 * attribute. If the attribute is not set, this will be a void string. 376 * Otherwise it's empty. 377 * For eSpeculativeLoadMaybeComplainAboutCharset mMsgId is used. 378 */ 379 union { 380 nsString mCharsetOrSrcset; 381 const Encoding* mEncoding; 382 const char* mMsgId; 383 }; 384 /** 385 * If mOpCode is eSpeculativeLoadSetDocumentCharset, this is a 386 * one-character string whose single character's code point is to be 387 * interpreted as a charset source integer. If mOpCode is 388 * eSpeculativeLoadSetDocumentMode, this is a one-character string whose 389 * single character's code point is to be interpreted as an 390 * nsHtml5DocumentMode. If mOpCode is eSpeculativeLoadCSP, this is a meta 391 * element's CSP value. If mOpCode is eSpeculativeLoadImage, this is the 392 * value of the "sizes" attribute. If the attribute is not set, this will 393 * be a void string. If mOpCode is eSpeculativeLoadStyle, this 394 * is the value of the "integrity" attribute. If the attribute is not set, 395 * this will be a void string. Otherwise, it is empty or the value of the type 396 * attribute. 397 */ 398 nsString mTypeOrCharsetSourceOrDocumentModeOrMetaCSPOrSizesOrIntegrity; 399 /** 400 * If mOpCode is eSpeculativeLoadImage or eSpeculativeLoadScript[FromHead] 401 * or eSpeculativeLoadPreconnect or eSpeculativeLoadStyle this is the value of 402 * the "crossorigin" attribute. If the attribute is not set, this will be a 403 * void string. 404 */ 405 nsString mCrossOrigin; 406 /** 407 * If mOpCode is eSpeculativeLoadPictureSource or eSpeculativeLoadStyle or 408 * Fetch or Image or Media or Script this is the value of the relevant "media" 409 * attribute of the <link rel="preload"> or <link rel="stylesheet">. If the 410 * attribute is not set, or the preload didn't originate from a <link>, this 411 * will be a void string. 412 */ 413 nsString mMedia; 414 /** 415 * If mOpCode is eSpeculativeLoadImage this represents the value of the "type" 416 * attribute. If the attribute is not set, this will be a void string. 417 * Otherwise, it is empty or the value of the "nonce" attribute. 418 */ 419 nsString mNonceOrType; 420 /** 421 * If mOpCode is eSpeculativeLoadNoModuleScript[FromHead] or 422 * eSpeculativeLoadScript[FromHead] this represents the value of the 423 * "fetchpriority" attribute. 424 */ 425 nsString mFetchPriority; 426 /** 427 * If mOpCode is eSpeculativeLoadScript[FromHead] this represents the value 428 * of the "referrerpolicy" attribute. This field holds one of the values 429 * (REFERRER_POLICY_*) defined in nsIHttpChannel. 430 */ 431 mozilla::dom::ReferrerPolicy mScriptReferrerPolicy; 432 }; 433 434 #endif // nsHtml5SpeculativeLoad_h