ImageLoader.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527
  1. /* This Source Code Form is subject to the terms of the Mozilla Public
  2. * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  3. * You can obtain one at http://mozilla.org/MPL/2.0/. */
  4. /* A class that handles style system image loads (other image loads are handled
  5. * by the nodes in the content tree).
  6. */
  7. #include "mozilla/css/ImageLoader.h"
  8. #include "nsAutoPtr.h"
  9. #include "nsContentUtils.h"
  10. #include "nsLayoutUtils.h"
  11. #include "nsError.h"
  12. #include "nsDisplayList.h"
  13. #include "FrameLayerBuilder.h"
  14. #include "nsSVGEffects.h"
  15. #include "imgIContainer.h"
  16. #include "Image.h"
  17. namespace mozilla {
  18. namespace css {
  19. void
  20. ImageLoader::DropDocumentReference()
  21. {
  22. // It's okay if GetPresContext returns null here (due to the presshell pointer
  23. // on the document being null) as that means the presshell has already
  24. // been destroyed, and it also calls ClearFrames when it is destroyed.
  25. ClearFrames(GetPresContext());
  26. for (auto it = mImages.Iter(); !it.Done(); it.Next()) {
  27. ImageLoader::Image* image = it.Get()->GetKey();
  28. imgIRequest* request = image->mRequests.GetWeak(mDocument);
  29. if (request) {
  30. request->CancelAndForgetObserver(NS_BINDING_ABORTED);
  31. }
  32. image->mRequests.Remove(mDocument);
  33. }
  34. mImages.Clear();
  35. mDocument = nullptr;
  36. }
  37. void
  38. ImageLoader::AssociateRequestToFrame(imgIRequest* aRequest,
  39. nsIFrame* aFrame)
  40. {
  41. nsCOMPtr<imgINotificationObserver> observer;
  42. aRequest->GetNotificationObserver(getter_AddRefs(observer));
  43. if (!observer) {
  44. // The request has already been canceled, so ignore it. This is ok because
  45. // we're not going to get any more notifications from a canceled request.
  46. return;
  47. }
  48. MOZ_ASSERT(observer == this);
  49. FrameSet* frameSet = nullptr;
  50. if (mRequestToFrameMap.Get(aRequest, &frameSet)) {
  51. NS_ASSERTION(frameSet, "This should never be null!");
  52. }
  53. if (!frameSet) {
  54. nsAutoPtr<FrameSet> newFrameSet(new FrameSet());
  55. mRequestToFrameMap.Put(aRequest, newFrameSet);
  56. frameSet = newFrameSet.forget();
  57. nsPresContext* presContext = GetPresContext();
  58. if (presContext) {
  59. nsLayoutUtils::RegisterImageRequestIfAnimated(presContext,
  60. aRequest,
  61. nullptr);
  62. }
  63. }
  64. RequestSet* requestSet = nullptr;
  65. if (mFrameToRequestMap.Get(aFrame, &requestSet)) {
  66. NS_ASSERTION(requestSet, "This should never be null");
  67. }
  68. if (!requestSet) {
  69. nsAutoPtr<RequestSet> newRequestSet(new RequestSet());
  70. mFrameToRequestMap.Put(aFrame, newRequestSet);
  71. requestSet = newRequestSet.forget();
  72. }
  73. // Add these to the sets, but only if they're not already there.
  74. uint32_t i = frameSet->IndexOfFirstElementGt(aFrame);
  75. if (i == 0 || aFrame != frameSet->ElementAt(i-1)) {
  76. frameSet->InsertElementAt(i, aFrame);
  77. }
  78. i = requestSet->IndexOfFirstElementGt(aRequest);
  79. if (i == 0 || aRequest != requestSet->ElementAt(i-1)) {
  80. requestSet->InsertElementAt(i, aRequest);
  81. }
  82. }
  83. void
  84. ImageLoader::MaybeRegisterCSSImage(ImageLoader::Image* aImage)
  85. {
  86. NS_ASSERTION(aImage, "This should never be null!");
  87. bool found = false;
  88. aImage->mRequests.GetWeak(mDocument, &found);
  89. if (found) {
  90. // This document already has a request.
  91. return;
  92. }
  93. imgRequestProxy* canonicalRequest = aImage->mRequests.GetWeak(nullptr);
  94. if (!canonicalRequest) {
  95. // The image was blocked or something.
  96. return;
  97. }
  98. RefPtr<imgRequestProxy> request;
  99. // Ignore errors here. If cloning fails for some reason we'll put a null
  100. // entry in the hash and we won't keep trying to clone.
  101. mInClone = true;
  102. canonicalRequest->Clone(this, getter_AddRefs(request));
  103. mInClone = false;
  104. aImage->mRequests.Put(mDocument, request);
  105. AddImage(aImage);
  106. }
  107. void
  108. ImageLoader::DeregisterCSSImage(ImageLoader::Image* aImage)
  109. {
  110. RemoveImage(aImage);
  111. }
  112. void
  113. ImageLoader::DisassociateRequestFromFrame(imgIRequest* aRequest,
  114. nsIFrame* aFrame)
  115. {
  116. FrameSet* frameSet = nullptr;
  117. RequestSet* requestSet = nullptr;
  118. #ifdef DEBUG
  119. {
  120. nsCOMPtr<imgINotificationObserver> observer;
  121. aRequest->GetNotificationObserver(getter_AddRefs(observer));
  122. MOZ_ASSERT(!observer || observer == this);
  123. }
  124. #endif
  125. mRequestToFrameMap.Get(aRequest, &frameSet);
  126. mFrameToRequestMap.Get(aFrame, &requestSet);
  127. if (frameSet) {
  128. frameSet->RemoveElementSorted(aFrame);
  129. }
  130. if (requestSet) {
  131. requestSet->RemoveElementSorted(aRequest);
  132. }
  133. if (frameSet && !frameSet->Length()) {
  134. mRequestToFrameMap.Remove(aRequest);
  135. nsPresContext* presContext = GetPresContext();
  136. if (presContext) {
  137. nsLayoutUtils::DeregisterImageRequest(presContext,
  138. aRequest,
  139. nullptr);
  140. }
  141. }
  142. if (requestSet && !requestSet->Length()) {
  143. mFrameToRequestMap.Remove(aFrame);
  144. }
  145. }
  146. void
  147. ImageLoader::DropRequestsForFrame(nsIFrame* aFrame)
  148. {
  149. RequestSet* requestSet = nullptr;
  150. if (!mFrameToRequestMap.Get(aFrame, &requestSet)) {
  151. return;
  152. }
  153. NS_ASSERTION(requestSet, "This should never be null");
  154. RequestSet frozenRequestSet(*requestSet);
  155. for (RequestSet::size_type i = frozenRequestSet.Length(); i != 0; --i) {
  156. imgIRequest* request = frozenRequestSet.ElementAt(i - 1);
  157. DisassociateRequestFromFrame(request, aFrame);
  158. }
  159. }
  160. void
  161. ImageLoader::SetAnimationMode(uint16_t aMode)
  162. {
  163. NS_ASSERTION(aMode == imgIContainer::kNormalAnimMode ||
  164. aMode == imgIContainer::kDontAnimMode ||
  165. aMode == imgIContainer::kLoopOnceAnimMode,
  166. "Wrong Animation Mode is being set!");
  167. for (auto iter = mRequestToFrameMap.ConstIter(); !iter.Done(); iter.Next()) {
  168. auto request = static_cast<imgIRequest*>(iter.Key());
  169. #ifdef DEBUG
  170. {
  171. nsCOMPtr<imgIRequest> debugRequest = do_QueryInterface(request);
  172. NS_ASSERTION(debugRequest == request, "This is bad");
  173. }
  174. #endif
  175. nsCOMPtr<imgIContainer> container;
  176. request->GetImage(getter_AddRefs(container));
  177. if (!container) {
  178. continue;
  179. }
  180. // This can fail if the image is in error, and we don't care.
  181. container->SetAnimationMode(aMode);
  182. }
  183. }
  184. void
  185. ImageLoader::ClearFrames(nsPresContext* aPresContext)
  186. {
  187. for (auto iter = mRequestToFrameMap.ConstIter(); !iter.Done(); iter.Next()) {
  188. auto request = static_cast<imgIRequest*>(iter.Key());
  189. #ifdef DEBUG
  190. {
  191. nsCOMPtr<imgIRequest> debugRequest = do_QueryInterface(request);
  192. NS_ASSERTION(debugRequest == request, "This is bad");
  193. }
  194. #endif
  195. if (aPresContext) {
  196. nsLayoutUtils::DeregisterImageRequest(aPresContext,
  197. request,
  198. nullptr);
  199. }
  200. }
  201. mRequestToFrameMap.Clear();
  202. mFrameToRequestMap.Clear();
  203. }
  204. void
  205. ImageLoader::LoadImage(nsIURI* aURI, nsIPrincipal* aOriginPrincipal,
  206. nsIURI* aReferrer, ImageLoader::Image* aImage)
  207. {
  208. NS_ASSERTION(aImage->mRequests.Count() == 0, "Huh?");
  209. aImage->mRequests.Put(nullptr, nullptr);
  210. if (!aURI) {
  211. return;
  212. }
  213. RefPtr<imgRequestProxy> request;
  214. nsresult rv = nsContentUtils::LoadImage(aURI, mDocument, mDocument,
  215. aOriginPrincipal, aReferrer,
  216. mDocument->GetReferrerPolicy(),
  217. nullptr, nsIRequest::LOAD_NORMAL,
  218. NS_LITERAL_STRING("css"),
  219. getter_AddRefs(request));
  220. if (NS_FAILED(rv) || !request) {
  221. return;
  222. }
  223. RefPtr<imgRequestProxy> clonedRequest;
  224. mInClone = true;
  225. rv = request->Clone(this, getter_AddRefs(clonedRequest));
  226. mInClone = false;
  227. if (NS_FAILED(rv)) {
  228. return;
  229. }
  230. aImage->mRequests.Put(nullptr, request);
  231. aImage->mRequests.Put(mDocument, clonedRequest);
  232. AddImage(aImage);
  233. }
  234. void
  235. ImageLoader::AddImage(ImageLoader::Image* aImage)
  236. {
  237. NS_ASSERTION(!mImages.Contains(aImage), "Huh?");
  238. mImages.PutEntry(aImage);
  239. }
  240. void
  241. ImageLoader::RemoveImage(ImageLoader::Image* aImage)
  242. {
  243. NS_ASSERTION(mImages.Contains(aImage), "Huh?");
  244. mImages.RemoveEntry(aImage);
  245. }
  246. nsPresContext*
  247. ImageLoader::GetPresContext()
  248. {
  249. if (!mDocument) {
  250. return nullptr;
  251. }
  252. nsIPresShell* shell = mDocument->GetShell();
  253. if (!shell) {
  254. return nullptr;
  255. }
  256. return shell->GetPresContext();
  257. }
  258. void InvalidateImagesCallback(nsIFrame* aFrame,
  259. FrameLayerBuilder::DisplayItemData* aItem)
  260. {
  261. nsDisplayItem::Type type = nsDisplayItem::GetDisplayItemTypeFromKey(aItem->GetDisplayItemKey());
  262. uint8_t flags = nsDisplayItem::GetDisplayItemFlagsForType(type);
  263. if (flags & nsDisplayItem::TYPE_RENDERS_NO_IMAGES) {
  264. return;
  265. }
  266. if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
  267. printf_stderr("Invalidating display item(type=%d) based on frame %p \
  268. because it might contain an invalidated image\n", type, aFrame);
  269. }
  270. aItem->Invalidate();
  271. aFrame->SchedulePaint();
  272. }
  273. void
  274. ImageLoader::DoRedraw(FrameSet* aFrameSet, bool aForcePaint)
  275. {
  276. NS_ASSERTION(aFrameSet, "Must have a frame set");
  277. NS_ASSERTION(mDocument, "Should have returned earlier!");
  278. FrameSet::size_type length = aFrameSet->Length();
  279. for (FrameSet::size_type i = 0; i < length; i++) {
  280. nsIFrame* frame = aFrameSet->ElementAt(i);
  281. if (frame->StyleVisibility()->IsVisible()) {
  282. if (frame->IsFrameOfType(nsIFrame::eTablePart)) {
  283. // Tables don't necessarily build border/background display items
  284. // for the individual table part frames, so IterateRetainedDataFor
  285. // might not find the right display item.
  286. frame->InvalidateFrame();
  287. } else {
  288. FrameLayerBuilder::IterateRetainedDataFor(frame, InvalidateImagesCallback);
  289. // Update ancestor rendering observers (-moz-element etc)
  290. nsIFrame *f = frame;
  291. while (f && !f->HasAnyStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT)) {
  292. nsSVGEffects::InvalidateDirectRenderingObservers(f);
  293. f = nsLayoutUtils::GetCrossDocParentFrame(f);
  294. }
  295. if (aForcePaint) {
  296. frame->SchedulePaint();
  297. }
  298. }
  299. }
  300. }
  301. }
  302. NS_IMPL_ADDREF(ImageLoader)
  303. NS_IMPL_RELEASE(ImageLoader)
  304. NS_INTERFACE_MAP_BEGIN(ImageLoader)
  305. NS_INTERFACE_MAP_ENTRY(imgINotificationObserver)
  306. NS_INTERFACE_MAP_ENTRY(imgIOnloadBlocker)
  307. NS_INTERFACE_MAP_END
  308. NS_IMETHODIMP
  309. ImageLoader::Notify(imgIRequest* aRequest, int32_t aType, const nsIntRect* aData)
  310. {
  311. if (aType == imgINotificationObserver::SIZE_AVAILABLE) {
  312. nsCOMPtr<imgIContainer> image;
  313. aRequest->GetImage(getter_AddRefs(image));
  314. return OnSizeAvailable(aRequest, image);
  315. }
  316. if (aType == imgINotificationObserver::IS_ANIMATED) {
  317. return OnImageIsAnimated(aRequest);
  318. }
  319. if (aType == imgINotificationObserver::FRAME_COMPLETE) {
  320. return OnFrameComplete(aRequest);
  321. }
  322. if (aType == imgINotificationObserver::FRAME_UPDATE) {
  323. return OnFrameUpdate(aRequest);
  324. }
  325. if (aType == imgINotificationObserver::DECODE_COMPLETE) {
  326. nsCOMPtr<imgIContainer> image;
  327. aRequest->GetImage(getter_AddRefs(image));
  328. if (image && mDocument) {
  329. image->PropagateUseCounters(mDocument);
  330. }
  331. }
  332. return NS_OK;
  333. }
  334. nsresult
  335. ImageLoader::OnSizeAvailable(imgIRequest* aRequest, imgIContainer* aImage)
  336. {
  337. nsPresContext* presContext = GetPresContext();
  338. if (!presContext) {
  339. return NS_OK;
  340. }
  341. aImage->SetAnimationMode(presContext->ImageAnimationMode());
  342. return NS_OK;
  343. }
  344. nsresult
  345. ImageLoader::OnImageIsAnimated(imgIRequest* aRequest)
  346. {
  347. if (!mDocument) {
  348. return NS_OK;
  349. }
  350. FrameSet* frameSet = nullptr;
  351. if (!mRequestToFrameMap.Get(aRequest, &frameSet)) {
  352. return NS_OK;
  353. }
  354. // Register with the refresh driver now that we are aware that
  355. // we are animated.
  356. nsPresContext* presContext = GetPresContext();
  357. if (presContext) {
  358. nsLayoutUtils::RegisterImageRequest(presContext,
  359. aRequest,
  360. nullptr);
  361. }
  362. return NS_OK;
  363. }
  364. nsresult
  365. ImageLoader::OnFrameComplete(imgIRequest* aRequest)
  366. {
  367. if (!mDocument || mInClone) {
  368. return NS_OK;
  369. }
  370. FrameSet* frameSet = nullptr;
  371. if (!mRequestToFrameMap.Get(aRequest, &frameSet)) {
  372. return NS_OK;
  373. }
  374. NS_ASSERTION(frameSet, "This should never be null!");
  375. // Since we just finished decoding a frame, we always want to paint, in case
  376. // we're now able to paint an image that we couldn't paint before (and hence
  377. // that we don't have retained data for).
  378. DoRedraw(frameSet, /* aForcePaint = */ true);
  379. return NS_OK;
  380. }
  381. nsresult
  382. ImageLoader::OnFrameUpdate(imgIRequest* aRequest)
  383. {
  384. if (!mDocument || mInClone) {
  385. return NS_OK;
  386. }
  387. FrameSet* frameSet = nullptr;
  388. if (!mRequestToFrameMap.Get(aRequest, &frameSet)) {
  389. return NS_OK;
  390. }
  391. NS_ASSERTION(frameSet, "This should never be null!");
  392. DoRedraw(frameSet, /* aForcePaint = */ false);
  393. return NS_OK;
  394. }
  395. NS_IMETHODIMP
  396. ImageLoader::BlockOnload(imgIRequest* aRequest)
  397. {
  398. if (!mDocument) {
  399. return NS_OK;
  400. }
  401. mDocument->BlockOnload();
  402. return NS_OK;
  403. }
  404. NS_IMETHODIMP
  405. ImageLoader::UnblockOnload(imgIRequest* aRequest)
  406. {
  407. if (!mDocument) {
  408. return NS_OK;
  409. }
  410. mDocument->UnblockOnload(false);
  411. return NS_OK;
  412. }
  413. void
  414. ImageLoader::FlushUseCounters()
  415. {
  416. for (auto iter = mImages.Iter(); !iter.Done(); iter.Next()) {
  417. nsPtrHashKey<Image>* key = iter.Get();
  418. ImageLoader::Image* image = key->GetKey();
  419. imgIRequest* request = image->mRequests.GetWeak(mDocument);
  420. nsCOMPtr<imgIContainer> container;
  421. request->GetImage(getter_AddRefs(container));
  422. }
  423. }
  424. } // namespace css
  425. } // namespace mozilla