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