michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* rendering object for replaced elements with bitmap image data */ michael@0: michael@0: #ifndef nsImageFrame_h___ michael@0: #define nsImageFrame_h___ michael@0: michael@0: #include "nsSplittableFrame.h" michael@0: #include "nsIIOService.h" michael@0: #include "nsIObserver.h" michael@0: michael@0: #include "imgINotificationObserver.h" michael@0: michael@0: #include "nsDisplayList.h" michael@0: #include "imgIContainer.h" michael@0: #include "mozilla/Attributes.h" michael@0: #include "mozilla/DebugOnly.h" michael@0: #include "nsIReflowCallback.h" michael@0: michael@0: class nsImageMap; michael@0: class nsIURI; michael@0: class nsILoadGroup; michael@0: struct nsHTMLReflowState; michael@0: struct nsHTMLReflowMetrics; michael@0: class nsDisplayImage; michael@0: class nsPresContext; michael@0: class nsImageFrame; michael@0: class nsTransform2D; michael@0: class nsImageLoadingContent; michael@0: michael@0: namespace mozilla { michael@0: namespace layers { michael@0: class ImageContainer; michael@0: class ImageLayer; michael@0: class LayerManager; michael@0: } michael@0: } michael@0: michael@0: class nsImageListener : public imgINotificationObserver michael@0: { michael@0: public: michael@0: nsImageListener(nsImageFrame *aFrame); michael@0: virtual ~nsImageListener(); michael@0: michael@0: NS_DECL_ISUPPORTS michael@0: NS_DECL_IMGINOTIFICATIONOBSERVER michael@0: michael@0: void SetFrame(nsImageFrame *frame) { mFrame = frame; } michael@0: michael@0: private: michael@0: nsImageFrame *mFrame; michael@0: }; michael@0: michael@0: typedef nsSplittableFrame ImageFrameSuper; michael@0: michael@0: class nsImageFrame : public ImageFrameSuper, michael@0: public nsIReflowCallback { michael@0: public: michael@0: typedef mozilla::layers::ImageContainer ImageContainer; michael@0: typedef mozilla::layers::ImageLayer ImageLayer; michael@0: typedef mozilla::layers::LayerManager LayerManager; michael@0: michael@0: NS_DECL_FRAMEARENA_HELPERS michael@0: michael@0: nsImageFrame(nsStyleContext* aContext); michael@0: michael@0: NS_DECL_QUERYFRAME_TARGET(nsImageFrame) michael@0: NS_DECL_QUERYFRAME michael@0: michael@0: virtual void DestroyFrom(nsIFrame* aDestructRoot) MOZ_OVERRIDE; michael@0: virtual void Init(nsIContent* aContent, michael@0: nsIFrame* aParent, michael@0: nsIFrame* aPrevInFlow) MOZ_OVERRIDE; michael@0: virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder, michael@0: const nsRect& aDirtyRect, michael@0: const nsDisplayListSet& aLists) MOZ_OVERRIDE; michael@0: virtual nscoord GetMinWidth(nsRenderingContext *aRenderingContext) MOZ_OVERRIDE; michael@0: virtual nscoord GetPrefWidth(nsRenderingContext *aRenderingContext) MOZ_OVERRIDE; michael@0: virtual mozilla::IntrinsicSize GetIntrinsicSize() MOZ_OVERRIDE; michael@0: virtual nsSize GetIntrinsicRatio() MOZ_OVERRIDE; michael@0: virtual nsresult Reflow(nsPresContext* aPresContext, michael@0: nsHTMLReflowMetrics& aDesiredSize, michael@0: const nsHTMLReflowState& aReflowState, michael@0: nsReflowStatus& aStatus) MOZ_OVERRIDE; michael@0: michael@0: virtual nsresult GetContentForEvent(mozilla::WidgetEvent* aEvent, michael@0: nsIContent** aContent) MOZ_OVERRIDE; michael@0: virtual nsresult HandleEvent(nsPresContext* aPresContext, michael@0: mozilla::WidgetGUIEvent* aEvent, michael@0: nsEventStatus* aEventStatus) MOZ_OVERRIDE; michael@0: virtual nsresult GetCursor(const nsPoint& aPoint, michael@0: nsIFrame::Cursor& aCursor) MOZ_OVERRIDE; michael@0: virtual nsresult AttributeChanged(int32_t aNameSpaceID, michael@0: nsIAtom* aAttribute, michael@0: int32_t aModType) MOZ_OVERRIDE; michael@0: michael@0: #ifdef ACCESSIBILITY michael@0: virtual mozilla::a11y::AccType AccessibleType() MOZ_OVERRIDE; michael@0: #endif michael@0: michael@0: virtual nsIAtom* GetType() const MOZ_OVERRIDE; michael@0: michael@0: virtual bool IsFrameOfType(uint32_t aFlags) const MOZ_OVERRIDE michael@0: { michael@0: return ImageFrameSuper::IsFrameOfType(aFlags & ~(nsIFrame::eReplaced)); michael@0: } michael@0: michael@0: #ifdef DEBUG_FRAME_DUMP michael@0: virtual nsresult GetFrameName(nsAString& aResult) const MOZ_OVERRIDE; michael@0: void List(FILE* out = stderr, const char* aPrefix = "", michael@0: uint32_t aFlags = 0) const MOZ_OVERRIDE; michael@0: #endif michael@0: michael@0: virtual int GetLogicalSkipSides(const nsHTMLReflowState* aReflowState = nullptr) const MOZ_OVERRIDE; michael@0: michael@0: nsresult GetIntrinsicImageSize(nsSize& aSize); michael@0: michael@0: static void ReleaseGlobals() { michael@0: if (gIconLoad) { michael@0: gIconLoad->Shutdown(); michael@0: NS_RELEASE(gIconLoad); michael@0: } michael@0: NS_IF_RELEASE(sIOService); michael@0: } michael@0: michael@0: nsresult Notify(imgIRequest *aRequest, int32_t aType, const nsIntRect* aData); michael@0: michael@0: /** michael@0: * Function to test whether aContent, which has aStyleContext as its style, michael@0: * should get an image frame. Note that this method is only used by the michael@0: * frame constructor; it's only here because it uses gIconLoad for now. michael@0: */ michael@0: static bool ShouldCreateImageFrameFor(mozilla::dom::Element* aElement, michael@0: nsStyleContext* aStyleContext); michael@0: michael@0: void DisplayAltFeedback(nsRenderingContext& aRenderingContext, michael@0: const nsRect& aDirtyRect, michael@0: imgIRequest* aRequest, michael@0: nsPoint aPt); michael@0: michael@0: nsRect GetInnerArea() const; michael@0: michael@0: /** michael@0: * Return a map element associated with this image. michael@0: */ michael@0: mozilla::dom::Element* GetMapElement() const michael@0: { michael@0: nsAutoString usemap; michael@0: if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::usemap, usemap)) { michael@0: return mContent->OwnerDoc()->FindImageMap(usemap); michael@0: } michael@0: return nullptr; michael@0: } michael@0: michael@0: /** michael@0: * Return true if the image has associated image map. michael@0: */ michael@0: bool HasImageMap() const { return mImageMap || GetMapElement(); } michael@0: michael@0: nsImageMap* GetImageMap(); michael@0: nsImageMap* GetExistingImageMap() const { return mImageMap; } michael@0: michael@0: virtual void AddInlineMinWidth(nsRenderingContext *aRenderingContext, michael@0: InlineMinWidthData *aData) MOZ_OVERRIDE; michael@0: michael@0: void DisconnectMap(); michael@0: michael@0: // nsIReflowCallback michael@0: virtual bool ReflowFinished() MOZ_OVERRIDE; michael@0: virtual void ReflowCallbackCanceled() MOZ_OVERRIDE; michael@0: michael@0: protected: michael@0: virtual ~nsImageFrame(); michael@0: michael@0: void EnsureIntrinsicSizeAndRatio(nsPresContext* aPresContext); michael@0: michael@0: virtual nsSize ComputeSize(nsRenderingContext *aRenderingContext, michael@0: nsSize aCBSize, nscoord aAvailableWidth, michael@0: nsSize aMargin, nsSize aBorder, nsSize aPadding, michael@0: uint32_t aFlags) MOZ_OVERRIDE; michael@0: michael@0: bool IsServerImageMap(); michael@0: michael@0: void TranslateEventCoords(const nsPoint& aPoint, michael@0: nsIntPoint& aResult); michael@0: michael@0: bool GetAnchorHREFTargetAndNode(nsIURI** aHref, nsString& aTarget, michael@0: nsIContent** aNode); michael@0: /** michael@0: * Computes the width of the string that fits into the available space michael@0: * michael@0: * @param in aLength total length of the string in PRUnichars michael@0: * @param in aMaxWidth width not to be exceeded michael@0: * @param out aMaxFit length of the string that fits within aMaxWidth michael@0: * in PRUnichars michael@0: * @return width of the string that fits within aMaxWidth michael@0: */ michael@0: nscoord MeasureString(const char16_t* aString, michael@0: int32_t aLength, michael@0: nscoord aMaxWidth, michael@0: uint32_t& aMaxFit, michael@0: nsRenderingContext& aContext); michael@0: michael@0: void DisplayAltText(nsPresContext* aPresContext, michael@0: nsRenderingContext& aRenderingContext, michael@0: const nsString& aAltText, michael@0: const nsRect& aRect); michael@0: michael@0: void PaintImage(nsRenderingContext& aRenderingContext, nsPoint aPt, michael@0: const nsRect& aDirtyRect, imgIContainer* aImage, michael@0: uint32_t aFlags); michael@0: michael@0: protected: michael@0: friend class nsImageListener; michael@0: friend class nsImageLoadingContent; michael@0: nsresult OnStartContainer(imgIRequest *aRequest, imgIContainer *aImage); michael@0: nsresult OnDataAvailable(imgIRequest *aRequest, const nsIntRect *rect); michael@0: nsresult OnStopRequest(imgIRequest *aRequest, michael@0: nsresult aStatus); michael@0: nsresult FrameChanged(imgIRequest *aRequest, michael@0: imgIContainer *aContainer); michael@0: /** michael@0: * Notification that aRequest will now be the current request. michael@0: */ michael@0: void NotifyNewCurrentRequest(imgIRequest *aRequest, nsresult aStatus); michael@0: michael@0: private: michael@0: // random helpers michael@0: inline void SpecToURI(const nsAString& aSpec, nsIIOService *aIOService, michael@0: nsIURI **aURI); michael@0: michael@0: inline void GetLoadGroup(nsPresContext *aPresContext, michael@0: nsILoadGroup **aLoadGroup); michael@0: nscoord GetContinuationOffset() const; michael@0: void GetDocumentCharacterSet(nsACString& aCharset) const; michael@0: bool ShouldDisplaySelection(); michael@0: michael@0: /** michael@0: * Recalculate mIntrinsicSize from the image. michael@0: * michael@0: * @return whether aImage's size did _not_ michael@0: * match our previous intrinsic size. michael@0: */ michael@0: bool UpdateIntrinsicSize(imgIContainer* aImage); michael@0: michael@0: /** michael@0: * Recalculate mIntrinsicRatio from the image. michael@0: * michael@0: * @return whether aImage's ratio did _not_ michael@0: * match our previous intrinsic ratio. michael@0: */ michael@0: bool UpdateIntrinsicRatio(imgIContainer* aImage); michael@0: michael@0: /** michael@0: * This function calculates the transform for converting between michael@0: * source space & destination space. May fail if our image has a michael@0: * percent-valued or zero-valued height or width. michael@0: * michael@0: * @param aTransform The transform object to populate. michael@0: * michael@0: * @return whether we succeeded in creating the transform. michael@0: */ michael@0: bool GetSourceToDestTransform(nsTransform2D& aTransform); michael@0: michael@0: /** michael@0: * Helper functions to check whether the request or image container michael@0: * corresponds to a load we don't care about. Most of the decoder michael@0: * observer methods will bail early if these return true. michael@0: */ michael@0: bool IsPendingLoad(imgIRequest* aRequest) const; michael@0: bool IsPendingLoad(imgIContainer* aContainer) const; michael@0: michael@0: /** michael@0: * Function to convert a dirty rect in the source image to a dirty michael@0: * rect for the image frame. michael@0: */ michael@0: nsRect SourceRectToDest(const nsIntRect & aRect); michael@0: michael@0: nsImageMap* mImageMap; michael@0: michael@0: nsCOMPtr mListener; michael@0: michael@0: nsCOMPtr mImage; michael@0: nsSize mComputedSize; michael@0: mozilla::IntrinsicSize mIntrinsicSize; michael@0: nsSize mIntrinsicRatio; michael@0: michael@0: bool mDisplayingIcon; michael@0: bool mFirstFrameComplete; michael@0: bool mReflowCallbackPosted; michael@0: michael@0: static nsIIOService* sIOService; michael@0: michael@0: /* loading / broken image icon support */ michael@0: michael@0: // XXXbz this should be handled by the prescontext, I think; that michael@0: // way we would have a single iconload per mozilla session instead michael@0: // of one per document... michael@0: michael@0: // LoadIcons: initiate the loading of the static icons used to show michael@0: // loading / broken images michael@0: nsresult LoadIcons(nsPresContext *aPresContext); michael@0: nsresult LoadIcon(const nsAString& aSpec, nsPresContext *aPresContext, michael@0: imgRequestProxy **aRequest); michael@0: michael@0: class IconLoad MOZ_FINAL : public nsIObserver, michael@0: public imgINotificationObserver { michael@0: // private class that wraps the data and logic needed for michael@0: // broken image and loading image icons michael@0: public: michael@0: IconLoad(); michael@0: michael@0: void Shutdown(); michael@0: michael@0: NS_DECL_ISUPPORTS michael@0: NS_DECL_NSIOBSERVER michael@0: NS_DECL_IMGINOTIFICATIONOBSERVER michael@0: michael@0: void AddIconObserver(nsImageFrame *frame) { michael@0: NS_ABORT_IF_FALSE(!mIconObservers.Contains(frame), michael@0: "Observer shouldn't aleady be in array"); michael@0: mIconObservers.AppendElement(frame); michael@0: } michael@0: michael@0: void RemoveIconObserver(nsImageFrame *frame) { michael@0: mozilla::DebugOnly didRemove = mIconObservers.RemoveElement(frame); michael@0: NS_ABORT_IF_FALSE(didRemove, "Observer not in array"); michael@0: } michael@0: michael@0: private: michael@0: void GetPrefs(); michael@0: nsTObserverArray mIconObservers; michael@0: michael@0: michael@0: public: michael@0: nsRefPtr mLoadingImage; michael@0: nsRefPtr mBrokenImage; michael@0: bool mPrefForceInlineAltText; michael@0: bool mPrefShowPlaceholders; michael@0: }; michael@0: michael@0: public: michael@0: static IconLoad* gIconLoad; // singleton pattern: one LoadIcons instance is used michael@0: michael@0: friend class nsDisplayImage; michael@0: }; michael@0: michael@0: /** michael@0: * Note that nsDisplayImage does not receive events. However, an image element michael@0: * is replaced content so its background will be z-adjacent to the michael@0: * image itself, and hence receive events just as if the image itself michael@0: * received events. michael@0: */ michael@0: class nsDisplayImage : public nsDisplayImageContainer { michael@0: public: michael@0: typedef mozilla::layers::LayerManager LayerManager; michael@0: michael@0: nsDisplayImage(nsDisplayListBuilder* aBuilder, nsImageFrame* aFrame, michael@0: imgIContainer* aImage) michael@0: : nsDisplayImageContainer(aBuilder, aFrame), mImage(aImage) { michael@0: MOZ_COUNT_CTOR(nsDisplayImage); michael@0: } michael@0: virtual ~nsDisplayImage() { michael@0: MOZ_COUNT_DTOR(nsDisplayImage); michael@0: } michael@0: virtual void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder, michael@0: const nsDisplayItemGeometry* aGeometry, michael@0: nsRegion* aInvalidRegion) MOZ_OVERRIDE; michael@0: virtual void Paint(nsDisplayListBuilder* aBuilder, michael@0: nsRenderingContext* aCtx) MOZ_OVERRIDE; michael@0: michael@0: /** michael@0: * Returns an ImageContainer for this image if the image type michael@0: * supports it (TYPE_RASTER only). michael@0: */ michael@0: virtual already_AddRefed GetContainer(LayerManager* aManager, michael@0: nsDisplayListBuilder* aBuilder) MOZ_OVERRIDE; michael@0: michael@0: gfxRect GetDestRect(); michael@0: michael@0: virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder, michael@0: LayerManager* aManager, michael@0: const ContainerLayerParameters& aParameters) MOZ_OVERRIDE; michael@0: virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) MOZ_OVERRIDE michael@0: { michael@0: *aSnap = true; michael@0: return nsRect(ToReferenceFrame(), Frame()->GetSize()); michael@0: } michael@0: michael@0: virtual already_AddRefed BuildLayer(nsDisplayListBuilder* aBuilder, michael@0: LayerManager* aManager, michael@0: const ContainerLayerParameters& aContainerParameters) MOZ_OVERRIDE; michael@0: michael@0: /** michael@0: * Configure an ImageLayer for this display item. michael@0: * Set the required filter and scaling transform. michael@0: */ michael@0: virtual void ConfigureLayer(ImageLayer* aLayer, const nsIntPoint& aOffset) MOZ_OVERRIDE; michael@0: michael@0: NS_DISPLAY_DECL_NAME("Image", TYPE_IMAGE) michael@0: private: michael@0: nsCOMPtr mImage; michael@0: }; michael@0: michael@0: #endif /* nsImageFrame_h___ */