1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/layout/xul/nsImageBoxFrame.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,771 @@ 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 +// 1.10 +// Eric Vaughan 1.11 +// Netscape Communications 1.12 +// 1.13 +// See documentation in associated header file 1.14 +// 1.15 + 1.16 +#include "nsImageBoxFrame.h" 1.17 +#include "nsGkAtoms.h" 1.18 +#include "nsStyleContext.h" 1.19 +#include "nsStyleConsts.h" 1.20 +#include "nsCOMPtr.h" 1.21 +#include "nsPresContext.h" 1.22 +#include "nsBoxLayoutState.h" 1.23 + 1.24 +#include "nsHTMLParts.h" 1.25 +#include "nsString.h" 1.26 +#include "nsLeafFrame.h" 1.27 +#include "nsIPresShell.h" 1.28 +#include "nsIDocument.h" 1.29 +#include "nsImageMap.h" 1.30 +#include "nsILinkHandler.h" 1.31 +#include "nsIURL.h" 1.32 +#include "nsILoadGroup.h" 1.33 +#include "nsContainerFrame.h" 1.34 +#include "prprf.h" 1.35 +#include "nsCSSRendering.h" 1.36 +#include "nsIDOMHTMLImageElement.h" 1.37 +#include "nsNameSpaceManager.h" 1.38 +#include "nsTextFragment.h" 1.39 +#include "nsIDOMHTMLMapElement.h" 1.40 +#include "nsTransform2D.h" 1.41 +#include "nsITheme.h" 1.42 + 1.43 +#include "nsIServiceManager.h" 1.44 +#include "nsIURI.h" 1.45 +#include "nsNetUtil.h" 1.46 +#include "nsThreadUtils.h" 1.47 +#include "nsDisplayList.h" 1.48 +#include "ImageLayers.h" 1.49 +#include "ImageContainer.h" 1.50 + 1.51 +#include "nsContentUtils.h" 1.52 + 1.53 +#include "mozilla/BasicEvents.h" 1.54 +#include "mozilla/EventDispatcher.h" 1.55 + 1.56 +#define ONLOAD_CALLED_TOO_EARLY 1 1.57 + 1.58 +using namespace mozilla; 1.59 +using namespace mozilla::layers; 1.60 + 1.61 +class nsImageBoxFrameEvent : public nsRunnable 1.62 +{ 1.63 +public: 1.64 + nsImageBoxFrameEvent(nsIContent *content, uint32_t message) 1.65 + : mContent(content), mMessage(message) {} 1.66 + 1.67 + NS_IMETHOD Run() MOZ_OVERRIDE; 1.68 + 1.69 +private: 1.70 + nsCOMPtr<nsIContent> mContent; 1.71 + uint32_t mMessage; 1.72 +}; 1.73 + 1.74 +NS_IMETHODIMP 1.75 +nsImageBoxFrameEvent::Run() 1.76 +{ 1.77 + nsIPresShell *pres_shell = mContent->OwnerDoc()->GetShell(); 1.78 + if (!pres_shell) { 1.79 + return NS_OK; 1.80 + } 1.81 + 1.82 + nsRefPtr<nsPresContext> pres_context = pres_shell->GetPresContext(); 1.83 + if (!pres_context) { 1.84 + return NS_OK; 1.85 + } 1.86 + 1.87 + nsEventStatus status = nsEventStatus_eIgnore; 1.88 + WidgetEvent event(true, mMessage); 1.89 + 1.90 + event.mFlags.mBubbles = false; 1.91 + EventDispatcher::Dispatch(mContent, pres_context, &event, nullptr, &status); 1.92 + return NS_OK; 1.93 +} 1.94 + 1.95 +// Fire off an event that'll asynchronously call the image elements 1.96 +// onload handler once handled. This is needed since the image library 1.97 +// can't decide if it wants to call it's observer methods 1.98 +// synchronously or asynchronously. If an image is loaded from the 1.99 +// cache the notifications come back synchronously, but if the image 1.100 +// is loaded from the netswork the notifications come back 1.101 +// asynchronously. 1.102 + 1.103 +void 1.104 +FireImageDOMEvent(nsIContent* aContent, uint32_t aMessage) 1.105 +{ 1.106 + NS_ASSERTION(aMessage == NS_LOAD || aMessage == NS_LOAD_ERROR, 1.107 + "invalid message"); 1.108 + 1.109 + nsCOMPtr<nsIRunnable> event = new nsImageBoxFrameEvent(aContent, aMessage); 1.110 + if (NS_FAILED(NS_DispatchToCurrentThread(event))) 1.111 + NS_WARNING("failed to dispatch image event"); 1.112 +} 1.113 + 1.114 +// 1.115 +// NS_NewImageBoxFrame 1.116 +// 1.117 +// Creates a new image frame and returns it 1.118 +// 1.119 +nsIFrame* 1.120 +NS_NewImageBoxFrame (nsIPresShell* aPresShell, nsStyleContext* aContext) 1.121 +{ 1.122 + return new (aPresShell) nsImageBoxFrame (aPresShell, aContext); 1.123 +} 1.124 + 1.125 +NS_IMPL_FRAMEARENA_HELPERS(nsImageBoxFrame) 1.126 + 1.127 +nsresult 1.128 +nsImageBoxFrame::AttributeChanged(int32_t aNameSpaceID, 1.129 + nsIAtom* aAttribute, 1.130 + int32_t aModType) 1.131 +{ 1.132 + nsresult rv = nsLeafBoxFrame::AttributeChanged(aNameSpaceID, aAttribute, 1.133 + aModType); 1.134 + 1.135 + if (aAttribute == nsGkAtoms::src) { 1.136 + UpdateImage(); 1.137 + PresContext()->PresShell()-> 1.138 + FrameNeedsReflow(this, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY); 1.139 + } 1.140 + else if (aAttribute == nsGkAtoms::validate) 1.141 + UpdateLoadFlags(); 1.142 + 1.143 + return rv; 1.144 +} 1.145 + 1.146 +nsImageBoxFrame::nsImageBoxFrame(nsIPresShell* aShell, nsStyleContext* aContext): 1.147 + nsLeafBoxFrame(aShell, aContext), 1.148 + mIntrinsicSize(0,0), 1.149 + mRequestRegistered(false), 1.150 + mLoadFlags(nsIRequest::LOAD_NORMAL), 1.151 + mUseSrcAttr(false), 1.152 + mSuppressStyleCheck(false), 1.153 + mFireEventOnDecode(false) 1.154 +{ 1.155 + MarkIntrinsicWidthsDirty(); 1.156 +} 1.157 + 1.158 +nsImageBoxFrame::~nsImageBoxFrame() 1.159 +{ 1.160 +} 1.161 + 1.162 + 1.163 +/* virtual */ void 1.164 +nsImageBoxFrame::MarkIntrinsicWidthsDirty() 1.165 +{ 1.166 + SizeNeedsRecalc(mImageSize); 1.167 + nsLeafBoxFrame::MarkIntrinsicWidthsDirty(); 1.168 +} 1.169 + 1.170 +void 1.171 +nsImageBoxFrame::DestroyFrom(nsIFrame* aDestructRoot) 1.172 +{ 1.173 + if (mImageRequest) { 1.174 + nsLayoutUtils::DeregisterImageRequest(PresContext(), mImageRequest, 1.175 + &mRequestRegistered); 1.176 + 1.177 + // Release image loader first so that it's refcnt can go to zero 1.178 + mImageRequest->CancelAndForgetObserver(NS_ERROR_FAILURE); 1.179 + } 1.180 + 1.181 + if (mListener) 1.182 + reinterpret_cast<nsImageBoxListener*>(mListener.get())->SetFrame(nullptr); // set the frame to null so we don't send messages to a dead object. 1.183 + 1.184 + nsLeafBoxFrame::DestroyFrom(aDestructRoot); 1.185 +} 1.186 + 1.187 + 1.188 +void 1.189 +nsImageBoxFrame::Init(nsIContent* aContent, 1.190 + nsIFrame* aParent, 1.191 + nsIFrame* aPrevInFlow) 1.192 +{ 1.193 + if (!mListener) { 1.194 + nsImageBoxListener *listener = new nsImageBoxListener(); 1.195 + NS_ADDREF(listener); 1.196 + listener->SetFrame(this); 1.197 + listener->QueryInterface(NS_GET_IID(imgINotificationObserver), getter_AddRefs(mListener)); 1.198 + NS_RELEASE(listener); 1.199 + } 1.200 + 1.201 + mSuppressStyleCheck = true; 1.202 + nsLeafBoxFrame::Init(aContent, aParent, aPrevInFlow); 1.203 + mSuppressStyleCheck = false; 1.204 + 1.205 + UpdateLoadFlags(); 1.206 + UpdateImage(); 1.207 +} 1.208 + 1.209 +void 1.210 +nsImageBoxFrame::UpdateImage() 1.211 +{ 1.212 + nsPresContext* presContext = PresContext(); 1.213 + 1.214 + if (mImageRequest) { 1.215 + nsLayoutUtils::DeregisterImageRequest(presContext, mImageRequest, 1.216 + &mRequestRegistered); 1.217 + mImageRequest->CancelAndForgetObserver(NS_ERROR_FAILURE); 1.218 + mImageRequest = nullptr; 1.219 + } 1.220 + 1.221 + // get the new image src 1.222 + nsAutoString src; 1.223 + mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::src, src); 1.224 + mUseSrcAttr = !src.IsEmpty(); 1.225 + if (mUseSrcAttr) { 1.226 + nsIDocument* doc = mContent->GetDocument(); 1.227 + if (!doc) { 1.228 + // No need to do anything here... 1.229 + return; 1.230 + } 1.231 + nsCOMPtr<nsIURI> baseURI = mContent->GetBaseURI(); 1.232 + nsCOMPtr<nsIURI> uri; 1.233 + nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(uri), 1.234 + src, 1.235 + doc, 1.236 + baseURI); 1.237 + 1.238 + if (uri && nsContentUtils::CanLoadImage(uri, mContent, doc, 1.239 + mContent->NodePrincipal())) { 1.240 + nsContentUtils::LoadImage(uri, doc, mContent->NodePrincipal(), 1.241 + doc->GetDocumentURI(), mListener, mLoadFlags, 1.242 + EmptyString(), getter_AddRefs(mImageRequest)); 1.243 + 1.244 + if (mImageRequest) { 1.245 + nsLayoutUtils::RegisterImageRequestIfAnimated(presContext, 1.246 + mImageRequest, 1.247 + &mRequestRegistered); 1.248 + } 1.249 + } 1.250 + } else { 1.251 + // Only get the list-style-image if we aren't being drawn 1.252 + // by a native theme. 1.253 + uint8_t appearance = StyleDisplay()->mAppearance; 1.254 + if (!(appearance && nsBox::gTheme && 1.255 + nsBox::gTheme->ThemeSupportsWidget(nullptr, this, appearance))) { 1.256 + // get the list-style-image 1.257 + imgRequestProxy *styleRequest = StyleList()->GetListStyleImage(); 1.258 + if (styleRequest) { 1.259 + styleRequest->Clone(mListener, getter_AddRefs(mImageRequest)); 1.260 + } 1.261 + } 1.262 + } 1.263 + 1.264 + if (!mImageRequest) { 1.265 + // We have no image, so size to 0 1.266 + mIntrinsicSize.SizeTo(0, 0); 1.267 + } else { 1.268 + // We don't want discarding or decode-on-draw for xul images. 1.269 + mImageRequest->StartDecoding(); 1.270 + mImageRequest->LockImage(); 1.271 + } 1.272 +} 1.273 + 1.274 +void 1.275 +nsImageBoxFrame::UpdateLoadFlags() 1.276 +{ 1.277 + static nsIContent::AttrValuesArray strings[] = 1.278 + {&nsGkAtoms::always, &nsGkAtoms::never, nullptr}; 1.279 + switch (mContent->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::validate, 1.280 + strings, eCaseMatters)) { 1.281 + case 0: 1.282 + mLoadFlags = nsIRequest::VALIDATE_ALWAYS; 1.283 + break; 1.284 + case 1: 1.285 + mLoadFlags = nsIRequest::VALIDATE_NEVER|nsIRequest::LOAD_FROM_CACHE; 1.286 + break; 1.287 + default: 1.288 + mLoadFlags = nsIRequest::LOAD_NORMAL; 1.289 + break; 1.290 + } 1.291 +} 1.292 + 1.293 +void 1.294 +nsImageBoxFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, 1.295 + const nsRect& aDirtyRect, 1.296 + const nsDisplayListSet& aLists) 1.297 +{ 1.298 + nsLeafBoxFrame::BuildDisplayList(aBuilder, aDirtyRect, aLists); 1.299 + 1.300 + if ((0 == mRect.width) || (0 == mRect.height)) { 1.301 + // Do not render when given a zero area. This avoids some useless 1.302 + // scaling work while we wait for our image dimensions to arrive 1.303 + // asynchronously. 1.304 + return; 1.305 + } 1.306 + 1.307 + if (!IsVisibleForPainting(aBuilder)) 1.308 + return; 1.309 + 1.310 + nsDisplayList list; 1.311 + list.AppendNewToTop( 1.312 + new (aBuilder) nsDisplayXULImage(aBuilder, this)); 1.313 + 1.314 + CreateOwnLayerIfNeeded(aBuilder, &list); 1.315 + 1.316 + aLists.Content()->AppendToTop(&list); 1.317 +} 1.318 + 1.319 +void 1.320 +nsImageBoxFrame::PaintImage(nsRenderingContext& aRenderingContext, 1.321 + const nsRect& aDirtyRect, nsPoint aPt, 1.322 + uint32_t aFlags) 1.323 +{ 1.324 + nsRect rect; 1.325 + GetClientRect(rect); 1.326 + 1.327 + rect += aPt; 1.328 + 1.329 + if (!mImageRequest) 1.330 + return; 1.331 + 1.332 + // don't draw if the image is not dirty 1.333 + nsRect dirty; 1.334 + if (!dirty.IntersectRect(aDirtyRect, rect)) 1.335 + return; 1.336 + 1.337 + nsCOMPtr<imgIContainer> imgCon; 1.338 + mImageRequest->GetImage(getter_AddRefs(imgCon)); 1.339 + 1.340 + if (imgCon) { 1.341 + bool hasSubRect = !mUseSrcAttr && (mSubRect.width > 0 || mSubRect.height > 0); 1.342 + nsLayoutUtils::DrawSingleImage(&aRenderingContext, imgCon, 1.343 + nsLayoutUtils::GetGraphicsFilterForFrame(this), 1.344 + rect, dirty, nullptr, aFlags, hasSubRect ? &mSubRect : nullptr); 1.345 + } 1.346 +} 1.347 + 1.348 +void nsDisplayXULImage::Paint(nsDisplayListBuilder* aBuilder, 1.349 + nsRenderingContext* aCtx) 1.350 +{ 1.351 + uint32_t flags = imgIContainer::FLAG_NONE; 1.352 + if (aBuilder->ShouldSyncDecodeImages()) 1.353 + flags |= imgIContainer::FLAG_SYNC_DECODE; 1.354 + if (aBuilder->IsPaintingToWindow()) 1.355 + flags |= imgIContainer::FLAG_HIGH_QUALITY_SCALING; 1.356 + 1.357 + static_cast<nsImageBoxFrame*>(mFrame)-> 1.358 + PaintImage(*aCtx, mVisibleRect, ToReferenceFrame(), flags); 1.359 +} 1.360 + 1.361 +void 1.362 +nsDisplayXULImage::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder, 1.363 + const nsDisplayItemGeometry* aGeometry, 1.364 + nsRegion* aInvalidRegion) 1.365 +{ 1.366 + 1.367 + if (aBuilder->ShouldSyncDecodeImages()) { 1.368 + nsImageBoxFrame* boxFrame = static_cast<nsImageBoxFrame*>(mFrame); 1.369 + nsCOMPtr<imgIContainer> image; 1.370 + if (boxFrame->mImageRequest) { 1.371 + boxFrame->mImageRequest->GetImage(getter_AddRefs(image)); 1.372 + } 1.373 + 1.374 + if (image && !image->IsDecoded()) { 1.375 + bool snap; 1.376 + aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap)); 1.377 + } 1.378 + } 1.379 + 1.380 + nsDisplayImageContainer::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion); 1.381 +} 1.382 + 1.383 +void 1.384 +nsDisplayXULImage::ConfigureLayer(ImageLayer* aLayer, const nsIntPoint& aOffset) 1.385 +{ 1.386 + aLayer->SetFilter(nsLayoutUtils::GetGraphicsFilterForFrame(mFrame)); 1.387 + 1.388 + int32_t factor = mFrame->PresContext()->AppUnitsPerDevPixel(); 1.389 + nsImageBoxFrame* imageFrame = static_cast<nsImageBoxFrame*>(mFrame); 1.390 + 1.391 + nsRect dest; 1.392 + imageFrame->GetClientRect(dest); 1.393 + dest += ToReferenceFrame(); 1.394 + gfxRect destRect(dest.x, dest.y, dest.width, dest.height); 1.395 + destRect.ScaleInverse(factor); 1.396 + 1.397 + nsCOMPtr<imgIContainer> imgCon; 1.398 + imageFrame->mImageRequest->GetImage(getter_AddRefs(imgCon)); 1.399 + int32_t imageWidth; 1.400 + int32_t imageHeight; 1.401 + imgCon->GetWidth(&imageWidth); 1.402 + imgCon->GetHeight(&imageHeight); 1.403 + 1.404 + NS_ASSERTION(imageWidth != 0 && imageHeight != 0, "Invalid image size!"); 1.405 + 1.406 + gfxPoint p = destRect.TopLeft() + aOffset; 1.407 + gfx::Matrix transform; 1.408 + transform.Translate(p.x, p.y); 1.409 + transform.Scale(destRect.Width()/imageWidth, 1.410 + destRect.Height()/imageHeight); 1.411 + aLayer->SetBaseTransform(gfx::Matrix4x4::From2D(transform)); 1.412 + 1.413 + aLayer->SetVisibleRegion(nsIntRect(0, 0, imageWidth, imageHeight)); 1.414 +} 1.415 + 1.416 +already_AddRefed<ImageContainer> 1.417 +nsDisplayXULImage::GetContainer(LayerManager* aManager, nsDisplayListBuilder* aBuilder) 1.418 +{ 1.419 + return static_cast<nsImageBoxFrame*>(mFrame)->GetContainer(aManager); 1.420 +} 1.421 + 1.422 +already_AddRefed<ImageContainer> 1.423 +nsImageBoxFrame::GetContainer(LayerManager* aManager) 1.424 +{ 1.425 + bool hasSubRect = !mUseSrcAttr && (mSubRect.width > 0 || mSubRect.height > 0); 1.426 + if (hasSubRect || !mImageRequest) { 1.427 + return nullptr; 1.428 + } 1.429 + 1.430 + nsCOMPtr<imgIContainer> imgCon; 1.431 + mImageRequest->GetImage(getter_AddRefs(imgCon)); 1.432 + if (!imgCon) { 1.433 + return nullptr; 1.434 + } 1.435 + 1.436 + nsRefPtr<ImageContainer> container; 1.437 + nsresult rv = imgCon->GetImageContainer(aManager, getter_AddRefs(container)); 1.438 + NS_ENSURE_SUCCESS(rv, nullptr); 1.439 + return container.forget(); 1.440 +} 1.441 + 1.442 + 1.443 +// 1.444 +// DidSetStyleContext 1.445 +// 1.446 +// When the style context changes, make sure that all of our image is up to date. 1.447 +// 1.448 +/* virtual */ void 1.449 +nsImageBoxFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext) 1.450 +{ 1.451 + nsLeafBoxFrame::DidSetStyleContext(aOldStyleContext); 1.452 + 1.453 + // Fetch our subrect. 1.454 + const nsStyleList* myList = StyleList(); 1.455 + mSubRect = myList->mImageRegion; // before |mSuppressStyleCheck| test! 1.456 + 1.457 + if (mUseSrcAttr || mSuppressStyleCheck) 1.458 + return; // No more work required, since the image isn't specified by style. 1.459 + 1.460 + // If we're using a native theme implementation, we shouldn't draw anything. 1.461 + const nsStyleDisplay* disp = StyleDisplay(); 1.462 + if (disp->mAppearance && nsBox::gTheme && 1.463 + nsBox::gTheme->ThemeSupportsWidget(nullptr, this, disp->mAppearance)) 1.464 + return; 1.465 + 1.466 + // If list-style-image changes, we have a new image. 1.467 + nsCOMPtr<nsIURI> oldURI, newURI; 1.468 + if (mImageRequest) 1.469 + mImageRequest->GetURI(getter_AddRefs(oldURI)); 1.470 + if (myList->GetListStyleImage()) 1.471 + myList->GetListStyleImage()->GetURI(getter_AddRefs(newURI)); 1.472 + bool equal; 1.473 + if (newURI == oldURI || // handles null==null 1.474 + (newURI && oldURI && 1.475 + NS_SUCCEEDED(newURI->Equals(oldURI, &equal)) && equal)) 1.476 + return; 1.477 + 1.478 + UpdateImage(); 1.479 +} // DidSetStyleContext 1.480 + 1.481 +void 1.482 +nsImageBoxFrame::GetImageSize() 1.483 +{ 1.484 + if (mIntrinsicSize.width > 0 && mIntrinsicSize.height > 0) { 1.485 + mImageSize.width = mIntrinsicSize.width; 1.486 + mImageSize.height = mIntrinsicSize.height; 1.487 + } else { 1.488 + mImageSize.width = 0; 1.489 + mImageSize.height = 0; 1.490 + } 1.491 +} 1.492 + 1.493 +/** 1.494 + * Ok return our dimensions 1.495 + */ 1.496 +nsSize 1.497 +nsImageBoxFrame::GetPrefSize(nsBoxLayoutState& aState) 1.498 +{ 1.499 + nsSize size(0,0); 1.500 + DISPLAY_PREF_SIZE(this, size); 1.501 + if (DoesNeedRecalc(mImageSize)) 1.502 + GetImageSize(); 1.503 + 1.504 + if (!mUseSrcAttr && (mSubRect.width > 0 || mSubRect.height > 0)) 1.505 + size = mSubRect.Size(); 1.506 + else 1.507 + size = mImageSize; 1.508 + 1.509 + nsSize intrinsicSize = size; 1.510 + 1.511 + nsMargin borderPadding(0,0,0,0); 1.512 + GetBorderAndPadding(borderPadding); 1.513 + size.width += borderPadding.LeftRight(); 1.514 + size.height += borderPadding.TopBottom(); 1.515 + 1.516 + bool widthSet, heightSet; 1.517 + nsIFrame::AddCSSPrefSize(this, size, widthSet, heightSet); 1.518 + NS_ASSERTION(size.width != NS_INTRINSICSIZE && size.height != NS_INTRINSICSIZE, 1.519 + "non-intrinsic size expected"); 1.520 + 1.521 + nsSize minSize = GetMinSize(aState); 1.522 + nsSize maxSize = GetMaxSize(aState); 1.523 + 1.524 + if (!widthSet && !heightSet) { 1.525 + if (minSize.width != NS_INTRINSICSIZE) 1.526 + minSize.width -= borderPadding.LeftRight(); 1.527 + if (minSize.height != NS_INTRINSICSIZE) 1.528 + minSize.height -= borderPadding.TopBottom(); 1.529 + if (maxSize.width != NS_INTRINSICSIZE) 1.530 + maxSize.width -= borderPadding.LeftRight(); 1.531 + if (maxSize.height != NS_INTRINSICSIZE) 1.532 + maxSize.height -= borderPadding.TopBottom(); 1.533 + 1.534 + size = nsLayoutUtils::ComputeAutoSizeWithIntrinsicDimensions(minSize.width, minSize.height, 1.535 + maxSize.width, maxSize.height, 1.536 + intrinsicSize.width, intrinsicSize.height); 1.537 + NS_ASSERTION(size.width != NS_INTRINSICSIZE && size.height != NS_INTRINSICSIZE, 1.538 + "non-intrinsic size expected"); 1.539 + size.width += borderPadding.LeftRight(); 1.540 + size.height += borderPadding.TopBottom(); 1.541 + return size; 1.542 + } 1.543 + 1.544 + if (!widthSet) { 1.545 + if (intrinsicSize.height > 0) { 1.546 + // Subtract off the border and padding from the height because the 1.547 + // content-box needs to be used to determine the ratio 1.548 + nscoord height = size.height - borderPadding.TopBottom(); 1.549 + size.width = nscoord(int64_t(height) * int64_t(intrinsicSize.width) / 1.550 + int64_t(intrinsicSize.height)); 1.551 + } 1.552 + else { 1.553 + size.width = intrinsicSize.width; 1.554 + } 1.555 + 1.556 + size.width += borderPadding.LeftRight(); 1.557 + } 1.558 + else if (!heightSet) { 1.559 + if (intrinsicSize.width > 0) { 1.560 + nscoord width = size.width - borderPadding.LeftRight(); 1.561 + size.height = nscoord(int64_t(width) * int64_t(intrinsicSize.height) / 1.562 + int64_t(intrinsicSize.width)); 1.563 + } 1.564 + else { 1.565 + size.height = intrinsicSize.height; 1.566 + } 1.567 + 1.568 + size.height += borderPadding.TopBottom(); 1.569 + } 1.570 + 1.571 + return BoundsCheck(minSize, size, maxSize); 1.572 +} 1.573 + 1.574 +nsSize 1.575 +nsImageBoxFrame::GetMinSize(nsBoxLayoutState& aState) 1.576 +{ 1.577 + // An image can always scale down to (0,0). 1.578 + nsSize size(0,0); 1.579 + DISPLAY_MIN_SIZE(this, size); 1.580 + AddBorderAndPadding(size); 1.581 + bool widthSet, heightSet; 1.582 + nsIFrame::AddCSSMinSize(aState, this, size, widthSet, heightSet); 1.583 + return size; 1.584 +} 1.585 + 1.586 +nscoord 1.587 +nsImageBoxFrame::GetBoxAscent(nsBoxLayoutState& aState) 1.588 +{ 1.589 + return GetPrefSize(aState).height; 1.590 +} 1.591 + 1.592 +nsIAtom* 1.593 +nsImageBoxFrame::GetType() const 1.594 +{ 1.595 + return nsGkAtoms::imageBoxFrame; 1.596 +} 1.597 + 1.598 +#ifdef DEBUG_FRAME_DUMP 1.599 +nsresult 1.600 +nsImageBoxFrame::GetFrameName(nsAString& aResult) const 1.601 +{ 1.602 + return MakeFrameName(NS_LITERAL_STRING("ImageBox"), aResult); 1.603 +} 1.604 +#endif 1.605 + 1.606 +nsresult 1.607 +nsImageBoxFrame::Notify(imgIRequest *aRequest, int32_t aType, const nsIntRect* aData) 1.608 +{ 1.609 + if (aType == imgINotificationObserver::SIZE_AVAILABLE) { 1.610 + nsCOMPtr<imgIContainer> image; 1.611 + aRequest->GetImage(getter_AddRefs(image)); 1.612 + return OnStartContainer(aRequest, image); 1.613 + } 1.614 + 1.615 + if (aType == imgINotificationObserver::DECODE_COMPLETE) { 1.616 + return OnStopDecode(aRequest); 1.617 + } 1.618 + 1.619 + if (aType == imgINotificationObserver::LOAD_COMPLETE) { 1.620 + uint32_t imgStatus; 1.621 + aRequest->GetImageStatus(&imgStatus); 1.622 + nsresult status = 1.623 + imgStatus & imgIRequest::STATUS_ERROR ? NS_ERROR_FAILURE : NS_OK; 1.624 + return OnStopRequest(aRequest, status); 1.625 + } 1.626 + 1.627 + if (aType == imgINotificationObserver::IS_ANIMATED) { 1.628 + return OnImageIsAnimated(aRequest); 1.629 + } 1.630 + 1.631 + if (aType == imgINotificationObserver::FRAME_UPDATE) { 1.632 + return FrameChanged(aRequest); 1.633 + } 1.634 + 1.635 + return NS_OK; 1.636 +} 1.637 + 1.638 +nsresult nsImageBoxFrame::OnStartContainer(imgIRequest *request, 1.639 + imgIContainer *image) 1.640 +{ 1.641 + NS_ENSURE_ARG_POINTER(image); 1.642 + 1.643 + // Ensure the animation (if any) is started. Note: There is no 1.644 + // corresponding call to Decrement for this. This Increment will be 1.645 + // 'cleaned up' by the Request when it is destroyed, but only then. 1.646 + request->IncrementAnimationConsumers(); 1.647 + 1.648 + nscoord w, h; 1.649 + image->GetWidth(&w); 1.650 + image->GetHeight(&h); 1.651 + 1.652 + mIntrinsicSize.SizeTo(nsPresContext::CSSPixelsToAppUnits(w), 1.653 + nsPresContext::CSSPixelsToAppUnits(h)); 1.654 + 1.655 + if (!(GetStateBits() & NS_FRAME_FIRST_REFLOW)) { 1.656 + PresContext()->PresShell()-> 1.657 + FrameNeedsReflow(this, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY); 1.658 + } 1.659 + 1.660 + return NS_OK; 1.661 +} 1.662 + 1.663 +nsresult nsImageBoxFrame::OnStopDecode(imgIRequest *request) 1.664 +{ 1.665 + if (mFireEventOnDecode) { 1.666 + mFireEventOnDecode = false; 1.667 + 1.668 + uint32_t reqStatus; 1.669 + request->GetImageStatus(&reqStatus); 1.670 + if (!(reqStatus & imgIRequest::STATUS_ERROR)) { 1.671 + FireImageDOMEvent(mContent, NS_LOAD); 1.672 + } else { 1.673 + // Fire an onerror DOM event. 1.674 + mIntrinsicSize.SizeTo(0, 0); 1.675 + PresContext()->PresShell()-> 1.676 + FrameNeedsReflow(this, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY); 1.677 + FireImageDOMEvent(mContent, NS_LOAD_ERROR); 1.678 + } 1.679 + } 1.680 + 1.681 + nsBoxLayoutState state(PresContext()); 1.682 + this->Redraw(state); 1.683 + 1.684 + return NS_OK; 1.685 +} 1.686 + 1.687 +nsresult nsImageBoxFrame::OnStopRequest(imgIRequest *request, 1.688 + nsresult aStatus) 1.689 +{ 1.690 + uint32_t reqStatus; 1.691 + request->GetImageStatus(&reqStatus); 1.692 + 1.693 + // We want to give the decoder a chance to find errors. If we haven't found 1.694 + // an error yet and we've already started decoding, we must only fire these 1.695 + // events after we finish decoding. 1.696 + if (NS_SUCCEEDED(aStatus) && !(reqStatus & imgIRequest::STATUS_ERROR) && 1.697 + reqStatus & imgIRequest::STATUS_DECODE_STARTED) { 1.698 + mFireEventOnDecode = true; 1.699 + } else { 1.700 + if (NS_SUCCEEDED(aStatus)) { 1.701 + // Fire an onload DOM event. 1.702 + FireImageDOMEvent(mContent, NS_LOAD); 1.703 + } else { 1.704 + // Fire an onerror DOM event. 1.705 + mIntrinsicSize.SizeTo(0, 0); 1.706 + PresContext()->PresShell()-> 1.707 + FrameNeedsReflow(this, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY); 1.708 + FireImageDOMEvent(mContent, NS_LOAD_ERROR); 1.709 + } 1.710 + } 1.711 + 1.712 + return NS_OK; 1.713 +} 1.714 + 1.715 +nsresult nsImageBoxFrame::OnImageIsAnimated(imgIRequest *aRequest) 1.716 +{ 1.717 + // Register with our refresh driver, if we're animated. 1.718 + nsLayoutUtils::RegisterImageRequest(PresContext(), aRequest, 1.719 + &mRequestRegistered); 1.720 + 1.721 + return NS_OK; 1.722 +} 1.723 + 1.724 +nsresult nsImageBoxFrame::FrameChanged(imgIRequest *aRequest) 1.725 +{ 1.726 + if ((0 == mRect.width) || (0 == mRect.height)) { 1.727 + return NS_OK; 1.728 + } 1.729 + 1.730 + InvalidateLayer(nsDisplayItem::TYPE_XUL_IMAGE); 1.731 + 1.732 + return NS_OK; 1.733 +} 1.734 + 1.735 +NS_IMPL_ISUPPORTS(nsImageBoxListener, imgINotificationObserver, imgIOnloadBlocker) 1.736 + 1.737 +nsImageBoxListener::nsImageBoxListener() 1.738 +{ 1.739 +} 1.740 + 1.741 +nsImageBoxListener::~nsImageBoxListener() 1.742 +{ 1.743 +} 1.744 + 1.745 +NS_IMETHODIMP 1.746 +nsImageBoxListener::Notify(imgIRequest *request, int32_t aType, const nsIntRect* aData) 1.747 +{ 1.748 + if (!mFrame) 1.749 + return NS_OK; 1.750 + 1.751 + return mFrame->Notify(request, aType, aData); 1.752 +} 1.753 + 1.754 +/* void blockOnload (in imgIRequest aRequest); */ 1.755 +NS_IMETHODIMP 1.756 +nsImageBoxListener::BlockOnload(imgIRequest *aRequest) 1.757 +{ 1.758 + if (mFrame && mFrame->GetContent() && mFrame->GetContent()->GetCurrentDoc()) { 1.759 + mFrame->GetContent()->GetCurrentDoc()->BlockOnload(); 1.760 + } 1.761 + 1.762 + return NS_OK; 1.763 +} 1.764 + 1.765 +/* void unblockOnload (in imgIRequest aRequest); */ 1.766 +NS_IMETHODIMP 1.767 +nsImageBoxListener::UnblockOnload(imgIRequest *aRequest) 1.768 +{ 1.769 + if (mFrame && mFrame->GetContent() && mFrame->GetContent()->GetCurrentDoc()) { 1.770 + mFrame->GetContent()->GetCurrentDoc()->UnblockOnload(false); 1.771 + } 1.772 + 1.773 + return NS_OK; 1.774 +}