layout/xul/nsImageBoxFrame.cpp

Fri, 16 Jan 2015 18:13:44 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 16 Jan 2015 18:13:44 +0100
branch
TOR_BUG_9701
changeset 14
925c144e1f1f
permissions
-rw-r--r--

Integrate suggestion from review to improve consistency with existing code.

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 //
michael@0 7 // Eric Vaughan
michael@0 8 // Netscape Communications
michael@0 9 //
michael@0 10 // See documentation in associated header file
michael@0 11 //
michael@0 12
michael@0 13 #include "nsImageBoxFrame.h"
michael@0 14 #include "nsGkAtoms.h"
michael@0 15 #include "nsStyleContext.h"
michael@0 16 #include "nsStyleConsts.h"
michael@0 17 #include "nsCOMPtr.h"
michael@0 18 #include "nsPresContext.h"
michael@0 19 #include "nsBoxLayoutState.h"
michael@0 20
michael@0 21 #include "nsHTMLParts.h"
michael@0 22 #include "nsString.h"
michael@0 23 #include "nsLeafFrame.h"
michael@0 24 #include "nsIPresShell.h"
michael@0 25 #include "nsIDocument.h"
michael@0 26 #include "nsImageMap.h"
michael@0 27 #include "nsILinkHandler.h"
michael@0 28 #include "nsIURL.h"
michael@0 29 #include "nsILoadGroup.h"
michael@0 30 #include "nsContainerFrame.h"
michael@0 31 #include "prprf.h"
michael@0 32 #include "nsCSSRendering.h"
michael@0 33 #include "nsIDOMHTMLImageElement.h"
michael@0 34 #include "nsNameSpaceManager.h"
michael@0 35 #include "nsTextFragment.h"
michael@0 36 #include "nsIDOMHTMLMapElement.h"
michael@0 37 #include "nsTransform2D.h"
michael@0 38 #include "nsITheme.h"
michael@0 39
michael@0 40 #include "nsIServiceManager.h"
michael@0 41 #include "nsIURI.h"
michael@0 42 #include "nsNetUtil.h"
michael@0 43 #include "nsThreadUtils.h"
michael@0 44 #include "nsDisplayList.h"
michael@0 45 #include "ImageLayers.h"
michael@0 46 #include "ImageContainer.h"
michael@0 47
michael@0 48 #include "nsContentUtils.h"
michael@0 49
michael@0 50 #include "mozilla/BasicEvents.h"
michael@0 51 #include "mozilla/EventDispatcher.h"
michael@0 52
michael@0 53 #define ONLOAD_CALLED_TOO_EARLY 1
michael@0 54
michael@0 55 using namespace mozilla;
michael@0 56 using namespace mozilla::layers;
michael@0 57
michael@0 58 class nsImageBoxFrameEvent : public nsRunnable
michael@0 59 {
michael@0 60 public:
michael@0 61 nsImageBoxFrameEvent(nsIContent *content, uint32_t message)
michael@0 62 : mContent(content), mMessage(message) {}
michael@0 63
michael@0 64 NS_IMETHOD Run() MOZ_OVERRIDE;
michael@0 65
michael@0 66 private:
michael@0 67 nsCOMPtr<nsIContent> mContent;
michael@0 68 uint32_t mMessage;
michael@0 69 };
michael@0 70
michael@0 71 NS_IMETHODIMP
michael@0 72 nsImageBoxFrameEvent::Run()
michael@0 73 {
michael@0 74 nsIPresShell *pres_shell = mContent->OwnerDoc()->GetShell();
michael@0 75 if (!pres_shell) {
michael@0 76 return NS_OK;
michael@0 77 }
michael@0 78
michael@0 79 nsRefPtr<nsPresContext> pres_context = pres_shell->GetPresContext();
michael@0 80 if (!pres_context) {
michael@0 81 return NS_OK;
michael@0 82 }
michael@0 83
michael@0 84 nsEventStatus status = nsEventStatus_eIgnore;
michael@0 85 WidgetEvent event(true, mMessage);
michael@0 86
michael@0 87 event.mFlags.mBubbles = false;
michael@0 88 EventDispatcher::Dispatch(mContent, pres_context, &event, nullptr, &status);
michael@0 89 return NS_OK;
michael@0 90 }
michael@0 91
michael@0 92 // Fire off an event that'll asynchronously call the image elements
michael@0 93 // onload handler once handled. This is needed since the image library
michael@0 94 // can't decide if it wants to call it's observer methods
michael@0 95 // synchronously or asynchronously. If an image is loaded from the
michael@0 96 // cache the notifications come back synchronously, but if the image
michael@0 97 // is loaded from the netswork the notifications come back
michael@0 98 // asynchronously.
michael@0 99
michael@0 100 void
michael@0 101 FireImageDOMEvent(nsIContent* aContent, uint32_t aMessage)
michael@0 102 {
michael@0 103 NS_ASSERTION(aMessage == NS_LOAD || aMessage == NS_LOAD_ERROR,
michael@0 104 "invalid message");
michael@0 105
michael@0 106 nsCOMPtr<nsIRunnable> event = new nsImageBoxFrameEvent(aContent, aMessage);
michael@0 107 if (NS_FAILED(NS_DispatchToCurrentThread(event)))
michael@0 108 NS_WARNING("failed to dispatch image event");
michael@0 109 }
michael@0 110
michael@0 111 //
michael@0 112 // NS_NewImageBoxFrame
michael@0 113 //
michael@0 114 // Creates a new image frame and returns it
michael@0 115 //
michael@0 116 nsIFrame*
michael@0 117 NS_NewImageBoxFrame (nsIPresShell* aPresShell, nsStyleContext* aContext)
michael@0 118 {
michael@0 119 return new (aPresShell) nsImageBoxFrame (aPresShell, aContext);
michael@0 120 }
michael@0 121
michael@0 122 NS_IMPL_FRAMEARENA_HELPERS(nsImageBoxFrame)
michael@0 123
michael@0 124 nsresult
michael@0 125 nsImageBoxFrame::AttributeChanged(int32_t aNameSpaceID,
michael@0 126 nsIAtom* aAttribute,
michael@0 127 int32_t aModType)
michael@0 128 {
michael@0 129 nsresult rv = nsLeafBoxFrame::AttributeChanged(aNameSpaceID, aAttribute,
michael@0 130 aModType);
michael@0 131
michael@0 132 if (aAttribute == nsGkAtoms::src) {
michael@0 133 UpdateImage();
michael@0 134 PresContext()->PresShell()->
michael@0 135 FrameNeedsReflow(this, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
michael@0 136 }
michael@0 137 else if (aAttribute == nsGkAtoms::validate)
michael@0 138 UpdateLoadFlags();
michael@0 139
michael@0 140 return rv;
michael@0 141 }
michael@0 142
michael@0 143 nsImageBoxFrame::nsImageBoxFrame(nsIPresShell* aShell, nsStyleContext* aContext):
michael@0 144 nsLeafBoxFrame(aShell, aContext),
michael@0 145 mIntrinsicSize(0,0),
michael@0 146 mRequestRegistered(false),
michael@0 147 mLoadFlags(nsIRequest::LOAD_NORMAL),
michael@0 148 mUseSrcAttr(false),
michael@0 149 mSuppressStyleCheck(false),
michael@0 150 mFireEventOnDecode(false)
michael@0 151 {
michael@0 152 MarkIntrinsicWidthsDirty();
michael@0 153 }
michael@0 154
michael@0 155 nsImageBoxFrame::~nsImageBoxFrame()
michael@0 156 {
michael@0 157 }
michael@0 158
michael@0 159
michael@0 160 /* virtual */ void
michael@0 161 nsImageBoxFrame::MarkIntrinsicWidthsDirty()
michael@0 162 {
michael@0 163 SizeNeedsRecalc(mImageSize);
michael@0 164 nsLeafBoxFrame::MarkIntrinsicWidthsDirty();
michael@0 165 }
michael@0 166
michael@0 167 void
michael@0 168 nsImageBoxFrame::DestroyFrom(nsIFrame* aDestructRoot)
michael@0 169 {
michael@0 170 if (mImageRequest) {
michael@0 171 nsLayoutUtils::DeregisterImageRequest(PresContext(), mImageRequest,
michael@0 172 &mRequestRegistered);
michael@0 173
michael@0 174 // Release image loader first so that it's refcnt can go to zero
michael@0 175 mImageRequest->CancelAndForgetObserver(NS_ERROR_FAILURE);
michael@0 176 }
michael@0 177
michael@0 178 if (mListener)
michael@0 179 reinterpret_cast<nsImageBoxListener*>(mListener.get())->SetFrame(nullptr); // set the frame to null so we don't send messages to a dead object.
michael@0 180
michael@0 181 nsLeafBoxFrame::DestroyFrom(aDestructRoot);
michael@0 182 }
michael@0 183
michael@0 184
michael@0 185 void
michael@0 186 nsImageBoxFrame::Init(nsIContent* aContent,
michael@0 187 nsIFrame* aParent,
michael@0 188 nsIFrame* aPrevInFlow)
michael@0 189 {
michael@0 190 if (!mListener) {
michael@0 191 nsImageBoxListener *listener = new nsImageBoxListener();
michael@0 192 NS_ADDREF(listener);
michael@0 193 listener->SetFrame(this);
michael@0 194 listener->QueryInterface(NS_GET_IID(imgINotificationObserver), getter_AddRefs(mListener));
michael@0 195 NS_RELEASE(listener);
michael@0 196 }
michael@0 197
michael@0 198 mSuppressStyleCheck = true;
michael@0 199 nsLeafBoxFrame::Init(aContent, aParent, aPrevInFlow);
michael@0 200 mSuppressStyleCheck = false;
michael@0 201
michael@0 202 UpdateLoadFlags();
michael@0 203 UpdateImage();
michael@0 204 }
michael@0 205
michael@0 206 void
michael@0 207 nsImageBoxFrame::UpdateImage()
michael@0 208 {
michael@0 209 nsPresContext* presContext = PresContext();
michael@0 210
michael@0 211 if (mImageRequest) {
michael@0 212 nsLayoutUtils::DeregisterImageRequest(presContext, mImageRequest,
michael@0 213 &mRequestRegistered);
michael@0 214 mImageRequest->CancelAndForgetObserver(NS_ERROR_FAILURE);
michael@0 215 mImageRequest = nullptr;
michael@0 216 }
michael@0 217
michael@0 218 // get the new image src
michael@0 219 nsAutoString src;
michael@0 220 mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::src, src);
michael@0 221 mUseSrcAttr = !src.IsEmpty();
michael@0 222 if (mUseSrcAttr) {
michael@0 223 nsIDocument* doc = mContent->GetDocument();
michael@0 224 if (!doc) {
michael@0 225 // No need to do anything here...
michael@0 226 return;
michael@0 227 }
michael@0 228 nsCOMPtr<nsIURI> baseURI = mContent->GetBaseURI();
michael@0 229 nsCOMPtr<nsIURI> uri;
michael@0 230 nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(uri),
michael@0 231 src,
michael@0 232 doc,
michael@0 233 baseURI);
michael@0 234
michael@0 235 if (uri && nsContentUtils::CanLoadImage(uri, mContent, doc,
michael@0 236 mContent->NodePrincipal())) {
michael@0 237 nsContentUtils::LoadImage(uri, doc, mContent->NodePrincipal(),
michael@0 238 doc->GetDocumentURI(), mListener, mLoadFlags,
michael@0 239 EmptyString(), getter_AddRefs(mImageRequest));
michael@0 240
michael@0 241 if (mImageRequest) {
michael@0 242 nsLayoutUtils::RegisterImageRequestIfAnimated(presContext,
michael@0 243 mImageRequest,
michael@0 244 &mRequestRegistered);
michael@0 245 }
michael@0 246 }
michael@0 247 } else {
michael@0 248 // Only get the list-style-image if we aren't being drawn
michael@0 249 // by a native theme.
michael@0 250 uint8_t appearance = StyleDisplay()->mAppearance;
michael@0 251 if (!(appearance && nsBox::gTheme &&
michael@0 252 nsBox::gTheme->ThemeSupportsWidget(nullptr, this, appearance))) {
michael@0 253 // get the list-style-image
michael@0 254 imgRequestProxy *styleRequest = StyleList()->GetListStyleImage();
michael@0 255 if (styleRequest) {
michael@0 256 styleRequest->Clone(mListener, getter_AddRefs(mImageRequest));
michael@0 257 }
michael@0 258 }
michael@0 259 }
michael@0 260
michael@0 261 if (!mImageRequest) {
michael@0 262 // We have no image, so size to 0
michael@0 263 mIntrinsicSize.SizeTo(0, 0);
michael@0 264 } else {
michael@0 265 // We don't want discarding or decode-on-draw for xul images.
michael@0 266 mImageRequest->StartDecoding();
michael@0 267 mImageRequest->LockImage();
michael@0 268 }
michael@0 269 }
michael@0 270
michael@0 271 void
michael@0 272 nsImageBoxFrame::UpdateLoadFlags()
michael@0 273 {
michael@0 274 static nsIContent::AttrValuesArray strings[] =
michael@0 275 {&nsGkAtoms::always, &nsGkAtoms::never, nullptr};
michael@0 276 switch (mContent->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::validate,
michael@0 277 strings, eCaseMatters)) {
michael@0 278 case 0:
michael@0 279 mLoadFlags = nsIRequest::VALIDATE_ALWAYS;
michael@0 280 break;
michael@0 281 case 1:
michael@0 282 mLoadFlags = nsIRequest::VALIDATE_NEVER|nsIRequest::LOAD_FROM_CACHE;
michael@0 283 break;
michael@0 284 default:
michael@0 285 mLoadFlags = nsIRequest::LOAD_NORMAL;
michael@0 286 break;
michael@0 287 }
michael@0 288 }
michael@0 289
michael@0 290 void
michael@0 291 nsImageBoxFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
michael@0 292 const nsRect& aDirtyRect,
michael@0 293 const nsDisplayListSet& aLists)
michael@0 294 {
michael@0 295 nsLeafBoxFrame::BuildDisplayList(aBuilder, aDirtyRect, aLists);
michael@0 296
michael@0 297 if ((0 == mRect.width) || (0 == mRect.height)) {
michael@0 298 // Do not render when given a zero area. This avoids some useless
michael@0 299 // scaling work while we wait for our image dimensions to arrive
michael@0 300 // asynchronously.
michael@0 301 return;
michael@0 302 }
michael@0 303
michael@0 304 if (!IsVisibleForPainting(aBuilder))
michael@0 305 return;
michael@0 306
michael@0 307 nsDisplayList list;
michael@0 308 list.AppendNewToTop(
michael@0 309 new (aBuilder) nsDisplayXULImage(aBuilder, this));
michael@0 310
michael@0 311 CreateOwnLayerIfNeeded(aBuilder, &list);
michael@0 312
michael@0 313 aLists.Content()->AppendToTop(&list);
michael@0 314 }
michael@0 315
michael@0 316 void
michael@0 317 nsImageBoxFrame::PaintImage(nsRenderingContext& aRenderingContext,
michael@0 318 const nsRect& aDirtyRect, nsPoint aPt,
michael@0 319 uint32_t aFlags)
michael@0 320 {
michael@0 321 nsRect rect;
michael@0 322 GetClientRect(rect);
michael@0 323
michael@0 324 rect += aPt;
michael@0 325
michael@0 326 if (!mImageRequest)
michael@0 327 return;
michael@0 328
michael@0 329 // don't draw if the image is not dirty
michael@0 330 nsRect dirty;
michael@0 331 if (!dirty.IntersectRect(aDirtyRect, rect))
michael@0 332 return;
michael@0 333
michael@0 334 nsCOMPtr<imgIContainer> imgCon;
michael@0 335 mImageRequest->GetImage(getter_AddRefs(imgCon));
michael@0 336
michael@0 337 if (imgCon) {
michael@0 338 bool hasSubRect = !mUseSrcAttr && (mSubRect.width > 0 || mSubRect.height > 0);
michael@0 339 nsLayoutUtils::DrawSingleImage(&aRenderingContext, imgCon,
michael@0 340 nsLayoutUtils::GetGraphicsFilterForFrame(this),
michael@0 341 rect, dirty, nullptr, aFlags, hasSubRect ? &mSubRect : nullptr);
michael@0 342 }
michael@0 343 }
michael@0 344
michael@0 345 void nsDisplayXULImage::Paint(nsDisplayListBuilder* aBuilder,
michael@0 346 nsRenderingContext* aCtx)
michael@0 347 {
michael@0 348 uint32_t flags = imgIContainer::FLAG_NONE;
michael@0 349 if (aBuilder->ShouldSyncDecodeImages())
michael@0 350 flags |= imgIContainer::FLAG_SYNC_DECODE;
michael@0 351 if (aBuilder->IsPaintingToWindow())
michael@0 352 flags |= imgIContainer::FLAG_HIGH_QUALITY_SCALING;
michael@0 353
michael@0 354 static_cast<nsImageBoxFrame*>(mFrame)->
michael@0 355 PaintImage(*aCtx, mVisibleRect, ToReferenceFrame(), flags);
michael@0 356 }
michael@0 357
michael@0 358 void
michael@0 359 nsDisplayXULImage::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
michael@0 360 const nsDisplayItemGeometry* aGeometry,
michael@0 361 nsRegion* aInvalidRegion)
michael@0 362 {
michael@0 363
michael@0 364 if (aBuilder->ShouldSyncDecodeImages()) {
michael@0 365 nsImageBoxFrame* boxFrame = static_cast<nsImageBoxFrame*>(mFrame);
michael@0 366 nsCOMPtr<imgIContainer> image;
michael@0 367 if (boxFrame->mImageRequest) {
michael@0 368 boxFrame->mImageRequest->GetImage(getter_AddRefs(image));
michael@0 369 }
michael@0 370
michael@0 371 if (image && !image->IsDecoded()) {
michael@0 372 bool snap;
michael@0 373 aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap));
michael@0 374 }
michael@0 375 }
michael@0 376
michael@0 377 nsDisplayImageContainer::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion);
michael@0 378 }
michael@0 379
michael@0 380 void
michael@0 381 nsDisplayXULImage::ConfigureLayer(ImageLayer* aLayer, const nsIntPoint& aOffset)
michael@0 382 {
michael@0 383 aLayer->SetFilter(nsLayoutUtils::GetGraphicsFilterForFrame(mFrame));
michael@0 384
michael@0 385 int32_t factor = mFrame->PresContext()->AppUnitsPerDevPixel();
michael@0 386 nsImageBoxFrame* imageFrame = static_cast<nsImageBoxFrame*>(mFrame);
michael@0 387
michael@0 388 nsRect dest;
michael@0 389 imageFrame->GetClientRect(dest);
michael@0 390 dest += ToReferenceFrame();
michael@0 391 gfxRect destRect(dest.x, dest.y, dest.width, dest.height);
michael@0 392 destRect.ScaleInverse(factor);
michael@0 393
michael@0 394 nsCOMPtr<imgIContainer> imgCon;
michael@0 395 imageFrame->mImageRequest->GetImage(getter_AddRefs(imgCon));
michael@0 396 int32_t imageWidth;
michael@0 397 int32_t imageHeight;
michael@0 398 imgCon->GetWidth(&imageWidth);
michael@0 399 imgCon->GetHeight(&imageHeight);
michael@0 400
michael@0 401 NS_ASSERTION(imageWidth != 0 && imageHeight != 0, "Invalid image size!");
michael@0 402
michael@0 403 gfxPoint p = destRect.TopLeft() + aOffset;
michael@0 404 gfx::Matrix transform;
michael@0 405 transform.Translate(p.x, p.y);
michael@0 406 transform.Scale(destRect.Width()/imageWidth,
michael@0 407 destRect.Height()/imageHeight);
michael@0 408 aLayer->SetBaseTransform(gfx::Matrix4x4::From2D(transform));
michael@0 409
michael@0 410 aLayer->SetVisibleRegion(nsIntRect(0, 0, imageWidth, imageHeight));
michael@0 411 }
michael@0 412
michael@0 413 already_AddRefed<ImageContainer>
michael@0 414 nsDisplayXULImage::GetContainer(LayerManager* aManager, nsDisplayListBuilder* aBuilder)
michael@0 415 {
michael@0 416 return static_cast<nsImageBoxFrame*>(mFrame)->GetContainer(aManager);
michael@0 417 }
michael@0 418
michael@0 419 already_AddRefed<ImageContainer>
michael@0 420 nsImageBoxFrame::GetContainer(LayerManager* aManager)
michael@0 421 {
michael@0 422 bool hasSubRect = !mUseSrcAttr && (mSubRect.width > 0 || mSubRect.height > 0);
michael@0 423 if (hasSubRect || !mImageRequest) {
michael@0 424 return nullptr;
michael@0 425 }
michael@0 426
michael@0 427 nsCOMPtr<imgIContainer> imgCon;
michael@0 428 mImageRequest->GetImage(getter_AddRefs(imgCon));
michael@0 429 if (!imgCon) {
michael@0 430 return nullptr;
michael@0 431 }
michael@0 432
michael@0 433 nsRefPtr<ImageContainer> container;
michael@0 434 nsresult rv = imgCon->GetImageContainer(aManager, getter_AddRefs(container));
michael@0 435 NS_ENSURE_SUCCESS(rv, nullptr);
michael@0 436 return container.forget();
michael@0 437 }
michael@0 438
michael@0 439
michael@0 440 //
michael@0 441 // DidSetStyleContext
michael@0 442 //
michael@0 443 // When the style context changes, make sure that all of our image is up to date.
michael@0 444 //
michael@0 445 /* virtual */ void
michael@0 446 nsImageBoxFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
michael@0 447 {
michael@0 448 nsLeafBoxFrame::DidSetStyleContext(aOldStyleContext);
michael@0 449
michael@0 450 // Fetch our subrect.
michael@0 451 const nsStyleList* myList = StyleList();
michael@0 452 mSubRect = myList->mImageRegion; // before |mSuppressStyleCheck| test!
michael@0 453
michael@0 454 if (mUseSrcAttr || mSuppressStyleCheck)
michael@0 455 return; // No more work required, since the image isn't specified by style.
michael@0 456
michael@0 457 // If we're using a native theme implementation, we shouldn't draw anything.
michael@0 458 const nsStyleDisplay* disp = StyleDisplay();
michael@0 459 if (disp->mAppearance && nsBox::gTheme &&
michael@0 460 nsBox::gTheme->ThemeSupportsWidget(nullptr, this, disp->mAppearance))
michael@0 461 return;
michael@0 462
michael@0 463 // If list-style-image changes, we have a new image.
michael@0 464 nsCOMPtr<nsIURI> oldURI, newURI;
michael@0 465 if (mImageRequest)
michael@0 466 mImageRequest->GetURI(getter_AddRefs(oldURI));
michael@0 467 if (myList->GetListStyleImage())
michael@0 468 myList->GetListStyleImage()->GetURI(getter_AddRefs(newURI));
michael@0 469 bool equal;
michael@0 470 if (newURI == oldURI || // handles null==null
michael@0 471 (newURI && oldURI &&
michael@0 472 NS_SUCCEEDED(newURI->Equals(oldURI, &equal)) && equal))
michael@0 473 return;
michael@0 474
michael@0 475 UpdateImage();
michael@0 476 } // DidSetStyleContext
michael@0 477
michael@0 478 void
michael@0 479 nsImageBoxFrame::GetImageSize()
michael@0 480 {
michael@0 481 if (mIntrinsicSize.width > 0 && mIntrinsicSize.height > 0) {
michael@0 482 mImageSize.width = mIntrinsicSize.width;
michael@0 483 mImageSize.height = mIntrinsicSize.height;
michael@0 484 } else {
michael@0 485 mImageSize.width = 0;
michael@0 486 mImageSize.height = 0;
michael@0 487 }
michael@0 488 }
michael@0 489
michael@0 490 /**
michael@0 491 * Ok return our dimensions
michael@0 492 */
michael@0 493 nsSize
michael@0 494 nsImageBoxFrame::GetPrefSize(nsBoxLayoutState& aState)
michael@0 495 {
michael@0 496 nsSize size(0,0);
michael@0 497 DISPLAY_PREF_SIZE(this, size);
michael@0 498 if (DoesNeedRecalc(mImageSize))
michael@0 499 GetImageSize();
michael@0 500
michael@0 501 if (!mUseSrcAttr && (mSubRect.width > 0 || mSubRect.height > 0))
michael@0 502 size = mSubRect.Size();
michael@0 503 else
michael@0 504 size = mImageSize;
michael@0 505
michael@0 506 nsSize intrinsicSize = size;
michael@0 507
michael@0 508 nsMargin borderPadding(0,0,0,0);
michael@0 509 GetBorderAndPadding(borderPadding);
michael@0 510 size.width += borderPadding.LeftRight();
michael@0 511 size.height += borderPadding.TopBottom();
michael@0 512
michael@0 513 bool widthSet, heightSet;
michael@0 514 nsIFrame::AddCSSPrefSize(this, size, widthSet, heightSet);
michael@0 515 NS_ASSERTION(size.width != NS_INTRINSICSIZE && size.height != NS_INTRINSICSIZE,
michael@0 516 "non-intrinsic size expected");
michael@0 517
michael@0 518 nsSize minSize = GetMinSize(aState);
michael@0 519 nsSize maxSize = GetMaxSize(aState);
michael@0 520
michael@0 521 if (!widthSet && !heightSet) {
michael@0 522 if (minSize.width != NS_INTRINSICSIZE)
michael@0 523 minSize.width -= borderPadding.LeftRight();
michael@0 524 if (minSize.height != NS_INTRINSICSIZE)
michael@0 525 minSize.height -= borderPadding.TopBottom();
michael@0 526 if (maxSize.width != NS_INTRINSICSIZE)
michael@0 527 maxSize.width -= borderPadding.LeftRight();
michael@0 528 if (maxSize.height != NS_INTRINSICSIZE)
michael@0 529 maxSize.height -= borderPadding.TopBottom();
michael@0 530
michael@0 531 size = nsLayoutUtils::ComputeAutoSizeWithIntrinsicDimensions(minSize.width, minSize.height,
michael@0 532 maxSize.width, maxSize.height,
michael@0 533 intrinsicSize.width, intrinsicSize.height);
michael@0 534 NS_ASSERTION(size.width != NS_INTRINSICSIZE && size.height != NS_INTRINSICSIZE,
michael@0 535 "non-intrinsic size expected");
michael@0 536 size.width += borderPadding.LeftRight();
michael@0 537 size.height += borderPadding.TopBottom();
michael@0 538 return size;
michael@0 539 }
michael@0 540
michael@0 541 if (!widthSet) {
michael@0 542 if (intrinsicSize.height > 0) {
michael@0 543 // Subtract off the border and padding from the height because the
michael@0 544 // content-box needs to be used to determine the ratio
michael@0 545 nscoord height = size.height - borderPadding.TopBottom();
michael@0 546 size.width = nscoord(int64_t(height) * int64_t(intrinsicSize.width) /
michael@0 547 int64_t(intrinsicSize.height));
michael@0 548 }
michael@0 549 else {
michael@0 550 size.width = intrinsicSize.width;
michael@0 551 }
michael@0 552
michael@0 553 size.width += borderPadding.LeftRight();
michael@0 554 }
michael@0 555 else if (!heightSet) {
michael@0 556 if (intrinsicSize.width > 0) {
michael@0 557 nscoord width = size.width - borderPadding.LeftRight();
michael@0 558 size.height = nscoord(int64_t(width) * int64_t(intrinsicSize.height) /
michael@0 559 int64_t(intrinsicSize.width));
michael@0 560 }
michael@0 561 else {
michael@0 562 size.height = intrinsicSize.height;
michael@0 563 }
michael@0 564
michael@0 565 size.height += borderPadding.TopBottom();
michael@0 566 }
michael@0 567
michael@0 568 return BoundsCheck(minSize, size, maxSize);
michael@0 569 }
michael@0 570
michael@0 571 nsSize
michael@0 572 nsImageBoxFrame::GetMinSize(nsBoxLayoutState& aState)
michael@0 573 {
michael@0 574 // An image can always scale down to (0,0).
michael@0 575 nsSize size(0,0);
michael@0 576 DISPLAY_MIN_SIZE(this, size);
michael@0 577 AddBorderAndPadding(size);
michael@0 578 bool widthSet, heightSet;
michael@0 579 nsIFrame::AddCSSMinSize(aState, this, size, widthSet, heightSet);
michael@0 580 return size;
michael@0 581 }
michael@0 582
michael@0 583 nscoord
michael@0 584 nsImageBoxFrame::GetBoxAscent(nsBoxLayoutState& aState)
michael@0 585 {
michael@0 586 return GetPrefSize(aState).height;
michael@0 587 }
michael@0 588
michael@0 589 nsIAtom*
michael@0 590 nsImageBoxFrame::GetType() const
michael@0 591 {
michael@0 592 return nsGkAtoms::imageBoxFrame;
michael@0 593 }
michael@0 594
michael@0 595 #ifdef DEBUG_FRAME_DUMP
michael@0 596 nsresult
michael@0 597 nsImageBoxFrame::GetFrameName(nsAString& aResult) const
michael@0 598 {
michael@0 599 return MakeFrameName(NS_LITERAL_STRING("ImageBox"), aResult);
michael@0 600 }
michael@0 601 #endif
michael@0 602
michael@0 603 nsresult
michael@0 604 nsImageBoxFrame::Notify(imgIRequest *aRequest, int32_t aType, const nsIntRect* aData)
michael@0 605 {
michael@0 606 if (aType == imgINotificationObserver::SIZE_AVAILABLE) {
michael@0 607 nsCOMPtr<imgIContainer> image;
michael@0 608 aRequest->GetImage(getter_AddRefs(image));
michael@0 609 return OnStartContainer(aRequest, image);
michael@0 610 }
michael@0 611
michael@0 612 if (aType == imgINotificationObserver::DECODE_COMPLETE) {
michael@0 613 return OnStopDecode(aRequest);
michael@0 614 }
michael@0 615
michael@0 616 if (aType == imgINotificationObserver::LOAD_COMPLETE) {
michael@0 617 uint32_t imgStatus;
michael@0 618 aRequest->GetImageStatus(&imgStatus);
michael@0 619 nsresult status =
michael@0 620 imgStatus & imgIRequest::STATUS_ERROR ? NS_ERROR_FAILURE : NS_OK;
michael@0 621 return OnStopRequest(aRequest, status);
michael@0 622 }
michael@0 623
michael@0 624 if (aType == imgINotificationObserver::IS_ANIMATED) {
michael@0 625 return OnImageIsAnimated(aRequest);
michael@0 626 }
michael@0 627
michael@0 628 if (aType == imgINotificationObserver::FRAME_UPDATE) {
michael@0 629 return FrameChanged(aRequest);
michael@0 630 }
michael@0 631
michael@0 632 return NS_OK;
michael@0 633 }
michael@0 634
michael@0 635 nsresult nsImageBoxFrame::OnStartContainer(imgIRequest *request,
michael@0 636 imgIContainer *image)
michael@0 637 {
michael@0 638 NS_ENSURE_ARG_POINTER(image);
michael@0 639
michael@0 640 // Ensure the animation (if any) is started. Note: There is no
michael@0 641 // corresponding call to Decrement for this. This Increment will be
michael@0 642 // 'cleaned up' by the Request when it is destroyed, but only then.
michael@0 643 request->IncrementAnimationConsumers();
michael@0 644
michael@0 645 nscoord w, h;
michael@0 646 image->GetWidth(&w);
michael@0 647 image->GetHeight(&h);
michael@0 648
michael@0 649 mIntrinsicSize.SizeTo(nsPresContext::CSSPixelsToAppUnits(w),
michael@0 650 nsPresContext::CSSPixelsToAppUnits(h));
michael@0 651
michael@0 652 if (!(GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
michael@0 653 PresContext()->PresShell()->
michael@0 654 FrameNeedsReflow(this, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
michael@0 655 }
michael@0 656
michael@0 657 return NS_OK;
michael@0 658 }
michael@0 659
michael@0 660 nsresult nsImageBoxFrame::OnStopDecode(imgIRequest *request)
michael@0 661 {
michael@0 662 if (mFireEventOnDecode) {
michael@0 663 mFireEventOnDecode = false;
michael@0 664
michael@0 665 uint32_t reqStatus;
michael@0 666 request->GetImageStatus(&reqStatus);
michael@0 667 if (!(reqStatus & imgIRequest::STATUS_ERROR)) {
michael@0 668 FireImageDOMEvent(mContent, NS_LOAD);
michael@0 669 } else {
michael@0 670 // Fire an onerror DOM event.
michael@0 671 mIntrinsicSize.SizeTo(0, 0);
michael@0 672 PresContext()->PresShell()->
michael@0 673 FrameNeedsReflow(this, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
michael@0 674 FireImageDOMEvent(mContent, NS_LOAD_ERROR);
michael@0 675 }
michael@0 676 }
michael@0 677
michael@0 678 nsBoxLayoutState state(PresContext());
michael@0 679 this->Redraw(state);
michael@0 680
michael@0 681 return NS_OK;
michael@0 682 }
michael@0 683
michael@0 684 nsresult nsImageBoxFrame::OnStopRequest(imgIRequest *request,
michael@0 685 nsresult aStatus)
michael@0 686 {
michael@0 687 uint32_t reqStatus;
michael@0 688 request->GetImageStatus(&reqStatus);
michael@0 689
michael@0 690 // We want to give the decoder a chance to find errors. If we haven't found
michael@0 691 // an error yet and we've already started decoding, we must only fire these
michael@0 692 // events after we finish decoding.
michael@0 693 if (NS_SUCCEEDED(aStatus) && !(reqStatus & imgIRequest::STATUS_ERROR) &&
michael@0 694 reqStatus & imgIRequest::STATUS_DECODE_STARTED) {
michael@0 695 mFireEventOnDecode = true;
michael@0 696 } else {
michael@0 697 if (NS_SUCCEEDED(aStatus)) {
michael@0 698 // Fire an onload DOM event.
michael@0 699 FireImageDOMEvent(mContent, NS_LOAD);
michael@0 700 } else {
michael@0 701 // Fire an onerror DOM event.
michael@0 702 mIntrinsicSize.SizeTo(0, 0);
michael@0 703 PresContext()->PresShell()->
michael@0 704 FrameNeedsReflow(this, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
michael@0 705 FireImageDOMEvent(mContent, NS_LOAD_ERROR);
michael@0 706 }
michael@0 707 }
michael@0 708
michael@0 709 return NS_OK;
michael@0 710 }
michael@0 711
michael@0 712 nsresult nsImageBoxFrame::OnImageIsAnimated(imgIRequest *aRequest)
michael@0 713 {
michael@0 714 // Register with our refresh driver, if we're animated.
michael@0 715 nsLayoutUtils::RegisterImageRequest(PresContext(), aRequest,
michael@0 716 &mRequestRegistered);
michael@0 717
michael@0 718 return NS_OK;
michael@0 719 }
michael@0 720
michael@0 721 nsresult nsImageBoxFrame::FrameChanged(imgIRequest *aRequest)
michael@0 722 {
michael@0 723 if ((0 == mRect.width) || (0 == mRect.height)) {
michael@0 724 return NS_OK;
michael@0 725 }
michael@0 726
michael@0 727 InvalidateLayer(nsDisplayItem::TYPE_XUL_IMAGE);
michael@0 728
michael@0 729 return NS_OK;
michael@0 730 }
michael@0 731
michael@0 732 NS_IMPL_ISUPPORTS(nsImageBoxListener, imgINotificationObserver, imgIOnloadBlocker)
michael@0 733
michael@0 734 nsImageBoxListener::nsImageBoxListener()
michael@0 735 {
michael@0 736 }
michael@0 737
michael@0 738 nsImageBoxListener::~nsImageBoxListener()
michael@0 739 {
michael@0 740 }
michael@0 741
michael@0 742 NS_IMETHODIMP
michael@0 743 nsImageBoxListener::Notify(imgIRequest *request, int32_t aType, const nsIntRect* aData)
michael@0 744 {
michael@0 745 if (!mFrame)
michael@0 746 return NS_OK;
michael@0 747
michael@0 748 return mFrame->Notify(request, aType, aData);
michael@0 749 }
michael@0 750
michael@0 751 /* void blockOnload (in imgIRequest aRequest); */
michael@0 752 NS_IMETHODIMP
michael@0 753 nsImageBoxListener::BlockOnload(imgIRequest *aRequest)
michael@0 754 {
michael@0 755 if (mFrame && mFrame->GetContent() && mFrame->GetContent()->GetCurrentDoc()) {
michael@0 756 mFrame->GetContent()->GetCurrentDoc()->BlockOnload();
michael@0 757 }
michael@0 758
michael@0 759 return NS_OK;
michael@0 760 }
michael@0 761
michael@0 762 /* void unblockOnload (in imgIRequest aRequest); */
michael@0 763 NS_IMETHODIMP
michael@0 764 nsImageBoxListener::UnblockOnload(imgIRequest *aRequest)
michael@0 765 {
michael@0 766 if (mFrame && mFrame->GetContent() && mFrame->GetContent()->GetCurrentDoc()) {
michael@0 767 mFrame->GetContent()->GetCurrentDoc()->UnblockOnload(false);
michael@0 768 }
michael@0 769
michael@0 770 return NS_OK;
michael@0 771 }

mercurial