Tue, 06 Jan 2015 21:39:09 +0100
Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
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 | } |