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