gecko-dev/layout/generic/nsImageFrame.cpp

2512 строки
81 KiB
C++
Исходник Обычный вид История

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
2012-05-21 15:12:37 +04:00
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* rendering object for replaced elements with image data */
#include "nsImageFrame.h"
#include "gfx2DGlue.h"
#include "gfxContext.h"
#include "gfxUtils.h"
#include "mozilla/ComputedStyle.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/Encoding.h"
#include "mozilla/EventStates.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/gfx/Helpers.h"
#include "mozilla/gfx/PathHelpers.h"
#include "mozilla/dom/HTMLImageElement.h"
#include "mozilla/dom/ResponsiveImageSelector.h"
#include "mozilla/layers/WebRenderLayerManager.h"
#include "mozilla/MouseEvents.h"
#include "mozilla/Unused.h"
#include "nsCOMPtr.h"
#include "nsFontMetrics.h"
#include "nsIImageLoadingContent.h"
1998-09-09 02:34:40 +04:00
#include "nsString.h"
#include "nsPrintfCString.h"
#include "nsPresContext.h"
1998-09-09 02:34:40 +04:00
#include "nsIPresShell.h"
#include "nsGkAtoms.h"
1998-09-09 02:34:40 +04:00
#include "nsIDocument.h"
#include "nsContentUtils.h"
#include "nsCSSAnonBoxes.h"
1998-09-09 02:34:40 +04:00
#include "nsStyleConsts.h"
#include "nsStyleCoord.h"
#include "nsStyleUtil.h"
#include "nsTransform2D.h"
1999-01-09 03:13:19 +03:00
#include "nsImageMap.h"
#include "nsIIOService.h"
#include "nsILoadGroup.h"
#include "nsISupportsPriority.h"
#include "nsNetUtil.h"
#include "nsNetCID.h"
1998-09-09 02:34:40 +04:00
#include "nsCSSRendering.h"
#include "nsNameSpaceManager.h"
#include <algorithm>
2001-08-17 07:13:07 +04:00
#ifdef ACCESSIBILITY
#include "nsAccessibilityService.h"
2001-08-17 07:13:07 +04:00
#endif
#include "nsLayoutUtils.h"
#include "nsDisplayList.h"
#include "nsIContent.h"
#include "nsIDocument.h"
#include "FrameLayerBuilder.h"
#include "mozilla/dom/Selection.h"
#include "nsIURIMutator.h"
#include "imgIContainer.h"
#include "imgLoader.h"
#include "imgRequestProxy.h"
#include "nsCSSFrameConstructor.h"
#include "nsRange.h"
#include "nsError.h"
#include "nsBidiUtils.h"
#include "nsBidiPresUtils.h"
#include "gfxRect.h"
#include "ImageLayers.h"
#include "ImageContainer.h"
#include "mozilla/ServoStyleSet.h"
#include "nsBlockFrame.h"
#include "nsStyleStructInlines.h"
#include "mozilla/Preferences.h"
#include "mozilla/dom/Link.h"
#include "SVGImageContext.h"
#include "mozilla/dom/HTMLAnchorElement.h"
using namespace mozilla;
using namespace mozilla::dom;
using namespace mozilla::gfx;
using namespace mozilla::image;
using namespace mozilla::layers;
// sizes (pixels) for image icon, padding and border frame
#define ICON_SIZE (16)
#define ICON_PADDING (3)
#define ALT_BORDER_WIDTH (1)
1998-09-09 02:34:40 +04:00
// Default alignment value (so we can tell an unset value from a set value)
#define ALIGN_UNSET uint8_t(-1)
1998-09-09 02:34:40 +04:00
// static icon information
StaticRefPtr<nsImageFrame::IconLoad> nsImageFrame::gIconLoad;
// cached IO service for loading icons
nsIIOService* nsImageFrame::sIOService;
// test if the width and height are fixed, looking at the style data
// This is used by nsImageFrame::ShouldCreateImageFrameFor and should
// not be used for layout decisions.
static bool HaveSpecifiedSize(const nsStylePosition* aStylePosition)
{
// check the width and height values in the reflow state's style struct
// - if width and height are specified as either coord or percentage, then
// the size of the image frame is constrained
return aStylePosition->mWidth.IsCoordPercentCalcUnit() &&
aStylePosition->mHeight.IsCoordPercentCalcUnit();
}
// Decide whether we can optimize away reflows that result from the
// image's intrinsic size changing.
inline bool HaveFixedSize(const ReflowInput& aReflowInput)
{
NS_ASSERTION(aReflowInput.mStylePosition, "crappy reflowInput - null stylePosition");
// Don't try to make this optimization when an image has percentages
// in its 'width' or 'height'. The percentages might be treated like
// auto (especially for intrinsic width calculations and for heights).
return aReflowInput.mStylePosition->mHeight.ConvertsToLength() &&
aReflowInput.mStylePosition->mWidth.ConvertsToLength();
}
nsIFrame*
NS_NewImageFrame(nsIPresShell* aPresShell, ComputedStyle* aStyle)
1998-09-09 02:34:40 +04:00
{
return new (aPresShell) nsImageFrame(aStyle, nsImageFrame::Kind::ImageElement);
}
nsIFrame*
NS_NewImageFrameForContentProperty(nsIPresShell* aPresShell,
ComputedStyle* aStyle)
{
return new (aPresShell) nsImageFrame(
aStyle, nsImageFrame::Kind::NonGeneratedContentProperty);
1998-09-09 02:34:40 +04:00
}
nsImageFrame*
nsImageFrame::CreateContinuingFrame(nsIPresShell* aPresShell,
ComputedStyle* aStyle) const
{
return new (aPresShell) nsImageFrame(aStyle, mKind);
}
NS_IMPL_FRAMEARENA_HELPERS(nsImageFrame)
nsImageFrame::nsImageFrame(ComputedStyle* aStyle, ClassID aID, Kind aKind)
: nsAtomicContainerFrame(aStyle, aID)
, mComputedSize(0, 0)
, mIntrinsicRatio(0, 0)
, mKind(aKind)
, mDisplayingIcon(false)
, mFirstFrameComplete(false)
, mReflowCallbackPosted(false)
, mForceSyncDecoding(false)
{
EnableVisibilityTracking();
// We assume our size is not constrained and we haven't gotten an
// initial reflow yet, so don't touch those flags.
mIntrinsicSize.width.SetCoordValue(0);
mIntrinsicSize.height.SetCoordValue(0);
}
nsImageFrame::~nsImageFrame()
1998-09-09 02:34:40 +04:00
{
}
NS_QUERYFRAME_HEAD(nsImageFrame)
NS_QUERYFRAME_ENTRY(nsImageFrame)
NS_QUERYFRAME_TAIL_INHERITING(nsAtomicContainerFrame)
2001-08-17 07:13:07 +04:00
#ifdef ACCESSIBILITY
a11y::AccType
nsImageFrame::AccessibleType()
{
// Don't use GetImageMap() to avoid reentrancy into accessibility.
if (HasImageMap()) {
return a11y::eHTMLImageMapType;
}
return a11y::eImageType;
}
2001-08-17 07:13:07 +04:00
#endif
void
nsImageFrame::DisconnectMap()
1998-09-09 02:34:40 +04:00
{
if (!mImageMap) {
return;
}
mImageMap->Destroy();
mImageMap = nullptr;
#ifdef ACCESSIBILITY
if (nsAccessibilityService* accService = GetAccService()) {
accService->RecreateAccessible(PresShell(), mContent);
}
#endif
}
void
nsImageFrame::DestroyFrom(nsIFrame* aDestructRoot, PostDestroyData& aPostDestroyData)
{
if (mReflowCallbackPosted) {
PresShell()->CancelReflowCallback(this);
mReflowCallbackPosted = false;
}
// Tell our image map, if there is one, to clean up
// This causes the nsImageMap to unregister itself as
// a DOM listener.
DisconnectMap();
1998-09-09 02:34:40 +04:00
MOZ_ASSERT(mListener);
if (mKind == Kind::ImageElement) {
MOZ_ASSERT(!mContentURLRequest);
nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent);
MOZ_ASSERT(imageLoader);
// Notify our image loading content that we are going away so it can
// deregister with our refresh driver.
imageLoader->FrameDestroyed(this);
imageLoader->RemoveNativeObserver(mListener);
} else {
if (mContentURLRequest) {
mContentURLRequest->Cancel(NS_BINDING_ABORTED);
}
}
// set the frame to null so we don't send messages to a dead object.
mListener->SetFrame(nullptr);
mListener = nullptr;
// If we were displaying an icon, take ourselves off the list
if (mDisplayingIcon)
gIconLoad->RemoveIconObserver(this);
nsAtomicContainerFrame::DestroyFrom(aDestructRoot, aPostDestroyData);
1998-09-09 02:34:40 +04:00
}
void
nsImageFrame::DidSetComputedStyle(ComputedStyle* aOldComputedStyle)
{
nsAtomicContainerFrame::DidSetComputedStyle(aOldComputedStyle);
if (!mImage) {
// We'll pick this change up whenever we do get an image.
return;
}
nsStyleImageOrientation newOrientation = StyleVisibility()->mImageOrientation;
// We need to update our orientation either if we had no ComputedStyle before
// because this is the first time it's been set, or if the image-orientation
// property changed from its previous value.
bool shouldUpdateOrientation =
!aOldComputedStyle ||
aOldComputedStyle->StyleVisibility()->mImageOrientation != newOrientation;
if (shouldUpdateOrientation) {
nsCOMPtr<imgIContainer> image(mImage->Unwrap());
mImage = nsLayoutUtils::OrientImage(image, newOrientation);
UpdateIntrinsicSize(mImage);
UpdateIntrinsicRatio(mImage);
}
}
static bool
SizeIsAvailable(imgIRequest* aRequest)
{
if (!aRequest) {
return false;
}
uint32_t imageStatus = 0;
nsresult rv = aRequest->GetImageStatus(&imageStatus);
return NS_SUCCEEDED(rv) && (imageStatus & imgIRequest::STATUS_SIZE_AVAILABLE);
}
void
nsImageFrame::Init(nsIContent* aContent,
nsContainerFrame* aParent,
nsIFrame* aPrevInFlow)
{
MOZ_ASSERT_IF(aPrevInFlow,
aPrevInFlow->Type() == Type() &&
static_cast<nsImageFrame*>(aPrevInFlow)->mKind == mKind);
nsAtomicContainerFrame::Init(aContent, aParent, aPrevInFlow);
mListener = new nsImageListener(this);
if (!gIconLoad)
LoadIcons(PresContext());
if (mKind == Kind::ImageElement) {
nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(aContent);
MOZ_ASSERT(imageLoader);
imageLoader->AddNativeObserver(mListener);
// We have a PresContext now, so we need to notify the image content node
// that it can register images.
imageLoader->FrameCreated(this);
} else {
if (auto* proxy = StyleContent()->ContentAt(0).GetImage()) {
proxy->Clone(mListener,
mContent->OwnerDoc(),
getter_AddRefs(mContentURLRequest));
// Make sure we get the intrinsic size and such ASAP if available.
if (SizeIsAvailable(mContentURLRequest)) {
nsCOMPtr<imgIContainer> image;
mContentURLRequest->GetImage(getter_AddRefs(image));
OnSizeAvailable(mContentURLRequest, image);
}
}
}
// Give image loads associated with an image frame a small priority boost.
if (nsCOMPtr<imgIRequest> currentRequest = GetCurrentRequest()) {
uint32_t categoryToBoostPriority = imgIRequest::CATEGORY_FRAME_INIT;
// Increase load priority further if intrinsic size might be important for layout.
if (!HaveSpecifiedSize(StylePosition())) {
categoryToBoostPriority |= imgIRequest::CATEGORY_SIZE_QUERY;
}
currentRequest->BoostPriority(categoryToBoostPriority);
}
}
static void
ScaleIntrinsicSizeForDensity(nsIContent& aContent, nsSize& aSize)
{
auto* image = HTMLImageElement::FromNode(aContent);
if (!image) {
return;
}
ResponsiveImageSelector* selector = image->GetResponsiveImageSelector();
if (!selector) {
return;
}
double density = selector->GetSelectedImageDensity();
MOZ_ASSERT(density > 0.0);
if (density == 1.0) {
return;
}
if (aSize.width != -1) {
aSize.width = NSToCoordRound(double(aSize.width) / density);
}
if (aSize.height != -1) {
aSize.height = NSToCoordRound(double(aSize.height) / density);
}
}
bool
nsImageFrame::UpdateIntrinsicSize(imgIContainer* aImage)
{
MOZ_ASSERT(aImage, "null image");
if (!aImage)
return false;
IntrinsicSize oldIntrinsicSize = mIntrinsicSize;
mIntrinsicSize = IntrinsicSize();
// Set intrinsic size to match aImage's reported intrinsic width & height.
nsSize intrinsicSize;
if (NS_SUCCEEDED(aImage->GetIntrinsicSize(&intrinsicSize))) {
if (mKind == Kind::ImageElement) {
ScaleIntrinsicSizeForDensity(*mContent, intrinsicSize);
}
// If the image has no intrinsic width, intrinsicSize.width will be -1, and
// we can leave mIntrinsicSize.width at its default value of eStyleUnit_None.
// Otherwise we use intrinsicSize.width. Height works the same way.
if (intrinsicSize.width != -1)
mIntrinsicSize.width.SetCoordValue(intrinsicSize.width);
if (intrinsicSize.height != -1)
mIntrinsicSize.height.SetCoordValue(intrinsicSize.height);
} else {
// Failure means that the image hasn't loaded enough to report a result. We
// treat this case as if the image's intrinsic size was 0x0.
mIntrinsicSize.width.SetCoordValue(0);
mIntrinsicSize.height.SetCoordValue(0);
1999-02-27 03:57:04 +03:00
}
return mIntrinsicSize != oldIntrinsicSize;
}
bool
nsImageFrame::UpdateIntrinsicRatio(imgIContainer* aImage)
{
MOZ_ASSERT(aImage, "null image");
if (!aImage)
return false;
nsSize oldIntrinsicRatio = mIntrinsicRatio;
// Set intrinsic ratio to match aImage's reported intrinsic ratio.
if (NS_FAILED(aImage->GetIntrinsicRatio(&mIntrinsicRatio)))
mIntrinsicRatio.SizeTo(0, 0);
return mIntrinsicRatio != oldIntrinsicRatio;
}
bool
nsImageFrame::GetSourceToDestTransform(nsTransform2D& aTransform)
{
// First, figure out destRect (the rect we're rendering into).
// NOTE: We use mComputedSize instead of just GetInnerArea()'s own size here,
// because GetInnerArea() might be smaller if we're fragmented, whereas
// mComputedSize has our full content-box size (which we need for
// ComputeObjectDestRect to work correctly).
nsRect constraintRect(GetInnerArea().TopLeft(), mComputedSize);
constraintRect.y -= GetContinuationOffset();
nsRect destRect = nsLayoutUtils::ComputeObjectDestRect(constraintRect,
mIntrinsicSize,
mIntrinsicRatio,
StylePosition());
// Set the translation components, based on destRect
// XXXbz does this introduce rounding errors because of the cast to
// float? Should we just manually add that stuff in every time
// instead?
aTransform.SetToTranslate(float(destRect.x), float(destRect.y));
// NOTE(emilio): This intrinsicSize is not the same as the layout intrinsic
// size (mIntrinsicSize), which can be scaled due to ResponsiveImageSelector,
// see ScaleIntrinsicSizeForDensity.
nsSize intrinsicSize;
if (!mImage ||
!NS_SUCCEEDED(mImage->GetIntrinsicSize(&intrinsicSize)) ||
intrinsicSize.IsEmpty()) {
return false;
}
aTransform.SetScale(float(destRect.width) / float(intrinsicSize.width),
float(destRect.height) / float(intrinsicSize.height));
return true;
}
// This function checks whether the given request is the current request for our
// mContent.
bool
nsImageFrame::IsPendingLoad(imgIRequest* aRequest) const
{
// Default to pending load in case of errors
if (mKind == Kind::NonGeneratedContentProperty) {
MOZ_ASSERT(aRequest == mContentURLRequest);
return false;
}
nsCOMPtr<nsIImageLoadingContent> imageLoader(do_QueryInterface(mContent));
MOZ_ASSERT(imageLoader);
int32_t requestType = nsIImageLoadingContent::UNKNOWN_REQUEST;
imageLoader->GetRequestType(aRequest, &requestType);
return requestType != nsIImageLoadingContent::CURRENT_REQUEST;
}
nsRect
nsImageFrame::SourceRectToDest(const nsIntRect& aRect)
{
// When scaling the image, row N of the source image may (depending on
// the scaling function) be used to draw any row in the destination image
// between floor(F * (N-1)) and ceil(F * (N+1)), where F is the
// floating-point scaling factor. The same holds true for columns.
// So, we start by computing that bound without the floor and ceiling.
nsRect r(nsPresContext::CSSPixelsToAppUnits(aRect.x - 1),
nsPresContext::CSSPixelsToAppUnits(aRect.y - 1),
nsPresContext::CSSPixelsToAppUnits(aRect.width + 2),
nsPresContext::CSSPixelsToAppUnits(aRect.height + 2));
nsTransform2D sourceToDest;
if (!GetSourceToDestTransform(sourceToDest)) {
// Failed to generate transform matrix. Return our whole inner area,
// to be on the safe side (since this method is used for generating
// invalidation rects).
return GetInnerArea();
}
sourceToDest.TransformCoord(&r.x, &r.y, &r.width, &r.height);
// Now, round the edges out to the pixel boundary.
nscoord scale = nsPresContext::CSSPixelsToAppUnits(1);
nscoord right = r.x + r.width;
nscoord bottom = r.y + r.height;
r.x -= (scale + (r.x % scale)) % scale;
r.y -= (scale + (r.y % scale)) % scale;
r.width = right + ((scale - (right % scale)) % scale) - r.x;
r.height = bottom + ((scale - (bottom % scale)) % scale) - r.y;
return r;
}
// Note that we treat NS_EVENT_STATE_SUPPRESSED images as "OK". This means
// that we'll construct image frames for them as needed if their display is
// toggled from "none" (though we won't paint them, unless their visibility
// is changed too).
#define BAD_STATES (NS_EVENT_STATE_BROKEN | NS_EVENT_STATE_USERDISABLED | \
NS_EVENT_STATE_LOADING)
// This is a macro so that we don't evaluate the boolean last arg
// unless we have to; it can be expensive
#define IMAGE_OK(_state, _loadingOK) \
(!(_state).HasAtLeastOneOfStates(BAD_STATES) || \
(!(_state).HasAtLeastOneOfStates(NS_EVENT_STATE_BROKEN | NS_EVENT_STATE_USERDISABLED) && \
(_state).HasState(NS_EVENT_STATE_LOADING) && (_loadingOK)))
/* static */
bool
nsImageFrame::ShouldCreateImageFrameFor(Element* aElement,
ComputedStyle* aComputedStyle)
{
EventStates state = aElement->State();
if (IMAGE_OK(state,
HaveSpecifiedSize(aComputedStyle->StylePosition()))) {
// Image is fine; do the image frame thing
return true;
}
// Check if we want to use a placeholder box with an icon or just
// let the presShell make us into inline text. Decide as follows:
//
// - if our special "force icons" style is set, show an icon
// - else if our "do not show placeholders" pref is set, skip the icon
// - else:
// - if there is a src attribute, there is no alt attribute,
// and this is not an <object> (which could not possibly have
// such an attribute), show an icon.
// - if QuirksMode, and the IMG has a size show an icon.
// - otherwise, skip the icon
bool useSizedBox;
if (aComputedStyle->StyleUIReset()->mForceBrokenImageIcon) {
useSizedBox = true;
}
else if (gIconLoad && gIconLoad->mPrefForceInlineAltText) {
useSizedBox = false;
}
else if (aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::src) &&
!aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::alt) &&
!aElement->IsHTMLElement(nsGkAtoms::object) &&
!aElement->IsHTMLElement(nsGkAtoms::input)) {
// Use a sized box if we have no alt text. This means no alt attribute
// and the node is not an object or an input (since those always have alt
// text).
useSizedBox = true;
}
else if (aElement->OwnerDoc()->GetCompatibilityMode() !=
eCompatibility_NavQuirks) {
useSizedBox = false;
}
else {
// check whether we have specified size
useSizedBox = HaveSpecifiedSize(aComputedStyle->StylePosition());
}
return useSizedBox;
}
nsresult
nsImageFrame::Notify(imgIRequest* aRequest,
int32_t aType,
const nsIntRect* aRect)
{
if (aType == imgINotificationObserver::SIZE_AVAILABLE) {
nsCOMPtr<imgIContainer> image;
aRequest->GetImage(getter_AddRefs(image));
return OnSizeAvailable(aRequest, image);
}
if (aType == imgINotificationObserver::FRAME_UPDATE) {
return OnFrameUpdate(aRequest, aRect);
}
if (aType == imgINotificationObserver::FRAME_COMPLETE) {
mFirstFrameComplete = true;
}
if (aType == imgINotificationObserver::LOAD_COMPLETE) {
uint32_t imgStatus;
aRequest->GetImageStatus(&imgStatus);
nsresult status =
imgStatus & imgIRequest::STATUS_ERROR ? NS_ERROR_FAILURE : NS_OK;
return OnLoadComplete(aRequest, status);
}
return NS_OK;
}
nsresult
nsImageFrame::OnSizeAvailable(imgIRequest* aRequest, imgIContainer* aImage)
{
if (!aImage) {
return NS_ERROR_INVALID_ARG;
}
2001-10-06 09:08:16 +04:00
/* Get requested animation policy from the pres context:
* normal = 0
* one frame = 1
* one loop = 2
*/
aImage->SetAnimationMode(PresContext()->ImageAnimationMode());
if (IsPendingLoad(aRequest)) {
// We don't care
return NS_OK;
}
bool intrinsicSizeChanged = false;
if (SizeIsAvailable(aRequest)) {
// This is valid and for the current request, so update our stored image
// container, orienting according to our style.
mImage = nsLayoutUtils::OrientImage(aImage, StyleVisibility()->mImageOrientation);
intrinsicSizeChanged = UpdateIntrinsicSize(mImage);
intrinsicSizeChanged = UpdateIntrinsicRatio(mImage) || intrinsicSizeChanged;
} else {
// We no longer have a valid image, so release our stored image container.
mImage = mPrevImage = nullptr;
// Have to size to 0,0 so that GetDesiredSize recalculates the size.
mIntrinsicSize.width.SetCoordValue(0);
mIntrinsicSize.height.SetCoordValue(0);
mIntrinsicRatio.SizeTo(0, 0);
intrinsicSizeChanged = true;
}
if (!GotInitialReflow()) {
return NS_OK;
}
MarkNeedsDisplayItemRebuild();
if (intrinsicSizeChanged) {
// Now we need to reflow if we have an unconstrained size and have
// already gotten the initial reflow
if (!(mState & IMAGE_SIZECONSTRAINED)) {
Bug 1149357: Properly update responsive images for density changes. r=dholbert Before that we were not notifying the image frame in any way if we ended up not doing a load, and we were instead relying on the reflow the viewport resize caused to get the new density in ComputeSize from the content node (but nowhere else, since that's the bug part 1 fixes). This was generally unsound, since you can stash random media in a sizes= attribute, which don't necessarily needs to cause a reflow. Now we need to notify necessarily because nsImageFrame stores the adjusted intrinsic size. mCurrentDensity could also get out of sync as well, when the selected image density changed, but we ended up returning early because our source hadn't change in the first early exit. This patch moves us to a model where we don't re-trigger loads for density changes if the source doesn't change (unless we pass aAlwaysLoad when we need to, per spec). This matches our previous behavior (without the bugginess of not updating the intrinsic size), and also Chromium, at least. This changes behavior in one case, which is when we don't load the same source node, but we have the same source URL, and the density does change. This could happen with <picture> and two <source>s with same source and different media and sizes. This makes our behavior consistent with the behavior we have when both the source and the density doesn't change. Blink and WebKit do trigger a second image load both when the source changes without changing density and when density changes. I'll file a spec issue, since per: https://html.spec.whatwg.org/#reacting-to-environment-changes We should be triggering the load when the density changes but the source doesn't as well, but no UA does that. I filed https://github.com/whatwg/html/issues/3709 with a little summary of the situation and what I think the behavior should be (which is what this patch implements). That being said, I'll update the impl if the spec people think otherwise :). MozReview-Commit-ID: Eqy16ygHRLo
2018-05-23 12:19:49 +03:00
PresShell()->FrameNeedsReflow(this, nsIPresShell::eStyleChange,
NS_FRAME_IS_DIRTY);
} else {
// We've already gotten the initial reflow, and our size hasn't changed,
// so we're ready to request a decode.
MaybeDecodeForPredictedSize();
}
mPrevImage = nullptr;
}
return NS_OK;
}
nsresult
nsImageFrame::OnFrameUpdate(imgIRequest* aRequest, const nsIntRect* aRect)
{
NS_ENSURE_ARG_POINTER(aRect);
if (!GotInitialReflow()) {
// Don't bother to do anything; we have a reflow coming up!
return NS_OK;
}
if (mFirstFrameComplete && !StyleVisibility()->IsVisible()) {
return NS_OK;
}
if (IsPendingLoad(aRequest)) {
// We don't care
return NS_OK;
}
2001-10-06 09:08:16 +04:00
nsIntRect layerInvalidRect = mImage
? mImage->GetImageSpaceInvalidationRect(*aRect)
: *aRect;
if (layerInvalidRect.IsEqualInterior(GetMaxSizedIntRect())) {
// Invalidate our entire area.
InvalidateSelf(nullptr, nullptr);
return NS_OK;
}
nsRect frameInvalidRect = SourceRectToDest(layerInvalidRect);
InvalidateSelf(&layerInvalidRect, &frameInvalidRect);
return NS_OK;
}
void
nsImageFrame::InvalidateSelf(const nsIntRect* aLayerInvalidRect,
const nsRect* aFrameInvalidRect)
{
// XXX: Do we really want to check whether we have a
// WebRenderUserDataProperty?
if (HasProperty(WebRenderUserDataProperty::Key())) {
RefPtr<WebRenderFallbackData> data = GetWebRenderUserData<WebRenderFallbackData>(this, static_cast<uint32_t>(DisplayItemType::TYPE_IMAGE));
if (data) {
data->SetInvalid(true);
}
SchedulePaint();
return;
}
InvalidateLayer(DisplayItemType::TYPE_IMAGE,
aLayerInvalidRect,
aFrameInvalidRect);
if (!mFirstFrameComplete) {
InvalidateLayer(DisplayItemType::TYPE_ALT_FEEDBACK,
aLayerInvalidRect,
aFrameInvalidRect);
}
}
nsresult
nsImageFrame::OnLoadComplete(imgIRequest* aRequest, nsresult aStatus)
{
NotifyNewCurrentRequest(aRequest, aStatus);
return NS_OK;
}
Bug 1149357: Properly update responsive images for density changes. r=dholbert Before that we were not notifying the image frame in any way if we ended up not doing a load, and we were instead relying on the reflow the viewport resize caused to get the new density in ComputeSize from the content node (but nowhere else, since that's the bug part 1 fixes). This was generally unsound, since you can stash random media in a sizes= attribute, which don't necessarily needs to cause a reflow. Now we need to notify necessarily because nsImageFrame stores the adjusted intrinsic size. mCurrentDensity could also get out of sync as well, when the selected image density changed, but we ended up returning early because our source hadn't change in the first early exit. This patch moves us to a model where we don't re-trigger loads for density changes if the source doesn't change (unless we pass aAlwaysLoad when we need to, per spec). This matches our previous behavior (without the bugginess of not updating the intrinsic size), and also Chromium, at least. This changes behavior in one case, which is when we don't load the same source node, but we have the same source URL, and the density does change. This could happen with <picture> and two <source>s with same source and different media and sizes. This makes our behavior consistent with the behavior we have when both the source and the density doesn't change. Blink and WebKit do trigger a second image load both when the source changes without changing density and when density changes. I'll file a spec issue, since per: https://html.spec.whatwg.org/#reacting-to-environment-changes We should be triggering the load when the density changes but the source doesn't as well, but no UA does that. I filed https://github.com/whatwg/html/issues/3709 with a little summary of the situation and what I think the behavior should be (which is what this patch implements). That being said, I'll update the impl if the spec people think otherwise :). MozReview-Commit-ID: Eqy16ygHRLo
2018-05-23 12:19:49 +03:00
void
nsImageFrame::ResponsiveContentDensityChanged()
{
if (!GotInitialReflow()) {
Bug 1149357: Properly update responsive images for density changes. r=dholbert Before that we were not notifying the image frame in any way if we ended up not doing a load, and we were instead relying on the reflow the viewport resize caused to get the new density in ComputeSize from the content node (but nowhere else, since that's the bug part 1 fixes). This was generally unsound, since you can stash random media in a sizes= attribute, which don't necessarily needs to cause a reflow. Now we need to notify necessarily because nsImageFrame stores the adjusted intrinsic size. mCurrentDensity could also get out of sync as well, when the selected image density changed, but we ended up returning early because our source hadn't change in the first early exit. This patch moves us to a model where we don't re-trigger loads for density changes if the source doesn't change (unless we pass aAlwaysLoad when we need to, per spec). This matches our previous behavior (without the bugginess of not updating the intrinsic size), and also Chromium, at least. This changes behavior in one case, which is when we don't load the same source node, but we have the same source URL, and the density does change. This could happen with <picture> and two <source>s with same source and different media and sizes. This makes our behavior consistent with the behavior we have when both the source and the density doesn't change. Blink and WebKit do trigger a second image load both when the source changes without changing density and when density changes. I'll file a spec issue, since per: https://html.spec.whatwg.org/#reacting-to-environment-changes We should be triggering the load when the density changes but the source doesn't as well, but no UA does that. I filed https://github.com/whatwg/html/issues/3709 with a little summary of the situation and what I think the behavior should be (which is what this patch implements). That being said, I'll update the impl if the spec people think otherwise :). MozReview-Commit-ID: Eqy16ygHRLo
2018-05-23 12:19:49 +03:00
return;
}
if (!mImage) {
return;
}
if (!UpdateIntrinsicSize(mImage) && !UpdateIntrinsicRatio(mImage)) {
return;
}
PresShell()->FrameNeedsReflow(this, nsIPresShell::eStyleChange,
NS_FRAME_IS_DIRTY);
}
void
nsImageFrame::NotifyNewCurrentRequest(imgIRequest* aRequest, nsresult aStatus)
{
nsCOMPtr<imgIContainer> image;
aRequest->GetImage(getter_AddRefs(image));
NS_ASSERTION(image || NS_FAILED(aStatus), "Successful load with no container?");
// May have to switch sizes here!
bool intrinsicSizeChanged = true;
if (NS_SUCCEEDED(aStatus) && image && SizeIsAvailable(aRequest)) {
// Update our stored image container, orienting according to our style.
mImage = nsLayoutUtils::OrientImage(image, StyleVisibility()->mImageOrientation);
intrinsicSizeChanged = UpdateIntrinsicSize(mImage);
intrinsicSizeChanged = UpdateIntrinsicRatio(mImage) || intrinsicSizeChanged;
} else {
// We no longer have a valid image, so release our stored image container.
mImage = mPrevImage = nullptr;
// Have to size to 0,0 so that GetDesiredSize recalculates the size
mIntrinsicSize.width.SetCoordValue(0);
mIntrinsicSize.height.SetCoordValue(0);
mIntrinsicRatio.SizeTo(0, 0);
}
if (GotInitialReflow()) {
if (intrinsicSizeChanged) {
if (!(mState & IMAGE_SIZECONSTRAINED)) {
Bug 1149357: Properly update responsive images for density changes. r=dholbert Before that we were not notifying the image frame in any way if we ended up not doing a load, and we were instead relying on the reflow the viewport resize caused to get the new density in ComputeSize from the content node (but nowhere else, since that's the bug part 1 fixes). This was generally unsound, since you can stash random media in a sizes= attribute, which don't necessarily needs to cause a reflow. Now we need to notify necessarily because nsImageFrame stores the adjusted intrinsic size. mCurrentDensity could also get out of sync as well, when the selected image density changed, but we ended up returning early because our source hadn't change in the first early exit. This patch moves us to a model where we don't re-trigger loads for density changes if the source doesn't change (unless we pass aAlwaysLoad when we need to, per spec). This matches our previous behavior (without the bugginess of not updating the intrinsic size), and also Chromium, at least. This changes behavior in one case, which is when we don't load the same source node, but we have the same source URL, and the density does change. This could happen with <picture> and two <source>s with same source and different media and sizes. This makes our behavior consistent with the behavior we have when both the source and the density doesn't change. Blink and WebKit do trigger a second image load both when the source changes without changing density and when density changes. I'll file a spec issue, since per: https://html.spec.whatwg.org/#reacting-to-environment-changes We should be triggering the load when the density changes but the source doesn't as well, but no UA does that. I filed https://github.com/whatwg/html/issues/3709 with a little summary of the situation and what I think the behavior should be (which is what this patch implements). That being said, I'll update the impl if the spec people think otherwise :). MozReview-Commit-ID: Eqy16ygHRLo
2018-05-23 12:19:49 +03:00
PresShell()->FrameNeedsReflow(this, nsIPresShell::eStyleChange,
NS_FRAME_IS_DIRTY);
} else {
// We've already gotten the initial reflow, and our size hasn't changed,
// so we're ready to request a decode.
MaybeDecodeForPredictedSize();
}
mPrevImage = nullptr;
2001-10-06 09:08:16 +04:00
}
// Update border+content to account for image change
InvalidateFrame();
2001-04-15 06:13:49 +04:00
}
}
void
nsImageFrame::MaybeDecodeForPredictedSize()
{
// Check that we're ready to decode.
if (!mImage) {
return; // Nothing to do yet.
}
if (mComputedSize.IsEmpty()) {
return; // We won't draw anything, so no point in decoding.
}
if (GetVisibility() != Visibility::APPROXIMATELY_VISIBLE) {
return; // We're not visible, so don't decode.
}
// OK, we're ready to decode. Compute the scale to the screen...
nsIPresShell* presShell = PresContext()->GetPresShell();
LayoutDeviceToScreenScale2D resolutionToScreen(
presShell->GetCumulativeResolution()
* nsLayoutUtils::GetTransformToAncestorScaleExcludingAnimated(this));
// ...and this frame's content box...
const nsPoint offset =
GetOffsetToCrossDoc(nsLayoutUtils::GetReferenceFrame(this));
const nsRect frameContentBox = GetInnerArea() + offset;
// ...and our predicted dest rect...
const int32_t factor = PresContext()->AppUnitsPerDevPixel();
const LayoutDeviceRect destRect =
LayoutDeviceRect::FromAppUnits(PredictedDestRect(frameContentBox), factor);
// ...and use them to compute our predicted size in screen pixels.
const ScreenSize predictedScreenSize = destRect.Size() * resolutionToScreen;
const ScreenIntSize predictedScreenIntSize = RoundedToInt(predictedScreenSize);
if (predictedScreenIntSize.IsEmpty()) {
return;
}
// Determine the optimal image size to use.
uint32_t flags = imgIContainer::FLAG_HIGH_QUALITY_SCALING
| imgIContainer::FLAG_ASYNC_NOTIFY;
SamplingFilter samplingFilter =
nsLayoutUtils::GetSamplingFilterForFrame(this);
gfxSize gfxPredictedScreenSize = gfxSize(predictedScreenIntSize.width,
predictedScreenIntSize.height);
nsIntSize predictedImageSize =
mImage->OptimalImageSizeForDest(gfxPredictedScreenSize,
imgIContainer::FRAME_CURRENT,
samplingFilter, flags);
// Request a decode.
mImage->RequestDecodeForSize(predictedImageSize, flags);
}
nsRect
nsImageFrame::PredictedDestRect(const nsRect& aFrameContentBox)
{
// Note: To get the "dest rect", we have to provide the "constraint rect"
// (which is the content-box, with the effects of fragmentation undone).
nsRect constraintRect(aFrameContentBox.TopLeft(), mComputedSize);
constraintRect.y -= GetContinuationOffset();
return nsLayoutUtils::ComputeObjectDestRect(constraintRect,
mIntrinsicSize,
mIntrinsicRatio,
StylePosition());
}
1998-09-09 02:34:40 +04:00
void
nsImageFrame::EnsureIntrinsicSizeAndRatio()
1998-09-09 02:34:40 +04:00
{
// If mIntrinsicSize.width and height are 0, then we need to update from the
// image container.
if (mIntrinsicSize.width.GetUnit() != eStyleUnit_Coord ||
mIntrinsicSize.width.GetCoordValue() != 0 ||
mIntrinsicSize.height.GetUnit() != eStyleUnit_Coord ||
mIntrinsicSize.height.GetCoordValue() != 0) {
return;
}
if (mImage) {
UpdateIntrinsicSize(mImage);
UpdateIntrinsicRatio(mImage);
return;
}
// NOTE(emilio, https://github.com/w3c/csswg-drafts/issues/2832): WebKit
// and Blink behave differently here for content: url(..), for now adapt to
// Blink's behavior.
const bool mayDisplayBrokenIcon = IsForNonGeneratedImageElement();
if (!mayDisplayBrokenIcon) {
return;
}
// image request is null or image size not known, probably an
// invalid image specified
bool imageInvalid = false;
// check for broken images. valid null images (eg. img src="") are
// not considered broken because they have no image requests
if (nsCOMPtr<imgIRequest> currentRequest = GetCurrentRequest()) {
uint32_t imageStatus;
imageInvalid =
NS_SUCCEEDED(currentRequest->GetImageStatus(&imageStatus)) &&
(imageStatus & imgIRequest::STATUS_ERROR);
} else {
MOZ_ASSERT(mKind == Kind::ImageElement);
nsCOMPtr<nsIImageLoadingContent> loader = do_QueryInterface(mContent);
MOZ_ASSERT(loader);
// check if images are user-disabled (or blocked for other reasons)
int16_t imageBlockingStatus;
loader->GetImageBlockingStatus(&imageBlockingStatus);
imageInvalid = imageBlockingStatus != nsIContentPolicy::ACCEPT;
}
// invalid image specified. make the image big enough for the "broken" icon
if (imageInvalid) {
nscoord edgeLengthToUse =
nsPresContext::CSSPixelsToAppUnits(
ICON_SIZE + (2 * (ICON_PADDING + ALT_BORDER_WIDTH)));
mIntrinsicSize.width.SetCoordValue(edgeLengthToUse);
mIntrinsicSize.height.SetCoordValue(edgeLengthToUse);
mIntrinsicRatio.SizeTo(1, 1);
2001-10-06 09:08:16 +04:00
}
}
2001-10-06 09:08:16 +04:00
/* virtual */
LogicalSize
nsImageFrame::ComputeSize(gfxContext *aRenderingContext,
WritingMode aWM,
const LogicalSize& aCBSize,
nscoord aAvailableISize,
const LogicalSize& aMargin,
const LogicalSize& aBorder,
const LogicalSize& aPadding,
ComputeSizeFlags aFlags)
{
EnsureIntrinsicSizeAndRatio();
return ComputeSizeWithIntrinsicDimensions(aRenderingContext, aWM,
mIntrinsicSize, mIntrinsicRatio,
aCBSize, aMargin, aBorder, aPadding,
aFlags);
1998-09-09 02:34:40 +04:00
}
// XXXdholbert This function's clients should probably just be calling
// GetContentRectRelativeToSelf() directly.
nsRect
nsImageFrame::GetInnerArea() const
{
return GetContentRectRelativeToSelf();
}
Element*
nsImageFrame::GetMapElement() const
{
nsAutoString usemap;
if (mContent->AsElement()->GetAttr(kNameSpaceID_None,
nsGkAtoms::usemap,
usemap)) {
return mContent->OwnerDoc()->FindImageMap(usemap);
}
return nullptr;
}
// get the offset into the content area of the image where aImg starts if it is a continuation.
nscoord
nsImageFrame::GetContinuationOffset() const
{
nscoord offset = 0;
for (nsIFrame *f = GetPrevInFlow(); f; f = f->GetPrevInFlow()) {
offset += f->GetContentRect().height;
}
NS_ASSERTION(offset >= 0, "bogus GetContentRect");
return offset;
}
/* virtual */ nscoord
nsImageFrame::GetMinISize(gfxContext *aRenderingContext)
{
// XXX The caller doesn't account for constraints of the height,
// min-height, and max-height properties.
DebugOnly<nscoord> result;
DISPLAY_MIN_WIDTH(this, result);
EnsureIntrinsicSizeAndRatio();
return mIntrinsicSize.width.GetUnit() == eStyleUnit_Coord ?
mIntrinsicSize.width.GetCoordValue() : 0;
}
/* virtual */ nscoord
nsImageFrame::GetPrefISize(gfxContext *aRenderingContext)
{
// XXX The caller doesn't account for constraints of the height,
// min-height, and max-height properties.
DebugOnly<nscoord> result;
DISPLAY_PREF_WIDTH(this, result);
EnsureIntrinsicSizeAndRatio();
// convert from normal twips to scaled twips (printing...)
return mIntrinsicSize.width.GetUnit() == eStyleUnit_Coord ?
mIntrinsicSize.width.GetCoordValue() : 0;
}
/* virtual */ IntrinsicSize
nsImageFrame::GetIntrinsicSize()
{
return mIntrinsicSize;
}
/* virtual */ nsSize
nsImageFrame::GetIntrinsicRatio()
{
return mIntrinsicRatio;
}
void
nsImageFrame::Reflow(nsPresContext* aPresContext,
ReflowOutput& aMetrics,
const ReflowInput& aReflowInput,
nsReflowStatus& aStatus)
{
MarkInReflow();
DO_GLOBAL_REFLOW_COUNT("nsImageFrame");
DISPLAY_REFLOW(aPresContext, this, aReflowInput, aMetrics, aStatus);
MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!");
NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
("enter nsImageFrame::Reflow: availSize=%d,%d",
aReflowInput.AvailableWidth(), aReflowInput.AvailableHeight()));
MOZ_ASSERT(mState & NS_FRAME_IN_REFLOW, "frame is not in reflow");
// see if we have a frozen size (i.e. a fixed width and height)
if (HaveFixedSize(aReflowInput)) {
AddStateBits(IMAGE_SIZECONSTRAINED);
} else {
RemoveStateBits(IMAGE_SIZECONSTRAINED);
}
mComputedSize =
nsSize(aReflowInput.ComputedWidth(), aReflowInput.ComputedHeight());
aMetrics.Width() = mComputedSize.width;
aMetrics.Height() = mComputedSize.height;
// add borders and padding
aMetrics.Width() += aReflowInput.ComputedPhysicalBorderPadding().LeftRight();
aMetrics.Height() += aReflowInput.ComputedPhysicalBorderPadding().TopBottom();
if (GetPrevInFlow()) {
aMetrics.Width() = GetPrevInFlow()->GetSize().width;
nscoord y = GetContinuationOffset();
aMetrics.Height() -= y + aReflowInput.ComputedPhysicalBorderPadding().top;
aMetrics.Height() = std::max(0, aMetrics.Height());
}
// we have to split images if we are:
// in Paginated mode, we need to have a constrained height, and have a height larger than our available height
uint32_t loadStatus = imgIRequest::STATUS_NONE;
if (nsCOMPtr<imgIRequest> currentRequest = GetCurrentRequest()) {
currentRequest->GetImageStatus(&loadStatus);
}
if (aPresContext->IsPaginated() &&
((loadStatus & imgIRequest::STATUS_SIZE_AVAILABLE) || (mState & IMAGE_SIZECONSTRAINED)) &&
NS_UNCONSTRAINEDSIZE != aReflowInput.AvailableHeight() &&
aMetrics.Height() > aReflowInput.AvailableHeight()) {
// our desired height was greater than 0, so to avoid infinite
// splitting, use 1 pixel as the min
aMetrics.Height() = std::max(nsPresContext::CSSPixelsToAppUnits(1), aReflowInput.AvailableHeight());
aStatus.SetIncomplete();
}
aMetrics.SetOverflowAreasToDesiredBounds();
EventStates contentState = mContent->AsElement()->State();
bool imageOK = IMAGE_OK(contentState, true);
// Determine if the size is available
bool haveSize = false;
if (loadStatus & imgIRequest::STATUS_SIZE_AVAILABLE) {
haveSize = true;
}
if (!imageOK || !haveSize) {
nsRect altFeedbackSize(0, 0,
nsPresContext::CSSPixelsToAppUnits(ICON_SIZE+2*(ICON_PADDING+ALT_BORDER_WIDTH)),
nsPresContext::CSSPixelsToAppUnits(ICON_SIZE+2*(ICON_PADDING+ALT_BORDER_WIDTH)));
// We include the altFeedbackSize in our visual overflow, but not in our
// scrollable overflow, since it doesn't really need to be scrolled to
// outside the image.
Bug 895322 - Part 1: Replace the usages of MOZ_STATIC_ASSERT with C++11 static_assert; r=Waldo This patch was mostly generated by running the following scripts on the codebase, with some manual changes made afterwards: # static_assert.sh #!/bin/bash # Command to convert an NSPR integer type to the equivalent standard integer type function convert() { echo "Converting $1 to $2..." find . ! -wholename "*nsprpub*" \ ! -wholename "*security/nss*" \ ! -wholename "*/.hg*" \ ! -wholename "obj-ff-dbg*" \ ! -name nsXPCOMCID.h \ ! -name prtypes.h \ -type f \ \( -iname "*.cpp" \ -o -iname "*.h" \ -o -iname "*.cc" \ -o -iname "*.mm" \) | \ xargs -n 1 `dirname $0`/assert_replacer.py #sed -i -e "s/\b$1\b/$2/g" } convert MOZ_STATIC_ASSERT static_assert hg rev --no-backup mfbt/Assertions.h \ media/webrtc/signaling/src/sipcc/core/includes/ccapi.h \ modules/libmar/src/mar_private.h \ modules/libmar/src/mar.h # assert_replacer.py #!/usr/bin/python import sys import re pattern = re.compile(r"\bMOZ_STATIC_ASSERT\b") def replaceInPlace(fname): print fname f = open(fname, "rw+") lines = f.readlines() for i in range(0, len(lines)): while True: index = re.search(pattern, lines[i]) if index != None: index = index.start() lines[i] = lines[i][0:index] + "static_assert" + lines[i][index+len("MOZ_STATIC_ASSERT"):] for j in range(i + 1, len(lines)): if lines[j].find(" ", index) == index: lines[j] = lines[j][0:index] + lines[j][index+4:] else: break else: break f.seek(0, 0) f.truncate() f.write("".join(lines)) f.close() argc = len(sys.argv) for i in range(1, argc): replaceInPlace(sys.argv[i]) --HG-- extra : rebase_source : 4b4a4047d82f2c205b9fad8d56dfc3f1afc0b045
2013-07-18 21:59:53 +04:00
static_assert(eOverflowType_LENGTH == 2, "Unknown overflow types?");
nsRect& visualOverflow = aMetrics.VisualOverflow();
visualOverflow.UnionRect(visualOverflow, altFeedbackSize);
} else {
// We've just reflowed and we should have an accurate size, so we're ready
// to request a decode.
MaybeDecodeForPredictedSize();
}
FinishAndStoreOverflow(&aMetrics, aReflowInput.mStyleDisplay);
if ((GetStateBits() & NS_FRAME_FIRST_REFLOW) && !mReflowCallbackPosted) {
nsIPresShell* shell = PresShell();
mReflowCallbackPosted = true;
shell->PostReflowCallback(this);
}
NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
("exit nsImageFrame::Reflow: size=%d,%d",
aMetrics.Width(), aMetrics.Height()));
NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aMetrics);
}
bool
nsImageFrame::ReflowFinished()
{
mReflowCallbackPosted = false;
// XXX(seth): We don't need this. The purpose of updating visibility
// synchronously is to ensure that animated images start animating
// immediately. In the short term, however,
// nsImageLoadingContent::OnUnlockedDraw() is enough to ensure that
// animations start as soon as the image is painted for the first time, and in
// the long term we want to update visibility information from the display
// list whenever we paint, so we don't actually need to do this. However, to
// avoid behavior changes during the transition from the old image visibility
// code, we'll leave it in for now.
UpdateVisibilitySynchronously();
return false;
}
void
nsImageFrame::ReflowCallbackCanceled()
{
mReflowCallbackPosted = false;
}
1998-09-09 02:34:40 +04:00
// Computes the width of the specified string. aMaxWidth specifies the maximum
// width available. Once this limit is reached no more characters are measured.
// The number of characters that fit within the maximum width are returned in
// aMaxFit. NOTE: it is assumed that the fontmetrics have already been selected
// into the rendering context before this is called (for performance). MMP
nscoord
nsImageFrame::MeasureString(const char16_t* aString,
int32_t aLength,
nscoord aMaxWidth,
uint32_t& aMaxFit,
gfxContext& aContext,
nsFontMetrics& aFontMetrics)
1998-09-09 02:34:40 +04:00
{
nscoord totalWidth = 0;
aFontMetrics.SetTextRunRTL(false);
nscoord spaceWidth = aFontMetrics.SpaceWidth();
1998-09-09 02:34:40 +04:00
aMaxFit = 0;
while (aLength > 0) {
// Find the next place we can line break
uint32_t len = aLength;
bool trailingSpace = false;
for (int32_t i = 0; i < aLength; i++) {
if (dom::IsSpaceCharacter(aString[i]) && (i > 0)) {
1998-09-09 02:34:40 +04:00
len = i; // don't include the space when measuring
trailingSpace = true;
1998-09-09 02:34:40 +04:00
break;
}
}
1998-09-09 02:34:40 +04:00
// Measure this chunk of text, and see if it fits
nscoord width =
nsLayoutUtils::AppUnitWidthOfStringBidi(aString, len, this, aFontMetrics,
aContext);
bool fits = (totalWidth + width) <= aMaxWidth;
1998-09-09 02:34:40 +04:00
// If it fits on the line, or it's the first word we've processed then
// include it
if (fits || (0 == totalWidth)) {
// New piece fits
totalWidth += width;
// If there's a trailing space then see if it fits as well
if (trailingSpace) {
if ((totalWidth + spaceWidth) <= aMaxWidth) {
totalWidth += spaceWidth;
} else {
// Space won't fit. Leave it at the end but don't include it in
// the width
fits = false;
1998-09-09 02:34:40 +04:00
}
len++;
}
aMaxFit += len;
aString += len;
aLength -= len;
}
if (!fits) {
break;
}
}
return totalWidth;
1998-09-09 02:34:40 +04:00
}
// Formats the alt-text to fit within the specified rectangle. Breaks lines
// between words if a word would extend past the edge of the rectangle
void
nsImageFrame::DisplayAltText(nsPresContext* aPresContext,
gfxContext& aRenderingContext,
const nsString& aAltText,
const nsRect& aRect)
1998-09-09 02:34:40 +04:00
{
// Set font and color
aRenderingContext.SetColor(Color::FromABGR(StyleColor()->mColor));
RefPtr<nsFontMetrics> fm =
nsLayoutUtils::GetInflatedFontMetricsForFrame(this);
1998-09-09 02:34:40 +04:00
// Format the text to display within the formatting rect
nscoord maxAscent = fm->MaxAscent();
nscoord maxDescent = fm->MaxDescent();
nscoord lineHeight = fm->MaxHeight(); // line-relative, so an x-coordinate
// length if writing mode is vertical
WritingMode wm = GetWritingMode();
bool isVertical = wm.IsVertical();
fm->SetVertical(isVertical);
fm->SetTextOrientation(StyleVisibility()->mTextOrientation);
1998-09-09 02:34:40 +04:00
// XXX It would be nice if there was a way to have the font metrics tell
// use where to break the text given a maximum width. At a minimum we need
// to be able to get the break character...
const char16_t* str = aAltText.get();
int32_t strLen = aAltText.Length();
nsPoint pt = wm.IsVerticalRL() ? aRect.TopRight() - nsPoint(lineHeight, 0)
: aRect.TopLeft();
nscoord iSize = isVertical ? aRect.height : aRect.width;
if (!aPresContext->BidiEnabled() && HasRTLChars(aAltText)) {
aPresContext->SetBidiEnabled();
}
// Always show the first line, even if we have to clip it below
bool firstLine = true;
while (strLen > 0) {
if (!firstLine) {
// If we've run out of space, break out of the loop
if ((!isVertical && (pt.y + maxDescent) >= aRect.YMost()) ||
(wm.IsVerticalRL() && (pt.x + maxDescent < aRect.x)) ||
(wm.IsVerticalLR() && (pt.x + maxDescent >= aRect.XMost()))) {
break;
}
}
1998-09-09 02:34:40 +04:00
// Determine how much of the text to display on this line
uint32_t maxFit; // number of characters that fit
nscoord strWidth = MeasureString(str, strLen, iSize, maxFit,
aRenderingContext, *fm);
1998-09-09 02:34:40 +04:00
// Display the text
nsresult rv = NS_ERROR_FAILURE;
if (aPresContext->BidiEnabled()) {
nsBidiDirection dir;
nscoord x, y;
if (isVertical) {
x = pt.x + maxDescent;
if (wm.IsBidiLTR()) {
y = aRect.y;
dir = NSBIDI_LTR;
} else {
y = aRect.YMost() - strWidth;
dir = NSBIDI_RTL;
}
} else {
y = pt.y + maxAscent;
if (wm.IsBidiLTR()) {
x = aRect.x;
dir = NSBIDI_LTR;
} else {
x = aRect.XMost() - strWidth;
dir = NSBIDI_RTL;
}
}
rv = nsBidiPresUtils::RenderText(str, maxFit, dir,
aPresContext, aRenderingContext,
aRenderingContext.GetDrawTarget(),
*fm, x, y);
}
if (NS_FAILED(rv)) {
nsLayoutUtils::DrawUniDirString(str, maxFit,
isVertical
? nsPoint(pt.x + maxDescent, pt.y)
: nsPoint(pt.x, pt.y + maxAscent),
*fm, aRenderingContext);
}
1998-09-09 02:34:40 +04:00
// Move to the next line
str += maxFit;
strLen -= maxFit;
if (wm.IsVerticalRL()) {
pt.x -= lineHeight;
} else if (wm.IsVerticalLR()) {
pt.x += lineHeight;
} else {
pt.y += lineHeight;
}
firstLine = false;
1998-09-09 02:34:40 +04:00
}
}
struct nsRecessedBorder : public nsStyleBorder {
nsRecessedBorder(nscoord aBorderWidth, nsPresContext* aPresContext)
: nsStyleBorder(aPresContext)
1998-09-09 02:34:40 +04:00
{
NS_FOR_CSS_SIDES(side) {
BorderColorFor(side) = StyleComplexColor::FromColor(NS_RGB(0, 0, 0));
mBorder.Side(side) = aBorderWidth;
// Note: use SetBorderStyle here because we want to affect
// mComputedBorder
SetBorderStyle(side, NS_STYLE_BORDER_STYLE_INSET);
}
1998-09-09 02:34:40 +04:00
}
};
class nsDisplayAltFeedback : public nsDisplayItem {
public:
nsDisplayAltFeedback(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
: nsDisplayItem(aBuilder, aFrame) {}
virtual nsDisplayItemGeometry*
AllocateGeometry(nsDisplayListBuilder* aBuilder) override
{
return new nsDisplayItemGenericImageGeometry(this, aBuilder);
}
virtual void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
const nsDisplayItemGeometry* aGeometry,
nsRegion* aInvalidRegion) const override
{
auto geometry =
static_cast<const nsDisplayItemGenericImageGeometry*>(aGeometry);
if (aBuilder->ShouldSyncDecodeImages() &&
geometry->ShouldInvalidateToSyncDecodeImages()) {
bool snap;
aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap));
}
nsDisplayItem::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion);
}
virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder,
bool* aSnap) const override
{
*aSnap = false;
return mFrame->GetVisualOverflowRectRelativeToSelf() + ToReferenceFrame();
}
virtual void Paint(nsDisplayListBuilder* aBuilder,
gfxContext* aCtx) override
{
// Always sync decode, because these icons are UI, and since they're not
// discardable we'll pay the price of sync decoding at most once.
uint32_t flags = imgIContainer::FLAG_SYNC_DECODE;
nsImageFrame* f = static_cast<nsImageFrame*>(mFrame);
ImgDrawResult result =
f->DisplayAltFeedback(*aCtx,
GetPaintRect(),
ToReferenceFrame(),
flags);
nsDisplayItemGenericImageGeometry::UpdateDrawResult(this, result);
}
NS_DISPLAY_DECL_NAME("AltFeedback", TYPE_ALT_FEEDBACK)
};
ImgDrawResult
nsImageFrame::DisplayAltFeedback(gfxContext& aRenderingContext,
const nsRect& aDirtyRect,
nsPoint aPt,
uint32_t aFlags)
{
// We should definitely have a gIconLoad here.
MOZ_ASSERT(gIconLoad, "How did we succeed in Init then?");
// Whether we draw the broken or loading icon.
bool isLoading = IMAGE_OK(GetContent()->AsElement()->State(), true);
// Calculate the inner area
nsRect inner = GetInnerArea() + aPt;
// Display a recessed one pixel border
nscoord borderEdgeWidth = nsPresContext::CSSPixelsToAppUnits(ALT_BORDER_WIDTH);
// if inner area is empty, then make it big enough for at least the icon
if (inner.IsEmpty()){
inner.SizeTo(2*(nsPresContext::CSSPixelsToAppUnits(ICON_SIZE+ICON_PADDING+ALT_BORDER_WIDTH)),
2*(nsPresContext::CSSPixelsToAppUnits(ICON_SIZE+ICON_PADDING+ALT_BORDER_WIDTH)));
}
// Make sure we have enough room to actually render the border within
// our frame bounds
if ((inner.width < 2 * borderEdgeWidth) || (inner.height < 2 * borderEdgeWidth)) {
return ImgDrawResult::SUCCESS;
}
// Paint the border
if (!isLoading || gIconLoad->mPrefShowLoadingPlaceholder) {
nsRecessedBorder recessedBorder(borderEdgeWidth, PresContext());
// Assert that we're not drawing a border-image here; if we were, we
// couldn't ignore the ImgDrawResult that PaintBorderWithStyleBorder returns.
MOZ_ASSERT(recessedBorder.mBorderImageSource.GetType() == eStyleImageType_Null);
Unused <<
nsCSSRendering::PaintBorderWithStyleBorder(PresContext(), aRenderingContext,
this, inner, inner,
recessedBorder, mComputedStyle,
PaintBorderFlags::SYNC_DECODE_IMAGES);
}
// Adjust the inner rect to account for the one pixel recessed border,
// and a six pixel padding on each edge
inner.Deflate(nsPresContext::CSSPixelsToAppUnits(ICON_PADDING+ALT_BORDER_WIDTH),
nsPresContext::CSSPixelsToAppUnits(ICON_PADDING+ALT_BORDER_WIDTH));
if (inner.IsEmpty()) {
return ImgDrawResult::SUCCESS;
}
DrawTarget* drawTarget = aRenderingContext.GetDrawTarget();
// Clip so we don't render outside the inner rect
aRenderingContext.Save();
aRenderingContext.Clip(
NSRectToSnappedRect(inner, PresContext()->AppUnitsPerDevPixel(), *drawTarget));
ImgDrawResult result = ImgDrawResult::NOT_READY;
// Check if we should display image placeholders
if (!gIconLoad->mPrefShowPlaceholders ||
(isLoading && !gIconLoad->mPrefShowLoadingPlaceholder)) {
result = ImgDrawResult::SUCCESS;
} else {
nscoord size = nsPresContext::CSSPixelsToAppUnits(ICON_SIZE);
imgIRequest* request = isLoading
? nsImageFrame::gIconLoad->mLoadingImage
: nsImageFrame::gIconLoad->mBrokenImage;
// If we weren't previously displaying an icon, register ourselves
// as an observer for load and animation updates and flag that we're
// doing so now.
if (request && !mDisplayingIcon) {
gIconLoad->AddIconObserver(this);
mDisplayingIcon = true;
}
WritingMode wm = GetWritingMode();
bool flushRight =
(!wm.IsVertical() && !wm.IsBidiLTR()) || wm.IsVerticalRL();
// If the icon in question is loaded, draw it.
uint32_t imageStatus = 0;
if (request)
request->GetImageStatus(&imageStatus);
if (imageStatus & imgIRequest::STATUS_LOAD_COMPLETE &&
!(imageStatus & imgIRequest::STATUS_ERROR)) {
nsCOMPtr<imgIContainer> imgCon;
request->GetImage(getter_AddRefs(imgCon));
MOZ_ASSERT(imgCon, "Load complete, but no image container?");
nsRect dest(flushRight ? inner.XMost() - size : inner.x,
inner.y, size, size);
result = nsLayoutUtils::DrawSingleImage(aRenderingContext, PresContext(), imgCon,
nsLayoutUtils::GetSamplingFilterForFrame(this), dest, aDirtyRect,
/* no SVGImageContext */ Nothing(), aFlags);
}
// If we could not draw the icon, just draw some graffiti in the mean time.
if (result == ImgDrawResult::NOT_READY) {
ColorPattern color(ToDeviceColor(Color(1.f, 0.f, 0.f, 1.f)));
nscoord iconXPos = flushRight ? inner.XMost() - size : inner.x;
// stroked rect:
nsRect rect(iconXPos, inner.y, size, size);
Rect devPxRect =
ToRect(nsLayoutUtils::RectToGfxRect(rect, PresContext()->AppUnitsPerDevPixel()));
drawTarget->StrokeRect(devPxRect, color);
// filled circle in bottom right quadrant of stroked rect:
nscoord twoPX = nsPresContext::CSSPixelsToAppUnits(2);
rect = nsRect(iconXPos + size/2, inner.y + size/2,
size/2 - twoPX, size/2 - twoPX);
devPxRect =
ToRect(nsLayoutUtils::RectToGfxRect(rect, PresContext()->AppUnitsPerDevPixel()));
Bug 1207245 - part 6 - rename nsRefPtr<T> to RefPtr<T>; r=ehsan; a=Tomcat The bulk of this commit was generated with a script, executed at the top level of a typical source code checkout. The only non-machine-generated part was modifying MFBT's moz.build to reflect the new naming. CLOSED TREE makes big refactorings like this a piece of cake. # The main substitution. find . -name '*.cpp' -o -name '*.cc' -o -name '*.h' -o -name '*.mm' -o -name '*.idl'| \ xargs perl -p -i -e ' s/nsRefPtr\.h/RefPtr\.h/g; # handle includes s/nsRefPtr ?</RefPtr</g; # handle declarations and variables ' # Handle a special friend declaration in gfx/layers/AtomicRefCountedWithFinalize.h. perl -p -i -e 's/::nsRefPtr;/::RefPtr;/' gfx/layers/AtomicRefCountedWithFinalize.h # Handle nsRefPtr.h itself, a couple places that define constructors # from nsRefPtr, and code generators specially. We do this here, rather # than indiscriminantly s/nsRefPtr/RefPtr/, because that would rename # things like nsRefPtrHashtable. perl -p -i -e 's/nsRefPtr/RefPtr/g' \ mfbt/nsRefPtr.h \ xpcom/glue/nsCOMPtr.h \ xpcom/base/OwningNonNull.h \ ipc/ipdl/ipdl/lower.py \ ipc/ipdl/ipdl/builtin.py \ dom/bindings/Codegen.py \ python/lldbutils/lldbutils/utils.py # In our indiscriminate substitution above, we renamed # nsRefPtrGetterAddRefs, the class behind getter_AddRefs. Fix that up. find . -name '*.cpp' -o -name '*.h' -o -name '*.idl' | \ xargs perl -p -i -e 's/nsRefPtrGetterAddRefs/RefPtrGetterAddRefs/g' if [ -d .git ]; then git mv mfbt/nsRefPtr.h mfbt/RefPtr.h else hg mv mfbt/nsRefPtr.h mfbt/RefPtr.h fi --HG-- rename : mfbt/nsRefPtr.h => mfbt/RefPtr.h
2015-10-18 08:24:48 +03:00
RefPtr<PathBuilder> builder = drawTarget->CreatePathBuilder();
AppendEllipseToPath(builder, devPxRect.Center(), devPxRect.Size());
Bug 1207245 - part 6 - rename nsRefPtr<T> to RefPtr<T>; r=ehsan; a=Tomcat The bulk of this commit was generated with a script, executed at the top level of a typical source code checkout. The only non-machine-generated part was modifying MFBT's moz.build to reflect the new naming. CLOSED TREE makes big refactorings like this a piece of cake. # The main substitution. find . -name '*.cpp' -o -name '*.cc' -o -name '*.h' -o -name '*.mm' -o -name '*.idl'| \ xargs perl -p -i -e ' s/nsRefPtr\.h/RefPtr\.h/g; # handle includes s/nsRefPtr ?</RefPtr</g; # handle declarations and variables ' # Handle a special friend declaration in gfx/layers/AtomicRefCountedWithFinalize.h. perl -p -i -e 's/::nsRefPtr;/::RefPtr;/' gfx/layers/AtomicRefCountedWithFinalize.h # Handle nsRefPtr.h itself, a couple places that define constructors # from nsRefPtr, and code generators specially. We do this here, rather # than indiscriminantly s/nsRefPtr/RefPtr/, because that would rename # things like nsRefPtrHashtable. perl -p -i -e 's/nsRefPtr/RefPtr/g' \ mfbt/nsRefPtr.h \ xpcom/glue/nsCOMPtr.h \ xpcom/base/OwningNonNull.h \ ipc/ipdl/ipdl/lower.py \ ipc/ipdl/ipdl/builtin.py \ dom/bindings/Codegen.py \ python/lldbutils/lldbutils/utils.py # In our indiscriminate substitution above, we renamed # nsRefPtrGetterAddRefs, the class behind getter_AddRefs. Fix that up. find . -name '*.cpp' -o -name '*.h' -o -name '*.idl' | \ xargs perl -p -i -e 's/nsRefPtrGetterAddRefs/RefPtrGetterAddRefs/g' if [ -d .git ]; then git mv mfbt/nsRefPtr.h mfbt/RefPtr.h else hg mv mfbt/nsRefPtr.h mfbt/RefPtr.h fi --HG-- rename : mfbt/nsRefPtr.h => mfbt/RefPtr.h
2015-10-18 08:24:48 +03:00
RefPtr<Path> ellipse = builder->Finish();
drawTarget->Fill(ellipse, color);
}
// Reduce the inner rect by the width of the icon, and leave an
// additional ICON_PADDING pixels for padding
int32_t paddedIconSize =
nsPresContext::CSSPixelsToAppUnits(ICON_SIZE + ICON_PADDING);
if (wm.IsVertical()) {
inner.y += paddedIconSize;
inner.height -= paddedIconSize;
} else {
if (!flushRight) {
inner.x += paddedIconSize;
}
inner.width -= paddedIconSize;
}
}
// If there's still room, display the alt-text
if (!inner.IsEmpty()) {
nsAutoString altText;
nsCSSFrameConstructor::GetAlternateTextFor(mContent->AsElement(),
mContent->NodeInfo()->NameAtom(),
altText);
DisplayAltText(PresContext(), aRenderingContext, altText, inner);
}
aRenderingContext.Restore();
return result;
}
#ifdef DEBUG
static void PaintDebugImageMap(nsIFrame* aFrame, DrawTarget* aDrawTarget,
const nsRect& aDirtyRect, nsPoint aPt)
{
nsImageFrame* f = static_cast<nsImageFrame*>(aFrame);
nsRect inner = f->GetInnerArea() + aPt;
gfxPoint devPixelOffset =
nsLayoutUtils::PointToGfxPoint(inner.TopLeft(),
aFrame->PresContext()->AppUnitsPerDevPixel());
AutoRestoreTransform autoRestoreTransform(aDrawTarget);
aDrawTarget->SetTransform(
aDrawTarget->GetTransform().PreTranslate(ToPoint(devPixelOffset)));
f->GetImageMap()->Draw(aFrame, *aDrawTarget,
ColorPattern(ToDeviceColor(Color(0.f, 0.f, 0.f, 1.f))));
}
#endif
2001-10-06 09:08:16 +04:00
void
nsDisplayImage::Paint(nsDisplayListBuilder* aBuilder,
gfxContext* aCtx)
{
uint32_t flags = imgIContainer::FLAG_NONE;
if (aBuilder->ShouldSyncDecodeImages()) {
flags |= imgIContainer::FLAG_SYNC_DECODE;
}
if (aBuilder->IsPaintingToWindow()) {
flags |= imgIContainer::FLAG_HIGH_QUALITY_SCALING;
}
ImgDrawResult result = static_cast<nsImageFrame*>(mFrame)->
PaintImage(*aCtx, ToReferenceFrame(), GetPaintRect(), mImage, flags);
if (result == ImgDrawResult::NOT_READY ||
result == ImgDrawResult::INCOMPLETE ||
result == ImgDrawResult::TEMPORARY_ERROR) {
// If the current image failed to paint because it's still loading or
// decoding, try painting the previous image.
if (mPrevImage) {
result = static_cast<nsImageFrame*>(mFrame)->
PaintImage(*aCtx, ToReferenceFrame(), GetPaintRect(), mPrevImage, flags);
}
}
nsDisplayItemGenericImageGeometry::UpdateDrawResult(this, result);
}
nsDisplayItemGeometry*
nsDisplayImage::AllocateGeometry(nsDisplayListBuilder* aBuilder)
{
return new nsDisplayItemGenericImageGeometry(this, aBuilder);
}
void
nsDisplayImage::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
const nsDisplayItemGeometry* aGeometry,
nsRegion* aInvalidRegion) const
{
auto geometry =
static_cast<const nsDisplayItemGenericImageGeometry*>(aGeometry);
if (aBuilder->ShouldSyncDecodeImages() &&
geometry->ShouldInvalidateToSyncDecodeImages()) {
bool snap;
aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap));
}
nsDisplayImageContainer::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion);
}
already_AddRefed<imgIContainer>
nsDisplayImage::GetImage()
{
nsCOMPtr<imgIContainer> image = mImage;
return image.forget();
}
nsRect
nsDisplayImage::GetDestRect() const
{
bool snap = true;
const nsRect frameContentBox = GetBounds(&snap);
nsImageFrame* imageFrame = static_cast<nsImageFrame*>(mFrame);
return imageFrame->PredictedDestRect(frameContentBox);
}
LayerState
nsDisplayImage::GetLayerState(nsDisplayListBuilder* aBuilder,
LayerManager* aManager,
const ContainerLayerParameters& aParameters)
{
if (!nsDisplayItem::ForceActiveLayers()) {
bool animated = false;
if (!nsLayoutUtils::AnimatedImageLayersEnabled() ||
mImage->GetType() != imgIContainer::TYPE_RASTER ||
NS_FAILED(mImage->GetAnimated(&animated)) ||
!animated) {
if (!aManager->IsCompositingCheap() ||
!nsLayoutUtils::GPUImageScalingEnabled()) {
return LAYER_NONE;
}
}
if (!animated) {
int32_t imageWidth;
int32_t imageHeight;
mImage->GetWidth(&imageWidth);
mImage->GetHeight(&imageHeight);
NS_ASSERTION(imageWidth != 0 && imageHeight != 0, "Invalid image size!");
const int32_t factor = mFrame->PresContext()->AppUnitsPerDevPixel();
const LayoutDeviceRect destRect =
LayoutDeviceRect::FromAppUnits(GetDestRect(), factor);
const LayerRect destLayerRect = destRect * aParameters.Scale();
// Calculate the scaling factor for the frame.
const gfxSize scale = gfxSize(destLayerRect.width / imageWidth,
destLayerRect.height / imageHeight);
// If we are not scaling at all, no point in separating this into a layer.
if (scale.width == 1.0f && scale.height == 1.0f) {
return LAYER_NONE;
}
// If the target size is pretty small, no point in using a layer.
if (destLayerRect.width * destLayerRect.height < 64 * 64) {
return LAYER_NONE;
}
}
}
if (!CanOptimizeToImageLayer(aManager, aBuilder)) {
return LAYER_NONE;
}
// Image layer doesn't support draw focus ring for image map.
nsImageFrame* f = static_cast<nsImageFrame*>(mFrame);
if (f->HasImageMap()) {
return LAYER_NONE;
}
return LAYER_ACTIVE;
}
/* virtual */ nsRegion
nsDisplayImage::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
bool* aSnap) const
{
*aSnap = false;
if (mImage && mImage->WillDrawOpaqueNow()) {
const nsRect frameContentBox = GetBounds(aSnap);
return GetDestRect().Intersect(frameContentBox);
}
return nsRegion();
}
already_AddRefed<Layer>
nsDisplayImage::BuildLayer(nsDisplayListBuilder* aBuilder,
LayerManager* aManager,
const ContainerLayerParameters& aParameters)
{
uint32_t flags = imgIContainer::FLAG_ASYNC_NOTIFY;
if (aBuilder->ShouldSyncDecodeImages()) {
flags |= imgIContainer::FLAG_SYNC_DECODE;
}
Bug 1207245 - part 6 - rename nsRefPtr<T> to RefPtr<T>; r=ehsan; a=Tomcat The bulk of this commit was generated with a script, executed at the top level of a typical source code checkout. The only non-machine-generated part was modifying MFBT's moz.build to reflect the new naming. CLOSED TREE makes big refactorings like this a piece of cake. # The main substitution. find . -name '*.cpp' -o -name '*.cc' -o -name '*.h' -o -name '*.mm' -o -name '*.idl'| \ xargs perl -p -i -e ' s/nsRefPtr\.h/RefPtr\.h/g; # handle includes s/nsRefPtr ?</RefPtr</g; # handle declarations and variables ' # Handle a special friend declaration in gfx/layers/AtomicRefCountedWithFinalize.h. perl -p -i -e 's/::nsRefPtr;/::RefPtr;/' gfx/layers/AtomicRefCountedWithFinalize.h # Handle nsRefPtr.h itself, a couple places that define constructors # from nsRefPtr, and code generators specially. We do this here, rather # than indiscriminantly s/nsRefPtr/RefPtr/, because that would rename # things like nsRefPtrHashtable. perl -p -i -e 's/nsRefPtr/RefPtr/g' \ mfbt/nsRefPtr.h \ xpcom/glue/nsCOMPtr.h \ xpcom/base/OwningNonNull.h \ ipc/ipdl/ipdl/lower.py \ ipc/ipdl/ipdl/builtin.py \ dom/bindings/Codegen.py \ python/lldbutils/lldbutils/utils.py # In our indiscriminate substitution above, we renamed # nsRefPtrGetterAddRefs, the class behind getter_AddRefs. Fix that up. find . -name '*.cpp' -o -name '*.h' -o -name '*.idl' | \ xargs perl -p -i -e 's/nsRefPtrGetterAddRefs/RefPtrGetterAddRefs/g' if [ -d .git ]; then git mv mfbt/nsRefPtr.h mfbt/RefPtr.h else hg mv mfbt/nsRefPtr.h mfbt/RefPtr.h fi --HG-- rename : mfbt/nsRefPtr.h => mfbt/RefPtr.h
2015-10-18 08:24:48 +03:00
RefPtr<ImageContainer> container =
mImage->GetImageContainer(aManager, flags);
if (!container || !container->HasCurrentImage()) {
return nullptr;
}
Bug 1207245 - part 6 - rename nsRefPtr<T> to RefPtr<T>; r=ehsan; a=Tomcat The bulk of this commit was generated with a script, executed at the top level of a typical source code checkout. The only non-machine-generated part was modifying MFBT's moz.build to reflect the new naming. CLOSED TREE makes big refactorings like this a piece of cake. # The main substitution. find . -name '*.cpp' -o -name '*.cc' -o -name '*.h' -o -name '*.mm' -o -name '*.idl'| \ xargs perl -p -i -e ' s/nsRefPtr\.h/RefPtr\.h/g; # handle includes s/nsRefPtr ?</RefPtr</g; # handle declarations and variables ' # Handle a special friend declaration in gfx/layers/AtomicRefCountedWithFinalize.h. perl -p -i -e 's/::nsRefPtr;/::RefPtr;/' gfx/layers/AtomicRefCountedWithFinalize.h # Handle nsRefPtr.h itself, a couple places that define constructors # from nsRefPtr, and code generators specially. We do this here, rather # than indiscriminantly s/nsRefPtr/RefPtr/, because that would rename # things like nsRefPtrHashtable. perl -p -i -e 's/nsRefPtr/RefPtr/g' \ mfbt/nsRefPtr.h \ xpcom/glue/nsCOMPtr.h \ xpcom/base/OwningNonNull.h \ ipc/ipdl/ipdl/lower.py \ ipc/ipdl/ipdl/builtin.py \ dom/bindings/Codegen.py \ python/lldbutils/lldbutils/utils.py # In our indiscriminate substitution above, we renamed # nsRefPtrGetterAddRefs, the class behind getter_AddRefs. Fix that up. find . -name '*.cpp' -o -name '*.h' -o -name '*.idl' | \ xargs perl -p -i -e 's/nsRefPtrGetterAddRefs/RefPtrGetterAddRefs/g' if [ -d .git ]; then git mv mfbt/nsRefPtr.h mfbt/RefPtr.h else hg mv mfbt/nsRefPtr.h mfbt/RefPtr.h fi --HG-- rename : mfbt/nsRefPtr.h => mfbt/RefPtr.h
2015-10-18 08:24:48 +03:00
RefPtr<ImageLayer> layer = static_cast<ImageLayer*>
(aManager->GetLayerBuilder()->GetLeafLayerFor(aBuilder, this));
if (!layer) {
layer = aManager->CreateImageLayer();
if (!layer)
return nullptr;
}
layer->SetContainer(container);
ConfigureLayer(layer, aParameters);
return layer.forget();
}
bool
nsDisplayImage::CreateWebRenderCommands(mozilla::wr::DisplayListBuilder& aBuilder,
mozilla::wr::IpcResourceUpdateQueue& aResources,
const StackingContextHelper& aSc,
WebRenderLayerManager* aManager,
nsDisplayListBuilder* aDisplayListBuilder)
{
if (!mImage) {
return false;
}
if (mFrame->IsImageFrame()) {
// Image layer doesn't support draw focus ring for image map.
nsImageFrame* f = static_cast<nsImageFrame*>(mFrame);
if (f->HasImageMap()) {
return false;
}
}
uint32_t flags = imgIContainer::FLAG_ASYNC_NOTIFY;
if (aDisplayListBuilder->IsPaintingToWindow()) {
flags |= imgIContainer::FLAG_HIGH_QUALITY_SCALING;
}
if (aDisplayListBuilder->ShouldSyncDecodeImages()) {
flags |= imgIContainer::FLAG_SYNC_DECODE;
}
const int32_t factor = mFrame->PresContext()->AppUnitsPerDevPixel();
const LayoutDeviceRect destRect(
LayoutDeviceRect::FromAppUnits(GetDestRect(), factor));
Maybe<SVGImageContext> svgContext;
IntSize decodeSize =
nsLayoutUtils::ComputeImageContainerDrawingParameters(mImage, mFrame, destRect,
aSc, flags, svgContext);
RefPtr<ImageContainer> container =
mImage->GetImageContainerAtSize(aManager, decodeSize, svgContext, flags);
if (!container) {
return false;
}
// If the image container is empty, we don't want to fallback. Any other
// failure will be due to resource constraints and fallback is unlikely to
// help us. Hence we can ignore the return value from PushImage.
aManager->CommandBuilder().PushImage(this, container, aBuilder, aResources, aSc, destRect);
return true;
}
ImgDrawResult
nsImageFrame::PaintImage(gfxContext& aRenderingContext, nsPoint aPt,
const nsRect& aDirtyRect, imgIContainer* aImage,
uint32_t aFlags)
{
DrawTarget* drawTarget = aRenderingContext.GetDrawTarget();
// Render the image into our content area (the area inside
// the borders and padding)
NS_ASSERTION(GetInnerArea().width == mComputedSize.width, "bad width");
// NOTE: We use mComputedSize instead of just GetInnerArea()'s own size here,
// because GetInnerArea() might be smaller if we're fragmented, whereas
// mComputedSize has our full content-box size (which we need for
// ComputeObjectDestRect to work correctly).
nsRect constraintRect(aPt + GetInnerArea().TopLeft(), mComputedSize);
constraintRect.y -= GetContinuationOffset();
nsPoint anchorPoint;
nsRect dest = nsLayoutUtils::ComputeObjectDestRect(constraintRect,
mIntrinsicSize,
mIntrinsicRatio,
StylePosition(),
&anchorPoint);
uint32_t flags = aFlags;
if (mForceSyncDecoding) {
flags |= imgIContainer::FLAG_SYNC_DECODE;
}
Maybe<SVGImageContext> svgContext;
SVGImageContext::MaybeStoreContextPaint(svgContext, this, aImage);
ImgDrawResult result =
nsLayoutUtils::DrawSingleImage(aRenderingContext,
PresContext(), aImage,
nsLayoutUtils::GetSamplingFilterForFrame(this), dest, aDirtyRect,
svgContext, flags, &anchorPoint);
if (nsImageMap* map = GetImageMap()) {
gfxPoint devPixelOffset =
nsLayoutUtils::PointToGfxPoint(dest.TopLeft(),
PresContext()->AppUnitsPerDevPixel());
AutoRestoreTransform autoRestoreTransform(drawTarget);
drawTarget->SetTransform(
drawTarget->GetTransform().PreTranslate(ToPoint(devPixelOffset)));
// solid white stroke:
ColorPattern white(ToDeviceColor(Color(1.f, 1.f, 1.f, 1.f)));
map->Draw(this, *drawTarget, white);
// then dashed black stroke over the top:
ColorPattern black(ToDeviceColor(Color(0.f, 0.f, 0.f, 1.f)));
StrokeOptions strokeOptions;
nsLayoutUtils::InitDashPattern(strokeOptions, NS_STYLE_BORDER_STYLE_DOTTED);
map->Draw(this, *drawTarget, black, strokeOptions);
}
if (result == ImgDrawResult::SUCCESS) {
mPrevImage = aImage;
} else if (result == ImgDrawResult::BAD_IMAGE) {
mPrevImage = nullptr;
}
return result;
}
already_AddRefed<imgIRequest>
nsImageFrame::GetCurrentRequest() const
{
if (mKind == Kind::NonGeneratedContentProperty) {
return do_AddRef(mContentURLRequest);
}
MOZ_ASSERT(!mContentURLRequest);
nsCOMPtr<imgIRequest> request;
nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent);
MOZ_ASSERT(imageLoader);
imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
getter_AddRefs(request));
return request.forget();
}
void
nsImageFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
const nsDisplayListSet& aLists)
{
if (!IsVisibleForPainting(aBuilder))
return;
DisplayBorderBackgroundOutline(aBuilder, aLists);
uint32_t clipFlags =
nsStyleUtil::ObjectPropsMightCauseOverflow(StylePosition()) ?
0 : DisplayListClipState::ASSUME_DRAWING_RESTRICTED_TO_CONTENT_RECT;
DisplayListClipState::AutoClipContainingBlockDescendantsToContentBox
clip(aBuilder, this, clipFlags);
if (mComputedSize.width != 0 && mComputedSize.height != 0) {
EventStates contentState = mContent->AsElement()->State();
bool imageOK = IMAGE_OK(contentState, true);
nsCOMPtr<imgIRequest> currentRequest = GetCurrentRequest();
// XXX(seth): The SizeIsAvailable check here should not be necessary - the
// intention is that a non-null mImage means we have a size, but there is
// currently some code that violates this invariant.
if (!imageOK || !mImage || !SizeIsAvailable(currentRequest)) {
// No image yet, or image load failed. Draw the alt-text and an icon
// indicating the status
aLists.Content()->AppendToTop(
MakeDisplayItem<nsDisplayAltFeedback>(aBuilder, this));
// This image is visible (we are being asked to paint it) but it's not
// decoded yet. And we are not going to ask the image to draw, so this
// may be the only chance to tell it that it should decode.
if (currentRequest) {
uint32_t status = 0;
currentRequest->GetImageStatus(&status);
if (!(status & imgIRequest::STATUS_DECODE_COMPLETE)) {
MaybeDecodeForPredictedSize();
}
// Increase loading priority if the image is ready to be displayed.
if (!(status & imgIRequest::STATUS_LOAD_COMPLETE)){
currentRequest->BoostPriority(imgIRequest::CATEGORY_DISPLAY);
}
}
} else {
aLists.Content()->AppendToTop(
MakeDisplayItem<nsDisplayImage>(aBuilder, this, mImage, mPrevImage));
// If we were previously displaying an icon, we're not anymore
if (mDisplayingIcon) {
gIconLoad->RemoveIconObserver(this);
mDisplayingIcon = false;
}
#ifdef DEBUG
if (GetShowFrameBorders() && GetImageMap()) {
aLists.Outlines()->AppendToTop(
MakeDisplayItem<nsDisplayGeneric>(aBuilder, this, PaintDebugImageMap, "DebugImageMap",
DisplayItemType::TYPE_DEBUG_IMAGE_MAP));
}
#endif
1999-04-10 21:32:33 +04:00
}
1998-09-09 02:34:40 +04:00
}
if (ShouldDisplaySelection()) {
DisplaySelectionOverlay(aBuilder, aLists.Content(),
nsISelectionDisplay::DISPLAY_IMAGES);
}
}
bool
nsImageFrame::ShouldDisplaySelection()
{
int16_t displaySelection = PresShell()->GetSelectionFlags();
if (!(displaySelection & nsISelectionDisplay::DISPLAY_IMAGES))
return false;//no need to check the blue border, we cannot be drawn selected
// If the image is the only selected node, don't draw the selection overlay.
// This can happen when selecting an image in contenteditable context.
if (displaySelection == nsISelectionDisplay::DISPLAY_ALL) {
if (const nsFrameSelection* frameSelection = GetConstFrameSelection()) {
const Selection* selection = frameSelection->GetSelection(SelectionType::eNormal);
if (selection && selection->RangeCount() == 1) {
nsINode* parent = mContent->GetParent();
int32_t thisOffset = parent->ComputeIndexOf(mContent);
nsRange* range = selection->GetRangeAt(0);
if (range->GetStartContainer() == parent &&
range->GetEndContainer() == parent &&
static_cast<int32_t>(range->StartOffset()) == thisOffset &&
static_cast<int32_t>(range->EndOffset()) == thisOffset + 1) {
return false;
}
}
}
}
return true;
}
1999-01-09 03:13:19 +03:00
nsImageMap*
nsImageFrame::GetImageMap()
1998-09-09 02:34:40 +04:00
{
if (!mImageMap) {
if (nsIContent* map = GetMapElement()) {
mImageMap = new nsImageMap();
mImageMap->Init(this, map);
1998-09-09 02:34:40 +04:00
}
}
1999-01-09 03:13:19 +03:00
1998-09-09 02:34:40 +04:00
return mImageMap;
}
bool
nsImageFrame::IsServerImageMap()
1998-09-09 02:34:40 +04:00
{
return mContent->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::ismap);
1998-09-09 02:34:40 +04:00
}
// Translate an point that is relative to our frame
// into a localized pixel coordinate that is relative to the
// content area of this frame (inside the border+padding).
void
nsImageFrame::TranslateEventCoords(const nsPoint& aPoint,
nsIntPoint& aResult)
{
nscoord x = aPoint.x;
nscoord y = aPoint.y;
// Subtract out border and padding here so that the coordinates are
// now relative to the content area of this frame.
nsRect inner = GetInnerArea();
x -= inner.x;
y -= inner.y;
aResult.x = nsPresContext::AppUnitsToIntCSSPixels(x);
aResult.y = nsPresContext::AppUnitsToIntCSSPixels(y);
}
bool
nsImageFrame::GetAnchorHREFTargetAndNode(nsIURI** aHref, nsString& aTarget,
nsIContent** aNode)
{
bool status = false;
aTarget.Truncate();
*aHref = nullptr;
*aNode = nullptr;
// Walk up the content tree, looking for an nsIDOMAnchorElement
for (nsIContent* content = mContent->GetParent();
content; content = content->GetParent()) {
nsCOMPtr<dom::Link> link(do_QueryInterface(content));
if (link) {
nsCOMPtr<nsIURI> href = content->GetHrefURI();
if (href) {
href.forget(aHref);
}
status = (*aHref != nullptr);
RefPtr<HTMLAnchorElement> anchor = HTMLAnchorElement::FromNode(content);
if (anchor) {
anchor->GetTarget(aTarget);
}
NS_ADDREF(*aNode = content);
break;
}
}
return status;
}
nsresult
nsImageFrame::GetContentForEvent(WidgetEvent* aEvent,
nsIContent** aContent)
{
NS_ENSURE_ARG_POINTER(aContent);
nsIFrame* f = nsLayoutUtils::GetNonGeneratedAncestor(this);
if (f != this) {
return f->GetContentForEvent(aEvent, aContent);
}
// XXX We need to make this special check for area element's capturing the
// mouse due to bug 135040. Remove it once that's fixed.
nsIContent* capturingContent =
aEvent->HasMouseEventMessage() ? nsIPresShell::GetCapturingContent() :
nullptr;
if (capturingContent && capturingContent->GetPrimaryFrame() == this) {
*aContent = capturingContent;
NS_IF_ADDREF(*aContent);
return NS_OK;
}
if (nsImageMap* map = GetImageMap()) {
nsIntPoint p;
TranslateEventCoords(
nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, this), p);
nsCOMPtr<nsIContent> area = map->GetArea(p.x, p.y);
if (area) {
area.forget(aContent);
return NS_OK;
}
}
2003-06-29 07:43:05 +04:00
*aContent = GetContent();
NS_IF_ADDREF(*aContent);
return NS_OK;
}
1998-09-09 02:34:40 +04:00
// XXX what should clicks on transparent pixels do?
nsresult
nsImageFrame::HandleEvent(nsPresContext* aPresContext,
WidgetGUIEvent* aEvent,
nsEventStatus* aEventStatus)
1998-09-09 02:34:40 +04:00
{
NS_ENSURE_ARG_POINTER(aEventStatus);
1998-09-09 02:34:40 +04:00
if ((aEvent->mMessage == eMouseClick &&
aEvent->AsMouseEvent()->button == WidgetMouseEvent::eLeftButton) ||
aEvent->mMessage == eMouseMove) {
nsImageMap* map = GetImageMap();
bool isServerMap = IsServerImageMap();
if (map || isServerMap) {
nsIntPoint p;
TranslateEventCoords(
nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, this), p);
bool inside = false;
// Even though client-side image map triggering happens
// through content, we need to make sure we're not inside
// (in case we deal with a case of both client-side and
// sever-side on the same image - it happens!)
if (nullptr != map) {
inside = !!map->GetArea(p.x, p.y);
}
2001-05-23 11:00:37 +04:00
if (!inside && isServerMap) {
// Server side image maps use the href in a containing anchor
// element to provide the basis for the destination url.
nsCOMPtr<nsIURI> uri;
nsAutoString target;
nsCOMPtr<nsIContent> anchorNode;
if (GetAnchorHREFTargetAndNode(getter_AddRefs(uri), target,
getter_AddRefs(anchorNode))) {
// XXX if the mouse is over/clicked in the border/padding area
// we should probably just pretend nothing happened. Nav4
// keeps the x,y coordinates positive as we do; IE doesn't
// bother. Both of them send the click through even when the
// mouse is over the border.
if (p.x < 0) p.x = 0;
if (p.y < 0) p.y = 0;
nsAutoCString spec;
nsresult rv = uri->GetSpec(spec);
NS_ENSURE_SUCCESS(rv, rv);
spec += nsPrintfCString("?%d,%d", p.x, p.y);
rv = NS_MutateURI(uri)
.SetSpec(spec)
.Finalize(uri);
NS_ENSURE_SUCCESS(rv, rv);
bool clicked = false;
if (aEvent->mMessage == eMouseClick && !aEvent->DefaultPrevented()) {
*aEventStatus = nsEventStatus_eConsumeDoDefault;
clicked = true;
}
nsContentUtils::TriggerLink(anchorNode, aPresContext, uri, target,
clicked, /* isTrusted */ true);
1998-09-09 02:34:40 +04:00
}
}
}
}
return nsAtomicContainerFrame::HandleEvent(aPresContext, aEvent, aEventStatus);
1998-09-09 02:34:40 +04:00
}
nsresult
nsImageFrame::GetCursor(const nsPoint& aPoint,
nsIFrame::Cursor& aCursor)
1998-09-09 02:34:40 +04:00
{
if (nsImageMap* map = GetImageMap()) {
nsIntPoint p;
TranslateEventCoords(aPoint, p);
nsCOMPtr<nsIContent> area = map->GetArea(p.x, p.y);
if (area) {
// Use the cursor from the style of the *area* element.
// XXX Using the image as the parent ComputedStyle isn't
// technically correct, but it's probably the right thing to do
// here, since it means that areas on which the cursor isn't
// specified will inherit the style from the image.
RefPtr<ComputedStyle> areaStyle =
PresShell()->StyleSet()->
ResolveStyleFor(area->AsElement(), Style(),
LazyComputeBehavior::Allow);
FillCursorInformationFromStyle(areaStyle->StyleUserInterface(),
aCursor);
if (NS_STYLE_CURSOR_AUTO == aCursor.mCursor) {
aCursor.mCursor = NS_STYLE_CURSOR_DEFAULT;
}
return NS_OK;
1998-09-09 02:34:40 +04:00
}
}
return nsFrame::GetCursor(aPoint, aCursor);
1998-09-09 02:34:40 +04:00
}
nsresult
nsImageFrame::AttributeChanged(int32_t aNameSpaceID,
nsAtom* aAttribute,
int32_t aModType)
1998-09-09 02:34:40 +04:00
{
nsresult rv = nsAtomicContainerFrame::AttributeChanged(aNameSpaceID,
aAttribute, aModType);
if (NS_FAILED(rv)) {
return rv;
}
if (nsGkAtoms::alt == aAttribute)
{
PresShell()->FrameNeedsReflow(this, nsIPresShell::eStyleChange,
NS_FRAME_IS_DIRTY);
}
1998-09-09 02:34:40 +04:00
return NS_OK;
}
void
nsImageFrame::OnVisibilityChange(Visibility aNewVisibility,
const Maybe<OnNonvisible>& aNonvisibleAction)
{
if (mKind == Kind::ImageElement) {
nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent);
imageLoader->OnVisibilityChange(aNewVisibility, aNonvisibleAction);
}
if (aNewVisibility == Visibility::APPROXIMATELY_VISIBLE) {
MaybeDecodeForPredictedSize();
}
nsAtomicContainerFrame::OnVisibilityChange(aNewVisibility, aNonvisibleAction);
}
#ifdef DEBUG_FRAME_DUMP
nsresult
nsImageFrame::GetFrameName(nsAString& aResult) const
{
return MakeFrameName(NS_LITERAL_STRING("ImageFrame"), aResult);
}
void
nsImageFrame::List(FILE* out, const char* aPrefix, uint32_t aFlags) const
{
nsCString str;
ListGeneric(str, aPrefix, aFlags);
// output the img src url
if (nsCOMPtr<imgIRequest> currentRequest = GetCurrentRequest()) {
nsCOMPtr<nsIURI> uri;
currentRequest->GetURI(getter_AddRefs(uri));
nsAutoCString uristr;
uri->GetAsciiSpec(uristr);
str += nsPrintfCString(" [src=%s]", uristr.get());
}
fprintf_stderr(out, "%s\n", str.get());
}
#endif
nsIFrame::LogicalSides
nsImageFrame::GetLogicalSkipSides(const ReflowInput* aReflowInput) const
{
if (MOZ_UNLIKELY(StyleBorder()->mBoxDecorationBreak ==
StyleBoxDecorationBreak::Clone)) {
return LogicalSides();
}
LogicalSides skip;
if (nullptr != GetPrevInFlow()) {
skip |= eLogicalSideBitsBStart;
}
if (nullptr != GetNextInFlow()) {
skip |= eLogicalSideBitsBEnd;
}
return skip;
}
nsresult
nsImageFrame::GetIntrinsicImageSize(nsSize& aSize)
{
if (mIntrinsicSize.width.GetUnit() == eStyleUnit_Coord &&
mIntrinsicSize.height.GetUnit() == eStyleUnit_Coord) {
aSize.SizeTo(mIntrinsicSize.width.GetCoordValue(),
mIntrinsicSize.height.GetCoordValue());
return NS_OK;
}
return NS_ERROR_FAILURE;
}
nsresult
nsImageFrame::LoadIcon(const nsAString& aSpec,
nsPresContext *aPresContext,
imgRequestProxy** aRequest)
{
nsresult rv = NS_OK;
MOZ_ASSERT(!aSpec.IsEmpty(), "What happened??");
if (!sIOService) {
rv = CallGetService(NS_IOSERVICE_CONTRACTID, &sIOService);
NS_ENSURE_SUCCESS(rv, rv);
}
2001-06-06 00:22:52 +04:00
nsCOMPtr<nsIURI> realURI;
SpecToURI(aSpec, sIOService, getter_AddRefs(realURI));
Bug 1207245 - part 6 - rename nsRefPtr<T> to RefPtr<T>; r=ehsan; a=Tomcat The bulk of this commit was generated with a script, executed at the top level of a typical source code checkout. The only non-machine-generated part was modifying MFBT's moz.build to reflect the new naming. CLOSED TREE makes big refactorings like this a piece of cake. # The main substitution. find . -name '*.cpp' -o -name '*.cc' -o -name '*.h' -o -name '*.mm' -o -name '*.idl'| \ xargs perl -p -i -e ' s/nsRefPtr\.h/RefPtr\.h/g; # handle includes s/nsRefPtr ?</RefPtr</g; # handle declarations and variables ' # Handle a special friend declaration in gfx/layers/AtomicRefCountedWithFinalize.h. perl -p -i -e 's/::nsRefPtr;/::RefPtr;/' gfx/layers/AtomicRefCountedWithFinalize.h # Handle nsRefPtr.h itself, a couple places that define constructors # from nsRefPtr, and code generators specially. We do this here, rather # than indiscriminantly s/nsRefPtr/RefPtr/, because that would rename # things like nsRefPtrHashtable. perl -p -i -e 's/nsRefPtr/RefPtr/g' \ mfbt/nsRefPtr.h \ xpcom/glue/nsCOMPtr.h \ xpcom/base/OwningNonNull.h \ ipc/ipdl/ipdl/lower.py \ ipc/ipdl/ipdl/builtin.py \ dom/bindings/Codegen.py \ python/lldbutils/lldbutils/utils.py # In our indiscriminate substitution above, we renamed # nsRefPtrGetterAddRefs, the class behind getter_AddRefs. Fix that up. find . -name '*.cpp' -o -name '*.h' -o -name '*.idl' | \ xargs perl -p -i -e 's/nsRefPtrGetterAddRefs/RefPtrGetterAddRefs/g' if [ -d .git ]; then git mv mfbt/nsRefPtr.h mfbt/RefPtr.h else hg mv mfbt/nsRefPtr.h mfbt/RefPtr.h fi --HG-- rename : mfbt/nsRefPtr.h => mfbt/RefPtr.h
2015-10-18 08:24:48 +03:00
RefPtr<imgLoader> il =
nsContentUtils::GetImgLoaderForDocument(aPresContext->Document());
nsCOMPtr<nsILoadGroup> loadGroup;
GetLoadGroup(aPresContext, getter_AddRefs(loadGroup));
// For icon loads, we don't need to merge with the loadgroup flags
nsLoadFlags loadFlags = nsIRequest::LOAD_NORMAL;
nsContentPolicyType contentPolicyType = nsIContentPolicy::TYPE_INTERNAL_IMAGE;
return il->LoadImage(realURI, /* icon URI */
nullptr, /* initial document URI; this is only
relevant for cookies, so does not
apply to icons. */
nullptr, /* referrer (not relevant for icons) */
mozilla::net::RP_Unset,
nullptr, /* principal (not relevant for icons) */
0,
loadGroup,
gIconLoad,
nullptr, /* No context */
nullptr, /* Not associated with any particular document */
loadFlags,
nullptr,
contentPolicyType,
EmptyString(),
false, /* aUseUrgentStartForChannel */
aRequest);
}
void
nsImageFrame::GetDocumentCharacterSet(nsACString& aCharset) const
{
if (mContent) {
NS_ASSERTION(mContent->GetComposedDoc(),
"Frame still alive after content removed from document!");
mContent->GetComposedDoc()->GetDocumentCharacterSet()->Name(aCharset);
}
}
2001-04-15 06:13:49 +04:00
void
nsImageFrame::SpecToURI(const nsAString& aSpec, nsIIOService *aIOService,
nsIURI **aURI)
{
2001-04-15 06:13:49 +04:00
nsCOMPtr<nsIURI> baseURI;
if (mContent) {
baseURI = mContent->GetBaseURI();
}
nsAutoCString charset;
GetDocumentCharacterSet(charset);
NS_NewURI(aURI, aSpec,
charset.IsEmpty() ? nullptr : charset.get(),
baseURI, aIOService);
2001-04-15 06:13:49 +04:00
}
void
nsImageFrame::GetLoadGroup(nsPresContext *aPresContext, nsILoadGroup **aLoadGroup)
{
if (!aPresContext)
return;
MOZ_ASSERT(nullptr != aLoadGroup, "null OUT parameter pointer");
nsIPresShell *shell = aPresContext->GetPresShell();
if (!shell)
return;
nsIDocument *doc = shell->GetDocument();
if (!doc)
return;
*aLoadGroup = doc->GetDocumentLoadGroup().take();
}
nsresult nsImageFrame::LoadIcons(nsPresContext *aPresContext)
{
NS_ASSERTION(!gIconLoad, "called LoadIcons twice");
NS_NAMED_LITERAL_STRING(loadingSrc,"resource://gre-resources/loading-image.png");
NS_NAMED_LITERAL_STRING(brokenSrc,"resource://gre-resources/broken-image.png");
gIconLoad = new IconLoad();
nsresult rv;
// create a loader and load the images
rv = LoadIcon(loadingSrc,
aPresContext,
getter_AddRefs(gIconLoad->mLoadingImage));
if (NS_FAILED(rv)) {
return rv;
}
rv = LoadIcon(brokenSrc,
aPresContext,
getter_AddRefs(gIconLoad->mBrokenImage));
if (NS_FAILED(rv)) {
return rv;
}
return rv;
}
NS_IMPL_ISUPPORTS(nsImageFrame::IconLoad, nsIObserver,
imgINotificationObserver)
static const char* kIconLoadPrefs[] = {
"browser.display.force_inline_alttext",
"browser.display.show_image_placeholders",
"browser.display.show_loading_image_placeholder",
nullptr
};
nsImageFrame::IconLoad::IconLoad()
{
// register observers
Preferences::AddStrongObservers(this, kIconLoadPrefs);
GetPrefs();
}
void
nsImageFrame::IconLoad::Shutdown()
{
Preferences::RemoveObservers(this, kIconLoadPrefs);
// in case the pref service releases us later
if (mLoadingImage) {
mLoadingImage->CancelAndForgetObserver(NS_ERROR_FAILURE);
mLoadingImage = nullptr;
}
if (mBrokenImage) {
mBrokenImage->CancelAndForgetObserver(NS_ERROR_FAILURE);
mBrokenImage = nullptr;
}
}
NS_IMETHODIMP
nsImageFrame::IconLoad::Observe(nsISupports *aSubject, const char* aTopic,
const char16_t* aData)
{
NS_ASSERTION(!nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID),
"wrong topic");
#ifdef DEBUG
// assert |aData| is one of our prefs.
uint32_t i = 0;
for (; i < ArrayLength(kIconLoadPrefs); ++i) {
if (NS_ConvertASCIItoUTF16(kIconLoadPrefs[i]) == nsDependentString(aData))
break;
}
MOZ_ASSERT(i < ArrayLength(kIconLoadPrefs));
#endif
GetPrefs();
return NS_OK;
}
void nsImageFrame::IconLoad::GetPrefs()
{
mPrefForceInlineAltText =
Preferences::GetBool("browser.display.force_inline_alttext");
mPrefShowPlaceholders =
Preferences::GetBool("browser.display.show_image_placeholders", true);
mPrefShowLoadingPlaceholder =
Preferences::GetBool("browser.display.show_loading_image_placeholder", true);
}
2001-04-15 06:13:49 +04:00
NS_IMETHODIMP
nsImageFrame::IconLoad::Notify(imgIRequest* aRequest,
int32_t aType,
const nsIntRect* aData)
{
MOZ_ASSERT(aRequest);
if (aType != imgINotificationObserver::LOAD_COMPLETE &&
aType != imgINotificationObserver::FRAME_UPDATE) {
return NS_OK;
}
if (aType == imgINotificationObserver::LOAD_COMPLETE) {
nsCOMPtr<imgIContainer> image;
aRequest->GetImage(getter_AddRefs(image));
if (!image) {
return NS_ERROR_FAILURE;
}
// Retrieve the image's intrinsic size.
int32_t width = 0;
int32_t height = 0;
image->GetWidth(&width);
image->GetHeight(&height);
// Request a decode at that size.
image->RequestDecodeForSize(IntSize(width, height),
imgIContainer::DECODE_FLAGS_DEFAULT);
}
nsTObserverArray<nsImageFrame*>::ForwardIterator iter(mIconObservers);
nsImageFrame *frame;
while (iter.HasMore()) {
frame = iter.GetNext();
frame->InvalidateFrame();
}
return NS_OK;
}
NS_IMPL_ISUPPORTS(nsImageListener, imgINotificationObserver)
nsImageListener::nsImageListener(nsImageFrame* aFrame)
: mFrame(aFrame)
{
}
nsImageListener::~nsImageListener() = default;
NS_IMETHODIMP
nsImageListener::Notify(imgIRequest *aRequest, int32_t aType, const nsIntRect* aData)
{
if (!mFrame)
return NS_ERROR_FAILURE;
return mFrame->Notify(aRequest, aType, aData);
}
static bool
IsInAutoWidthTableCellForQuirk(nsIFrame *aFrame)
{
if (eCompatibility_NavQuirks != aFrame->PresContext()->CompatibilityMode())
return false;
// Check if the parent of the closest nsBlockFrame has auto width.
nsBlockFrame *ancestor = nsLayoutUtils::FindNearestBlockAncestor(aFrame);
if (ancestor->Style()->GetPseudo() == nsCSSAnonBoxes::cellContent) {
// Assume direct parent is a table cell frame.
nsFrame *grandAncestor = static_cast<nsFrame*>(ancestor->GetParent());
return grandAncestor &&
grandAncestor->StylePosition()->mWidth.GetUnit() == eStyleUnit_Auto;
}
return false;
}
/* virtual */ void
nsImageFrame::AddInlineMinISize(gfxContext* aRenderingContext,
nsIFrame::InlineMinISizeData* aData)
{
nscoord isize = nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
this, nsLayoutUtils::MIN_ISIZE);
bool canBreak = !IsInAutoWidthTableCellForQuirk(this);
aData->DefaultAddInlineMinISize(this, isize, canBreak);
}