Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/layout/xul/nsImageBoxFrame.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
 * License, v. 2.0. If a copy of the MPL was not distributed with this
5
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
//
8
// Eric Vaughan
9
// Netscape Communications
10
//
11
// See documentation in associated header file
12
//
13
14
#include "gfxContext.h"
15
#include "nsImageBoxFrame.h"
16
#include "nsGkAtoms.h"
17
#include "mozilla/ComputedStyle.h"
18
#include "nsStyleConsts.h"
19
#include "nsStyleUtil.h"
20
#include "nsCOMPtr.h"
21
#include "nsPresContext.h"
22
#include "nsBoxLayoutState.h"
23
24
#include "nsHTMLParts.h"
25
#include "nsString.h"
26
#include "nsLeafFrame.h"
27
#include "nsIPresShell.h"
28
#include "nsIDocument.h"
29
#include "nsImageMap.h"
30
#include "nsILinkHandler.h"
31
#include "nsIURL.h"
32
#include "nsILoadGroup.h"
33
#include "nsContainerFrame.h"
34
#include "nsCSSRendering.h"
35
#include "nsNameSpaceManager.h"
36
#include "nsTextFragment.h"
37
#include "nsTransform2D.h"
38
#include "nsITheme.h"
39
40
#include "nsIServiceManager.h"
41
#include "nsIURI.h"
42
#include "nsThreadUtils.h"
43
#include "nsDisplayList.h"
44
#include "ImageLayers.h"
45
#include "ImageContainer.h"
46
#include "nsIContent.h"
47
48
#include "nsContentUtils.h"
49
50
#include "mozilla/BasicEvents.h"
51
#include "mozilla/EventDispatcher.h"
52
#include "mozilla/Maybe.h"
53
#include "SVGImageContext.h"
54
#include "Units.h"
55
#include "mozilla/layers/WebRenderLayerManager.h"
56
57
#if defined(XP_WIN)
58
// Undefine LoadImage to prevent naming conflict with Windows.
59
#undef LoadImage
60
#endif
61
62
#define ONLOAD_CALLED_TOO_EARLY 1
63
64
using namespace mozilla;
65
using namespace mozilla::gfx;
66
using namespace mozilla::image;
67
using namespace mozilla::layers;
68
69
class nsImageBoxFrameEvent : public Runnable
70
{
71
public:
72
  nsImageBoxFrameEvent(nsIContent* content, EventMessage message)
73
    : mozilla::Runnable("nsImageBoxFrameEvent")
74
    , mContent(content)
75
    , mMessage(message)
76
0
  {
77
0
  }
78
79
  NS_IMETHOD Run() override;
80
81
private:
82
  nsCOMPtr<nsIContent> mContent;
83
  EventMessage mMessage;
84
};
85
86
NS_IMETHODIMP
87
nsImageBoxFrameEvent::Run()
88
0
{
89
0
  RefPtr<nsPresContext> pres_context = mContent->OwnerDoc()->GetPresContext();
90
0
  if (!pres_context) {
91
0
    return NS_OK;
92
0
  }
93
0
94
0
  nsEventStatus status = nsEventStatus_eIgnore;
95
0
  WidgetEvent event(true, mMessage);
96
0
97
0
  event.mFlags.mBubbles = false;
98
0
  EventDispatcher::Dispatch(mContent, pres_context, &event, nullptr, &status);
99
0
  return NS_OK;
100
0
}
101
102
// Fire off an event that'll asynchronously call the image elements
103
// onload handler once handled. This is needed since the image library
104
// can't decide if it wants to call its observer methods
105
// synchronously or asynchronously. If an image is loaded from the
106
// cache the notifications come back synchronously, but if the image
107
// is loaded from the network the notifications come back
108
// asynchronously.
109
static void
110
FireImageDOMEvent(nsIContent* aContent, EventMessage aMessage)
111
0
{
112
0
  NS_ASSERTION(aMessage == eLoad || aMessage == eLoadError,
113
0
               "invalid message");
114
0
115
0
  nsCOMPtr<nsIRunnable> event = new nsImageBoxFrameEvent(aContent, aMessage);
116
0
  nsresult rv = aContent->OwnerDoc()->Dispatch(TaskCategory::Other,
117
0
                                               event.forget());
118
0
  if (NS_FAILED(rv)) {
119
0
    NS_WARNING("failed to dispatch image event");
120
0
  }
121
0
}
122
123
//
124
// NS_NewImageBoxFrame
125
//
126
// Creates a new image frame and returns it
127
//
128
nsIFrame*
129
NS_NewImageBoxFrame (nsIPresShell* aPresShell, ComputedStyle* aStyle)
130
0
{
131
0
  return new (aPresShell) nsImageBoxFrame(aStyle);
132
0
}
133
134
NS_IMPL_FRAMEARENA_HELPERS(nsImageBoxFrame)
135
136
nsresult
137
nsImageBoxFrame::AttributeChanged(int32_t aNameSpaceID,
138
                                  nsAtom* aAttribute,
139
                                  int32_t aModType)
140
0
{
141
0
  nsresult rv = nsLeafBoxFrame::AttributeChanged(aNameSpaceID, aAttribute,
142
0
                                                 aModType);
143
0
144
0
  if (aAttribute == nsGkAtoms::src) {
145
0
    UpdateImage();
146
0
    PresShell()->
147
0
      FrameNeedsReflow(this, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
148
0
  }
149
0
  else if (aAttribute == nsGkAtoms::validate)
150
0
    UpdateLoadFlags();
151
0
152
0
  return rv;
153
0
}
154
155
nsImageBoxFrame::nsImageBoxFrame(ComputedStyle* aStyle)
156
  : nsLeafBoxFrame(aStyle, kClassID)
157
  , mIntrinsicSize(0, 0)
158
  , mLoadFlags(nsIRequest::LOAD_NORMAL)
159
  , mRequestRegistered(false)
160
  , mUseSrcAttr(false)
161
  , mSuppressStyleCheck(false)
162
0
{
163
0
  MarkIntrinsicISizesDirty();
164
0
}
165
166
nsImageBoxFrame::~nsImageBoxFrame()
167
0
{
168
0
}
169
170
171
/* virtual */ void
172
nsImageBoxFrame::MarkIntrinsicISizesDirty()
173
0
{
174
0
  SizeNeedsRecalc(mImageSize);
175
0
  nsLeafBoxFrame::MarkIntrinsicISizesDirty();
176
0
}
177
178
void
179
nsImageBoxFrame::DestroyFrom(nsIFrame* aDestructRoot, PostDestroyData& aPostDestroyData)
180
0
{
181
0
  if (mImageRequest) {
182
0
    nsLayoutUtils::DeregisterImageRequest(PresContext(), mImageRequest,
183
0
                                          &mRequestRegistered);
184
0
185
0
    mImageRequest->UnlockImage();
186
0
187
0
    // Release image loader first so that it's refcnt can go to zero
188
0
    mImageRequest->CancelAndForgetObserver(NS_ERROR_FAILURE);
189
0
  }
190
0
191
0
  if (mListener)
192
0
    reinterpret_cast<nsImageBoxListener*>(mListener.get())->ClearFrame(); // set the frame to null so we don't send messages to a dead object.
193
0
194
0
  nsLeafBoxFrame::DestroyFrom(aDestructRoot, aPostDestroyData);
195
0
}
196
197
198
void
199
nsImageBoxFrame::Init(nsIContent*       aContent,
200
                      nsContainerFrame* aParent,
201
                      nsIFrame*         aPrevInFlow)
202
0
{
203
0
  if (!mListener) {
204
0
    RefPtr<nsImageBoxListener> listener = new nsImageBoxListener(this);
205
0
    mListener = listener.forget();
206
0
  }
207
0
208
0
  mSuppressStyleCheck = true;
209
0
  nsLeafBoxFrame::Init(aContent, aParent, aPrevInFlow);
210
0
  mSuppressStyleCheck = false;
211
0
212
0
  UpdateLoadFlags();
213
0
  UpdateImage();
214
0
}
215
216
void
217
nsImageBoxFrame::UpdateImage()
218
0
{
219
0
  nsPresContext* presContext = PresContext();
220
0
221
0
  RefPtr<imgRequestProxy> oldImageRequest = mImageRequest;
222
0
223
0
  if (mImageRequest) {
224
0
    nsLayoutUtils::DeregisterImageRequest(presContext, mImageRequest,
225
0
                                          &mRequestRegistered);
226
0
    mImageRequest->CancelAndForgetObserver(NS_ERROR_FAILURE);
227
0
    mImageRequest = nullptr;
228
0
  }
229
0
230
0
  // get the new image src
231
0
  nsAutoString src;
232
0
  mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::src, src);
233
0
  mUseSrcAttr = !src.IsEmpty();
234
0
  if (mUseSrcAttr) {
235
0
    nsIDocument* doc = mContent->GetComposedDoc();
236
0
    if (doc) {
237
0
      nsContentPolicyType contentPolicyType;
238
0
      nsCOMPtr<nsIPrincipal> triggeringPrincipal;
239
0
      uint64_t requestContextID = 0;
240
0
      nsContentUtils::GetContentPolicyTypeForUIImageLoading(mContent,
241
0
                                                            getter_AddRefs(triggeringPrincipal),
242
0
                                                            contentPolicyType,
243
0
                                                            &requestContextID);
244
0
245
0
      nsCOMPtr<nsIURI> baseURI = mContent->GetBaseURI();
246
0
      nsCOMPtr<nsIURI> uri;
247
0
      nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(uri),
248
0
                                                src,
249
0
                                                doc,
250
0
                                                baseURI);
251
0
      if (uri) {
252
0
        nsresult rv = nsContentUtils::LoadImage(uri, mContent, doc,
253
0
                                                triggeringPrincipal,
254
0
                                                requestContextID,
255
0
                                                doc->GetDocumentURI(),
256
0
                                                doc->GetReferrerPolicy(),
257
0
                                                mListener, mLoadFlags,
258
0
                                                EmptyString(),
259
0
                                                getter_AddRefs(mImageRequest),
260
0
                                                contentPolicyType);
261
0
262
0
        if (NS_SUCCEEDED(rv) && mImageRequest) {
263
0
          nsLayoutUtils::RegisterImageRequestIfAnimated(presContext,
264
0
                                                        mImageRequest,
265
0
                                                        &mRequestRegistered);
266
0
        }
267
0
      }
268
0
    }
269
0
  } else {
270
0
    // Only get the list-style-image if we aren't being drawn
271
0
    // by a native theme.
272
0
    auto* display = StyleDisplay();
273
0
    if (!(display->HasAppearance() && nsBox::gTheme &&
274
0
          nsBox::gTheme->ThemeSupportsWidget(nullptr, this, display->mAppearance))) {
275
0
      // get the list-style-image
276
0
      imgRequestProxy *styleRequest = StyleList()->GetListStyleImage();
277
0
      if (styleRequest) {
278
0
        styleRequest->SyncClone(mListener,
279
0
                                mContent->GetComposedDoc(),
280
0
                                getter_AddRefs(mImageRequest));
281
0
      }
282
0
    }
283
0
  }
284
0
285
0
  if (!mImageRequest) {
286
0
    // We have no image, so size to 0
287
0
    mIntrinsicSize.SizeTo(0, 0);
288
0
  } else {
289
0
    // We don't want discarding or decode-on-draw for xul images.
290
0
    mImageRequest->StartDecoding(imgIContainer::FLAG_NONE);
291
0
    mImageRequest->LockImage();
292
0
  }
293
0
294
0
  // Do this _after_ locking the new image in case they are the same image.
295
0
  if (oldImageRequest) {
296
0
    oldImageRequest->UnlockImage();
297
0
  }
298
0
}
299
300
void
301
nsImageBoxFrame::UpdateLoadFlags()
302
{
303
  static Element::AttrValuesArray strings[] =
304
    {&nsGkAtoms::always, &nsGkAtoms::never, nullptr};
305
  switch (mContent->AsElement()->FindAttrValueIn(kNameSpaceID_None,
306
                                                 nsGkAtoms::validate, strings,
307
                                                 eCaseMatters)) {
308
    case 0:
309
      mLoadFlags = nsIRequest::VALIDATE_ALWAYS;
310
      break;
311
    case 1:
312
      mLoadFlags = nsIRequest::VALIDATE_NEVER|nsIRequest::LOAD_FROM_CACHE;
313
      break;
314
    default:
315
      mLoadFlags = nsIRequest::LOAD_NORMAL;
316
      break;
317
  }
318
}
319
320
void
321
nsImageBoxFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
322
                                  const nsDisplayListSet& aLists)
323
0
{
324
0
  nsLeafBoxFrame::BuildDisplayList(aBuilder, aLists);
325
0
326
0
  if ((0 == mRect.width) || (0 == mRect.height)) {
327
0
    // Do not render when given a zero area. This avoids some useless
328
0
    // scaling work while we wait for our image dimensions to arrive
329
0
    // asynchronously.
330
0
    return;
331
0
  }
332
0
333
0
  if (!IsVisibleForPainting(aBuilder))
334
0
    return;
335
0
336
0
  uint32_t clipFlags =
337
0
    nsStyleUtil::ObjectPropsMightCauseOverflow(StylePosition()) ?
338
0
    0 : DisplayListClipState::ASSUME_DRAWING_RESTRICTED_TO_CONTENT_RECT;
339
0
340
0
  DisplayListClipState::AutoClipContainingBlockDescendantsToContentBox
341
0
    clip(aBuilder, this, clipFlags);
342
0
343
0
  nsDisplayList list;
344
0
  list.AppendToTop(
345
0
    MakeDisplayItem<nsDisplayXULImage>(aBuilder, this));
346
0
347
0
  CreateOwnLayerIfNeeded(aBuilder, &list);
348
0
349
0
  aLists.Content()->AppendToTop(&list);
350
0
}
351
352
already_AddRefed<imgIContainer>
353
nsImageBoxFrame::GetImageContainerForPainting(const nsPoint& aPt,
354
                                              ImgDrawResult& aDrawResult,
355
                                              Maybe<nsPoint>& aAnchorPoint,
356
                                              nsRect& aDest)
357
0
{
358
0
  if (!mImageRequest) {
359
0
    // This probably means we're drawn by a native theme.
360
0
    aDrawResult = ImgDrawResult::SUCCESS;
361
0
    return nullptr;
362
0
  }
363
0
364
0
  // Don't draw if the image's size isn't available.
365
0
  uint32_t imgStatus;
366
0
  if (!NS_SUCCEEDED(mImageRequest->GetImageStatus(&imgStatus)) ||
367
0
      !(imgStatus & imgIRequest::STATUS_SIZE_AVAILABLE)) {
368
0
    aDrawResult = ImgDrawResult::NOT_READY;
369
0
    return nullptr;
370
0
  }
371
0
372
0
  nsCOMPtr<imgIContainer> imgCon;
373
0
  mImageRequest->GetImage(getter_AddRefs(imgCon));
374
0
375
0
  if (!imgCon) {
376
0
    aDrawResult = ImgDrawResult::NOT_READY;
377
0
    return nullptr;
378
0
  }
379
0
380
0
  aDest = GetDestRect(aPt, aAnchorPoint);
381
0
  aDrawResult = ImgDrawResult::SUCCESS;
382
0
  return imgCon.forget();
383
0
}
384
385
ImgDrawResult
386
nsImageBoxFrame::PaintImage(gfxContext& aRenderingContext,
387
                            const nsRect& aDirtyRect, nsPoint aPt,
388
                            uint32_t aFlags)
389
0
{
390
0
  ImgDrawResult result;
391
0
  Maybe<nsPoint> anchorPoint;
392
0
  nsRect dest;
393
0
  nsCOMPtr<imgIContainer> imgCon = GetImageContainerForPainting(aPt, result,
394
0
                                                                anchorPoint,
395
0
                                                                dest);
396
0
  if (!imgCon) {
397
0
    return result;
398
0
  }
399
0
400
0
  // don't draw if the image is not dirty
401
0
  // XXX(seth): Can this actually happen anymore?
402
0
  nsRect dirty;
403
0
  if (!dirty.IntersectRect(aDirtyRect, dest)) {
404
0
    return ImgDrawResult::TEMPORARY_ERROR;
405
0
  }
406
0
407
0
  bool hasSubRect = !mUseSrcAttr && (mSubRect.width > 0 || mSubRect.height > 0);
408
0
409
0
  Maybe<SVGImageContext> svgContext;
410
0
  SVGImageContext::MaybeStoreContextPaint(svgContext, this, imgCon);
411
0
  return nsLayoutUtils::DrawSingleImage(
412
0
           aRenderingContext,
413
0
           PresContext(), imgCon,
414
0
           nsLayoutUtils::GetSamplingFilterForFrame(this),
415
0
           dest, dirty,
416
0
           svgContext, aFlags,
417
0
           anchorPoint.ptrOr(nullptr),
418
0
           hasSubRect ? &mSubRect : nullptr);
419
0
}
420
421
ImgDrawResult
422
nsImageBoxFrame::CreateWebRenderCommands(mozilla::wr::DisplayListBuilder& aBuilder,
423
                                         mozilla::wr::IpcResourceUpdateQueue& aResources,
424
                                         const StackingContextHelper& aSc,
425
                                         mozilla::layers::WebRenderLayerManager* aManager,
426
                                         nsDisplayItem* aItem,
427
                                         nsPoint aPt,
428
                                         uint32_t aFlags)
429
0
{
430
0
  ImgDrawResult result;
431
0
  Maybe<nsPoint> anchorPoint;
432
0
  nsRect dest;
433
0
  nsCOMPtr<imgIContainer> imgCon = GetImageContainerForPainting(aPt, result,
434
0
                                                                anchorPoint,
435
0
                                                                dest);
436
0
  if (!imgCon) {
437
0
    return result;
438
0
  }
439
0
440
0
  uint32_t containerFlags = imgIContainer::FLAG_ASYNC_NOTIFY;
441
0
  if (aFlags & nsImageRenderer::FLAG_PAINTING_TO_WINDOW) {
442
0
    containerFlags |= imgIContainer::FLAG_HIGH_QUALITY_SCALING;
443
0
  }
444
0
  if (aFlags & nsImageRenderer::FLAG_SYNC_DECODE_IMAGES) {
445
0
    containerFlags |= imgIContainer::FLAG_SYNC_DECODE;
446
0
  }
447
0
448
0
  const int32_t appUnitsPerDevPixel = PresContext()->AppUnitsPerDevPixel();
449
0
  LayoutDeviceRect fillRect = LayoutDeviceRect::FromAppUnits(dest,
450
0
                                                             appUnitsPerDevPixel);
451
0
  Maybe<SVGImageContext> svgContext;
452
0
  gfx::IntSize decodeSize =
453
0
    nsLayoutUtils::ComputeImageContainerDrawingParameters(imgCon, aItem->Frame(), fillRect,
454
0
                                                          aSc, containerFlags, svgContext);
455
0
456
0
  RefPtr<layers::ImageContainer> container;
457
0
  result = imgCon->GetImageContainerAtSize(aManager, decodeSize, svgContext,
458
0
                                           containerFlags, getter_AddRefs(container));
459
0
  if (!container) {
460
0
    NS_WARNING("Failed to get image container");
461
0
    return result;
462
0
  }
463
0
464
0
  mozilla::wr::ImageRendering rendering = wr::ToImageRendering(
465
0
    nsLayoutUtils::GetSamplingFilterForFrame(aItem->Frame()));
466
0
  gfx::IntSize size;
467
0
  Maybe<wr::ImageKey> key = aManager->CommandBuilder().CreateImageKey(
468
0
    aItem, container, aBuilder, aResources, rendering, aSc, size, Nothing());
469
0
  if (key.isNothing()) {
470
0
    return result;
471
0
  }
472
0
  wr::LayoutRect fill = wr::ToRoundedLayoutRect(fillRect);
473
0
474
0
  LayoutDeviceSize gapSize(0, 0);
475
0
  aBuilder.PushImage(fill,
476
0
                     fill,
477
0
                     !BackfaceIsHidden(),
478
0
                     wr::ToLayoutSize(fillRect.Size()),
479
0
                     wr::ToLayoutSize(gapSize),
480
0
                     rendering,
481
0
                     key.value());
482
0
483
0
  return result;
484
0
}
485
486
nsRect
487
nsImageBoxFrame::GetDestRect(const nsPoint& aOffset, Maybe<nsPoint>& aAnchorPoint)
488
0
{
489
0
  nsCOMPtr<imgIContainer> imgCon;
490
0
  mImageRequest->GetImage(getter_AddRefs(imgCon));
491
0
  MOZ_ASSERT(imgCon);
492
0
493
0
  nsRect clientRect;
494
0
  GetXULClientRect(clientRect);
495
0
  clientRect += aOffset;
496
0
  nsRect dest;
497
0
  if (!mUseSrcAttr) {
498
0
    // Our image (if we have one) is coming from the CSS property
499
0
    // 'list-style-image' (combined with '-moz-image-region'). For now, ignore
500
0
    // 'object-fit' & 'object-position' in this case, and just fill our rect.
501
0
    // XXXdholbert Should we even honor these properties in this case? They only
502
0
    // apply to replaced elements, and I'm not sure we count as a replaced
503
0
    // element when our image data is determined by CSS.
504
0
    dest = clientRect;
505
0
  } else {
506
0
    // Determine dest rect based on intrinsic size & ratio, along with
507
0
    // 'object-fit' & 'object-position' properties:
508
0
    IntrinsicSize intrinsicSize;
509
0
    nsSize intrinsicRatio;
510
0
    if (mIntrinsicSize.width > 0 && mIntrinsicSize.height > 0) {
511
0
      // Image has a valid size; use it as intrinsic size & ratio.
512
0
      intrinsicSize.width.SetCoordValue(mIntrinsicSize.width);
513
0
      intrinsicSize.height.SetCoordValue(mIntrinsicSize.height);
514
0
      intrinsicRatio = mIntrinsicSize;
515
0
    } else {
516
0
      // Image doesn't have a (valid) intrinsic size.
517
0
      // Try to look up intrinsic ratio and use that at least.
518
0
      imgCon->GetIntrinsicRatio(&intrinsicRatio);
519
0
    }
520
0
    aAnchorPoint.emplace();
521
0
    dest = nsLayoutUtils::ComputeObjectDestRect(clientRect,
522
0
                                                intrinsicSize,
523
0
                                                intrinsicRatio,
524
0
                                                StylePosition(),
525
0
                                                aAnchorPoint.ptr());
526
0
  }
527
0
528
0
  return dest;
529
0
}
530
531
void nsDisplayXULImage::Paint(nsDisplayListBuilder* aBuilder,
532
                              gfxContext* aCtx)
533
0
{
534
0
  // Even though we call StartDecoding when we get a new image we pass
535
0
  // FLAG_SYNC_DECODE_IF_FAST here for the case where the size we draw at is not
536
0
  // the intrinsic size of the image and we aren't likely to implement predictive
537
0
  // decoding at the correct size for this class like nsImageFrame has.
538
0
  uint32_t flags = imgIContainer::FLAG_SYNC_DECODE_IF_FAST;
539
0
  if (aBuilder->ShouldSyncDecodeImages())
540
0
    flags |= imgIContainer::FLAG_SYNC_DECODE;
541
0
  if (aBuilder->IsPaintingToWindow())
542
0
    flags |= imgIContainer::FLAG_HIGH_QUALITY_SCALING;
543
0
544
0
  ImgDrawResult result = static_cast<nsImageBoxFrame*>(mFrame)->
545
0
    PaintImage(*aCtx, GetPaintRect(), ToReferenceFrame(), flags);
546
0
547
0
  nsDisplayItemGenericImageGeometry::UpdateDrawResult(this, result);
548
0
}
549
550
bool
551
nsDisplayXULImage::CreateWebRenderCommands(mozilla::wr::DisplayListBuilder& aBuilder,
552
                                           mozilla::wr::IpcResourceUpdateQueue& aResources,
553
                                           const StackingContextHelper& aSc,
554
                                           mozilla::layers::WebRenderLayerManager* aManager,
555
                                           nsDisplayListBuilder* aDisplayListBuilder)
556
0
{
557
0
  nsImageBoxFrame* imageFrame = static_cast<nsImageBoxFrame*>(mFrame);
558
0
  if (!imageFrame->CanOptimizeToImageLayer()) {
559
0
    return false;
560
0
  }
561
0
562
0
  if (!imageFrame->mImageRequest) {
563
0
    return true;
564
0
  }
565
0
566
0
  uint32_t flags = imgIContainer::FLAG_SYNC_DECODE_IF_FAST;
567
0
  if (aDisplayListBuilder->ShouldSyncDecodeImages()) {
568
0
    flags |= imgIContainer::FLAG_SYNC_DECODE;
569
0
  }
570
0
  if (aDisplayListBuilder->IsPaintingToWindow()) {
571
0
    flags |= imgIContainer::FLAG_HIGH_QUALITY_SCALING;
572
0
  }
573
0
574
0
  ImgDrawResult result = imageFrame->
575
0
    CreateWebRenderCommands(aBuilder, aResources, aSc, aManager, this, ToReferenceFrame(), flags);
576
0
  if (result == ImgDrawResult::NOT_SUPPORTED) {
577
0
    return false;
578
0
  }
579
0
580
0
  nsDisplayItemGenericImageGeometry::UpdateDrawResult(this, result);
581
0
  return true;
582
0
}
583
584
nsDisplayItemGeometry*
585
nsDisplayXULImage::AllocateGeometry(nsDisplayListBuilder* aBuilder)
586
0
{
587
0
  return new nsDisplayItemGenericImageGeometry(this, aBuilder);
588
0
}
589
590
void
591
nsDisplayXULImage::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
592
                                             const nsDisplayItemGeometry* aGeometry,
593
                                             nsRegion* aInvalidRegion) const
594
0
{
595
0
  auto boxFrame = static_cast<nsImageBoxFrame*>(mFrame);
596
0
  auto geometry =
597
0
    static_cast<const nsDisplayItemGenericImageGeometry*>(aGeometry);
598
0
599
0
  if (aBuilder->ShouldSyncDecodeImages() &&
600
0
      boxFrame->mImageRequest &&
601
0
      geometry->ShouldInvalidateToSyncDecodeImages()) {
602
0
      bool snap;
603
0
      aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap));
604
0
  }
605
0
606
0
  nsDisplayImageContainer::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion);
607
0
}
608
609
bool
610
nsDisplayXULImage::CanOptimizeToImageLayer(LayerManager* aManager,
611
                                           nsDisplayListBuilder* aBuilder)
612
0
{
613
0
  nsImageBoxFrame* imageFrame = static_cast<nsImageBoxFrame*>(mFrame);
614
0
  if (!imageFrame->CanOptimizeToImageLayer()) {
615
0
    return false;
616
0
  }
617
0
618
0
  return nsDisplayImageContainer::CanOptimizeToImageLayer(aManager, aBuilder);
619
0
}
620
621
already_AddRefed<imgIContainer>
622
nsDisplayXULImage::GetImage()
623
0
{
624
0
  nsImageBoxFrame* imageFrame = static_cast<nsImageBoxFrame*>(mFrame);
625
0
  if (!imageFrame->mImageRequest) {
626
0
    return nullptr;
627
0
  }
628
0
629
0
  nsCOMPtr<imgIContainer> imgCon;
630
0
  imageFrame->mImageRequest->GetImage(getter_AddRefs(imgCon));
631
0
632
0
  return imgCon.forget();
633
0
}
634
635
nsRect
636
nsDisplayXULImage::GetDestRect() const
637
0
{
638
0
  Maybe<nsPoint> anchorPoint;
639
0
  return static_cast<nsImageBoxFrame*>(mFrame)->GetDestRect(ToReferenceFrame(), anchorPoint);
640
0
}
641
642
bool
643
nsImageBoxFrame::CanOptimizeToImageLayer()
644
0
{
645
0
  bool hasSubRect = !mUseSrcAttr && (mSubRect.width > 0 || mSubRect.height > 0);
646
0
  if (hasSubRect) {
647
0
    return false;
648
0
  }
649
0
  return true;
650
0
}
651
652
//
653
// DidSetComputedStyle
654
//
655
// When the ComputedStyle changes, make sure that all of our image is up to
656
// date.
657
//
658
/* virtual */ void
659
nsImageBoxFrame::DidSetComputedStyle(ComputedStyle* aOldComputedStyle)
660
0
{
661
0
  nsLeafBoxFrame::DidSetComputedStyle(aOldComputedStyle);
662
0
663
0
  // Fetch our subrect.
664
0
  const nsStyleList* myList = StyleList();
665
0
  mSubRect = myList->mImageRegion; // before |mSuppressStyleCheck| test!
666
0
667
0
  if (mUseSrcAttr || mSuppressStyleCheck)
668
0
    return; // No more work required, since the image isn't specified by style.
669
0
670
0
  // If we're using a native theme implementation, we shouldn't draw anything.
671
0
  const nsStyleDisplay* disp = StyleDisplay();
672
0
  if (disp->HasAppearance() && nsBox::gTheme &&
673
0
      nsBox::gTheme->ThemeSupportsWidget(nullptr, this, disp->mAppearance))
674
0
    return;
675
0
676
0
  // If list-style-image changes, we have a new image.
677
0
  nsCOMPtr<nsIURI> oldURI, newURI;
678
0
  if (mImageRequest)
679
0
    mImageRequest->GetURI(getter_AddRefs(oldURI));
680
0
  if (myList->GetListStyleImage())
681
0
    myList->GetListStyleImage()->GetURI(getter_AddRefs(newURI));
682
0
  bool equal;
683
0
  if (newURI == oldURI ||   // handles null==null
684
0
      (newURI && oldURI &&
685
0
       NS_SUCCEEDED(newURI->Equals(oldURI, &equal)) && equal))
686
0
    return;
687
0
688
0
  UpdateImage();
689
0
} // DidSetComputedStyle
690
691
void
692
nsImageBoxFrame::GetImageSize()
693
0
{
694
0
  if (mIntrinsicSize.width > 0 && mIntrinsicSize.height > 0) {
695
0
    mImageSize.width = mIntrinsicSize.width;
696
0
    mImageSize.height = mIntrinsicSize.height;
697
0
  } else {
698
0
    mImageSize.width = 0;
699
0
    mImageSize.height = 0;
700
0
  }
701
0
}
702
703
/**
704
 * Ok return our dimensions
705
 */
706
nsSize
707
nsImageBoxFrame::GetXULPrefSize(nsBoxLayoutState& aState)
708
0
{
709
0
  nsSize size(0,0);
710
0
  DISPLAY_PREF_SIZE(this, size);
711
0
  if (DoesNeedRecalc(mImageSize))
712
0
     GetImageSize();
713
0
714
0
  if (!mUseSrcAttr && (mSubRect.width > 0 || mSubRect.height > 0))
715
0
    size = mSubRect.Size();
716
0
  else
717
0
    size = mImageSize;
718
0
719
0
  nsSize intrinsicSize = size;
720
0
721
0
  nsMargin borderPadding(0,0,0,0);
722
0
  GetXULBorderAndPadding(borderPadding);
723
0
  size.width += borderPadding.LeftRight();
724
0
  size.height += borderPadding.TopBottom();
725
0
726
0
  bool widthSet, heightSet;
727
0
  nsIFrame::AddXULPrefSize(this, size, widthSet, heightSet);
728
0
  NS_ASSERTION(size.width != NS_INTRINSICSIZE && size.height != NS_INTRINSICSIZE,
729
0
               "non-intrinsic size expected");
730
0
731
0
  nsSize minSize = GetXULMinSize(aState);
732
0
  nsSize maxSize = GetXULMaxSize(aState);
733
0
734
0
  if (!widthSet && !heightSet) {
735
0
    if (minSize.width != NS_INTRINSICSIZE)
736
0
      minSize.width -= borderPadding.LeftRight();
737
0
    if (minSize.height != NS_INTRINSICSIZE)
738
0
      minSize.height -= borderPadding.TopBottom();
739
0
    if (maxSize.width != NS_INTRINSICSIZE)
740
0
      maxSize.width -= borderPadding.LeftRight();
741
0
    if (maxSize.height != NS_INTRINSICSIZE)
742
0
      maxSize.height -= borderPadding.TopBottom();
743
0
744
0
    size = nsLayoutUtils::ComputeAutoSizeWithIntrinsicDimensions(minSize.width, minSize.height,
745
0
                                                                 maxSize.width, maxSize.height,
746
0
                                                                 intrinsicSize.width, intrinsicSize.height);
747
0
    NS_ASSERTION(size.width != NS_INTRINSICSIZE && size.height != NS_INTRINSICSIZE,
748
0
                 "non-intrinsic size expected");
749
0
    size.width += borderPadding.LeftRight();
750
0
    size.height += borderPadding.TopBottom();
751
0
    return size;
752
0
  }
753
0
754
0
  if (!widthSet) {
755
0
    if (intrinsicSize.height > 0) {
756
0
      // Subtract off the border and padding from the height because the
757
0
      // content-box needs to be used to determine the ratio
758
0
      nscoord height = size.height - borderPadding.TopBottom();
759
0
      size.width = nscoord(int64_t(height) * int64_t(intrinsicSize.width) /
760
0
                           int64_t(intrinsicSize.height));
761
0
    }
762
0
    else {
763
0
      size.width = intrinsicSize.width;
764
0
    }
765
0
766
0
    size.width += borderPadding.LeftRight();
767
0
  }
768
0
  else if (!heightSet) {
769
0
    if (intrinsicSize.width > 0) {
770
0
      nscoord width = size.width - borderPadding.LeftRight();
771
0
      size.height = nscoord(int64_t(width) * int64_t(intrinsicSize.height) /
772
0
                            int64_t(intrinsicSize.width));
773
0
    }
774
0
    else {
775
0
      size.height = intrinsicSize.height;
776
0
    }
777
0
778
0
    size.height += borderPadding.TopBottom();
779
0
  }
780
0
781
0
  return BoundsCheck(minSize, size, maxSize);
782
0
}
783
784
nsSize
785
nsImageBoxFrame::GetXULMinSize(nsBoxLayoutState& aState)
786
0
{
787
0
  // An image can always scale down to (0,0).
788
0
  nsSize size(0,0);
789
0
  DISPLAY_MIN_SIZE(this, size);
790
0
  AddBorderAndPadding(size);
791
0
  bool widthSet, heightSet;
792
0
  nsIFrame::AddXULMinSize(aState, this, size, widthSet, heightSet);
793
0
  return size;
794
0
}
795
796
nscoord
797
nsImageBoxFrame::GetXULBoxAscent(nsBoxLayoutState& aState)
798
0
{
799
0
  return GetXULPrefSize(aState).height;
800
0
}
801
802
#ifdef DEBUG_FRAME_DUMP
803
nsresult
804
nsImageBoxFrame::GetFrameName(nsAString& aResult) const
805
{
806
  return MakeFrameName(NS_LITERAL_STRING("ImageBox"), aResult);
807
}
808
#endif
809
810
nsresult
811
nsImageBoxFrame::Notify(imgIRequest* aRequest,
812
                        int32_t aType,
813
                        const nsIntRect* aData)
814
0
{
815
0
  if (aType == imgINotificationObserver::SIZE_AVAILABLE) {
816
0
    nsCOMPtr<imgIContainer> image;
817
0
    aRequest->GetImage(getter_AddRefs(image));
818
0
    return OnSizeAvailable(aRequest, image);
819
0
  }
820
0
821
0
  if (aType == imgINotificationObserver::DECODE_COMPLETE) {
822
0
    return OnDecodeComplete(aRequest);
823
0
  }
824
0
825
0
  if (aType == imgINotificationObserver::LOAD_COMPLETE) {
826
0
    uint32_t imgStatus;
827
0
    aRequest->GetImageStatus(&imgStatus);
828
0
    nsresult status =
829
0
        imgStatus & imgIRequest::STATUS_ERROR ? NS_ERROR_FAILURE : NS_OK;
830
0
    return OnLoadComplete(aRequest, status);
831
0
  }
832
0
833
0
  if (aType == imgINotificationObserver::IS_ANIMATED) {
834
0
    return OnImageIsAnimated(aRequest);
835
0
  }
836
0
837
0
  if (aType == imgINotificationObserver::FRAME_UPDATE) {
838
0
    return OnFrameUpdate(aRequest);
839
0
  }
840
0
841
0
  return NS_OK;
842
0
}
843
844
nsresult
845
nsImageBoxFrame::OnSizeAvailable(imgIRequest* aRequest, imgIContainer* aImage)
846
0
{
847
0
  NS_ENSURE_ARG_POINTER(aImage);
848
0
849
0
  // Ensure the animation (if any) is started. Note: There is no
850
0
  // corresponding call to Decrement for this. This Increment will be
851
0
  // 'cleaned up' by the Request when it is destroyed, but only then.
852
0
  aRequest->IncrementAnimationConsumers();
853
0
854
0
  nscoord w, h;
855
0
  aImage->GetWidth(&w);
856
0
  aImage->GetHeight(&h);
857
0
858
0
  mIntrinsicSize.SizeTo(nsPresContext::CSSPixelsToAppUnits(w),
859
0
                        nsPresContext::CSSPixelsToAppUnits(h));
860
0
861
0
  if (!(GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
862
0
    PresShell()->
863
0
      FrameNeedsReflow(this, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
864
0
  }
865
0
866
0
  return NS_OK;
867
0
}
868
869
nsresult
870
nsImageBoxFrame::OnDecodeComplete(imgIRequest* aRequest)
871
0
{
872
0
  nsBoxLayoutState state(PresContext());
873
0
  this->XULRedraw(state);
874
0
  return NS_OK;
875
0
}
876
877
nsresult
878
nsImageBoxFrame::OnLoadComplete(imgIRequest* aRequest, nsresult aStatus)
879
0
{
880
0
  if (NS_SUCCEEDED(aStatus)) {
881
0
    // Fire an onload DOM event.
882
0
    FireImageDOMEvent(mContent, eLoad);
883
0
  } else {
884
0
    // Fire an onerror DOM event.
885
0
    mIntrinsicSize.SizeTo(0, 0);
886
0
    PresShell()->
887
0
      FrameNeedsReflow(this, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
888
0
    FireImageDOMEvent(mContent, eLoadError);
889
0
  }
890
0
891
0
  return NS_OK;
892
0
}
893
894
nsresult
895
nsImageBoxFrame::OnImageIsAnimated(imgIRequest* aRequest)
896
0
{
897
0
  // Register with our refresh driver, if we're animated.
898
0
  nsLayoutUtils::RegisterImageRequest(PresContext(), aRequest,
899
0
                                      &mRequestRegistered);
900
0
901
0
  return NS_OK;
902
0
}
903
904
nsresult
905
nsImageBoxFrame::OnFrameUpdate(imgIRequest* aRequest)
906
0
{
907
0
  if ((0 == mRect.width) || (0 == mRect.height)) {
908
0
    return NS_OK;
909
0
  }
910
0
911
0
  // Check if WebRender has interacted with this frame. If it has
912
0
  // we need to let it know that things have changed.
913
0
  if (HasProperty(WebRenderUserDataProperty::Key())) {
914
0
    uint32_t key = static_cast<uint32_t>(DisplayItemType::TYPE_XUL_IMAGE);
915
0
    RefPtr<WebRenderFallbackData> data =
916
0
      GetWebRenderUserData<WebRenderFallbackData>(this, key);
917
0
    if (data) {
918
0
      data->SetInvalid(true);
919
0
    }
920
0
    SchedulePaint();
921
0
    return NS_OK;
922
0
  }
923
0
924
0
  InvalidateLayer(DisplayItemType::TYPE_XUL_IMAGE);
925
0
926
0
  return NS_OK;
927
0
}
928
929
NS_IMPL_ISUPPORTS(nsImageBoxListener, imgINotificationObserver)
930
931
nsImageBoxListener::nsImageBoxListener(nsImageBoxFrame *frame)
932
  : mFrame(frame)
933
0
{
934
0
}
935
936
nsImageBoxListener::~nsImageBoxListener()
937
0
{
938
0
}
939
940
NS_IMETHODIMP
941
nsImageBoxListener::Notify(imgIRequest *request, int32_t aType, const nsIntRect* aData)
942
0
{
943
0
  if (!mFrame)
944
0
    return NS_OK;
945
0
946
0
  return mFrame->Notify(request, aType, aData);
947
0
}