author | John Schoenick <jschoenick@mozilla.com> |
Wed, 10 Dec 2014 18:54:00 -0500 | |
changeset 229467 | b64c085b06e1446de958cbd0186d2900c50026fc |
parent 229466 | c1b3abf6bb988443bf0670035219ec4c585e0bee |
child 229468 | 9c39680606e8d0a808c64b6fdd4fca999e392058 |
push id | 28287 |
push user | ryanvm@gmail.com |
push date | Tue, 17 Feb 2015 20:08:22 +0000 |
treeherder | mozilla-central@b6c56fab513d [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
bugs | 1067345 |
milestone | 38.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/dom/base/nsDocument.cpp +++ b/dom/base/nsDocument.cpp @@ -141,16 +141,17 @@ #include "mozilla/dom/StyleSheetApplicableStateChangeEvent.h" #include "nsJSUtils.h" #include "nsFrameLoader.h" #include "nsEscape.h" #include "nsObjectLoadingContent.h" #include "nsHtml5TreeOpExecutor.h" #include "mozilla/dom/HTMLLinkElement.h" #include "mozilla/dom/HTMLMediaElement.h" +#include "mozilla/dom/HTMLImageElement.h" #include "mozilla/dom/MediaSource.h" #include "mozAutoDocUpdate.h" #include "nsGlobalWindow.h" #include "mozilla/dom/EncodingUtils.h" #include "nsDOMNavigationTiming.h" #include "nsSMILAnimationController.h" @@ -1601,16 +1602,19 @@ nsDocument::nsDocument(const char* aCont if (!gCspPRLog) gCspPRLog = PR_NewLogModule("CSP"); #endif // Start out mLastStyleSheetSet as null, per spec SetDOMStringToNull(mLastStyleSheetSet); + // void state used to differentiate an empty source from an unselected source + mPreloadPictureFoundSource.SetIsVoid(true); + if (!sProcessingStack) { sProcessingStack.emplace(); // Add the base queue sentinel to the processing stack. sProcessingStack->AppendElement((CustomElementData*) nullptr); } } static PLDHashOperator @@ -9665,16 +9669,97 @@ FireOrClearDelayedEvents(nsTArray<nsCOMP aDocuments[i]->GetInnerWindow()->IsCurrentInnerWindow(); shell->FireOrClearDelayedEvents(fire); } } } } void +nsDocument::PreloadPictureOpened() +{ + mPreloadPictureDepth++; +} + +void +nsDocument::PreloadPictureClosed() +{ + mPreloadPictureDepth--; + if (mPreloadPictureDepth == 0) { + mPreloadPictureFoundSource.SetIsVoid(true); + } else { + MOZ_ASSERT(mPreloadPictureDepth >= 0); + } +} + +void +nsDocument::PreloadPictureImageSource(const nsAString& aSrcsetAttr, + const nsAString& aSizesAttr, + const nsAString& aTypeAttr, + const nsAString& aMediaAttr) +{ + // Nested pictures are not valid syntax, so while we'll eventually load them, + // it's not worth tracking sources mixed between nesting levels to preload + // them effectively. + if (mPreloadPictureDepth == 1 && mPreloadPictureFoundSource.IsVoid()) { + // <picture> selects the first matching source, so if this returns a URI we + // needn't consider new sources until a new <picture> is encountered. + bool found = + HTMLImageElement::SelectSourceForTagWithAttrs(this, true, NullString(), + aSrcsetAttr, aSizesAttr, + aTypeAttr, aMediaAttr, + mPreloadPictureFoundSource); + if (found && mPreloadPictureFoundSource.IsVoid()) { + // Found an empty source, which counts + mPreloadPictureFoundSource.SetIsVoid(false); + } + } +} + +already_AddRefed<nsIURI> +nsDocument::ResolvePreloadImage(nsIURI *aBaseURI, + const nsAString& aSrcAttr, + const nsAString& aSrcsetAttr, + const nsAString& aSizesAttr) +{ + nsString sourceURL; + if (mPreloadPictureDepth == 1 && !mPreloadPictureFoundSource.IsVoid()) { + // We're in a <picture> element and found a URI from a source previous to + // this image, use it. + sourceURL = mPreloadPictureFoundSource; + } else { + // Otherwise try to use this <img> as a source + HTMLImageElement::SelectSourceForTagWithAttrs(this, false, aSrcAttr, + aSrcsetAttr, aSizesAttr, + NullString(), NullString(), + sourceURL); + } + + // Empty sources are not loaded by <img> (i.e. not resolved to the baseURI) + if (sourceURL.IsEmpty()) { + return nullptr; + } + + // Construct into URI using passed baseURI (the parser may know of base URI + // changes that have not reached us) + nsresult rv; + nsCOMPtr<nsIURI> uri; + rv = nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(uri), sourceURL, + this, aBaseURI); + if (NS_FAILED(rv)) { + return nullptr; + } + + // We don't clear mPreloadPictureFoundSource because subsequent <img> tags in + // this this <picture> share the same <sources> (though this is not valid per + // spec) + return uri.forget(); +} + +void nsDocument::MaybePreLoadImage(nsIURI* uri, const nsAString &aCrossOriginAttr, ReferrerPolicy aReferrerPolicy) { // Early exit if the img is already present in the img-cache // which indicates that the "real" load has already started and // that we shouldn't preload it. int16_t blockingStatus; if (nsContentUtils::IsImageInCache(uri, static_cast<nsIDocument *>(this)) ||
--- a/dom/base/nsDocument.h +++ b/dom/base/nsDocument.h @@ -1097,16 +1097,31 @@ public: } nsresult CloneDocHelper(nsDocument* clone) const; void MaybeInitializeFinalizeFrameLoaders(); void MaybeEndOutermostXBLUpdate(); + virtual void PreloadPictureOpened() MOZ_OVERRIDE; + virtual void PreloadPictureClosed() MOZ_OVERRIDE; + + virtual void + PreloadPictureImageSource(const nsAString& aSrcsetAttr, + const nsAString& aSizesAttr, + const nsAString& aTypeAttr, + const nsAString& aMediaAttr) MOZ_OVERRIDE; + + virtual already_AddRefed<nsIURI> + ResolvePreloadImage(nsIURI *aBaseURI, + const nsAString& aSrcAttr, + const nsAString& aSrcsetAttr, + const nsAString& aSizesAttr) MOZ_OVERRIDE; + virtual void MaybePreLoadImage(nsIURI* uri, const nsAString &aCrossOriginAttr, ReferrerPolicy aReferrerPolicy) MOZ_OVERRIDE; virtual void ForgetImagePreload(nsIURI* aURI) MOZ_OVERRIDE; virtual void PreloadStyle(nsIURI* uri, const nsAString& charset, const nsAString& aCrossOriginAttr, ReferrerPolicy aReferrerPolicy) MOZ_OVERRIDE; @@ -1762,16 +1777,22 @@ private: nsExternalResourceMap mExternalResourceMap; // All images in process of being preloaded. This is a hashtable so // we can remove them as the real image loads start; that way we // make sure to not keep the image load going when no one cares // about it anymore. nsRefPtrHashtable<nsURIHashKey, imgIRequest> mPreloadingImages; + // Current depth of picture elements from parser + int32_t mPreloadPictureDepth; + + // Set if we've found a URL for the current picture + nsString mPreloadPictureFoundSource; + nsRefPtr<mozilla::dom::DOMImplementation> mDOMImplementation; nsRefPtr<nsContentList> mImageMaps; nsCString mScrollToRef; uint8_t mScrolledToRefAlready : 1; uint8_t mChangeScrollPosWhenScrollingToRef : 1;
--- a/dom/base/nsIDocument.h +++ b/dom/base/nsIDocument.h @@ -141,19 +141,20 @@ typedef CallbackObjectHolder<NodeFilter, struct FullScreenOptions { FullScreenOptions() { } nsRefPtr<gfx::VRHMDInfo> mVRHMDDevice; }; } // namespace dom } // namespace mozilla +// 137c6144-513e-4edf-840e-5e3156638ed6 #define NS_IDOCUMENT_IID \ -{ 0xf63d2f6e, 0xd1c1, 0x49b9, \ - { 0x88, 0x26, 0xd5, 0x9e, 0x5d, 0x72, 0x2a, 0x42 } } +{ 0x137c6144, 0x513e, 0x4edf, \ + { 0x84, 0x0e, 0x5e, 0x31, 0x56, 0x63, 0x8e, 0xd6 } } // Enum for requesting a particular type of document when creating a doc enum DocumentFlavor { DocumentFlavorLegacyGuess, // compat with old code until made HTML5-compliant DocumentFlavorHTML, // HTMLDocument with HTMLness bit set to true DocumentFlavorSVG, // SVGDocument DocumentFlavorPlain, // Just a Document }; @@ -1920,16 +1921,49 @@ public: */ nsIDocument* GetOriginalDocument() { MOZ_ASSERT(!mOriginalDocument || !mOriginalDocument->GetOriginalDocument()); return mOriginalDocument; } /** + * These are called by the parser as it encounters <picture> tags, the end of + * said tags, and possible picture <source srcset> sources respectively. These + * are used to inform ResolvePreLoadImage() calls. Unset attributes are + * expected to be marked void. + * + * NOTE that the parser does not attempt to track the current picture nesting + * level or whether the given <source> tag is within a picture -- it is only + * guaranteed to order these calls properly with respect to + * ResolvePreLoadImage. + */ + + virtual void PreloadPictureOpened() = 0; + + virtual void PreloadPictureClosed() = 0; + + virtual void PreloadPictureImageSource(const nsAString& aSrcsetAttr, + const nsAString& aSizesAttr, + const nsAString& aTypeAttr, + const nsAString& aMediaAttr) = 0; + + /** + * Called by the parser to resolve an image for preloading. The parser will + * call the PreloadPicture* functions to inform us of possible <picture> + * nesting and possible sources, which are used to inform URL selection + * responsive <picture> or <img srcset> images. Unset attributes are expected + * to be marked void. + */ + virtual already_AddRefed<nsIURI> + ResolvePreloadImage(nsIURI *aBaseURI, + const nsAString& aSrcAttr, + const nsAString& aSrcsetAttr, + const nsAString& aSizesAttr) = 0; + /** * Called by nsParser to preload images. Can be removed and code moved * to nsPreloadURIs::PreloadURIs() in file nsParser.cpp whenever the * parser-module is linked with gklayout-module. aCrossOriginAttr should * be a void string if the attr is not present. */ virtual void MaybePreLoadImage(nsIURI* uri, const nsAString& aCrossOriginAttr, ReferrerPolicy aReferrerPolicy) = 0;
--- a/parser/html/nsHtml5SpeculativeLoad.cpp +++ b/parser/html/nsHtml5SpeculativeLoad.cpp @@ -26,17 +26,27 @@ nsHtml5SpeculativeLoad::Perform(nsHtml5T switch (mOpCode) { case eSpeculativeLoadBase: aExecutor->SetSpeculationBase(mUrl); break; case eSpeculativeLoadMetaReferrer: aExecutor->SetSpeculationReferrerPolicy(mMetaReferrerPolicy); break; case eSpeculativeLoadImage: - aExecutor->PreloadImage(mUrl, mCrossOrigin); + aExecutor->PreloadImage(mUrl, mCrossOrigin, mSrcset, mSizes); + break; + case eSpeculativeLoadOpenPicture: + aExecutor->PreloadOpenPicture(); + break; + case eSpeculativeLoadEndPicture: + aExecutor->PreloadEndPicture(); + break; + case eSpeculativeLoadPictureSource: + aExecutor->PreloadPictureSource(mSrcset, mSizes, mTypeOrCharsetSource, + mMedia); break; case eSpeculativeLoadScript: aExecutor->PreloadScript(mUrl, mCharset, mTypeOrCharsetSource, mCrossOrigin, false); break; case eSpeculativeLoadScriptFromHead: aExecutor->PreloadScript(mUrl, mCharset, mTypeOrCharsetSource, mCrossOrigin, true);
--- a/parser/html/nsHtml5SpeculativeLoad.h +++ b/parser/html/nsHtml5SpeculativeLoad.h @@ -12,28 +12,31 @@ class nsHtml5TreeOpExecutor; enum eHtml5SpeculativeLoad { #ifdef DEBUG eSpeculativeLoadUninitialized, #endif eSpeculativeLoadBase, eSpeculativeLoadMetaReferrer, eSpeculativeLoadImage, + eSpeculativeLoadOpenPicture, + eSpeculativeLoadEndPicture, + eSpeculativeLoadPictureSource, eSpeculativeLoadScript, eSpeculativeLoadScriptFromHead, eSpeculativeLoadStyle, eSpeculativeLoadManifest, eSpeculativeLoadSetDocumentCharset }; class nsHtml5SpeculativeLoad { public: nsHtml5SpeculativeLoad(); ~nsHtml5SpeculativeLoad(); - + inline void InitBase(const nsAString& aUrl) { NS_PRECONDITION(mOpCode == eSpeculativeLoadUninitialized, "Trying to reinitialize a speculative load!"); mOpCode = eSpeculativeLoadBase; mUrl.Assign(aUrl); } @@ -41,23 +44,62 @@ class nsHtml5SpeculativeLoad { NS_PRECONDITION(mOpCode == eSpeculativeLoadUninitialized, "Trying to reinitialize a speculative load!"); mOpCode = eSpeculativeLoadMetaReferrer; mMetaReferrerPolicy.Assign( nsContentUtils::TrimWhitespace<nsContentUtils::IsHTMLWhitespace>(aReferrerPolicy)); } inline void InitImage(const nsAString& aUrl, - const nsAString& aCrossOrigin) + const nsAString& aCrossOrigin, + const nsAString& aSrcset, + const nsAString& aSizes) { NS_PRECONDITION(mOpCode == eSpeculativeLoadUninitialized, "Trying to reinitialize a speculative load!"); mOpCode = eSpeculativeLoadImage; mUrl.Assign(aUrl); mCrossOrigin.Assign(aCrossOrigin); + mSrcset.Assign(aSrcset); + mSizes.Assign(aSizes); + } + + // <picture> elements have multiple <source> nodes followed by an <img>, + // where we use the first valid source, which may be the img. Because we + // can't determine validity at this point without parsing CSS and getting + // main thread state, we push preload operations for picture pushed and + // popped, so that the target of the preload ops can determine what picture + // and nesting level each source/img from the main preloading code exists + // at. + inline void InitOpenPicture() + { + NS_PRECONDITION(mOpCode == eSpeculativeLoadUninitialized, + "Trying to reinitialize a speculative load!"); + mOpCode = eSpeculativeLoadOpenPicture; + } + + inline void InitEndPicture() + { + NS_PRECONDITION(mOpCode == eSpeculativeLoadUninitialized, + "Trying to reinitialize a speculative load!"); + mOpCode = eSpeculativeLoadEndPicture; + } + + inline void InitPictureSource(const nsAString& aSrcset, + const nsAString& aSizes, + const nsAString& aType, + const nsAString& aMedia) + { + NS_PRECONDITION(mOpCode == eSpeculativeLoadUninitialized, + "Trying to reinitialize a speculative load!"); + mOpCode = eSpeculativeLoadPictureSource; + mSrcset.Assign(aSrcset); + mSizes.Assign(aSizes); + mTypeOrCharsetSource.Assign(aType); + mMedia.Assign(aMedia); } inline void InitScript(const nsAString& aUrl, const nsAString& aCharset, const nsAString& aType, const nsAString& aCrossOrigin, bool aParserInHead) { @@ -65,19 +107,19 @@ class nsHtml5SpeculativeLoad { "Trying to reinitialize a speculative load!"); mOpCode = aParserInHead ? eSpeculativeLoadScriptFromHead : eSpeculativeLoadScript; mUrl.Assign(aUrl); mCharset.Assign(aCharset); mTypeOrCharsetSource.Assign(aType); mCrossOrigin.Assign(aCrossOrigin); } - + inline void InitStyle(const nsAString& aUrl, const nsAString& aCharset, - const nsAString& aCrossOrigin) + const nsAString& aCrossOrigin) { NS_PRECONDITION(mOpCode == eSpeculativeLoadUninitialized, "Trying to reinitialize a speculative load!"); mOpCode = eSpeculativeLoadStyle; mUrl.Assign(aUrl); mCharset.Assign(aCharset); mCrossOrigin.Assign(aCrossOrigin); } @@ -117,17 +159,17 @@ class nsHtml5SpeculativeLoad { NS_PRECONDITION(mOpCode == eSpeculativeLoadUninitialized, "Trying to reinitialize a speculative load!"); mOpCode = eSpeculativeLoadSetDocumentCharset; CopyUTF8toUTF16(aCharset, mCharset); mTypeOrCharsetSource.Assign((char16_t)aCharsetSource); } void Perform(nsHtml5TreeOpExecutor* aExecutor); - + private: eHtml5SpeculativeLoad mOpCode; nsString mUrl; nsString mMetaReferrerPolicy; /** * If mOpCode is eSpeculativeLoadStyle or eSpeculativeLoadScript[FromHead] * then this is the value of the "charset" attribute. For * eSpeculativeLoadSetDocumentCharset it is the charset that the @@ -142,11 +184,27 @@ class nsHtml5SpeculativeLoad { */ nsString mTypeOrCharsetSource; /** * If mOpCode is eSpeculativeLoadImage or eSpeculativeLoadScript[FromHead], * this is the value of the "crossorigin" attribute. If the * attribute is not set, this will be a void string. */ nsString mCrossOrigin; + /** + * If mOpCode is eSpeculativeLoadImage or eSpeculativeLoadPictureSource, + * this is the value of "srcset" attribute. If the attribute is not set, + * this will be a void string. + */ + nsString mSrcset; + /** + * If mOpCode is eSpeculativeLoadPictureSource, this is the value of "sizes" + * attribute. If the attribute is not set, this will be a void string. + */ + nsString mSizes; + /** + * If mOpCode is eSpeculativeLoadPictureSource, this is the value of "media" + * attribute. If the attribute is not set, this will be a void string. + */ + nsString mMedia; }; #endif // nsHtml5SpeculativeLoad_h
--- a/parser/html/nsHtml5TreeBuilderCppSupplement.h +++ b/parser/html/nsHtml5TreeBuilderCppSupplement.h @@ -110,30 +110,53 @@ nsHtml5TreeBuilder::createElement(int32_ treeOp->Init(aNamespace, aName, aAttributes, content, aIntendedParent, !!mSpeculativeLoadStage); // mSpeculativeLoadStage is non-null only in the off-the-main-thread // tree builder, which handles the network stream - + // Start wall of code for speculative loading and line numbers - + if (mSpeculativeLoadStage) { switch (aNamespace) { case kNameSpaceID_XHTML: if (nsHtml5Atoms::img == aName) { nsString* url = aAttributes->getValue(nsHtml5AttributeName::ATTR_SRC); - if (url) { - nsString* crossOrigin = - aAttributes->getValue(nsHtml5AttributeName::ATTR_CROSSORIGIN); + nsString* srcset = + aAttributes->getValue(nsHtml5AttributeName::ATTR_SRCSET); + nsString* crossOrigin = + aAttributes->getValue(nsHtml5AttributeName::ATTR_CROSSORIGIN); + nsString* sizes = + aAttributes->getValue(nsHtml5AttributeName::ATTR_SIZES); + mSpeculativeLoadQueue.AppendElement()-> + InitImage(url ? *url : NullString(), + crossOrigin ? *crossOrigin : NullString(), + srcset ? *srcset : NullString(), + sizes ? *sizes : NullString()); + } else if (nsHtml5Atoms::source == aName) { + nsString* srcset = + aAttributes->getValue(nsHtml5AttributeName::ATTR_SRCSET); + // Sources without srcset cannot be selected. The source could also be + // for a media element, but in that context doesn't use srcset. See + // comments in nsHtml5SpeculativeLoad.h about <picture> preloading + if (srcset) { + nsString* sizes = + aAttributes->getValue(nsHtml5AttributeName::ATTR_SIZES); + nsString* type = + aAttributes->getValue(nsHtml5AttributeName::ATTR_TYPE); + nsString* media = + aAttributes->getValue(nsHtml5AttributeName::ATTR_MEDIA); mSpeculativeLoadQueue.AppendElement()-> - InitImage(*url, - crossOrigin ? *crossOrigin : NullString()); + InitPictureSource(*srcset, + sizes ? *sizes : NullString(), + type ? *type : NullString(), + media ? *media : NullString()); } } else if (nsHtml5Atoms::script == aName) { nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); NS_ASSERTION(treeOp, "Tree op allocation failed."); treeOp->Init(eTreeOpSetScriptLineNumberAndFreeze, content, tokenizer->getLineNumber()); nsString* url = aAttributes->getValue(nsHtml5AttributeName::ATTR_SRC); if (url) { @@ -142,17 +165,17 @@ nsHtml5TreeBuilder::createElement(int32_ nsString* crossOrigin = aAttributes->getValue(nsHtml5AttributeName::ATTR_CROSSORIGIN); mSpeculativeLoadQueue.AppendElement()-> InitScript(*url, (charset) ? *charset : EmptyString(), (type) ? *type : EmptyString(), (crossOrigin) ? *crossOrigin : NullString(), mode == NS_HTML5TREE_BUILDER_IN_HEAD); - mCurrentHtmlScriptIsAsyncOrDefer = + mCurrentHtmlScriptIsAsyncOrDefer = aAttributes->contains(nsHtml5AttributeName::ATTR_ASYNC) || aAttributes->contains(nsHtml5AttributeName::ATTR_DEFER); } } else if (nsHtml5Atoms::link == aName) { nsString* rel = aAttributes->getValue(nsHtml5AttributeName::ATTR_REL); // Not splitting on space here is bogus but the old parser didn't even // do a case-insensitive check. if (rel && rel->LowerCaseEqualsASCII("stylesheet")) { @@ -165,17 +188,19 @@ nsHtml5TreeBuilder::createElement(int32_ InitStyle(*url, (charset) ? *charset : EmptyString(), (crossOrigin) ? *crossOrigin : NullString()); } } } else if (nsHtml5Atoms::video == aName) { nsString* url = aAttributes->getValue(nsHtml5AttributeName::ATTR_POSTER); if (url) { - mSpeculativeLoadQueue.AppendElement()->InitImage(*url, NullString()); + mSpeculativeLoadQueue.AppendElement()->InitImage(*url, NullString(), + NullString(), + NullString()); } } else if (nsHtml5Atoms::style == aName) { nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); NS_ASSERTION(treeOp, "Tree op allocation failed."); treeOp->Init(eTreeOpSetStyleLineNumber, content, tokenizer->getLineNumber()); } else if (nsHtml5Atoms::html == aName) { nsString* url = aAttributes->getValue(nsHtml5AttributeName::ATTR_MANIFEST); if (url) { @@ -199,17 +224,19 @@ nsHtml5TreeBuilder::createElement(int32_ } } } break; case kNameSpaceID_SVG: if (nsHtml5Atoms::image == aName) { nsString* url = aAttributes->getValue(nsHtml5AttributeName::ATTR_XLINK_HREF); if (url) { - mSpeculativeLoadQueue.AppendElement()->InitImage(*url, NullString()); + mSpeculativeLoadQueue.AppendElement()->InitImage(*url, NullString(), + NullString(), + NullString()); } } else if (nsHtml5Atoms::script == aName) { nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); NS_ASSERTION(treeOp, "Tree op allocation failed."); treeOp->Init(eTreeOpSetScriptLineNumberAndFreeze, content, tokenizer->getLineNumber()); nsString* url = aAttributes->getValue(nsHtml5AttributeName::ATTR_XLINK_HREF); if (url) { @@ -231,17 +258,17 @@ nsHtml5TreeBuilder::createElement(int32_ nsString* url = aAttributes->getValue(nsHtml5AttributeName::ATTR_XLINK_HREF); if (url) { nsString* crossOrigin = aAttributes->getValue(nsHtml5AttributeName::ATTR_CROSSORIGIN); mSpeculativeLoadQueue.AppendElement()-> InitStyle(*url, EmptyString(), (crossOrigin) ? *crossOrigin : NullString()); } - } + } break; } } else if (aNamespace != kNameSpaceID_MathML) { // No speculative loader--just line numbers and defer/async check if (nsHtml5Atoms::style == aName) { nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); NS_ASSERTION(treeOp, "Tree op allocation failed."); treeOp->Init(eTreeOpSetStyleLineNumber, content, tokenizer->getLineNumber()); @@ -735,16 +762,23 @@ nsHtml5TreeBuilder::elementPushed(int32_ aName == nsHtml5Atoms::menuitem) { if (mBuilder) { nsHtml5TreeOperation::DoneCreatingElement(static_cast<nsIContent*>(aElement)); } else { mOpQueue.AppendElement()->Init(eTreeOpDoneCreatingElement, aElement); } return; } + if (mSpeculativeLoadStage && aName == nsHtml5Atoms::picture) { + // mSpeculativeLoadStage is non-null only in the off-the-main-thread + // tree builder, which handles the network stream + // + // See comments in nsHtml5SpeculativeLoad.h about <picture> preloading + mSpeculativeLoadQueue.AppendElement()->InitOpenPicture(); + } } void nsHtml5TreeBuilder::elementPopped(int32_t aNamespace, nsIAtom* aName, nsIContentHandle* aElement) { NS_ASSERTION(aNamespace == kNameSpaceID_XHTML || aNamespace == kNameSpaceID_SVG || aNamespace == kNameSpaceID_MathML, "Element isn't HTML, SVG or MathML!"); NS_ASSERTION(aName, "Element doesn't have local name!"); NS_ASSERTION(aElement, "No element!"); @@ -835,16 +869,23 @@ nsHtml5TreeBuilder::elementPopped(int32_ return; } if (aName == nsHtml5Atoms::meta && !fragment && !mBuilder) { nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); NS_ASSERTION(treeOp, "Tree op allocation failed."); treeOp->Init(eTreeOpProcessMeta, aElement); return; } + if (mSpeculativeLoadStage && aName == nsHtml5Atoms::picture) { + // mSpeculativeLoadStage is non-null only in the off-the-main-thread + // tree builder, which handles the network stream + // + // See comments in nsHtml5SpeculativeLoad.h about <picture> preloading + mSpeculativeLoadQueue.AppendElement()->InitEndPicture(); + } return; } void nsHtml5TreeBuilder::accumulateCharacters(const char16_t* aBuf, int32_t aStart, int32_t aLength) { int32_t newFillLen = charBufferLen + aLength; if (newFillLen > charBuffer.length) {
--- a/parser/html/nsHtml5TreeOpExecutor.cpp +++ b/parser/html/nsHtml5TreeOpExecutor.cpp @@ -854,48 +854,66 @@ nsHtml5TreeOpExecutor::IsExternalViewSou if (mDocumentURI) { mDocumentURI->SchemeIs("view-source", &isViewSource); } return isViewSource; } // Speculative loading +nsIURI* +nsHtml5TreeOpExecutor::BaseURIForPreload() +{ + // The URL of the document without <base> + nsIURI* documentURI = mDocument->GetDocumentURI(); + // The URL of the document with non-speculative <base> + nsIURI* documentBaseURI = mDocument->GetDocBaseURI(); + + // If the two above are different, use documentBaseURI. If they are the same, + // the document object isn't aware of a <base>, so attempt to use the + // mSpeculationBaseURI or, failing, that, documentURI. + return (documentURI == documentBaseURI) ? + (mSpeculationBaseURI ? + mSpeculationBaseURI.get() : documentURI) + : documentBaseURI; +} + already_AddRefed<nsIURI> nsHtml5TreeOpExecutor::ConvertIfNotPreloadedYet(const nsAString& aURL) { if (aURL.IsEmpty()) { return nullptr; } - // The URL of the document without <base> - nsIURI* documentURI = mDocument->GetDocumentURI(); - // The URL of the document with non-speculative <base> - nsIURI* documentBaseURI = mDocument->GetDocBaseURI(); - // If the two above are different, use documentBaseURI. If they are the - // same, the document object isn't aware of a <base>, so attempt to use the - // mSpeculationBaseURI or, failing, that, documentURI. - nsIURI* base = (documentURI == documentBaseURI) ? - (mSpeculationBaseURI ? - mSpeculationBaseURI.get() : documentURI) - : documentBaseURI; + nsIURI* base = BaseURIForPreload(); const nsCString& charset = mDocument->GetDocumentCharacterSet(); nsCOMPtr<nsIURI> uri; nsresult rv = NS_NewURI(getter_AddRefs(uri), aURL, charset.get(), base); if (NS_FAILED(rv)) { NS_WARNING("Failed to create a URI"); return nullptr; } + + if (ShouldPreloadURI(uri)) { + return uri.forget(); + } + + return nullptr; +} + +bool +nsHtml5TreeOpExecutor::ShouldPreloadURI(nsIURI *aURI) +{ nsAutoCString spec; - uri->GetSpec(spec); + aURI->GetSpec(spec); if (mPreloadedURLs.Contains(spec)) { - return nullptr; + return false; } mPreloadedURLs.PutEntry(spec); - return uri.forget(); + return true; } void nsHtml5TreeOpExecutor::PreloadScript(const nsAString& aURL, const nsAString& aCharset, const nsAString& aType, const nsAString& aCrossOrigin, bool aScriptFromHead) @@ -919,23 +937,49 @@ nsHtml5TreeOpExecutor::PreloadStyle(cons return; } mDocument->PreloadStyle(uri, aCharset, aCrossOrigin, mSpeculationReferrerPolicy); } void nsHtml5TreeOpExecutor::PreloadImage(const nsAString& aURL, - const nsAString& aCrossOrigin) + const nsAString& aCrossOrigin, + const nsAString& aSrcset, + const nsAString& aSizes) { - nsCOMPtr<nsIURI> uri = ConvertIfNotPreloadedYet(aURL); - if (!uri) { - return; + nsCOMPtr<nsIURI> baseURI = BaseURIForPreload(); + nsCOMPtr<nsIURI> uri = mDocument->ResolvePreloadImage(baseURI, aURL, aSrcset, + aSizes); + if (uri && ShouldPreloadURI(uri)) { + mDocument->MaybePreLoadImage(uri, aCrossOrigin, mSpeculationReferrerPolicy); } - mDocument->MaybePreLoadImage(uri, aCrossOrigin, mSpeculationReferrerPolicy); +} + +// These calls inform the document of picture state and seen sources, such that +// it can use them to inform ResolvePreLoadImage as necessary +void +nsHtml5TreeOpExecutor::PreloadPictureSource(const nsAString& aSrcset, + const nsAString& aSizes, + const nsAString& aType, + const nsAString& aMedia) +{ + mDocument->PreloadPictureImageSource(aSrcset, aSizes, aType, aMedia); +} + +void +nsHtml5TreeOpExecutor::PreloadOpenPicture() +{ + mDocument->PreloadPictureOpened(); +} + +void +nsHtml5TreeOpExecutor::PreloadEndPicture() +{ + mDocument->PreloadPictureClosed(); } void nsHtml5TreeOpExecutor::AddBase(const nsAString& aURL) { const nsCString& charset = mDocument->GetDocumentCharacterSet(); nsresult rv = NS_NewURI(getter_AddRefs(mViewSourceBaseURI), aURL, charset.get(), GetViewSourceBaseURI());
--- a/parser/html/nsHtml5TreeOpExecutor.h +++ b/parser/html/nsHtml5TreeOpExecutor.h @@ -249,34 +249,56 @@ class nsHtml5TreeOpExecutor MOZ_FINAL : void PreloadScript(const nsAString& aURL, const nsAString& aCharset, const nsAString& aType, const nsAString& aCrossOrigin, bool aScriptFromHead); void PreloadStyle(const nsAString& aURL, const nsAString& aCharset, - const nsAString& aCrossOrigin); + const nsAString& aCrossOrigin); + + void PreloadImage(const nsAString& aURL, + const nsAString& aCrossOrigin, + const nsAString& aSrcset, + const nsAString& aSizes); - void PreloadImage(const nsAString& aURL, const nsAString& aCrossOrigin); + void PreloadOpenPicture(); + + void PreloadEndPicture(); + + void PreloadPictureSource(const nsAString& aSrcset, + const nsAString& aSizes, + const nsAString& aType, + const nsAString& aMedia); void SetSpeculationBase(const nsAString& aURL); void SetSpeculationReferrerPolicy(ReferrerPolicy aReferrerPolicy); void SetSpeculationReferrerPolicy(const nsAString& aReferrerPolicy); - + void AddBase(const nsAString& aURL); static void InitializeStatics(); private: nsHtml5Parser* GetParser(); bool IsExternalViewSource(); /** * Get a nsIURI for an nsString if the URL hasn't been preloaded yet. */ already_AddRefed<nsIURI> ConvertIfNotPreloadedYet(const nsAString& aURL); + /** + * The base URI we would use for current preload operations + */ + nsIURI* BaseURIForPreload(); + + /** + * Returns true if we haven't preloaded this URI yet, and adds it to the + * list of preloaded URIs + */ + bool ShouldPreloadURI(nsIURI *aURI); }; #endif // nsHtml5TreeOpExecutor_h