nsImageBoxFrame.cpp 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828
  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. // Eric Vaughan
  7. // Netscape Communications
  8. //
  9. // See documentation in associated header file
  10. //
  11. #include "nsImageBoxFrame.h"
  12. #include "nsGkAtoms.h"
  13. #include "nsRenderingContext.h"
  14. #include "nsStyleContext.h"
  15. #include "nsStyleConsts.h"
  16. #include "nsStyleUtil.h"
  17. #include "nsCOMPtr.h"
  18. #include "nsPresContext.h"
  19. #include "nsBoxLayoutState.h"
  20. #include "nsHTMLParts.h"
  21. #include "nsString.h"
  22. #include "nsLeafFrame.h"
  23. #include "nsIPresShell.h"
  24. #include "nsIDocument.h"
  25. #include "nsImageMap.h"
  26. #include "nsILinkHandler.h"
  27. #include "nsIURL.h"
  28. #include "nsILoadGroup.h"
  29. #include "nsContainerFrame.h"
  30. #include "prprf.h"
  31. #include "nsCSSRendering.h"
  32. #include "nsIDOMHTMLImageElement.h"
  33. #include "nsNameSpaceManager.h"
  34. #include "nsTextFragment.h"
  35. #include "nsIDOMHTMLMapElement.h"
  36. #include "nsTransform2D.h"
  37. #include "nsITheme.h"
  38. #include "nsIServiceManager.h"
  39. #include "nsIURI.h"
  40. #include "nsThreadUtils.h"
  41. #include "nsDisplayList.h"
  42. #include "ImageLayers.h"
  43. #include "ImageContainer.h"
  44. #include "nsIContent.h"
  45. #include "nsContentUtils.h"
  46. #include "nsSerializationHelper.h"
  47. #include "mozilla/BasicEvents.h"
  48. #include "mozilla/EventDispatcher.h"
  49. #include "mozilla/Maybe.h"
  50. #define ONLOAD_CALLED_TOO_EARLY 1
  51. using namespace mozilla;
  52. using namespace mozilla::gfx;
  53. using namespace mozilla::image;
  54. using namespace mozilla::layers;
  55. class nsImageBoxFrameEvent : public Runnable
  56. {
  57. public:
  58. nsImageBoxFrameEvent(nsIContent *content, EventMessage message)
  59. : mContent(content), mMessage(message) {}
  60. NS_IMETHOD Run() override;
  61. private:
  62. nsCOMPtr<nsIContent> mContent;
  63. EventMessage mMessage;
  64. };
  65. NS_IMETHODIMP
  66. nsImageBoxFrameEvent::Run()
  67. {
  68. nsIPresShell *pres_shell = mContent->OwnerDoc()->GetShell();
  69. if (!pres_shell) {
  70. return NS_OK;
  71. }
  72. RefPtr<nsPresContext> pres_context = pres_shell->GetPresContext();
  73. if (!pres_context) {
  74. return NS_OK;
  75. }
  76. nsEventStatus status = nsEventStatus_eIgnore;
  77. WidgetEvent event(true, mMessage);
  78. event.mFlags.mBubbles = false;
  79. EventDispatcher::Dispatch(mContent, pres_context, &event, nullptr, &status);
  80. return NS_OK;
  81. }
  82. // Fire off an event that'll asynchronously call the image elements
  83. // onload handler once handled. This is needed since the image library
  84. // can't decide if it wants to call it's observer methods
  85. // synchronously or asynchronously. If an image is loaded from the
  86. // cache the notifications come back synchronously, but if the image
  87. // is loaded from the netswork the notifications come back
  88. // asynchronously.
  89. void
  90. FireImageDOMEvent(nsIContent* aContent, EventMessage aMessage)
  91. {
  92. NS_ASSERTION(aMessage == eLoad || aMessage == eLoadError,
  93. "invalid message");
  94. nsCOMPtr<nsIRunnable> event = new nsImageBoxFrameEvent(aContent, aMessage);
  95. if (NS_FAILED(NS_DispatchToCurrentThread(event)))
  96. NS_WARNING("failed to dispatch image event");
  97. }
  98. //
  99. // NS_NewImageBoxFrame
  100. //
  101. // Creates a new image frame and returns it
  102. //
  103. nsIFrame*
  104. NS_NewImageBoxFrame (nsIPresShell* aPresShell, nsStyleContext* aContext)
  105. {
  106. return new (aPresShell) nsImageBoxFrame(aContext);
  107. }
  108. NS_IMPL_FRAMEARENA_HELPERS(nsImageBoxFrame)
  109. nsresult
  110. nsImageBoxFrame::AttributeChanged(int32_t aNameSpaceID,
  111. nsIAtom* aAttribute,
  112. int32_t aModType)
  113. {
  114. nsresult rv = nsLeafBoxFrame::AttributeChanged(aNameSpaceID, aAttribute,
  115. aModType);
  116. if (aAttribute == nsGkAtoms::src) {
  117. UpdateImage();
  118. PresContext()->PresShell()->
  119. FrameNeedsReflow(this, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
  120. }
  121. else if (aAttribute == nsGkAtoms::validate)
  122. UpdateLoadFlags();
  123. return rv;
  124. }
  125. nsImageBoxFrame::nsImageBoxFrame(nsStyleContext* aContext):
  126. nsLeafBoxFrame(aContext),
  127. mIntrinsicSize(0,0),
  128. mLoadFlags(nsIRequest::LOAD_NORMAL),
  129. mRequestRegistered(false),
  130. mUseSrcAttr(false),
  131. mSuppressStyleCheck(false)
  132. {
  133. MarkIntrinsicISizesDirty();
  134. }
  135. nsImageBoxFrame::~nsImageBoxFrame()
  136. {
  137. }
  138. /* virtual */ void
  139. nsImageBoxFrame::MarkIntrinsicISizesDirty()
  140. {
  141. SizeNeedsRecalc(mImageSize);
  142. nsLeafBoxFrame::MarkIntrinsicISizesDirty();
  143. }
  144. void
  145. nsImageBoxFrame::DestroyFrom(nsIFrame* aDestructRoot)
  146. {
  147. if (mImageRequest) {
  148. nsLayoutUtils::DeregisterImageRequest(PresContext(), mImageRequest,
  149. &mRequestRegistered);
  150. // Release image loader first so that it's refcnt can go to zero
  151. mImageRequest->CancelAndForgetObserver(NS_ERROR_FAILURE);
  152. }
  153. if (mListener)
  154. reinterpret_cast<nsImageBoxListener*>(mListener.get())->SetFrame(nullptr); // set the frame to null so we don't send messages to a dead object.
  155. nsLeafBoxFrame::DestroyFrom(aDestructRoot);
  156. }
  157. void
  158. nsImageBoxFrame::Init(nsIContent* aContent,
  159. nsContainerFrame* aParent,
  160. nsIFrame* aPrevInFlow)
  161. {
  162. if (!mListener) {
  163. RefPtr<nsImageBoxListener> listener = new nsImageBoxListener();
  164. listener->SetFrame(this);
  165. mListener = listener.forget();
  166. }
  167. mSuppressStyleCheck = true;
  168. nsLeafBoxFrame::Init(aContent, aParent, aPrevInFlow);
  169. mSuppressStyleCheck = false;
  170. UpdateLoadFlags();
  171. UpdateImage();
  172. }
  173. void
  174. nsImageBoxFrame::UpdateImage()
  175. {
  176. nsPresContext* presContext = PresContext();
  177. RefPtr<imgRequestProxy> oldImageRequest = mImageRequest;
  178. if (mImageRequest) {
  179. nsLayoutUtils::DeregisterImageRequest(presContext, mImageRequest,
  180. &mRequestRegistered);
  181. mImageRequest->CancelAndForgetObserver(NS_ERROR_FAILURE);
  182. mImageRequest = nullptr;
  183. }
  184. // get the new image src
  185. nsAutoString src;
  186. mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::src, src);
  187. mUseSrcAttr = !src.IsEmpty();
  188. if (mUseSrcAttr) {
  189. nsIDocument* doc = mContent->GetComposedDoc();
  190. if (doc) {
  191. // Use the serialized loadingPrincipal from the image element. Fall back
  192. // to mContent's principal (SystemPrincipal) if not available.
  193. nsContentPolicyType contentPolicyType = nsIContentPolicy::TYPE_INTERNAL_IMAGE;
  194. nsCOMPtr<nsIPrincipal> loadingPrincipal = mContent->NodePrincipal();
  195. nsAutoString imageLoadingPrincipal;
  196. mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::loadingprincipal,
  197. imageLoadingPrincipal);
  198. if (!imageLoadingPrincipal.IsEmpty()) {
  199. nsCOMPtr<nsISupports> serializedPrincipal;
  200. NS_DeserializeObject(NS_ConvertUTF16toUTF8(imageLoadingPrincipal),
  201. getter_AddRefs(serializedPrincipal));
  202. loadingPrincipal = do_QueryInterface(serializedPrincipal);
  203. if (loadingPrincipal) {
  204. // Set the content policy type to TYPE_INTERNAL_IMAGE_FAVICON for
  205. // indicating it's a favicon loading.
  206. contentPolicyType = nsIContentPolicy::TYPE_INTERNAL_IMAGE_FAVICON;
  207. } else {
  208. // Fallback if the deserialization is failed.
  209. loadingPrincipal = mContent->NodePrincipal();
  210. }
  211. }
  212. nsCOMPtr<nsIURI> baseURI = mContent->GetBaseURI();
  213. nsCOMPtr<nsIURI> uri;
  214. nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(uri),
  215. src,
  216. doc,
  217. baseURI);
  218. if (uri) {
  219. nsresult rv = nsContentUtils::LoadImage(uri, mContent, doc, loadingPrincipal,
  220. doc->GetDocumentURI(), doc->GetReferrerPolicy(),
  221. mListener, mLoadFlags,
  222. EmptyString(), getter_AddRefs(mImageRequest),
  223. contentPolicyType);
  224. if (NS_SUCCEEDED(rv) && mImageRequest) {
  225. nsLayoutUtils::RegisterImageRequestIfAnimated(presContext,
  226. mImageRequest,
  227. &mRequestRegistered);
  228. }
  229. }
  230. }
  231. } else {
  232. // Only get the list-style-image if we aren't being drawn
  233. // by a native theme.
  234. uint8_t appearance = StyleDisplay()->mAppearance;
  235. if (!(appearance && nsBox::gTheme &&
  236. nsBox::gTheme->ThemeSupportsWidget(nullptr, this, appearance))) {
  237. // get the list-style-image
  238. imgRequestProxy *styleRequest = StyleList()->GetListStyleImage();
  239. if (styleRequest) {
  240. styleRequest->Clone(mListener, getter_AddRefs(mImageRequest));
  241. }
  242. }
  243. }
  244. if (!mImageRequest) {
  245. // We have no image, so size to 0
  246. mIntrinsicSize.SizeTo(0, 0);
  247. } else {
  248. // We don't want discarding or decode-on-draw for xul images.
  249. mImageRequest->StartDecoding();
  250. mImageRequest->LockImage();
  251. }
  252. // Do this _after_ locking the new image in case they are the same image.
  253. if (oldImageRequest) {
  254. oldImageRequest->UnlockImage();
  255. }
  256. }
  257. void
  258. nsImageBoxFrame::UpdateLoadFlags()
  259. {
  260. static nsIContent::AttrValuesArray strings[] =
  261. {&nsGkAtoms::always, &nsGkAtoms::never, nullptr};
  262. switch (mContent->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::validate,
  263. strings, eCaseMatters)) {
  264. case 0:
  265. mLoadFlags = nsIRequest::VALIDATE_ALWAYS;
  266. break;
  267. case 1:
  268. mLoadFlags = nsIRequest::VALIDATE_NEVER|nsIRequest::LOAD_FROM_CACHE;
  269. break;
  270. default:
  271. mLoadFlags = nsIRequest::LOAD_NORMAL;
  272. break;
  273. }
  274. }
  275. void
  276. nsImageBoxFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
  277. const nsDisplayListSet& aLists)
  278. {
  279. nsLeafBoxFrame::BuildDisplayList(aBuilder, aLists);
  280. if ((0 == mRect.width) || (0 == mRect.height)) {
  281. // Do not render when given a zero area. This avoids some useless
  282. // scaling work while we wait for our image dimensions to arrive
  283. // asynchronously.
  284. return;
  285. }
  286. if (!IsVisibleForPainting(aBuilder))
  287. return;
  288. uint32_t clipFlags =
  289. nsStyleUtil::ObjectPropsMightCauseOverflow(StylePosition()) ?
  290. 0 : DisplayListClipState::ASSUME_DRAWING_RESTRICTED_TO_CONTENT_RECT;
  291. DisplayListClipState::AutoClipContainingBlockDescendantsToContentBox
  292. clip(aBuilder, this, clipFlags);
  293. nsDisplayList list;
  294. list.AppendNewToTop(
  295. new (aBuilder) nsDisplayXULImage(aBuilder, this));
  296. CreateOwnLayerIfNeeded(aBuilder, &list);
  297. aLists.Content()->AppendToTop(&list);
  298. }
  299. DrawResult
  300. nsImageBoxFrame::PaintImage(nsRenderingContext& aRenderingContext,
  301. const nsRect& aDirtyRect, nsPoint aPt,
  302. uint32_t aFlags)
  303. {
  304. nsRect constraintRect;
  305. GetXULClientRect(constraintRect);
  306. constraintRect += aPt;
  307. if (!mImageRequest) {
  308. // This probably means we're drawn by a native theme.
  309. return DrawResult::SUCCESS;
  310. }
  311. // don't draw if the image is not dirty
  312. // XXX(seth): Can this actually happen anymore?
  313. nsRect dirty;
  314. if (!dirty.IntersectRect(aDirtyRect, constraintRect)) {
  315. return DrawResult::TEMPORARY_ERROR;
  316. }
  317. // Don't draw if the image's size isn't available.
  318. uint32_t imgStatus;
  319. if (!NS_SUCCEEDED(mImageRequest->GetImageStatus(&imgStatus)) ||
  320. !(imgStatus & imgIRequest::STATUS_SIZE_AVAILABLE)) {
  321. return DrawResult::NOT_READY;
  322. }
  323. nsCOMPtr<imgIContainer> imgCon;
  324. mImageRequest->GetImage(getter_AddRefs(imgCon));
  325. if (!imgCon) {
  326. return DrawResult::NOT_READY;
  327. }
  328. bool hasSubRect = !mUseSrcAttr && (mSubRect.width > 0 || mSubRect.height > 0);
  329. Maybe<nsPoint> anchorPoint;
  330. nsRect dest;
  331. if (!mUseSrcAttr) {
  332. // Our image (if we have one) is coming from the CSS property
  333. // 'list-style-image' (combined with '-moz-image-region'). For now, ignore
  334. // 'object-fit' & 'object-position' in this case, and just fill our rect.
  335. // XXXdholbert Should we even honor these properties in this case? They only
  336. // apply to replaced elements, and I'm not sure we count as a replaced
  337. // element when our image data is determined by CSS.
  338. dest = constraintRect;
  339. } else {
  340. // Determine dest rect based on intrinsic size & ratio, along with
  341. // 'object-fit' & 'object-position' properties:
  342. IntrinsicSize intrinsicSize;
  343. AspectRatio intrinsicRatio;
  344. if (mIntrinsicSize.width > 0 && mIntrinsicSize.height > 0) {
  345. // Image has a valid size; use it as intrinsic size & ratio.
  346. intrinsicSize.width.SetCoordValue(mIntrinsicSize.width);
  347. intrinsicSize.height.SetCoordValue(mIntrinsicSize.height);
  348. intrinsicRatio = AspectRatio::FromSize(mIntrinsicSize);
  349. } else {
  350. // Image doesn't have a (valid) intrinsic size.
  351. // Try to look up intrinsic ratio and use that at least.
  352. imgCon->GetIntrinsicRatio(&intrinsicRatio);
  353. }
  354. anchorPoint.emplace();
  355. dest = nsLayoutUtils::ComputeObjectDestRect(constraintRect,
  356. intrinsicSize,
  357. intrinsicRatio,
  358. StylePosition(),
  359. anchorPoint.ptr());
  360. }
  361. return nsLayoutUtils::DrawSingleImage(
  362. *aRenderingContext.ThebesContext(),
  363. PresContext(), imgCon,
  364. nsLayoutUtils::GetSamplingFilterForFrame(this),
  365. dest, dirty, nullptr, aFlags,
  366. anchorPoint.ptrOr(nullptr),
  367. hasSubRect ? &mSubRect : nullptr);
  368. }
  369. void nsDisplayXULImage::Paint(nsDisplayListBuilder* aBuilder,
  370. nsRenderingContext* aCtx)
  371. {
  372. uint32_t flags = imgIContainer::FLAG_NONE;
  373. if (aBuilder->ShouldSyncDecodeImages())
  374. flags |= imgIContainer::FLAG_SYNC_DECODE;
  375. if (aBuilder->IsPaintingToWindow())
  376. flags |= imgIContainer::FLAG_HIGH_QUALITY_SCALING;
  377. DrawResult result = static_cast<nsImageBoxFrame*>(mFrame)->
  378. PaintImage(*aCtx, mVisibleRect, ToReferenceFrame(), flags);
  379. nsDisplayItemGenericImageGeometry::UpdateDrawResult(this, result);
  380. }
  381. nsDisplayItemGeometry*
  382. nsDisplayXULImage::AllocateGeometry(nsDisplayListBuilder* aBuilder)
  383. {
  384. return new nsDisplayItemGenericImageGeometry(this, aBuilder);
  385. }
  386. void
  387. nsDisplayXULImage::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
  388. const nsDisplayItemGeometry* aGeometry,
  389. nsRegion* aInvalidRegion)
  390. {
  391. auto boxFrame = static_cast<nsImageBoxFrame*>(mFrame);
  392. auto geometry =
  393. static_cast<const nsDisplayItemGenericImageGeometry*>(aGeometry);
  394. if (aBuilder->ShouldSyncDecodeImages() &&
  395. boxFrame->mImageRequest &&
  396. geometry->ShouldInvalidateToSyncDecodeImages()) {
  397. bool snap;
  398. aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap));
  399. }
  400. nsDisplayImageContainer::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion);
  401. }
  402. bool
  403. nsDisplayXULImage::CanOptimizeToImageLayer(LayerManager* aManager,
  404. nsDisplayListBuilder* aBuilder)
  405. {
  406. nsImageBoxFrame* imageFrame = static_cast<nsImageBoxFrame*>(mFrame);
  407. if (!imageFrame->CanOptimizeToImageLayer()) {
  408. return false;
  409. }
  410. return nsDisplayImageContainer::CanOptimizeToImageLayer(aManager, aBuilder);
  411. }
  412. already_AddRefed<imgIContainer>
  413. nsDisplayXULImage::GetImage()
  414. {
  415. nsImageBoxFrame* imageFrame = static_cast<nsImageBoxFrame*>(mFrame);
  416. if (!imageFrame->mImageRequest) {
  417. return nullptr;
  418. }
  419. nsCOMPtr<imgIContainer> imgCon;
  420. imageFrame->mImageRequest->GetImage(getter_AddRefs(imgCon));
  421. return imgCon.forget();
  422. }
  423. nsRect
  424. nsDisplayXULImage::GetDestRect()
  425. {
  426. nsImageBoxFrame* imageFrame = static_cast<nsImageBoxFrame*>(mFrame);
  427. nsRect clientRect;
  428. imageFrame->GetXULClientRect(clientRect);
  429. return clientRect + ToReferenceFrame();
  430. }
  431. bool
  432. nsImageBoxFrame::CanOptimizeToImageLayer()
  433. {
  434. bool hasSubRect = !mUseSrcAttr && (mSubRect.width > 0 || mSubRect.height > 0);
  435. if (hasSubRect) {
  436. return false;
  437. }
  438. return true;
  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. // Fetch our subrect.
  450. const nsStyleList* myList = StyleList();
  451. mSubRect = myList->mImageRegion; // before |mSuppressStyleCheck| test!
  452. if (mUseSrcAttr || mSuppressStyleCheck)
  453. return; // No more work required, since the image isn't specified by style.
  454. // If we're using a native theme implementation, we shouldn't draw anything.
  455. const nsStyleDisplay* disp = StyleDisplay();
  456. if (disp->mAppearance && nsBox::gTheme &&
  457. nsBox::gTheme->ThemeSupportsWidget(nullptr, this, disp->mAppearance))
  458. return;
  459. // If list-style-image changes, we have a new image.
  460. nsCOMPtr<nsIURI> oldURI, newURI;
  461. if (mImageRequest)
  462. mImageRequest->GetURI(getter_AddRefs(oldURI));
  463. if (myList->GetListStyleImage())
  464. myList->GetListStyleImage()->GetURI(getter_AddRefs(newURI));
  465. bool equal;
  466. if (newURI == oldURI || // handles null==null
  467. (newURI && oldURI &&
  468. NS_SUCCEEDED(newURI->Equals(oldURI, &equal)) && equal))
  469. return;
  470. UpdateImage();
  471. } // DidSetStyleContext
  472. void
  473. nsImageBoxFrame::GetImageSize()
  474. {
  475. if (mIntrinsicSize.width > 0 && mIntrinsicSize.height > 0) {
  476. mImageSize.width = mIntrinsicSize.width;
  477. mImageSize.height = mIntrinsicSize.height;
  478. } else {
  479. mImageSize.width = 0;
  480. mImageSize.height = 0;
  481. }
  482. }
  483. /**
  484. * Ok return our dimensions
  485. */
  486. nsSize
  487. nsImageBoxFrame::GetXULPrefSize(nsBoxLayoutState& aState)
  488. {
  489. nsSize size(0,0);
  490. DISPLAY_PREF_SIZE(this, size);
  491. if (DoesNeedRecalc(mImageSize))
  492. GetImageSize();
  493. if (!mUseSrcAttr && (mSubRect.width > 0 || mSubRect.height > 0))
  494. size = mSubRect.Size();
  495. else
  496. size = mImageSize;
  497. nsSize intrinsicSize = size;
  498. nsMargin borderPadding(0,0,0,0);
  499. GetXULBorderAndPadding(borderPadding);
  500. size.width += borderPadding.LeftRight();
  501. size.height += borderPadding.TopBottom();
  502. bool widthSet, heightSet;
  503. nsIFrame::AddXULPrefSize(this, size, widthSet, heightSet);
  504. NS_ASSERTION(size.width != NS_INTRINSICSIZE && size.height != NS_INTRINSICSIZE,
  505. "non-intrinsic size expected");
  506. nsSize minSize = GetXULMinSize(aState);
  507. nsSize maxSize = GetXULMaxSize(aState);
  508. if (!widthSet && !heightSet) {
  509. if (minSize.width != NS_INTRINSICSIZE)
  510. minSize.width -= borderPadding.LeftRight();
  511. if (minSize.height != NS_INTRINSICSIZE)
  512. minSize.height -= borderPadding.TopBottom();
  513. if (maxSize.width != NS_INTRINSICSIZE)
  514. maxSize.width -= borderPadding.LeftRight();
  515. if (maxSize.height != NS_INTRINSICSIZE)
  516. maxSize.height -= borderPadding.TopBottom();
  517. size = nsLayoutUtils::ComputeAutoSizeWithIntrinsicDimensions(minSize.width, minSize.height,
  518. maxSize.width, maxSize.height,
  519. intrinsicSize.width, intrinsicSize.height);
  520. NS_ASSERTION(size.width != NS_INTRINSICSIZE && size.height != NS_INTRINSICSIZE,
  521. "non-intrinsic size expected");
  522. size.width += borderPadding.LeftRight();
  523. size.height += borderPadding.TopBottom();
  524. return size;
  525. }
  526. if (!widthSet) {
  527. if (intrinsicSize.height > 0) {
  528. // Subtract off the border and padding from the height because the
  529. // content-box needs to be used to determine the ratio
  530. nscoord height = size.height - borderPadding.TopBottom();
  531. size.width = nscoord(int64_t(height) * int64_t(intrinsicSize.width) /
  532. int64_t(intrinsicSize.height));
  533. }
  534. else {
  535. size.width = intrinsicSize.width;
  536. }
  537. size.width += borderPadding.LeftRight();
  538. }
  539. else if (!heightSet) {
  540. if (intrinsicSize.width > 0) {
  541. nscoord width = size.width - borderPadding.LeftRight();
  542. size.height = nscoord(int64_t(width) * int64_t(intrinsicSize.height) /
  543. int64_t(intrinsicSize.width));
  544. }
  545. else {
  546. size.height = intrinsicSize.height;
  547. }
  548. size.height += borderPadding.TopBottom();
  549. }
  550. return BoundsCheck(minSize, size, maxSize);
  551. }
  552. nsSize
  553. nsImageBoxFrame::GetXULMinSize(nsBoxLayoutState& aState)
  554. {
  555. // An image can always scale down to (0,0).
  556. nsSize size(0,0);
  557. DISPLAY_MIN_SIZE(this, size);
  558. AddBorderAndPadding(size);
  559. bool widthSet, heightSet;
  560. nsIFrame::AddXULMinSize(aState, this, size, widthSet, heightSet);
  561. return size;
  562. }
  563. nscoord
  564. nsImageBoxFrame::GetXULBoxAscent(nsBoxLayoutState& aState)
  565. {
  566. return GetXULPrefSize(aState).height;
  567. }
  568. nsIAtom*
  569. nsImageBoxFrame::GetType() const
  570. {
  571. return nsGkAtoms::imageBoxFrame;
  572. }
  573. #ifdef DEBUG_FRAME_DUMP
  574. nsresult
  575. nsImageBoxFrame::GetFrameName(nsAString& aResult) const
  576. {
  577. return MakeFrameName(NS_LITERAL_STRING("ImageBox"), aResult);
  578. }
  579. #endif
  580. nsresult
  581. nsImageBoxFrame::Notify(imgIRequest* aRequest,
  582. int32_t aType,
  583. const nsIntRect* aData)
  584. {
  585. if (aType == imgINotificationObserver::SIZE_AVAILABLE) {
  586. nsCOMPtr<imgIContainer> image;
  587. aRequest->GetImage(getter_AddRefs(image));
  588. return OnSizeAvailable(aRequest, image);
  589. }
  590. if (aType == imgINotificationObserver::DECODE_COMPLETE) {
  591. return OnDecodeComplete(aRequest);
  592. }
  593. if (aType == imgINotificationObserver::LOAD_COMPLETE) {
  594. uint32_t imgStatus;
  595. aRequest->GetImageStatus(&imgStatus);
  596. nsresult status =
  597. imgStatus & imgIRequest::STATUS_ERROR ? NS_ERROR_FAILURE : NS_OK;
  598. return OnLoadComplete(aRequest, status);
  599. }
  600. if (aType == imgINotificationObserver::IS_ANIMATED) {
  601. return OnImageIsAnimated(aRequest);
  602. }
  603. if (aType == imgINotificationObserver::FRAME_UPDATE) {
  604. return OnFrameUpdate(aRequest);
  605. }
  606. return NS_OK;
  607. }
  608. nsresult
  609. nsImageBoxFrame::OnSizeAvailable(imgIRequest* aRequest, imgIContainer* aImage)
  610. {
  611. NS_ENSURE_ARG_POINTER(aImage);
  612. // Ensure the animation (if any) is started. Note: There is no
  613. // corresponding call to Decrement for this. This Increment will be
  614. // 'cleaned up' by the Request when it is destroyed, but only then.
  615. aRequest->IncrementAnimationConsumers();
  616. nscoord w, h;
  617. aImage->GetWidth(&w);
  618. aImage->GetHeight(&h);
  619. mIntrinsicSize.SizeTo(nsPresContext::CSSPixelsToAppUnits(w),
  620. nsPresContext::CSSPixelsToAppUnits(h));
  621. if (!(GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
  622. PresContext()->PresShell()->
  623. FrameNeedsReflow(this, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
  624. }
  625. return NS_OK;
  626. }
  627. nsresult
  628. nsImageBoxFrame::OnDecodeComplete(imgIRequest* aRequest)
  629. {
  630. nsBoxLayoutState state(PresContext());
  631. this->XULRedraw(state);
  632. return NS_OK;
  633. }
  634. nsresult
  635. nsImageBoxFrame::OnLoadComplete(imgIRequest* aRequest, nsresult aStatus)
  636. {
  637. if (NS_SUCCEEDED(aStatus)) {
  638. // Fire an onload DOM event.
  639. FireImageDOMEvent(mContent, eLoad);
  640. } else {
  641. // Fire an onerror DOM event.
  642. mIntrinsicSize.SizeTo(0, 0);
  643. PresContext()->PresShell()->
  644. FrameNeedsReflow(this, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
  645. FireImageDOMEvent(mContent, eLoadError);
  646. }
  647. return NS_OK;
  648. }
  649. nsresult
  650. nsImageBoxFrame::OnImageIsAnimated(imgIRequest* aRequest)
  651. {
  652. // Register with our refresh driver, if we're animated.
  653. nsLayoutUtils::RegisterImageRequest(PresContext(), aRequest,
  654. &mRequestRegistered);
  655. return NS_OK;
  656. }
  657. nsresult
  658. nsImageBoxFrame::OnFrameUpdate(imgIRequest* aRequest)
  659. {
  660. if ((0 == mRect.width) || (0 == mRect.height)) {
  661. return NS_OK;
  662. }
  663. InvalidateLayer(nsDisplayItem::TYPE_XUL_IMAGE);
  664. return NS_OK;
  665. }
  666. NS_IMPL_ISUPPORTS(nsImageBoxListener, imgINotificationObserver, imgIOnloadBlocker)
  667. nsImageBoxListener::nsImageBoxListener()
  668. {
  669. }
  670. nsImageBoxListener::~nsImageBoxListener()
  671. {
  672. }
  673. NS_IMETHODIMP
  674. nsImageBoxListener::Notify(imgIRequest *request, int32_t aType, const nsIntRect* aData)
  675. {
  676. if (!mFrame)
  677. return NS_OK;
  678. return mFrame->Notify(request, aType, aData);
  679. }
  680. NS_IMETHODIMP
  681. nsImageBoxListener::BlockOnload(imgIRequest *aRequest)
  682. {
  683. if (mFrame && mFrame->GetContent() && mFrame->GetContent()->GetUncomposedDoc()) {
  684. mFrame->GetContent()->GetUncomposedDoc()->BlockOnload();
  685. }
  686. return NS_OK;
  687. }
  688. NS_IMETHODIMP
  689. nsImageBoxListener::UnblockOnload(imgIRequest *aRequest)
  690. {
  691. if (mFrame && mFrame->GetContent() && mFrame->GetContent()->GetUncomposedDoc()) {
  692. mFrame->GetContent()->GetUncomposedDoc()->UnblockOnload(false);
  693. }
  694. return NS_OK;
  695. }