1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/layout/generic/nsImageFrame.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,2067 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +/* rendering object for replaced elements with bitmap image data */ 1.10 + 1.11 +#include "nsImageFrame.h" 1.12 + 1.13 +#include "mozilla/DebugOnly.h" 1.14 +#include "mozilla/EventStates.h" 1.15 +#include "mozilla/MouseEvents.h" 1.16 + 1.17 +#include "nsCOMPtr.h" 1.18 +#include "nsIImageLoadingContent.h" 1.19 +#include "nsString.h" 1.20 +#include "nsPrintfCString.h" 1.21 +#include "nsPresContext.h" 1.22 +#include "nsRenderingContext.h" 1.23 +#include "nsIPresShell.h" 1.24 +#include "nsGkAtoms.h" 1.25 +#include "nsIDocument.h" 1.26 +#include "nsContentUtils.h" 1.27 +#include "nsCSSAnonBoxes.h" 1.28 +#include "nsStyleContext.h" 1.29 +#include "nsStyleConsts.h" 1.30 +#include "nsStyleCoord.h" 1.31 +#include "nsTransform2D.h" 1.32 +#include "nsImageMap.h" 1.33 +#include "nsIIOService.h" 1.34 +#include "nsILoadGroup.h" 1.35 +#include "nsISupportsPriority.h" 1.36 +#include "nsNetUtil.h" 1.37 +#include "nsCSSRendering.h" 1.38 +#include "nsIDOMHTMLAnchorElement.h" 1.39 +#include "nsNameSpaceManager.h" 1.40 +#include <algorithm> 1.41 +#ifdef ACCESSIBILITY 1.42 +#include "nsAccessibilityService.h" 1.43 +#endif 1.44 +#include "nsIDOMNode.h" 1.45 +#include "nsLayoutUtils.h" 1.46 +#include "nsDisplayList.h" 1.47 + 1.48 +#include "imgIContainer.h" 1.49 +#include "imgLoader.h" 1.50 +#include "imgRequestProxy.h" 1.51 + 1.52 +#include "nsCSSFrameConstructor.h" 1.53 +#include "nsIDOMRange.h" 1.54 + 1.55 +#include "nsError.h" 1.56 +#include "nsBidiUtils.h" 1.57 +#include "nsBidiPresUtils.h" 1.58 +#include "mozIThirdPartyUtil.h" 1.59 + 1.60 +#include "gfxRect.h" 1.61 +#include "ImageLayers.h" 1.62 +#include "ImageContainer.h" 1.63 +#include "nsStyleSet.h" 1.64 +#include "nsBlockFrame.h" 1.65 +#include "nsStyleStructInlines.h" 1.66 + 1.67 +#include "mozilla/Preferences.h" 1.68 + 1.69 +#include "mozilla/dom/Link.h" 1.70 + 1.71 +using namespace mozilla; 1.72 + 1.73 +// sizes (pixels) for image icon, padding and border frame 1.74 +#define ICON_SIZE (16) 1.75 +#define ICON_PADDING (3) 1.76 +#define ALT_BORDER_WIDTH (1) 1.77 + 1.78 + 1.79 +//we must add hooks soon 1.80 +#define IMAGE_EDITOR_CHECK 1 1.81 + 1.82 +// Default alignment value (so we can tell an unset value from a set value) 1.83 +#define ALIGN_UNSET uint8_t(-1) 1.84 + 1.85 +using namespace mozilla::layers; 1.86 +using namespace mozilla::dom; 1.87 + 1.88 +// static icon information 1.89 +nsImageFrame::IconLoad* nsImageFrame::gIconLoad = nullptr; 1.90 + 1.91 +// cached IO service for loading icons 1.92 +nsIIOService* nsImageFrame::sIOService; 1.93 + 1.94 +// test if the width and height are fixed, looking at the style data 1.95 +static bool HaveFixedSize(const nsStylePosition* aStylePosition) 1.96 +{ 1.97 + // check the width and height values in the reflow state's style struct 1.98 + // - if width and height are specified as either coord or percentage, then 1.99 + // the size of the image frame is constrained 1.100 + return aStylePosition->mWidth.IsCoordPercentCalcUnit() && 1.101 + aStylePosition->mHeight.IsCoordPercentCalcUnit(); 1.102 +} 1.103 +// use the data in the reflow state to decide if the image has a constrained size 1.104 +// (i.e. width and height that are based on the containing block size and not the image size) 1.105 +// so we can avoid animated GIF related reflows 1.106 +inline bool HaveFixedSize(const nsHTMLReflowState& aReflowState) 1.107 +{ 1.108 + NS_ASSERTION(aReflowState.mStylePosition, "crappy reflowState - null stylePosition"); 1.109 + // when an image has percent css style height or width, but ComputedHeight() 1.110 + // or ComputedWidth() of reflow state is NS_UNCONSTRAINEDSIZE 1.111 + // it needs to return false to cause an incremental reflow later 1.112 + // if an image is inside table like bug 156731 simple testcase III, 1.113 + // during pass 1 reflow, ComputedWidth() is NS_UNCONSTRAINEDSIZE 1.114 + // in pass 2 reflow, ComputedWidth() is 0, it also needs to return false 1.115 + // see bug 156731 1.116 + const nsStyleCoord &height = aReflowState.mStylePosition->mHeight; 1.117 + const nsStyleCoord &width = aReflowState.mStylePosition->mWidth; 1.118 + return ((height.HasPercent() && 1.119 + NS_UNCONSTRAINEDSIZE == aReflowState.ComputedHeight()) || 1.120 + (width.HasPercent() && 1.121 + (NS_UNCONSTRAINEDSIZE == aReflowState.ComputedWidth() || 1.122 + 0 == aReflowState.ComputedWidth()))) 1.123 + ? false 1.124 + : HaveFixedSize(aReflowState.mStylePosition); 1.125 +} 1.126 + 1.127 +nsIFrame* 1.128 +NS_NewImageFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) 1.129 +{ 1.130 + return new (aPresShell) nsImageFrame(aContext); 1.131 +} 1.132 + 1.133 +NS_IMPL_FRAMEARENA_HELPERS(nsImageFrame) 1.134 + 1.135 + 1.136 +nsImageFrame::nsImageFrame(nsStyleContext* aContext) : 1.137 + ImageFrameSuper(aContext), 1.138 + mComputedSize(0, 0), 1.139 + mIntrinsicRatio(0, 0), 1.140 + mDisplayingIcon(false), 1.141 + mFirstFrameComplete(false), 1.142 + mReflowCallbackPosted(false) 1.143 +{ 1.144 + // We assume our size is not constrained and we haven't gotten an 1.145 + // initial reflow yet, so don't touch those flags. 1.146 + mIntrinsicSize.width.SetCoordValue(0); 1.147 + mIntrinsicSize.height.SetCoordValue(0); 1.148 +} 1.149 + 1.150 +nsImageFrame::~nsImageFrame() 1.151 +{ 1.152 +} 1.153 + 1.154 +NS_QUERYFRAME_HEAD(nsImageFrame) 1.155 + NS_QUERYFRAME_ENTRY(nsImageFrame) 1.156 +NS_QUERYFRAME_TAIL_INHERITING(ImageFrameSuper) 1.157 + 1.158 +#ifdef ACCESSIBILITY 1.159 +a11y::AccType 1.160 +nsImageFrame::AccessibleType() 1.161 +{ 1.162 + // Don't use GetImageMap() to avoid reentrancy into accessibility. 1.163 + if (HasImageMap()) { 1.164 + return a11y::eHTMLImageMapType; 1.165 + } 1.166 + 1.167 + return a11y::eImageType; 1.168 +} 1.169 +#endif 1.170 + 1.171 +void 1.172 +nsImageFrame::DisconnectMap() 1.173 +{ 1.174 + if (mImageMap) { 1.175 + mImageMap->Destroy(); 1.176 + NS_RELEASE(mImageMap); 1.177 + 1.178 +#ifdef ACCESSIBILITY 1.179 + nsAccessibilityService* accService = GetAccService(); 1.180 + if (accService) { 1.181 + accService->RecreateAccessible(PresContext()->PresShell(), mContent); 1.182 + } 1.183 +#endif 1.184 + } 1.185 +} 1.186 + 1.187 +void 1.188 +nsImageFrame::DestroyFrom(nsIFrame* aDestructRoot) 1.189 +{ 1.190 + if (mReflowCallbackPosted) { 1.191 + PresContext()->PresShell()->CancelReflowCallback(this); 1.192 + mReflowCallbackPosted = false; 1.193 + } 1.194 + 1.195 + // Tell our image map, if there is one, to clean up 1.196 + // This causes the nsImageMap to unregister itself as 1.197 + // a DOM listener. 1.198 + DisconnectMap(); 1.199 + 1.200 + // set the frame to null so we don't send messages to a dead object. 1.201 + if (mListener) { 1.202 + nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent); 1.203 + if (imageLoader) { 1.204 + // Notify our image loading content that we are going away so it can 1.205 + // deregister with our refresh driver. 1.206 + imageLoader->FrameDestroyed(this); 1.207 + 1.208 + imageLoader->RemoveObserver(mListener); 1.209 + } 1.210 + 1.211 + reinterpret_cast<nsImageListener*>(mListener.get())->SetFrame(nullptr); 1.212 + } 1.213 + 1.214 + mListener = nullptr; 1.215 + 1.216 + // If we were displaying an icon, take ourselves off the list 1.217 + if (mDisplayingIcon) 1.218 + gIconLoad->RemoveIconObserver(this); 1.219 + 1.220 + nsSplittableFrame::DestroyFrom(aDestructRoot); 1.221 +} 1.222 + 1.223 + 1.224 + 1.225 +void 1.226 +nsImageFrame::Init(nsIContent* aContent, 1.227 + nsIFrame* aParent, 1.228 + nsIFrame* aPrevInFlow) 1.229 +{ 1.230 + nsSplittableFrame::Init(aContent, aParent, aPrevInFlow); 1.231 + 1.232 + mListener = new nsImageListener(this); 1.233 + 1.234 + nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(aContent); 1.235 + if (!imageLoader) { 1.236 + NS_RUNTIMEABORT("Why do we have an nsImageFrame here at all?"); 1.237 + } 1.238 + 1.239 + imageLoader->AddObserver(mListener); 1.240 + 1.241 + nsPresContext *aPresContext = PresContext(); 1.242 + 1.243 + if (!gIconLoad) 1.244 + LoadIcons(aPresContext); 1.245 + 1.246 + // We have a PresContext now, so we need to notify the image content node 1.247 + // that it can register images. 1.248 + imageLoader->FrameCreated(this); 1.249 + 1.250 + // Give image loads associated with an image frame a small priority boost! 1.251 + nsCOMPtr<imgIRequest> currentRequest; 1.252 + imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST, 1.253 + getter_AddRefs(currentRequest)); 1.254 + nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(currentRequest); 1.255 + if (p) 1.256 + p->AdjustPriority(-1); 1.257 + 1.258 + // If we already have an image container, OnStartContainer won't be called 1.259 + if (currentRequest) { 1.260 + nsCOMPtr<imgIContainer> image; 1.261 + currentRequest->GetImage(getter_AddRefs(image)); 1.262 + OnStartContainer(currentRequest, image); 1.263 + } 1.264 +} 1.265 + 1.266 +bool 1.267 +nsImageFrame::UpdateIntrinsicSize(imgIContainer* aImage) 1.268 +{ 1.269 + NS_PRECONDITION(aImage, "null image"); 1.270 + if (!aImage) 1.271 + return false; 1.272 + 1.273 + IntrinsicSize oldIntrinsicSize = mIntrinsicSize; 1.274 + mIntrinsicSize = IntrinsicSize(); 1.275 + 1.276 + // Set intrinsic size to match aImage's reported intrinsic width & height. 1.277 + nsSize intrinsicSize; 1.278 + if (NS_SUCCEEDED(aImage->GetIntrinsicSize(&intrinsicSize))) { 1.279 + // If the image has no intrinsic width, intrinsicSize.width will be -1, and 1.280 + // we can leave mIntrinsicSize.width at its default value of eStyleUnit_None. 1.281 + // Otherwise we use intrinsicSize.width. Height works the same way. 1.282 + if (intrinsicSize.width != -1) 1.283 + mIntrinsicSize.width.SetCoordValue(intrinsicSize.width); 1.284 + if (intrinsicSize.height != -1) 1.285 + mIntrinsicSize.height.SetCoordValue(intrinsicSize.height); 1.286 + } else { 1.287 + // Failure means that the image hasn't loaded enough to report a result. We 1.288 + // treat this case as if the image's intrinsic size was 0x0. 1.289 + mIntrinsicSize.width.SetCoordValue(0); 1.290 + mIntrinsicSize.height.SetCoordValue(0); 1.291 + } 1.292 + 1.293 + return mIntrinsicSize != oldIntrinsicSize; 1.294 +} 1.295 + 1.296 +bool 1.297 +nsImageFrame::UpdateIntrinsicRatio(imgIContainer* aImage) 1.298 +{ 1.299 + NS_PRECONDITION(aImage, "null image"); 1.300 + 1.301 + if (!aImage) 1.302 + return false; 1.303 + 1.304 + nsSize oldIntrinsicRatio = mIntrinsicRatio; 1.305 + 1.306 + // Set intrinsic ratio to match aImage's reported intrinsic ratio. 1.307 + if (NS_FAILED(aImage->GetIntrinsicRatio(&mIntrinsicRatio))) 1.308 + mIntrinsicRatio.SizeTo(0, 0); 1.309 + 1.310 + return mIntrinsicRatio != oldIntrinsicRatio; 1.311 +} 1.312 + 1.313 +bool 1.314 +nsImageFrame::GetSourceToDestTransform(nsTransform2D& aTransform) 1.315 +{ 1.316 + // Set the translation components. 1.317 + // XXXbz does this introduce rounding errors because of the cast to 1.318 + // float? Should we just manually add that stuff in every time 1.319 + // instead? 1.320 + nsRect innerArea = GetInnerArea(); 1.321 + aTransform.SetToTranslate(float(innerArea.x), 1.322 + float(innerArea.y - GetContinuationOffset())); 1.323 + 1.324 + // Set the scale factors. 1.325 + if (mIntrinsicSize.width.GetUnit() == eStyleUnit_Coord && 1.326 + mIntrinsicSize.width.GetCoordValue() != 0 && 1.327 + mIntrinsicSize.height.GetUnit() == eStyleUnit_Coord && 1.328 + mIntrinsicSize.height.GetCoordValue() != 0 && 1.329 + mIntrinsicSize.width.GetCoordValue() != mComputedSize.width && 1.330 + mIntrinsicSize.height.GetCoordValue() != mComputedSize.height) { 1.331 + 1.332 + aTransform.SetScale(float(mComputedSize.width) / 1.333 + float(mIntrinsicSize.width.GetCoordValue()), 1.334 + float(mComputedSize.height) / 1.335 + float(mIntrinsicSize.height.GetCoordValue())); 1.336 + return true; 1.337 + } 1.338 + 1.339 + return false; 1.340 +} 1.341 + 1.342 +/* 1.343 + * These two functions basically do the same check. The first one 1.344 + * checks that the given request is the current request for our 1.345 + * mContent. The second checks that the given image container the 1.346 + * same as the image container on the current request for our 1.347 + * mContent. 1.348 + */ 1.349 +bool 1.350 +nsImageFrame::IsPendingLoad(imgIRequest* aRequest) const 1.351 +{ 1.352 + // Default to pending load in case of errors 1.353 + nsCOMPtr<nsIImageLoadingContent> imageLoader(do_QueryInterface(mContent)); 1.354 + NS_ASSERTION(imageLoader, "No image loading content?"); 1.355 + 1.356 + int32_t requestType = nsIImageLoadingContent::UNKNOWN_REQUEST; 1.357 + imageLoader->GetRequestType(aRequest, &requestType); 1.358 + 1.359 + return requestType != nsIImageLoadingContent::CURRENT_REQUEST; 1.360 +} 1.361 + 1.362 +bool 1.363 +nsImageFrame::IsPendingLoad(imgIContainer* aContainer) const 1.364 +{ 1.365 + // default to pending load in case of errors 1.366 + if (!aContainer) { 1.367 + NS_ERROR("No image container!"); 1.368 + return true; 1.369 + } 1.370 + 1.371 + nsCOMPtr<nsIImageLoadingContent> imageLoader(do_QueryInterface(mContent)); 1.372 + NS_ASSERTION(imageLoader, "No image loading content?"); 1.373 + 1.374 + nsCOMPtr<imgIRequest> currentRequest; 1.375 + imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST, 1.376 + getter_AddRefs(currentRequest)); 1.377 + if (!currentRequest) { 1.378 + NS_ERROR("No current request"); 1.379 + return true; 1.380 + } 1.381 + 1.382 + nsCOMPtr<imgIContainer> currentContainer; 1.383 + currentRequest->GetImage(getter_AddRefs(currentContainer)); 1.384 + 1.385 + return currentContainer != aContainer; 1.386 + 1.387 +} 1.388 + 1.389 +nsRect 1.390 +nsImageFrame::SourceRectToDest(const nsIntRect& aRect) 1.391 +{ 1.392 + // When scaling the image, row N of the source image may (depending on 1.393 + // the scaling function) be used to draw any row in the destination image 1.394 + // between floor(F * (N-1)) and ceil(F * (N+1)), where F is the 1.395 + // floating-point scaling factor. The same holds true for columns. 1.396 + // So, we start by computing that bound without the floor and ceiling. 1.397 + 1.398 + nsRect r(nsPresContext::CSSPixelsToAppUnits(aRect.x - 1), 1.399 + nsPresContext::CSSPixelsToAppUnits(aRect.y - 1), 1.400 + nsPresContext::CSSPixelsToAppUnits(aRect.width + 2), 1.401 + nsPresContext::CSSPixelsToAppUnits(aRect.height + 2)); 1.402 + 1.403 + nsTransform2D sourceToDest; 1.404 + if (!GetSourceToDestTransform(sourceToDest)) { 1.405 + // Failed to generate transform matrix. Return our whole inner area, 1.406 + // to be on the safe side (since this method is used for generating 1.407 + // invalidation rects). 1.408 + return GetInnerArea(); 1.409 + } 1.410 + 1.411 + sourceToDest.TransformCoord(&r.x, &r.y, &r.width, &r.height); 1.412 + 1.413 + // Now, round the edges out to the pixel boundary. 1.414 + nscoord scale = nsPresContext::CSSPixelsToAppUnits(1); 1.415 + nscoord right = r.x + r.width; 1.416 + nscoord bottom = r.y + r.height; 1.417 + 1.418 + r.x -= (scale + (r.x % scale)) % scale; 1.419 + r.y -= (scale + (r.y % scale)) % scale; 1.420 + r.width = right + ((scale - (right % scale)) % scale) - r.x; 1.421 + r.height = bottom + ((scale - (bottom % scale)) % scale) - r.y; 1.422 + 1.423 + return r; 1.424 +} 1.425 + 1.426 +// Note that we treat NS_EVENT_STATE_SUPPRESSED images as "OK". This means 1.427 +// that we'll construct image frames for them as needed if their display is 1.428 +// toggled from "none" (though we won't paint them, unless their visibility 1.429 +// is changed too). 1.430 +#define BAD_STATES (NS_EVENT_STATE_BROKEN | NS_EVENT_STATE_USERDISABLED | \ 1.431 + NS_EVENT_STATE_LOADING) 1.432 + 1.433 +// This is a macro so that we don't evaluate the boolean last arg 1.434 +// unless we have to; it can be expensive 1.435 +#define IMAGE_OK(_state, _loadingOK) \ 1.436 + (!(_state).HasAtLeastOneOfStates(BAD_STATES) || \ 1.437 + (!(_state).HasAtLeastOneOfStates(NS_EVENT_STATE_BROKEN | NS_EVENT_STATE_USERDISABLED) && \ 1.438 + (_state).HasState(NS_EVENT_STATE_LOADING) && (_loadingOK))) 1.439 + 1.440 +/* static */ 1.441 +bool 1.442 +nsImageFrame::ShouldCreateImageFrameFor(Element* aElement, 1.443 + nsStyleContext* aStyleContext) 1.444 +{ 1.445 + EventStates state = aElement->State(); 1.446 + if (IMAGE_OK(state, 1.447 + HaveFixedSize(aStyleContext->StylePosition()))) { 1.448 + // Image is fine; do the image frame thing 1.449 + return true; 1.450 + } 1.451 + 1.452 + // Check if we want to use a placeholder box with an icon or just 1.453 + // let the presShell make us into inline text. Decide as follows: 1.454 + // 1.455 + // - if our special "force icons" style is set, show an icon 1.456 + // - else if our "do not show placeholders" pref is set, skip the icon 1.457 + // - else: 1.458 + // - if there is a src attribute, there is no alt attribute, 1.459 + // and this is not an <object> (which could not possibly have 1.460 + // such an attribute), show an icon. 1.461 + // - if QuirksMode, and the IMG has a size show an icon. 1.462 + // - otherwise, skip the icon 1.463 + bool useSizedBox; 1.464 + 1.465 + if (aStyleContext->StyleUIReset()->mForceBrokenImageIcon) { 1.466 + useSizedBox = true; 1.467 + } 1.468 + else if (gIconLoad && gIconLoad->mPrefForceInlineAltText) { 1.469 + useSizedBox = false; 1.470 + } 1.471 + else if (aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::src) && 1.472 + !aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::alt) && 1.473 + !aElement->IsHTML(nsGkAtoms::object) && 1.474 + !aElement->IsHTML(nsGkAtoms::input)) { 1.475 + // Use a sized box if we have no alt text. This means no alt attribute 1.476 + // and the node is not an object or an input (since those always have alt 1.477 + // text). 1.478 + useSizedBox = true; 1.479 + } 1.480 + else if (aStyleContext->PresContext()->CompatibilityMode() != 1.481 + eCompatibility_NavQuirks) { 1.482 + useSizedBox = false; 1.483 + } 1.484 + else { 1.485 + // check whether we have fixed size 1.486 + useSizedBox = HaveFixedSize(aStyleContext->StylePosition()); 1.487 + } 1.488 + 1.489 + return useSizedBox; 1.490 +} 1.491 + 1.492 +nsresult 1.493 +nsImageFrame::Notify(imgIRequest* aRequest, int32_t aType, const nsIntRect* aData) 1.494 +{ 1.495 + if (aType == imgINotificationObserver::SIZE_AVAILABLE) { 1.496 + nsCOMPtr<imgIContainer> image; 1.497 + aRequest->GetImage(getter_AddRefs(image)); 1.498 + return OnStartContainer(aRequest, image); 1.499 + } 1.500 + 1.501 + if (aType == imgINotificationObserver::FRAME_UPDATE) { 1.502 + return OnDataAvailable(aRequest, aData); 1.503 + } 1.504 + 1.505 + if (aType == imgINotificationObserver::FRAME_COMPLETE) { 1.506 + mFirstFrameComplete = true; 1.507 + } 1.508 + 1.509 + if (aType == imgINotificationObserver::LOAD_COMPLETE) { 1.510 + uint32_t imgStatus; 1.511 + aRequest->GetImageStatus(&imgStatus); 1.512 + nsresult status = 1.513 + imgStatus & imgIRequest::STATUS_ERROR ? NS_ERROR_FAILURE : NS_OK; 1.514 + return OnStopRequest(aRequest, status); 1.515 + } 1.516 + 1.517 + return NS_OK; 1.518 +} 1.519 + 1.520 +static bool 1.521 +SizeIsAvailable(imgIRequest* aRequest) 1.522 +{ 1.523 + if (!aRequest) 1.524 + return false; 1.525 + 1.526 + uint32_t imageStatus = 0; 1.527 + nsresult rv = aRequest->GetImageStatus(&imageStatus); 1.528 + 1.529 + return NS_SUCCEEDED(rv) && (imageStatus & imgIRequest::STATUS_SIZE_AVAILABLE); 1.530 +} 1.531 + 1.532 +nsresult 1.533 +nsImageFrame::OnStartContainer(imgIRequest *aRequest, imgIContainer *aImage) 1.534 +{ 1.535 + if (!aImage) return NS_ERROR_INVALID_ARG; 1.536 + 1.537 + /* Get requested animation policy from the pres context: 1.538 + * normal = 0 1.539 + * one frame = 1 1.540 + * one loop = 2 1.541 + */ 1.542 + nsPresContext *presContext = PresContext(); 1.543 + aImage->SetAnimationMode(presContext->ImageAnimationMode()); 1.544 + 1.545 + if (IsPendingLoad(aRequest)) { 1.546 + // We don't care 1.547 + return NS_OK; 1.548 + } 1.549 + 1.550 + bool intrinsicSizeChanged = false; 1.551 + if (SizeIsAvailable(aRequest)) { 1.552 + // This is valid and for the current request, so update our stored image 1.553 + // container, orienting according to our style. 1.554 + mImage = nsLayoutUtils::OrientImage(aImage, StyleVisibility()->mImageOrientation); 1.555 + 1.556 + intrinsicSizeChanged = UpdateIntrinsicSize(mImage); 1.557 + intrinsicSizeChanged = UpdateIntrinsicRatio(mImage) || intrinsicSizeChanged; 1.558 + } else { 1.559 + // We no longer have a valid image, so release our stored image container. 1.560 + mImage = nullptr; 1.561 + 1.562 + // Have to size to 0,0 so that GetDesiredSize recalculates the size. 1.563 + mIntrinsicSize.width.SetCoordValue(0); 1.564 + mIntrinsicSize.height.SetCoordValue(0); 1.565 + mIntrinsicRatio.SizeTo(0, 0); 1.566 + intrinsicSizeChanged = true; 1.567 + } 1.568 + 1.569 + if (intrinsicSizeChanged && (mState & IMAGE_GOTINITIALREFLOW)) { 1.570 + // Now we need to reflow if we have an unconstrained size and have 1.571 + // already gotten the initial reflow 1.572 + if (!(mState & IMAGE_SIZECONSTRAINED)) { 1.573 + nsIPresShell *presShell = presContext->GetPresShell(); 1.574 + NS_ASSERTION(presShell, "No PresShell."); 1.575 + if (presShell) { 1.576 + presShell->FrameNeedsReflow(this, nsIPresShell::eStyleChange, 1.577 + NS_FRAME_IS_DIRTY); 1.578 + } 1.579 + } 1.580 + } 1.581 + 1.582 + return NS_OK; 1.583 +} 1.584 + 1.585 +nsresult 1.586 +nsImageFrame::OnDataAvailable(imgIRequest *aRequest, 1.587 + const nsIntRect *aRect) 1.588 +{ 1.589 + if (mFirstFrameComplete) { 1.590 + nsCOMPtr<imgIContainer> container; 1.591 + aRequest->GetImage(getter_AddRefs(container)); 1.592 + return FrameChanged(aRequest, container); 1.593 + } 1.594 + 1.595 + // XXX do we need to make sure that the reflow from the 1.596 + // OnStartContainer has been processed before we start calling 1.597 + // invalidate? 1.598 + 1.599 + NS_ENSURE_ARG_POINTER(aRect); 1.600 + 1.601 + if (!(mState & IMAGE_GOTINITIALREFLOW)) { 1.602 + // Don't bother to do anything; we have a reflow coming up! 1.603 + return NS_OK; 1.604 + } 1.605 + 1.606 + if (IsPendingLoad(aRequest)) { 1.607 + // We don't care 1.608 + return NS_OK; 1.609 + } 1.610 + 1.611 +#ifdef DEBUG_decode 1.612 + printf("Source rect (%d,%d,%d,%d)\n", 1.613 + aRect->x, aRect->y, aRect->width, aRect->height); 1.614 +#endif 1.615 + 1.616 + if (aRect->IsEqualInterior(nsIntRect::GetMaxSizedIntRect())) { 1.617 + InvalidateFrame(nsDisplayItem::TYPE_IMAGE); 1.618 + InvalidateFrame(nsDisplayItem::TYPE_ALT_FEEDBACK); 1.619 + } else { 1.620 + nsRect invalid = SourceRectToDest(*aRect); 1.621 + InvalidateFrameWithRect(invalid, nsDisplayItem::TYPE_IMAGE); 1.622 + InvalidateFrameWithRect(invalid, nsDisplayItem::TYPE_ALT_FEEDBACK); 1.623 + } 1.624 + 1.625 + return NS_OK; 1.626 +} 1.627 + 1.628 +nsresult 1.629 +nsImageFrame::OnStopRequest(imgIRequest *aRequest, 1.630 + nsresult aStatus) 1.631 +{ 1.632 + // Check what request type we're dealing with 1.633 + nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent); 1.634 + NS_ASSERTION(imageLoader, "Who's notifying us??"); 1.635 + int32_t loadType = nsIImageLoadingContent::UNKNOWN_REQUEST; 1.636 + imageLoader->GetRequestType(aRequest, &loadType); 1.637 + if (loadType != nsIImageLoadingContent::CURRENT_REQUEST && 1.638 + loadType != nsIImageLoadingContent::PENDING_REQUEST) { 1.639 + return NS_ERROR_FAILURE; 1.640 + } 1.641 + 1.642 + NotifyNewCurrentRequest(aRequest, aStatus); 1.643 + return NS_OK; 1.644 +} 1.645 + 1.646 +void 1.647 +nsImageFrame::NotifyNewCurrentRequest(imgIRequest *aRequest, 1.648 + nsresult aStatus) 1.649 +{ 1.650 + nsCOMPtr<imgIContainer> image; 1.651 + aRequest->GetImage(getter_AddRefs(image)); 1.652 + NS_ASSERTION(image || NS_FAILED(aStatus), "Successful load with no container?"); 1.653 + 1.654 + // May have to switch sizes here! 1.655 + bool intrinsicSizeChanged = true; 1.656 + if (NS_SUCCEEDED(aStatus) && image && SizeIsAvailable(aRequest)) { 1.657 + // Update our stored image container, orienting according to our style. 1.658 + mImage = nsLayoutUtils::OrientImage(image, StyleVisibility()->mImageOrientation); 1.659 + 1.660 + intrinsicSizeChanged = UpdateIntrinsicSize(mImage); 1.661 + intrinsicSizeChanged = UpdateIntrinsicRatio(mImage) || intrinsicSizeChanged; 1.662 + } else { 1.663 + // We no longer have a valid image, so release our stored image container. 1.664 + mImage = nullptr; 1.665 + 1.666 + // Have to size to 0,0 so that GetDesiredSize recalculates the size 1.667 + mIntrinsicSize.width.SetCoordValue(0); 1.668 + mIntrinsicSize.height.SetCoordValue(0); 1.669 + mIntrinsicRatio.SizeTo(0, 0); 1.670 + } 1.671 + 1.672 + if (mState & IMAGE_GOTINITIALREFLOW) { // do nothing if we haven't gotten the initial reflow yet 1.673 + if (!(mState & IMAGE_SIZECONSTRAINED) && intrinsicSizeChanged) { 1.674 + nsIPresShell *presShell = PresContext()->GetPresShell(); 1.675 + if (presShell) { 1.676 + presShell->FrameNeedsReflow(this, nsIPresShell::eStyleChange, 1.677 + NS_FRAME_IS_DIRTY); 1.678 + } 1.679 + } 1.680 + // Update border+content to account for image change 1.681 + InvalidateFrame(); 1.682 + } 1.683 +} 1.684 + 1.685 +nsresult 1.686 +nsImageFrame::FrameChanged(imgIRequest *aRequest, 1.687 + imgIContainer *aContainer) 1.688 +{ 1.689 + if (!StyleVisibility()->IsVisible()) { 1.690 + return NS_OK; 1.691 + } 1.692 + 1.693 + if (IsPendingLoad(aContainer)) { 1.694 + // We don't care about it 1.695 + return NS_OK; 1.696 + } 1.697 + 1.698 + InvalidateLayer(nsDisplayItem::TYPE_IMAGE); 1.699 + return NS_OK; 1.700 +} 1.701 + 1.702 +void 1.703 +nsImageFrame::EnsureIntrinsicSizeAndRatio(nsPresContext* aPresContext) 1.704 +{ 1.705 + // If mIntrinsicSize.width and height are 0, then we need to update from the 1.706 + // image container. 1.707 + if (mIntrinsicSize.width.GetUnit() == eStyleUnit_Coord && 1.708 + mIntrinsicSize.width.GetCoordValue() == 0 && 1.709 + mIntrinsicSize.height.GetUnit() == eStyleUnit_Coord && 1.710 + mIntrinsicSize.height.GetCoordValue() == 0) { 1.711 + 1.712 + if (mImage) { 1.713 + UpdateIntrinsicSize(mImage); 1.714 + UpdateIntrinsicRatio(mImage); 1.715 + } else { 1.716 + // image request is null or image size not known, probably an 1.717 + // invalid image specified 1.718 + // - make the image big enough for the icon (it may not be 1.719 + // used if inline alt expansion is used instead) 1.720 + if (!(GetStateBits() & NS_FRAME_GENERATED_CONTENT)) { 1.721 + nscoord edgeLengthToUse = 1.722 + nsPresContext::CSSPixelsToAppUnits( 1.723 + ICON_SIZE + (2 * (ICON_PADDING + ALT_BORDER_WIDTH))); 1.724 + mIntrinsicSize.width.SetCoordValue(edgeLengthToUse); 1.725 + mIntrinsicSize.height.SetCoordValue(edgeLengthToUse); 1.726 + mIntrinsicRatio.SizeTo(1, 1); 1.727 + } 1.728 + } 1.729 + } 1.730 +} 1.731 + 1.732 +/* virtual */ nsSize 1.733 +nsImageFrame::ComputeSize(nsRenderingContext *aRenderingContext, 1.734 + nsSize aCBSize, nscoord aAvailableWidth, 1.735 + nsSize aMargin, nsSize aBorder, nsSize aPadding, 1.736 + uint32_t aFlags) 1.737 +{ 1.738 + nsPresContext *presContext = PresContext(); 1.739 + EnsureIntrinsicSizeAndRatio(presContext); 1.740 + 1.741 + return nsLayoutUtils::ComputeSizeWithIntrinsicDimensions( 1.742 + aRenderingContext, this, 1.743 + mIntrinsicSize, mIntrinsicRatio, aCBSize, 1.744 + aMargin, aBorder, aPadding); 1.745 +} 1.746 + 1.747 +nsRect 1.748 +nsImageFrame::GetInnerArea() const 1.749 +{ 1.750 + return GetContentRect() - GetPosition(); 1.751 +} 1.752 + 1.753 +// get the offset into the content area of the image where aImg starts if it is a continuation. 1.754 +nscoord 1.755 +nsImageFrame::GetContinuationOffset() const 1.756 +{ 1.757 + nscoord offset = 0; 1.758 + for (nsIFrame *f = GetPrevInFlow(); f; f = f->GetPrevInFlow()) { 1.759 + offset += f->GetContentRect().height; 1.760 + } 1.761 + NS_ASSERTION(offset >= 0, "bogus GetContentRect"); 1.762 + return offset; 1.763 +} 1.764 + 1.765 +/* virtual */ nscoord 1.766 +nsImageFrame::GetMinWidth(nsRenderingContext *aRenderingContext) 1.767 +{ 1.768 + // XXX The caller doesn't account for constraints of the height, 1.769 + // min-height, and max-height properties. 1.770 + DebugOnly<nscoord> result; 1.771 + DISPLAY_MIN_WIDTH(this, result); 1.772 + nsPresContext *presContext = PresContext(); 1.773 + EnsureIntrinsicSizeAndRatio(presContext); 1.774 + return mIntrinsicSize.width.GetUnit() == eStyleUnit_Coord ? 1.775 + mIntrinsicSize.width.GetCoordValue() : 0; 1.776 +} 1.777 + 1.778 +/* virtual */ nscoord 1.779 +nsImageFrame::GetPrefWidth(nsRenderingContext *aRenderingContext) 1.780 +{ 1.781 + // XXX The caller doesn't account for constraints of the height, 1.782 + // min-height, and max-height properties. 1.783 + DebugOnly<nscoord> result; 1.784 + DISPLAY_PREF_WIDTH(this, result); 1.785 + nsPresContext *presContext = PresContext(); 1.786 + EnsureIntrinsicSizeAndRatio(presContext); 1.787 + // convert from normal twips to scaled twips (printing...) 1.788 + return mIntrinsicSize.width.GetUnit() == eStyleUnit_Coord ? 1.789 + mIntrinsicSize.width.GetCoordValue() : 0; 1.790 +} 1.791 + 1.792 +/* virtual */ IntrinsicSize 1.793 +nsImageFrame::GetIntrinsicSize() 1.794 +{ 1.795 + return mIntrinsicSize; 1.796 +} 1.797 + 1.798 +/* virtual */ nsSize 1.799 +nsImageFrame::GetIntrinsicRatio() 1.800 +{ 1.801 + return mIntrinsicRatio; 1.802 +} 1.803 + 1.804 +nsresult 1.805 +nsImageFrame::Reflow(nsPresContext* aPresContext, 1.806 + nsHTMLReflowMetrics& aMetrics, 1.807 + const nsHTMLReflowState& aReflowState, 1.808 + nsReflowStatus& aStatus) 1.809 +{ 1.810 + DO_GLOBAL_REFLOW_COUNT("nsImageFrame"); 1.811 + DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aStatus); 1.812 + NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS, 1.813 + ("enter nsImageFrame::Reflow: availSize=%d,%d", 1.814 + aReflowState.AvailableWidth(), aReflowState.AvailableHeight())); 1.815 + 1.816 + NS_PRECONDITION(mState & NS_FRAME_IN_REFLOW, "frame is not in reflow"); 1.817 + 1.818 + aStatus = NS_FRAME_COMPLETE; 1.819 + 1.820 + // see if we have a frozen size (i.e. a fixed width and height) 1.821 + if (HaveFixedSize(aReflowState)) { 1.822 + mState |= IMAGE_SIZECONSTRAINED; 1.823 + } else { 1.824 + mState &= ~IMAGE_SIZECONSTRAINED; 1.825 + } 1.826 + 1.827 + // XXXldb These two bits are almost exact opposites (except in the 1.828 + // middle of the initial reflow); remove IMAGE_GOTINITIALREFLOW. 1.829 + if (GetStateBits() & NS_FRAME_FIRST_REFLOW) { 1.830 + mState |= IMAGE_GOTINITIALREFLOW; 1.831 + } 1.832 + 1.833 + mComputedSize = 1.834 + nsSize(aReflowState.ComputedWidth(), aReflowState.ComputedHeight()); 1.835 + 1.836 + aMetrics.Width() = mComputedSize.width; 1.837 + aMetrics.Height() = mComputedSize.height; 1.838 + 1.839 + // add borders and padding 1.840 + aMetrics.Width() += aReflowState.ComputedPhysicalBorderPadding().LeftRight(); 1.841 + aMetrics.Height() += aReflowState.ComputedPhysicalBorderPadding().TopBottom(); 1.842 + 1.843 + if (GetPrevInFlow()) { 1.844 + aMetrics.Width() = GetPrevInFlow()->GetSize().width; 1.845 + nscoord y = GetContinuationOffset(); 1.846 + aMetrics.Height() -= y + aReflowState.ComputedPhysicalBorderPadding().top; 1.847 + aMetrics.Height() = std::max(0, aMetrics.Height()); 1.848 + } 1.849 + 1.850 + 1.851 + // we have to split images if we are: 1.852 + // in Paginated mode, we need to have a constrained height, and have a height larger than our available height 1.853 + uint32_t loadStatus = imgIRequest::STATUS_NONE; 1.854 + nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent); 1.855 + NS_ASSERTION(imageLoader, "No content node??"); 1.856 + if (imageLoader) { 1.857 + nsCOMPtr<imgIRequest> currentRequest; 1.858 + imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST, 1.859 + getter_AddRefs(currentRequest)); 1.860 + if (currentRequest) { 1.861 + currentRequest->GetImageStatus(&loadStatus); 1.862 + } 1.863 + } 1.864 + if (aPresContext->IsPaginated() && 1.865 + ((loadStatus & imgIRequest::STATUS_SIZE_AVAILABLE) || (mState & IMAGE_SIZECONSTRAINED)) && 1.866 + NS_UNCONSTRAINEDSIZE != aReflowState.AvailableHeight() && 1.867 + aMetrics.Height() > aReflowState.AvailableHeight()) { 1.868 + // our desired height was greater than 0, so to avoid infinite 1.869 + // splitting, use 1 pixel as the min 1.870 + aMetrics.Height() = std::max(nsPresContext::CSSPixelsToAppUnits(1), aReflowState.AvailableHeight()); 1.871 + aStatus = NS_FRAME_NOT_COMPLETE; 1.872 + } 1.873 + 1.874 + aMetrics.SetOverflowAreasToDesiredBounds(); 1.875 + EventStates contentState = mContent->AsElement()->State(); 1.876 + bool imageOK = IMAGE_OK(contentState, true); 1.877 + 1.878 + // Determine if the size is available 1.879 + bool haveSize = false; 1.880 + if (loadStatus & imgIRequest::STATUS_SIZE_AVAILABLE) { 1.881 + haveSize = true; 1.882 + } 1.883 + 1.884 + if (!imageOK || !haveSize) { 1.885 + nsRect altFeedbackSize(0, 0, 1.886 + nsPresContext::CSSPixelsToAppUnits(ICON_SIZE+2*(ICON_PADDING+ALT_BORDER_WIDTH)), 1.887 + nsPresContext::CSSPixelsToAppUnits(ICON_SIZE+2*(ICON_PADDING+ALT_BORDER_WIDTH))); 1.888 + // We include the altFeedbackSize in our visual overflow, but not in our 1.889 + // scrollable overflow, since it doesn't really need to be scrolled to 1.890 + // outside the image. 1.891 + static_assert(eOverflowType_LENGTH == 2, "Unknown overflow types?"); 1.892 + nsRect& visualOverflow = aMetrics.VisualOverflow(); 1.893 + visualOverflow.UnionRect(visualOverflow, altFeedbackSize); 1.894 + } 1.895 + FinishAndStoreOverflow(&aMetrics); 1.896 + 1.897 + if ((GetStateBits() & NS_FRAME_FIRST_REFLOW) && !mReflowCallbackPosted) { 1.898 + nsIPresShell* shell = PresContext()->PresShell(); 1.899 + mReflowCallbackPosted = true; 1.900 + shell->PostReflowCallback(this); 1.901 + } 1.902 + 1.903 + NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS, 1.904 + ("exit nsImageFrame::Reflow: size=%d,%d", 1.905 + aMetrics.Width(), aMetrics.Height())); 1.906 + NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aMetrics); 1.907 + return NS_OK; 1.908 +} 1.909 + 1.910 +bool 1.911 +nsImageFrame::ReflowFinished() 1.912 +{ 1.913 + mReflowCallbackPosted = false; 1.914 + 1.915 + nsLayoutUtils::UpdateImageVisibilityForFrame(this); 1.916 + 1.917 + return false; 1.918 +} 1.919 + 1.920 +void 1.921 +nsImageFrame::ReflowCallbackCanceled() 1.922 +{ 1.923 + mReflowCallbackPosted = false; 1.924 +} 1.925 + 1.926 +// Computes the width of the specified string. aMaxWidth specifies the maximum 1.927 +// width available. Once this limit is reached no more characters are measured. 1.928 +// The number of characters that fit within the maximum width are returned in 1.929 +// aMaxFit. NOTE: it is assumed that the fontmetrics have already been selected 1.930 +// into the rendering context before this is called (for performance). MMP 1.931 +nscoord 1.932 +nsImageFrame::MeasureString(const char16_t* aString, 1.933 + int32_t aLength, 1.934 + nscoord aMaxWidth, 1.935 + uint32_t& aMaxFit, 1.936 + nsRenderingContext& aContext) 1.937 +{ 1.938 + nscoord totalWidth = 0; 1.939 + aContext.SetTextRunRTL(false); 1.940 + nscoord spaceWidth = aContext.GetWidth(' '); 1.941 + 1.942 + aMaxFit = 0; 1.943 + while (aLength > 0) { 1.944 + // Find the next place we can line break 1.945 + uint32_t len = aLength; 1.946 + bool trailingSpace = false; 1.947 + for (int32_t i = 0; i < aLength; i++) { 1.948 + if (dom::IsSpaceCharacter(aString[i]) && (i > 0)) { 1.949 + len = i; // don't include the space when measuring 1.950 + trailingSpace = true; 1.951 + break; 1.952 + } 1.953 + } 1.954 + 1.955 + // Measure this chunk of text, and see if it fits 1.956 + nscoord width = 1.957 + nsLayoutUtils::GetStringWidth(this, &aContext, aString, len); 1.958 + bool fits = (totalWidth + width) <= aMaxWidth; 1.959 + 1.960 + // If it fits on the line, or it's the first word we've processed then 1.961 + // include it 1.962 + if (fits || (0 == totalWidth)) { 1.963 + // New piece fits 1.964 + totalWidth += width; 1.965 + 1.966 + // If there's a trailing space then see if it fits as well 1.967 + if (trailingSpace) { 1.968 + if ((totalWidth + spaceWidth) <= aMaxWidth) { 1.969 + totalWidth += spaceWidth; 1.970 + } else { 1.971 + // Space won't fit. Leave it at the end but don't include it in 1.972 + // the width 1.973 + fits = false; 1.974 + } 1.975 + 1.976 + len++; 1.977 + } 1.978 + 1.979 + aMaxFit += len; 1.980 + aString += len; 1.981 + aLength -= len; 1.982 + } 1.983 + 1.984 + if (!fits) { 1.985 + break; 1.986 + } 1.987 + } 1.988 + return totalWidth; 1.989 +} 1.990 + 1.991 +// Formats the alt-text to fit within the specified rectangle. Breaks lines 1.992 +// between words if a word would extend past the edge of the rectangle 1.993 +void 1.994 +nsImageFrame::DisplayAltText(nsPresContext* aPresContext, 1.995 + nsRenderingContext& aRenderingContext, 1.996 + const nsString& aAltText, 1.997 + const nsRect& aRect) 1.998 +{ 1.999 + // Set font and color 1.1000 + aRenderingContext.SetColor(StyleColor()->mColor); 1.1001 + nsRefPtr<nsFontMetrics> fm; 1.1002 + nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm), 1.1003 + nsLayoutUtils::FontSizeInflationFor(this)); 1.1004 + aRenderingContext.SetFont(fm); 1.1005 + 1.1006 + // Format the text to display within the formatting rect 1.1007 + 1.1008 + nscoord maxAscent = fm->MaxAscent(); 1.1009 + nscoord maxDescent = fm->MaxDescent(); 1.1010 + nscoord height = fm->MaxHeight(); 1.1011 + 1.1012 + // XXX It would be nice if there was a way to have the font metrics tell 1.1013 + // use where to break the text given a maximum width. At a minimum we need 1.1014 + // to be able to get the break character... 1.1015 + const char16_t* str = aAltText.get(); 1.1016 + int32_t strLen = aAltText.Length(); 1.1017 + nscoord y = aRect.y; 1.1018 + 1.1019 + if (!aPresContext->BidiEnabled() && HasRTLChars(aAltText)) { 1.1020 + aPresContext->SetBidiEnabled(); 1.1021 + } 1.1022 + 1.1023 + // Always show the first line, even if we have to clip it below 1.1024 + bool firstLine = true; 1.1025 + while ((strLen > 0) && (firstLine || (y + maxDescent) < aRect.YMost())) { 1.1026 + // Determine how much of the text to display on this line 1.1027 + uint32_t maxFit; // number of characters that fit 1.1028 + nscoord strWidth = MeasureString(str, strLen, aRect.width, maxFit, 1.1029 + aRenderingContext); 1.1030 + 1.1031 + // Display the text 1.1032 + nsresult rv = NS_ERROR_FAILURE; 1.1033 + 1.1034 + if (aPresContext->BidiEnabled()) { 1.1035 + const nsStyleVisibility* vis = StyleVisibility(); 1.1036 + if (vis->mDirection == NS_STYLE_DIRECTION_RTL) 1.1037 + rv = nsBidiPresUtils::RenderText(str, maxFit, NSBIDI_RTL, 1.1038 + aPresContext, aRenderingContext, 1.1039 + aRenderingContext, 1.1040 + aRect.XMost() - strWidth, y + maxAscent); 1.1041 + else 1.1042 + rv = nsBidiPresUtils::RenderText(str, maxFit, NSBIDI_LTR, 1.1043 + aPresContext, aRenderingContext, 1.1044 + aRenderingContext, 1.1045 + aRect.x, y + maxAscent); 1.1046 + } 1.1047 + if (NS_FAILED(rv)) 1.1048 + aRenderingContext.DrawString(str, maxFit, aRect.x, y + maxAscent); 1.1049 + 1.1050 + // Move to the next line 1.1051 + str += maxFit; 1.1052 + strLen -= maxFit; 1.1053 + y += height; 1.1054 + firstLine = false; 1.1055 + } 1.1056 +} 1.1057 + 1.1058 +struct nsRecessedBorder : public nsStyleBorder { 1.1059 + nsRecessedBorder(nscoord aBorderWidth, nsPresContext* aPresContext) 1.1060 + : nsStyleBorder(aPresContext) 1.1061 + { 1.1062 + NS_FOR_CSS_SIDES(side) { 1.1063 + // Note: use SetBorderColor here because we want to make sure 1.1064 + // the "special" flags are unset. 1.1065 + SetBorderColor(side, NS_RGB(0, 0, 0)); 1.1066 + mBorder.Side(side) = aBorderWidth; 1.1067 + // Note: use SetBorderStyle here because we want to affect 1.1068 + // mComputedBorder 1.1069 + SetBorderStyle(side, NS_STYLE_BORDER_STYLE_INSET); 1.1070 + } 1.1071 + } 1.1072 +}; 1.1073 + 1.1074 +class nsDisplayAltFeedback : public nsDisplayItem { 1.1075 +public: 1.1076 + nsDisplayAltFeedback(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame) 1.1077 + : nsDisplayItem(aBuilder, aFrame) {} 1.1078 + 1.1079 + virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, 1.1080 + bool* aSnap) MOZ_OVERRIDE 1.1081 + { 1.1082 + *aSnap = false; 1.1083 + return mFrame->GetVisualOverflowRectRelativeToSelf() + ToReferenceFrame(); 1.1084 + } 1.1085 + 1.1086 + virtual void Paint(nsDisplayListBuilder* aBuilder, 1.1087 + nsRenderingContext* aCtx) MOZ_OVERRIDE 1.1088 + { 1.1089 + nsImageFrame* f = static_cast<nsImageFrame*>(mFrame); 1.1090 + EventStates state = f->GetContent()->AsElement()->State(); 1.1091 + f->DisplayAltFeedback(*aCtx, 1.1092 + mVisibleRect, 1.1093 + IMAGE_OK(state, true) 1.1094 + ? nsImageFrame::gIconLoad->mLoadingImage 1.1095 + : nsImageFrame::gIconLoad->mBrokenImage, 1.1096 + ToReferenceFrame()); 1.1097 + 1.1098 + } 1.1099 + 1.1100 + NS_DISPLAY_DECL_NAME("AltFeedback", TYPE_ALT_FEEDBACK) 1.1101 +}; 1.1102 + 1.1103 +void 1.1104 +nsImageFrame::DisplayAltFeedback(nsRenderingContext& aRenderingContext, 1.1105 + const nsRect& aDirtyRect, 1.1106 + imgIRequest* aRequest, 1.1107 + nsPoint aPt) 1.1108 +{ 1.1109 + // We should definitely have a gIconLoad here. 1.1110 + NS_ABORT_IF_FALSE(gIconLoad, "How did we succeed in Init then?"); 1.1111 + 1.1112 + // Calculate the inner area 1.1113 + nsRect inner = GetInnerArea() + aPt; 1.1114 + 1.1115 + // Display a recessed one pixel border 1.1116 + nscoord borderEdgeWidth = nsPresContext::CSSPixelsToAppUnits(ALT_BORDER_WIDTH); 1.1117 + 1.1118 + // if inner area is empty, then make it big enough for at least the icon 1.1119 + if (inner.IsEmpty()){ 1.1120 + inner.SizeTo(2*(nsPresContext::CSSPixelsToAppUnits(ICON_SIZE+ICON_PADDING+ALT_BORDER_WIDTH)), 1.1121 + 2*(nsPresContext::CSSPixelsToAppUnits(ICON_SIZE+ICON_PADDING+ALT_BORDER_WIDTH))); 1.1122 + } 1.1123 + 1.1124 + // Make sure we have enough room to actually render the border within 1.1125 + // our frame bounds 1.1126 + if ((inner.width < 2 * borderEdgeWidth) || (inner.height < 2 * borderEdgeWidth)) { 1.1127 + return; 1.1128 + } 1.1129 + 1.1130 + // Paint the border 1.1131 + nsRecessedBorder recessedBorder(borderEdgeWidth, PresContext()); 1.1132 + nsCSSRendering::PaintBorderWithStyleBorder(PresContext(), aRenderingContext, 1.1133 + this, inner, inner, 1.1134 + recessedBorder, mStyleContext); 1.1135 + 1.1136 + // Adjust the inner rect to account for the one pixel recessed border, 1.1137 + // and a six pixel padding on each edge 1.1138 + inner.Deflate(nsPresContext::CSSPixelsToAppUnits(ICON_PADDING+ALT_BORDER_WIDTH), 1.1139 + nsPresContext::CSSPixelsToAppUnits(ICON_PADDING+ALT_BORDER_WIDTH)); 1.1140 + if (inner.IsEmpty()) { 1.1141 + return; 1.1142 + } 1.1143 + 1.1144 + // Clip so we don't render outside the inner rect 1.1145 + aRenderingContext.PushState(); 1.1146 + aRenderingContext.IntersectClip(inner); 1.1147 + 1.1148 + // Check if we should display image placeholders 1.1149 + if (gIconLoad->mPrefShowPlaceholders) { 1.1150 + const nsStyleVisibility* vis = StyleVisibility(); 1.1151 + nscoord size = nsPresContext::CSSPixelsToAppUnits(ICON_SIZE); 1.1152 + 1.1153 + bool iconUsed = false; 1.1154 + 1.1155 + // If we weren't previously displaying an icon, register ourselves 1.1156 + // as an observer for load and animation updates and flag that we're 1.1157 + // doing so now. 1.1158 + if (aRequest && !mDisplayingIcon) { 1.1159 + gIconLoad->AddIconObserver(this); 1.1160 + mDisplayingIcon = true; 1.1161 + } 1.1162 + 1.1163 + 1.1164 + // If the icon in question is loaded and decoded, draw it 1.1165 + uint32_t imageStatus = 0; 1.1166 + if (aRequest) 1.1167 + aRequest->GetImageStatus(&imageStatus); 1.1168 + if (imageStatus & imgIRequest::STATUS_FRAME_COMPLETE) { 1.1169 + nsCOMPtr<imgIContainer> imgCon; 1.1170 + aRequest->GetImage(getter_AddRefs(imgCon)); 1.1171 + NS_ABORT_IF_FALSE(imgCon, "Frame Complete, but no image container?"); 1.1172 + nsRect dest((vis->mDirection == NS_STYLE_DIRECTION_RTL) ? 1.1173 + inner.XMost() - size : inner.x, 1.1174 + inner.y, size, size); 1.1175 + nsLayoutUtils::DrawSingleImage(&aRenderingContext, imgCon, 1.1176 + nsLayoutUtils::GetGraphicsFilterForFrame(this), dest, aDirtyRect, 1.1177 + nullptr, imgIContainer::FLAG_NONE); 1.1178 + iconUsed = true; 1.1179 + } 1.1180 + 1.1181 + // if we could not draw the icon, flag that we're waiting for it and 1.1182 + // just draw some graffiti in the mean time 1.1183 + if (!iconUsed) { 1.1184 + nscoord iconXPos = (vis->mDirection == NS_STYLE_DIRECTION_RTL) ? 1.1185 + inner.XMost() - size : inner.x; 1.1186 + nscoord twoPX = nsPresContext::CSSPixelsToAppUnits(2); 1.1187 + aRenderingContext.DrawRect(iconXPos, inner.y,size,size); 1.1188 + aRenderingContext.PushState(); 1.1189 + aRenderingContext.SetColor(NS_RGB(0xFF,0,0)); 1.1190 + aRenderingContext.FillEllipse(size/2 + iconXPos, size/2 + inner.y, 1.1191 + size/2 - twoPX, size/2 - twoPX); 1.1192 + aRenderingContext.PopState(); 1.1193 + } 1.1194 + 1.1195 + // Reduce the inner rect by the width of the icon, and leave an 1.1196 + // additional ICON_PADDING pixels for padding 1.1197 + int32_t iconWidth = nsPresContext::CSSPixelsToAppUnits(ICON_SIZE + ICON_PADDING); 1.1198 + if (vis->mDirection != NS_STYLE_DIRECTION_RTL) 1.1199 + inner.x += iconWidth; 1.1200 + inner.width -= iconWidth; 1.1201 + } 1.1202 + 1.1203 + // If there's still room, display the alt-text 1.1204 + if (!inner.IsEmpty()) { 1.1205 + nsIContent* content = GetContent(); 1.1206 + if (content) { 1.1207 + nsXPIDLString altText; 1.1208 + nsCSSFrameConstructor::GetAlternateTextFor(content, content->Tag(), 1.1209 + altText); 1.1210 + DisplayAltText(PresContext(), aRenderingContext, altText, inner); 1.1211 + } 1.1212 + } 1.1213 + 1.1214 + aRenderingContext.PopState(); 1.1215 +} 1.1216 + 1.1217 +#ifdef DEBUG 1.1218 +static void PaintDebugImageMap(nsIFrame* aFrame, nsRenderingContext* aCtx, 1.1219 + const nsRect& aDirtyRect, nsPoint aPt) { 1.1220 + nsImageFrame* f = static_cast<nsImageFrame*>(aFrame); 1.1221 + nsRect inner = f->GetInnerArea() + aPt; 1.1222 + 1.1223 + aCtx->SetColor(NS_RGB(0, 0, 0)); 1.1224 + aCtx->PushState(); 1.1225 + aCtx->Translate(inner.TopLeft()); 1.1226 + f->GetImageMap()->Draw(aFrame, *aCtx); 1.1227 + aCtx->PopState(); 1.1228 +} 1.1229 +#endif 1.1230 + 1.1231 +void 1.1232 +nsDisplayImage::Paint(nsDisplayListBuilder* aBuilder, 1.1233 + nsRenderingContext* aCtx) { 1.1234 + uint32_t flags = imgIContainer::FLAG_NONE; 1.1235 + if (aBuilder->ShouldSyncDecodeImages()) { 1.1236 + flags |= imgIContainer::FLAG_SYNC_DECODE; 1.1237 + } 1.1238 + if (aBuilder->IsPaintingToWindow()) { 1.1239 + flags |= imgIContainer::FLAG_HIGH_QUALITY_SCALING; 1.1240 + } 1.1241 + static_cast<nsImageFrame*>(mFrame)-> 1.1242 + PaintImage(*aCtx, ToReferenceFrame(), mVisibleRect, mImage, flags); 1.1243 +} 1.1244 + 1.1245 +void 1.1246 +nsDisplayImage::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder, 1.1247 + const nsDisplayItemGeometry* aGeometry, 1.1248 + nsRegion* aInvalidRegion) 1.1249 +{ 1.1250 + if (aBuilder->ShouldSyncDecodeImages() && mImage && !mImage->IsDecoded()) { 1.1251 + bool snap; 1.1252 + aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap)); 1.1253 + } 1.1254 + 1.1255 + nsDisplayImageContainer::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion); 1.1256 +} 1.1257 + 1.1258 +already_AddRefed<ImageContainer> 1.1259 +nsDisplayImage::GetContainer(LayerManager* aManager, 1.1260 + nsDisplayListBuilder* aBuilder) 1.1261 +{ 1.1262 + nsRefPtr<ImageContainer> container; 1.1263 + nsresult rv = mImage->GetImageContainer(aManager, getter_AddRefs(container)); 1.1264 + NS_ENSURE_SUCCESS(rv, nullptr); 1.1265 + return container.forget(); 1.1266 +} 1.1267 + 1.1268 +gfxRect 1.1269 +nsDisplayImage::GetDestRect() 1.1270 +{ 1.1271 + int32_t factor = mFrame->PresContext()->AppUnitsPerDevPixel(); 1.1272 + nsImageFrame* imageFrame = static_cast<nsImageFrame*>(mFrame); 1.1273 + 1.1274 + nsRect dest = imageFrame->GetInnerArea() + ToReferenceFrame(); 1.1275 + gfxRect destRect(dest.x, dest.y, dest.width, dest.height); 1.1276 + destRect.ScaleInverse(factor); 1.1277 + 1.1278 + return destRect; 1.1279 +} 1.1280 + 1.1281 +LayerState 1.1282 +nsDisplayImage::GetLayerState(nsDisplayListBuilder* aBuilder, 1.1283 + LayerManager* aManager, 1.1284 + const ContainerLayerParameters& aParameters) 1.1285 +{ 1.1286 + bool animated = false; 1.1287 + if (!nsLayoutUtils::AnimatedImageLayersEnabled() || 1.1288 + mImage->GetType() != imgIContainer::TYPE_RASTER || 1.1289 + NS_FAILED(mImage->GetAnimated(&animated)) || 1.1290 + !animated) { 1.1291 + if (!aManager->IsCompositingCheap() || 1.1292 + !nsLayoutUtils::GPUImageScalingEnabled()) { 1.1293 + return LAYER_NONE; 1.1294 + } 1.1295 + } 1.1296 + 1.1297 + if (!animated) { 1.1298 + int32_t imageWidth; 1.1299 + int32_t imageHeight; 1.1300 + mImage->GetWidth(&imageWidth); 1.1301 + mImage->GetHeight(&imageHeight); 1.1302 + 1.1303 + NS_ASSERTION(imageWidth != 0 && imageHeight != 0, "Invalid image size!"); 1.1304 + 1.1305 + gfxRect destRect = GetDestRect(); 1.1306 + 1.1307 + destRect.width *= aParameters.mXScale; 1.1308 + destRect.height *= aParameters.mYScale; 1.1309 + 1.1310 + // Calculate the scaling factor for the frame. 1.1311 + gfxSize scale = gfxSize(destRect.width / imageWidth, 1.1312 + destRect.height / imageHeight); 1.1313 + 1.1314 + // If we are not scaling at all, no point in separating this into a layer. 1.1315 + if (scale.width == 1.0f && scale.height == 1.0f) { 1.1316 + return LAYER_NONE; 1.1317 + } 1.1318 + 1.1319 + // If the target size is pretty small, no point in using a layer. 1.1320 + if (destRect.width * destRect.height < 64 * 64) { 1.1321 + return LAYER_NONE; 1.1322 + } 1.1323 + } 1.1324 + 1.1325 + nsRefPtr<ImageContainer> container; 1.1326 + mImage->GetImageContainer(aManager, getter_AddRefs(container)); 1.1327 + if (!container) { 1.1328 + return LAYER_NONE; 1.1329 + } 1.1330 + 1.1331 + return LAYER_ACTIVE; 1.1332 +} 1.1333 + 1.1334 +already_AddRefed<Layer> 1.1335 +nsDisplayImage::BuildLayer(nsDisplayListBuilder* aBuilder, 1.1336 + LayerManager* aManager, 1.1337 + const ContainerLayerParameters& aParameters) 1.1338 +{ 1.1339 + nsRefPtr<ImageContainer> container; 1.1340 + nsresult rv = mImage->GetImageContainer(aManager, getter_AddRefs(container)); 1.1341 + NS_ENSURE_SUCCESS(rv, nullptr); 1.1342 + 1.1343 + nsRefPtr<ImageLayer> layer = static_cast<ImageLayer*> 1.1344 + (aManager->GetLayerBuilder()->GetLeafLayerFor(aBuilder, this)); 1.1345 + if (!layer) { 1.1346 + layer = aManager->CreateImageLayer(); 1.1347 + if (!layer) 1.1348 + return nullptr; 1.1349 + } 1.1350 + layer->SetContainer(container); 1.1351 + ConfigureLayer(layer, aParameters.mOffset); 1.1352 + return layer.forget(); 1.1353 +} 1.1354 + 1.1355 +void 1.1356 +nsDisplayImage::ConfigureLayer(ImageLayer *aLayer, const nsIntPoint& aOffset) 1.1357 +{ 1.1358 + aLayer->SetFilter(nsLayoutUtils::GetGraphicsFilterForFrame(mFrame)); 1.1359 + 1.1360 + int32_t imageWidth; 1.1361 + int32_t imageHeight; 1.1362 + mImage->GetWidth(&imageWidth); 1.1363 + mImage->GetHeight(&imageHeight); 1.1364 + 1.1365 + NS_ASSERTION(imageWidth != 0 && imageHeight != 0, "Invalid image size!"); 1.1366 + 1.1367 + const gfxRect destRect = GetDestRect(); 1.1368 + 1.1369 + gfx::Matrix transform; 1.1370 + gfxPoint p = destRect.TopLeft() + aOffset; 1.1371 + transform.Translate(p.x, p.y); 1.1372 + transform.Scale(destRect.Width()/imageWidth, 1.1373 + destRect.Height()/imageHeight); 1.1374 + aLayer->SetBaseTransform(gfx::Matrix4x4::From2D(transform)); 1.1375 + aLayer->SetVisibleRegion(nsIntRect(0, 0, imageWidth, imageHeight)); 1.1376 +} 1.1377 + 1.1378 +void 1.1379 +nsImageFrame::PaintImage(nsRenderingContext& aRenderingContext, nsPoint aPt, 1.1380 + const nsRect& aDirtyRect, imgIContainer* aImage, 1.1381 + uint32_t aFlags) 1.1382 +{ 1.1383 + // Render the image into our content area (the area inside 1.1384 + // the borders and padding) 1.1385 + NS_ASSERTION(GetInnerArea().width == mComputedSize.width, "bad width"); 1.1386 + nsRect inner = GetInnerArea() + aPt; 1.1387 + nsRect dest(inner.TopLeft(), mComputedSize); 1.1388 + dest.y -= GetContinuationOffset(); 1.1389 + 1.1390 + nsLayoutUtils::DrawSingleImage(&aRenderingContext, aImage, 1.1391 + nsLayoutUtils::GetGraphicsFilterForFrame(this), dest, aDirtyRect, 1.1392 + nullptr, aFlags); 1.1393 + 1.1394 + nsImageMap* map = GetImageMap(); 1.1395 + if (nullptr != map) { 1.1396 + aRenderingContext.PushState(); 1.1397 + aRenderingContext.Translate(inner.TopLeft()); 1.1398 + aRenderingContext.SetColor(NS_RGB(255, 255, 255)); 1.1399 + aRenderingContext.SetLineStyle(nsLineStyle_kSolid); 1.1400 + map->Draw(this, aRenderingContext); 1.1401 + aRenderingContext.SetColor(NS_RGB(0, 0, 0)); 1.1402 + aRenderingContext.SetLineStyle(nsLineStyle_kDotted); 1.1403 + map->Draw(this, aRenderingContext); 1.1404 + aRenderingContext.PopState(); 1.1405 + } 1.1406 +} 1.1407 + 1.1408 +void 1.1409 +nsImageFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, 1.1410 + const nsRect& aDirtyRect, 1.1411 + const nsDisplayListSet& aLists) 1.1412 +{ 1.1413 + if (!IsVisibleForPainting(aBuilder)) 1.1414 + return; 1.1415 + 1.1416 + DisplayBorderBackgroundOutline(aBuilder, aLists); 1.1417 + 1.1418 + DisplayListClipState::AutoClipContainingBlockDescendantsToContentBox 1.1419 + clip(aBuilder, this, DisplayListClipState::ASSUME_DRAWING_RESTRICTED_TO_CONTENT_RECT); 1.1420 + 1.1421 + if (mComputedSize.width != 0 && mComputedSize.height != 0) { 1.1422 + nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent); 1.1423 + NS_ASSERTION(imageLoader, "Not an image loading content?"); 1.1424 + 1.1425 + nsCOMPtr<imgIRequest> currentRequest; 1.1426 + if (imageLoader) { 1.1427 + imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST, 1.1428 + getter_AddRefs(currentRequest)); 1.1429 + } 1.1430 + 1.1431 + EventStates contentState = mContent->AsElement()->State(); 1.1432 + bool imageOK = IMAGE_OK(contentState, true); 1.1433 + 1.1434 + // XXX(seth): The SizeIsAvailable check here should not be necessary - the 1.1435 + // intention is that a non-null mImage means we have a size, but there is 1.1436 + // currently some code that violates this invariant. 1.1437 + if (!imageOK || !mImage || !SizeIsAvailable(currentRequest)) { 1.1438 + // No image yet, or image load failed. Draw the alt-text and an icon 1.1439 + // indicating the status 1.1440 + aLists.Content()->AppendNewToTop(new (aBuilder) 1.1441 + nsDisplayAltFeedback(aBuilder, this)); 1.1442 + } else { 1.1443 + aLists.Content()->AppendNewToTop(new (aBuilder) 1.1444 + nsDisplayImage(aBuilder, this, mImage)); 1.1445 + 1.1446 + // If we were previously displaying an icon, we're not anymore 1.1447 + if (mDisplayingIcon) { 1.1448 + gIconLoad->RemoveIconObserver(this); 1.1449 + mDisplayingIcon = false; 1.1450 + } 1.1451 + 1.1452 +#ifdef DEBUG 1.1453 + if (GetShowFrameBorders() && GetImageMap()) { 1.1454 + aLists.Outlines()->AppendNewToTop(new (aBuilder) 1.1455 + nsDisplayGeneric(aBuilder, this, PaintDebugImageMap, "DebugImageMap", 1.1456 + nsDisplayItem::TYPE_DEBUG_IMAGE_MAP)); 1.1457 + } 1.1458 +#endif 1.1459 + } 1.1460 + } 1.1461 + 1.1462 + if (ShouldDisplaySelection()) { 1.1463 + DisplaySelectionOverlay(aBuilder, aLists.Content(), 1.1464 + nsISelectionDisplay::DISPLAY_IMAGES); 1.1465 + } 1.1466 +} 1.1467 + 1.1468 +bool 1.1469 +nsImageFrame::ShouldDisplaySelection() 1.1470 +{ 1.1471 + // XXX what on EARTH is this code for? 1.1472 + nsresult result; 1.1473 + nsPresContext* presContext = PresContext(); 1.1474 + int16_t displaySelection = presContext->PresShell()->GetSelectionFlags(); 1.1475 + if (!(displaySelection & nsISelectionDisplay::DISPLAY_IMAGES)) 1.1476 + return false;//no need to check the blue border, we cannot be drawn selected 1.1477 +//insert hook here for image selection drawing 1.1478 +#if IMAGE_EDITOR_CHECK 1.1479 + //check to see if this frame is in an editor context 1.1480 + //isEditor check. this needs to be changed to have better way to check 1.1481 + if (displaySelection == nsISelectionDisplay::DISPLAY_ALL) 1.1482 + { 1.1483 + nsCOMPtr<nsISelectionController> selCon; 1.1484 + result = GetSelectionController(presContext, getter_AddRefs(selCon)); 1.1485 + if (NS_SUCCEEDED(result) && selCon) 1.1486 + { 1.1487 + nsCOMPtr<nsISelection> selection; 1.1488 + result = selCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection)); 1.1489 + if (NS_SUCCEEDED(result) && selection) 1.1490 + { 1.1491 + int32_t rangeCount; 1.1492 + selection->GetRangeCount(&rangeCount); 1.1493 + if (rangeCount == 1) //if not one then let code drop to nsFrame::Paint 1.1494 + { 1.1495 + nsCOMPtr<nsIContent> parentContent = mContent->GetParent(); 1.1496 + if (parentContent) 1.1497 + { 1.1498 + int32_t thisOffset = parentContent->IndexOf(mContent); 1.1499 + nsCOMPtr<nsIDOMNode> parentNode = do_QueryInterface(parentContent); 1.1500 + nsCOMPtr<nsIDOMNode> rangeNode; 1.1501 + int32_t rangeOffset; 1.1502 + nsCOMPtr<nsIDOMRange> range; 1.1503 + selection->GetRangeAt(0,getter_AddRefs(range)); 1.1504 + if (range) 1.1505 + { 1.1506 + range->GetStartContainer(getter_AddRefs(rangeNode)); 1.1507 + range->GetStartOffset(&rangeOffset); 1.1508 + 1.1509 + if (parentNode && rangeNode && (rangeNode == parentNode) && rangeOffset == thisOffset) 1.1510 + { 1.1511 + range->GetEndContainer(getter_AddRefs(rangeNode)); 1.1512 + range->GetEndOffset(&rangeOffset); 1.1513 + if ((rangeNode == parentNode) && (rangeOffset == (thisOffset +1))) //+1 since that would mean this whole content is selected only 1.1514 + return false; //do not allow nsFrame do draw any further selection 1.1515 + } 1.1516 + } 1.1517 + } 1.1518 + } 1.1519 + } 1.1520 + } 1.1521 + } 1.1522 +#endif 1.1523 + return true; 1.1524 +} 1.1525 + 1.1526 +nsImageMap* 1.1527 +nsImageFrame::GetImageMap() 1.1528 +{ 1.1529 + if (!mImageMap) { 1.1530 + nsIContent* map = GetMapElement(); 1.1531 + if (map) { 1.1532 + mImageMap = new nsImageMap(); 1.1533 + NS_ADDREF(mImageMap); 1.1534 + mImageMap->Init(this, map); 1.1535 + } 1.1536 + } 1.1537 + 1.1538 + return mImageMap; 1.1539 +} 1.1540 + 1.1541 +bool 1.1542 +nsImageFrame::IsServerImageMap() 1.1543 +{ 1.1544 + return mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::ismap); 1.1545 +} 1.1546 + 1.1547 +// Translate an point that is relative to our frame 1.1548 +// into a localized pixel coordinate that is relative to the 1.1549 +// content area of this frame (inside the border+padding). 1.1550 +void 1.1551 +nsImageFrame::TranslateEventCoords(const nsPoint& aPoint, 1.1552 + nsIntPoint& aResult) 1.1553 +{ 1.1554 + nscoord x = aPoint.x; 1.1555 + nscoord y = aPoint.y; 1.1556 + 1.1557 + // Subtract out border and padding here so that the coordinates are 1.1558 + // now relative to the content area of this frame. 1.1559 + nsRect inner = GetInnerArea(); 1.1560 + x -= inner.x; 1.1561 + y -= inner.y; 1.1562 + 1.1563 + aResult.x = nsPresContext::AppUnitsToIntCSSPixels(x); 1.1564 + aResult.y = nsPresContext::AppUnitsToIntCSSPixels(y); 1.1565 +} 1.1566 + 1.1567 +bool 1.1568 +nsImageFrame::GetAnchorHREFTargetAndNode(nsIURI** aHref, nsString& aTarget, 1.1569 + nsIContent** aNode) 1.1570 +{ 1.1571 + bool status = false; 1.1572 + aTarget.Truncate(); 1.1573 + *aHref = nullptr; 1.1574 + *aNode = nullptr; 1.1575 + 1.1576 + // Walk up the content tree, looking for an nsIDOMAnchorElement 1.1577 + for (nsIContent* content = mContent->GetParent(); 1.1578 + content; content = content->GetParent()) { 1.1579 + nsCOMPtr<dom::Link> link(do_QueryInterface(content)); 1.1580 + if (link) { 1.1581 + nsCOMPtr<nsIURI> href = content->GetHrefURI(); 1.1582 + if (href) { 1.1583 + href->Clone(aHref); 1.1584 + } 1.1585 + status = (*aHref != nullptr); 1.1586 + 1.1587 + nsCOMPtr<nsIDOMHTMLAnchorElement> anchor(do_QueryInterface(content)); 1.1588 + if (anchor) { 1.1589 + anchor->GetTarget(aTarget); 1.1590 + } 1.1591 + NS_ADDREF(*aNode = content); 1.1592 + break; 1.1593 + } 1.1594 + } 1.1595 + return status; 1.1596 +} 1.1597 + 1.1598 +nsresult 1.1599 +nsImageFrame::GetContentForEvent(WidgetEvent* aEvent, 1.1600 + nsIContent** aContent) 1.1601 +{ 1.1602 + NS_ENSURE_ARG_POINTER(aContent); 1.1603 + 1.1604 + nsIFrame* f = nsLayoutUtils::GetNonGeneratedAncestor(this); 1.1605 + if (f != this) { 1.1606 + return f->GetContentForEvent(aEvent, aContent); 1.1607 + } 1.1608 + 1.1609 + // XXX We need to make this special check for area element's capturing the 1.1610 + // mouse due to bug 135040. Remove it once that's fixed. 1.1611 + nsIContent* capturingContent = 1.1612 + aEvent->HasMouseEventMessage() ? nsIPresShell::GetCapturingContent() : 1.1613 + nullptr; 1.1614 + if (capturingContent && capturingContent->GetPrimaryFrame() == this) { 1.1615 + *aContent = capturingContent; 1.1616 + NS_IF_ADDREF(*aContent); 1.1617 + return NS_OK; 1.1618 + } 1.1619 + 1.1620 + nsImageMap* map = GetImageMap(); 1.1621 + 1.1622 + if (nullptr != map) { 1.1623 + nsIntPoint p; 1.1624 + TranslateEventCoords( 1.1625 + nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, this), p); 1.1626 + nsCOMPtr<nsIContent> area = map->GetArea(p.x, p.y); 1.1627 + if (area) { 1.1628 + area.forget(aContent); 1.1629 + return NS_OK; 1.1630 + } 1.1631 + } 1.1632 + 1.1633 + *aContent = GetContent(); 1.1634 + NS_IF_ADDREF(*aContent); 1.1635 + return NS_OK; 1.1636 +} 1.1637 + 1.1638 +// XXX what should clicks on transparent pixels do? 1.1639 +nsresult 1.1640 +nsImageFrame::HandleEvent(nsPresContext* aPresContext, 1.1641 + WidgetGUIEvent* aEvent, 1.1642 + nsEventStatus* aEventStatus) 1.1643 +{ 1.1644 + NS_ENSURE_ARG_POINTER(aEventStatus); 1.1645 + 1.1646 + if ((aEvent->message == NS_MOUSE_BUTTON_UP && 1.1647 + aEvent->AsMouseEvent()->button == WidgetMouseEvent::eLeftButton) || 1.1648 + aEvent->message == NS_MOUSE_MOVE) { 1.1649 + nsImageMap* map = GetImageMap(); 1.1650 + bool isServerMap = IsServerImageMap(); 1.1651 + if ((nullptr != map) || isServerMap) { 1.1652 + nsIntPoint p; 1.1653 + TranslateEventCoords( 1.1654 + nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, this), p); 1.1655 + bool inside = false; 1.1656 + // Even though client-side image map triggering happens 1.1657 + // through content, we need to make sure we're not inside 1.1658 + // (in case we deal with a case of both client-side and 1.1659 + // sever-side on the same image - it happens!) 1.1660 + if (nullptr != map) { 1.1661 + inside = !!map->GetArea(p.x, p.y); 1.1662 + } 1.1663 + 1.1664 + if (!inside && isServerMap) { 1.1665 + 1.1666 + // Server side image maps use the href in a containing anchor 1.1667 + // element to provide the basis for the destination url. 1.1668 + nsCOMPtr<nsIURI> uri; 1.1669 + nsAutoString target; 1.1670 + nsCOMPtr<nsIContent> anchorNode; 1.1671 + if (GetAnchorHREFTargetAndNode(getter_AddRefs(uri), target, 1.1672 + getter_AddRefs(anchorNode))) { 1.1673 + // XXX if the mouse is over/clicked in the border/padding area 1.1674 + // we should probably just pretend nothing happened. Nav4 1.1675 + // keeps the x,y coordinates positive as we do; IE doesn't 1.1676 + // bother. Both of them send the click through even when the 1.1677 + // mouse is over the border. 1.1678 + if (p.x < 0) p.x = 0; 1.1679 + if (p.y < 0) p.y = 0; 1.1680 + nsAutoCString spec; 1.1681 + uri->GetSpec(spec); 1.1682 + spec += nsPrintfCString("?%d,%d", p.x, p.y); 1.1683 + uri->SetSpec(spec); 1.1684 + 1.1685 + bool clicked = false; 1.1686 + if (aEvent->message == NS_MOUSE_BUTTON_UP) { 1.1687 + *aEventStatus = nsEventStatus_eConsumeDoDefault; 1.1688 + clicked = true; 1.1689 + } 1.1690 + nsContentUtils::TriggerLink(anchorNode, aPresContext, uri, target, 1.1691 + clicked, true, true); 1.1692 + } 1.1693 + } 1.1694 + } 1.1695 + } 1.1696 + 1.1697 + return nsSplittableFrame::HandleEvent(aPresContext, aEvent, aEventStatus); 1.1698 +} 1.1699 + 1.1700 +nsresult 1.1701 +nsImageFrame::GetCursor(const nsPoint& aPoint, 1.1702 + nsIFrame::Cursor& aCursor) 1.1703 +{ 1.1704 + nsImageMap* map = GetImageMap(); 1.1705 + if (nullptr != map) { 1.1706 + nsIntPoint p; 1.1707 + TranslateEventCoords(aPoint, p); 1.1708 + nsCOMPtr<nsIContent> area = map->GetArea(p.x, p.y); 1.1709 + if (area) { 1.1710 + // Use the cursor from the style of the *area* element. 1.1711 + // XXX Using the image as the parent style context isn't 1.1712 + // technically correct, but it's probably the right thing to do 1.1713 + // here, since it means that areas on which the cursor isn't 1.1714 + // specified will inherit the style from the image. 1.1715 + nsRefPtr<nsStyleContext> areaStyle = 1.1716 + PresContext()->PresShell()->StyleSet()-> 1.1717 + ResolveStyleFor(area->AsElement(), StyleContext()); 1.1718 + FillCursorInformationFromStyle(areaStyle->StyleUserInterface(), 1.1719 + aCursor); 1.1720 + if (NS_STYLE_CURSOR_AUTO == aCursor.mCursor) { 1.1721 + aCursor.mCursor = NS_STYLE_CURSOR_DEFAULT; 1.1722 + } 1.1723 + return NS_OK; 1.1724 + } 1.1725 + } 1.1726 + return nsFrame::GetCursor(aPoint, aCursor); 1.1727 +} 1.1728 + 1.1729 +nsresult 1.1730 +nsImageFrame::AttributeChanged(int32_t aNameSpaceID, 1.1731 + nsIAtom* aAttribute, 1.1732 + int32_t aModType) 1.1733 +{ 1.1734 + nsresult rv = nsSplittableFrame::AttributeChanged(aNameSpaceID, 1.1735 + aAttribute, aModType); 1.1736 + if (NS_FAILED(rv)) { 1.1737 + return rv; 1.1738 + } 1.1739 + if (nsGkAtoms::alt == aAttribute) 1.1740 + { 1.1741 + PresContext()->PresShell()->FrameNeedsReflow(this, 1.1742 + nsIPresShell::eStyleChange, 1.1743 + NS_FRAME_IS_DIRTY); 1.1744 + } 1.1745 + 1.1746 + return NS_OK; 1.1747 +} 1.1748 + 1.1749 +nsIAtom* 1.1750 +nsImageFrame::GetType() const 1.1751 +{ 1.1752 + return nsGkAtoms::imageFrame; 1.1753 +} 1.1754 + 1.1755 +#ifdef DEBUG_FRAME_DUMP 1.1756 +nsresult 1.1757 +nsImageFrame::GetFrameName(nsAString& aResult) const 1.1758 +{ 1.1759 + return MakeFrameName(NS_LITERAL_STRING("ImageFrame"), aResult); 1.1760 +} 1.1761 + 1.1762 +void 1.1763 +nsImageFrame::List(FILE* out, const char* aPrefix, uint32_t aFlags) const 1.1764 +{ 1.1765 + nsCString str; 1.1766 + ListGeneric(str, aPrefix, aFlags); 1.1767 + 1.1768 + // output the img src url 1.1769 + nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent); 1.1770 + if (imageLoader) { 1.1771 + nsCOMPtr<imgIRequest> currentRequest; 1.1772 + imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST, 1.1773 + getter_AddRefs(currentRequest)); 1.1774 + if (currentRequest) { 1.1775 + nsCOMPtr<nsIURI> uri; 1.1776 + currentRequest->GetURI(getter_AddRefs(uri)); 1.1777 + nsAutoCString uristr; 1.1778 + uri->GetAsciiSpec(uristr); 1.1779 + str += nsPrintfCString(" [src=%s]", uristr.get()); 1.1780 + } 1.1781 + } 1.1782 + fprintf_stderr(out, "%s\n", str.get()); 1.1783 +} 1.1784 +#endif 1.1785 + 1.1786 +int 1.1787 +nsImageFrame::GetLogicalSkipSides(const nsHTMLReflowState* aReflowState) const 1.1788 +{ 1.1789 + int skip = 0; 1.1790 + if (nullptr != GetPrevInFlow()) { 1.1791 + skip |= LOGICAL_SIDE_B_START; 1.1792 + } 1.1793 + if (nullptr != GetNextInFlow()) { 1.1794 + skip |= LOGICAL_SIDE_B_END; 1.1795 + } 1.1796 + return skip; 1.1797 +} 1.1798 + 1.1799 +nsresult 1.1800 +nsImageFrame::GetIntrinsicImageSize(nsSize& aSize) 1.1801 +{ 1.1802 + if (mIntrinsicSize.width.GetUnit() == eStyleUnit_Coord && 1.1803 + mIntrinsicSize.height.GetUnit() == eStyleUnit_Coord) { 1.1804 + aSize.SizeTo(mIntrinsicSize.width.GetCoordValue(), 1.1805 + mIntrinsicSize.height.GetCoordValue()); 1.1806 + return NS_OK; 1.1807 + } 1.1808 + 1.1809 + return NS_ERROR_FAILURE; 1.1810 +} 1.1811 + 1.1812 +nsresult 1.1813 +nsImageFrame::LoadIcon(const nsAString& aSpec, 1.1814 + nsPresContext *aPresContext, 1.1815 + imgRequestProxy** aRequest) 1.1816 +{ 1.1817 + nsresult rv = NS_OK; 1.1818 + NS_PRECONDITION(!aSpec.IsEmpty(), "What happened??"); 1.1819 + NS_PRECONDITION(aPresContext, "NULL PresContext"); 1.1820 + 1.1821 + if (!sIOService) { 1.1822 + rv = CallGetService(NS_IOSERVICE_CONTRACTID, &sIOService); 1.1823 + NS_ENSURE_SUCCESS(rv, rv); 1.1824 + } 1.1825 + 1.1826 + nsCOMPtr<nsIURI> realURI; 1.1827 + SpecToURI(aSpec, sIOService, getter_AddRefs(realURI)); 1.1828 + 1.1829 + nsRefPtr<imgLoader> il = 1.1830 + nsContentUtils::GetImgLoaderForDocument(aPresContext->Document()); 1.1831 + 1.1832 + nsCOMPtr<nsILoadGroup> loadGroup; 1.1833 + GetLoadGroup(aPresContext, getter_AddRefs(loadGroup)); 1.1834 + 1.1835 + // For icon loads, we don't need to merge with the loadgroup flags 1.1836 + nsLoadFlags loadFlags = nsIRequest::LOAD_NORMAL; 1.1837 + 1.1838 + nsCOMPtr<nsIURI> firstPartyIsolationURI; 1.1839 + nsCOMPtr<mozIThirdPartyUtil> thirdPartySvc 1.1840 + = do_GetService(THIRDPARTYUTIL_CONTRACTID); 1.1841 + // XXX: Should we pass the loadgroup, too? Is document ever likely 1.1842 + // to be unset? 1.1843 + thirdPartySvc->GetFirstPartyIsolationURI(nullptr, aPresContext->Document(), 1.1844 + getter_AddRefs(firstPartyIsolationURI)); 1.1845 + 1.1846 + return il->LoadImage(realURI, /* icon URI */ 1.1847 + firstPartyIsolationURI, /* initial document URI; this is only 1.1848 + relevant for cookies, so does not 1.1849 + apply to icons. */ 1.1850 + nullptr, /* referrer (not relevant for icons) */ 1.1851 + nullptr, /* principal (not relevant for icons) */ 1.1852 + loadGroup, 1.1853 + gIconLoad, 1.1854 + nullptr, /* Not associated with any particular document */ 1.1855 + loadFlags, 1.1856 + nullptr, 1.1857 + nullptr, /* channel policy not needed */ 1.1858 + EmptyString(), 1.1859 + aRequest); 1.1860 +} 1.1861 + 1.1862 +void 1.1863 +nsImageFrame::GetDocumentCharacterSet(nsACString& aCharset) const 1.1864 +{ 1.1865 + if (mContent) { 1.1866 + NS_ASSERTION(mContent->GetDocument(), 1.1867 + "Frame still alive after content removed from document!"); 1.1868 + aCharset = mContent->GetDocument()->GetDocumentCharacterSet(); 1.1869 + } 1.1870 +} 1.1871 + 1.1872 +void 1.1873 +nsImageFrame::SpecToURI(const nsAString& aSpec, nsIIOService *aIOService, 1.1874 + nsIURI **aURI) 1.1875 +{ 1.1876 + nsCOMPtr<nsIURI> baseURI; 1.1877 + if (mContent) { 1.1878 + baseURI = mContent->GetBaseURI(); 1.1879 + } 1.1880 + nsAutoCString charset; 1.1881 + GetDocumentCharacterSet(charset); 1.1882 + NS_NewURI(aURI, aSpec, 1.1883 + charset.IsEmpty() ? nullptr : charset.get(), 1.1884 + baseURI, aIOService); 1.1885 +} 1.1886 + 1.1887 +void 1.1888 +nsImageFrame::GetLoadGroup(nsPresContext *aPresContext, nsILoadGroup **aLoadGroup) 1.1889 +{ 1.1890 + if (!aPresContext) 1.1891 + return; 1.1892 + 1.1893 + NS_PRECONDITION(nullptr != aLoadGroup, "null OUT parameter pointer"); 1.1894 + 1.1895 + nsIPresShell *shell = aPresContext->GetPresShell(); 1.1896 + 1.1897 + if (!shell) 1.1898 + return; 1.1899 + 1.1900 + nsIDocument *doc = shell->GetDocument(); 1.1901 + if (!doc) 1.1902 + return; 1.1903 + 1.1904 + *aLoadGroup = doc->GetDocumentLoadGroup().take(); 1.1905 +} 1.1906 + 1.1907 +nsresult nsImageFrame::LoadIcons(nsPresContext *aPresContext) 1.1908 +{ 1.1909 + NS_ASSERTION(!gIconLoad, "called LoadIcons twice"); 1.1910 + 1.1911 + NS_NAMED_LITERAL_STRING(loadingSrc,"resource://gre-resources/loading-image.png"); 1.1912 + NS_NAMED_LITERAL_STRING(brokenSrc,"resource://gre-resources/broken-image.png"); 1.1913 + 1.1914 + gIconLoad = new IconLoad(); 1.1915 + NS_ADDREF(gIconLoad); 1.1916 + 1.1917 + nsresult rv; 1.1918 + // create a loader and load the images 1.1919 + rv = LoadIcon(loadingSrc, 1.1920 + aPresContext, 1.1921 + getter_AddRefs(gIconLoad->mLoadingImage)); 1.1922 + if (NS_FAILED(rv)) { 1.1923 + return rv; 1.1924 + } 1.1925 + 1.1926 + rv = LoadIcon(brokenSrc, 1.1927 + aPresContext, 1.1928 + getter_AddRefs(gIconLoad->mBrokenImage)); 1.1929 + return rv; 1.1930 +} 1.1931 + 1.1932 +NS_IMPL_ISUPPORTS(nsImageFrame::IconLoad, nsIObserver, 1.1933 + imgINotificationObserver) 1.1934 + 1.1935 +static const char* kIconLoadPrefs[] = { 1.1936 + "browser.display.force_inline_alttext", 1.1937 + "browser.display.show_image_placeholders", 1.1938 + nullptr 1.1939 +}; 1.1940 + 1.1941 +nsImageFrame::IconLoad::IconLoad() 1.1942 +{ 1.1943 + // register observers 1.1944 + Preferences::AddStrongObservers(this, kIconLoadPrefs); 1.1945 + GetPrefs(); 1.1946 +} 1.1947 + 1.1948 +void 1.1949 +nsImageFrame::IconLoad::Shutdown() 1.1950 +{ 1.1951 + Preferences::RemoveObservers(this, kIconLoadPrefs); 1.1952 + // in case the pref service releases us later 1.1953 + if (mLoadingImage) { 1.1954 + mLoadingImage->CancelAndForgetObserver(NS_ERROR_FAILURE); 1.1955 + mLoadingImage = nullptr; 1.1956 + } 1.1957 + if (mBrokenImage) { 1.1958 + mBrokenImage->CancelAndForgetObserver(NS_ERROR_FAILURE); 1.1959 + mBrokenImage = nullptr; 1.1960 + } 1.1961 +} 1.1962 + 1.1963 +NS_IMETHODIMP 1.1964 +nsImageFrame::IconLoad::Observe(nsISupports *aSubject, const char* aTopic, 1.1965 + const char16_t* aData) 1.1966 +{ 1.1967 + NS_ASSERTION(!nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID), 1.1968 + "wrong topic"); 1.1969 +#ifdef DEBUG 1.1970 + // assert |aData| is one of our prefs. 1.1971 + for (uint32_t i = 0; i < ArrayLength(kIconLoadPrefs) || 1.1972 + (NS_NOTREACHED("wrong pref"), false); ++i) 1.1973 + if (NS_ConvertASCIItoUTF16(kIconLoadPrefs[i]) == nsDependentString(aData)) 1.1974 + break; 1.1975 +#endif 1.1976 + 1.1977 + GetPrefs(); 1.1978 + return NS_OK; 1.1979 +} 1.1980 + 1.1981 +void nsImageFrame::IconLoad::GetPrefs() 1.1982 +{ 1.1983 + mPrefForceInlineAltText = 1.1984 + Preferences::GetBool("browser.display.force_inline_alttext"); 1.1985 + 1.1986 + mPrefShowPlaceholders = 1.1987 + Preferences::GetBool("browser.display.show_image_placeholders", true); 1.1988 +} 1.1989 + 1.1990 +NS_IMETHODIMP 1.1991 +nsImageFrame::IconLoad::Notify(imgIRequest *aRequest, int32_t aType, const nsIntRect* aData) 1.1992 +{ 1.1993 + if (aType != imgINotificationObserver::LOAD_COMPLETE && 1.1994 + aType != imgINotificationObserver::FRAME_UPDATE) { 1.1995 + return NS_OK; 1.1996 + } 1.1997 + 1.1998 + nsTObserverArray<nsImageFrame*>::ForwardIterator iter(mIconObservers); 1.1999 + nsImageFrame *frame; 1.2000 + while (iter.HasMore()) { 1.2001 + frame = iter.GetNext(); 1.2002 + frame->InvalidateFrame(); 1.2003 + } 1.2004 + 1.2005 + return NS_OK; 1.2006 +} 1.2007 + 1.2008 +NS_IMPL_ISUPPORTS(nsImageListener, imgINotificationObserver) 1.2009 + 1.2010 +nsImageListener::nsImageListener(nsImageFrame *aFrame) : 1.2011 + mFrame(aFrame) 1.2012 +{ 1.2013 +} 1.2014 + 1.2015 +nsImageListener::~nsImageListener() 1.2016 +{ 1.2017 +} 1.2018 + 1.2019 +NS_IMETHODIMP 1.2020 +nsImageListener::Notify(imgIRequest *aRequest, int32_t aType, const nsIntRect* aData) 1.2021 +{ 1.2022 + if (!mFrame) 1.2023 + return NS_ERROR_FAILURE; 1.2024 + 1.2025 + return mFrame->Notify(aRequest, aType, aData); 1.2026 +} 1.2027 + 1.2028 +static bool 1.2029 +IsInAutoWidthTableCellForQuirk(nsIFrame *aFrame) 1.2030 +{ 1.2031 + if (eCompatibility_NavQuirks != aFrame->PresContext()->CompatibilityMode()) 1.2032 + return false; 1.2033 + // Check if the parent of the closest nsBlockFrame has auto width. 1.2034 + nsBlockFrame *ancestor = nsLayoutUtils::FindNearestBlockAncestor(aFrame); 1.2035 + if (ancestor->StyleContext()->GetPseudo() == nsCSSAnonBoxes::cellContent) { 1.2036 + // Assume direct parent is a table cell frame. 1.2037 + nsFrame *grandAncestor = static_cast<nsFrame*>(ancestor->GetParent()); 1.2038 + return grandAncestor && 1.2039 + grandAncestor->StylePosition()->mWidth.GetUnit() == eStyleUnit_Auto; 1.2040 + } 1.2041 + return false; 1.2042 +} 1.2043 + 1.2044 +/* virtual */ void 1.2045 +nsImageFrame::AddInlineMinWidth(nsRenderingContext *aRenderingContext, 1.2046 + nsIFrame::InlineMinWidthData *aData) 1.2047 +{ 1.2048 + 1.2049 + NS_ASSERTION(GetParent(), "Must have a parent if we get here!"); 1.2050 + 1.2051 + nsIFrame* parent = GetParent(); 1.2052 + bool canBreak = 1.2053 + !CanContinueTextRun() && 1.2054 + parent->StyleText()->WhiteSpaceCanWrap(parent) && 1.2055 + !IsInAutoWidthTableCellForQuirk(this); 1.2056 + 1.2057 + if (canBreak) 1.2058 + aData->OptionallyBreak(aRenderingContext); 1.2059 + 1.2060 + aData->trailingWhitespace = 0; 1.2061 + aData->skipWhitespace = false; 1.2062 + aData->trailingTextFrame = nullptr; 1.2063 + aData->currentLine += nsLayoutUtils::IntrinsicForContainer(aRenderingContext, 1.2064 + this, nsLayoutUtils::MIN_WIDTH); 1.2065 + aData->atStartOfLine = false; 1.2066 + 1.2067 + if (canBreak) 1.2068 + aData->OptionallyBreak(aRenderingContext); 1.2069 + 1.2070 +}