Bug 666317 - Discard decoded images on a memory-pressure notification. r=joe
authorJustin Lebar <justin.lebar@gmail.com>
Thu, 30 Jun 2011 17:13:56 -0400
changeset 72160 aa26a78af69545a008d93edbb2015a8aba46edbf
parent 72159 ed9bdae5798140b219022866447ffae0697c82ed
child 72161 e3cb39d085f3300298f246d18ab2499b166b0018
push id20667
push usermak77@bonardo.net
push dateSat, 02 Jul 2011 08:40:37 +0000
treeherdermozilla-central@cff486d4d6a4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjoe
bugs666317
milestone7.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 666317 - Discard decoded images on a memory-pressure notification. r=joe
modules/libpr0n/src/DiscardTracker.cpp
modules/libpr0n/src/DiscardTracker.h
modules/libpr0n/src/imgLoader.cpp
modules/libpr0n/test/browser/Makefile.in
modules/libpr0n/test/browser/big.png
modules/libpr0n/test/browser/browser_bug666317.js
--- a/modules/libpr0n/src/DiscardTracker.cpp
+++ b/modules/libpr0n/src/DiscardTracker.cpp
@@ -112,16 +112,45 @@ DiscardTracker::Remove(DiscardTrackerNod
   // Connect around ourselves
   node->prev->next = node->next;
   node->next->prev = node->prev;
 
   // Clean up the node we removed.
   node->prev = node->next = nsnull;
 }
 
+/*
+ * Discard all the images we're tracking.
+ */
+void
+DiscardTracker::DiscardAll()
+{
+  if (!sInitialized)
+    return;
+
+  // Remove the sentinel from the list so that the only elements in the list
+  // which don't track an image are the head and tail.
+  Remove(&sSentinel);
+
+  // Discard all tracked images.
+  for (DiscardTrackerNode *node = sHead.next;
+       node != &sTail; node = sHead.next) {
+    NS_ABORT_IF_FALSE(node->curr, "empty node!");
+    Remove(node);
+    node->curr->Discard();
+  }
+
+  // Add the sentinel back to the (now empty) list.
+  Reset(&sSentinel);
+
+  // Because the sentinel is the only element in the list, the next timer event
+  // would be a no-op.  Disable the timer as an optimization.
+  TimerOff();
+}
+
 /**
  * Initialize the tracker.
  */
 nsresult
 DiscardTracker::Initialize()
 {
   nsresult rv;
 
--- a/modules/libpr0n/src/DiscardTracker.h
+++ b/modules/libpr0n/src/DiscardTracker.h
@@ -71,16 +71,17 @@ struct DiscardTrackerNode
  */
 class DiscardTracker
 {
   public:
     static nsresult Reset(struct DiscardTrackerNode *node);
     static void Remove(struct DiscardTrackerNode *node);
     static void Shutdown();
     static void ReloadTimeout();
+    static void DiscardAll();
   private:
     static nsresult Initialize();
     static nsresult TimerOn();
     static void TimerOff();
     static void TimerCallback(nsITimer *aTimer, void *aClosure);
 };
 
 } // namespace imagelib
--- a/modules/libpr0n/src/imgLoader.cpp
+++ b/modules/libpr0n/src/imgLoader.cpp
@@ -719,16 +719,17 @@ private:
 };
 
 NS_IMPL_ISUPPORTS1(imgCacheObserver, nsIObserver)
 
 NS_IMETHODIMP
 imgCacheObserver::Observe(nsISupports* aSubject, const char* aTopic, const PRUnichar* aSomeData)
 {
   if (strcmp(aTopic, "memory-pressure") == 0) {
+    DiscardTracker::DiscardAll();
     mLoader.MinimizeCaches();
   } else if (strcmp(aTopic, "chrome-flush-skin-caches") == 0 ||
              strcmp(aTopic, "chrome-flush-caches") == 0) {
     mLoader.ClearChromeImageCache();
   }
   return NS_OK;
 }
 
--- a/modules/libpr0n/test/browser/Makefile.in
+++ b/modules/libpr0n/test/browser/Makefile.in
@@ -44,13 +44,15 @@ include $(DEPTH)/config/autoconf.mk
 include $(topsrcdir)/config/rules.mk
 
 _BROWSER_FILES = head.js \
                  browser_image.js \
                  image.html \
                  imageX2.html \
                  animated.gif \
                  animated2.gif \
+		 browser_bug666317.js \
+		 big.png \
                  $(NULL)
 
 libs::	$(_BROWSER_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
 
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..94e7eb6db226f884fd67467a1e7d63c22c9b83d4
GIT binary patch
literal 129497
zc%1Fsc`()e-^cMUAtGBslB9^N*%L(+vdfaVgltifh-AHpN{C$cEJZ7d6e)XBB1`>j
zmpxnA%9bVEntR+c_uRkvIdlK6ncvmS%gmYc9lpmo-^b^9%=zOpZ&!4+RoAcMSVs`V
zdUZ7weS)CP|9PV(U;fUbyOqfD9fh5emJ&f^htvMBq$G%SDkqhdb=8%XxpiHfY)_uD
zAqWq$o6+tDEKO{coz<sAbAOBGvH4t>N0}y4sP+fnx_6`G8YYc4Y>#DBD0!}4rZ(Ea
zZ&hN?&n$TBC^a?fFc}TcVY)+18~><&%{ldb@6Oo;?T)SP`zE}<C%F%F%)j46@RMyL
zx1`S?6rbtxgiU_8eO<9r>{}s0rQ)|nnsCm4eyfhZ*xoNo@wl`En_J|!<Fw>mQ^J17
zh%8fzK#4ft&r4&<5rWJbgkM=bv6pLqOo+?2DwA2}2+d@bxTHvd6YSeh?2hNIdLXVn
z|Ek}2&dW+?(?rfC`H!m6PcFyQ+&IftwRs}VD=LC9DrB7EhgXzSN=7no4Qt8|DjQGf
z<Qud^5YGo*uQPI0jr>Pm)kmC)8X#9pN%O7{IG!DN+^?U8*sGeWBBZ7fBNqCcPjj=K
z9jk<2a&kqCno!xEHqEfuO6vjZ5ZN+b!fVdfghe;<AJJD>Tk2o8)Yq3aG7B#aOob;T
z7cOqHE?nsSCf-w<q;xGwv^Az#^q96Vks**PZCYe6u$Ro<l)>*yyJ-C4KBAYF%zkA4
z%SHB_3+gdyDk4Tkv$M0&<olZRjp}NLeC52Re_WjTBCpYMA=$BVviq(}nhEorjC*Z~
z8JzcH;{<!0XEj5XxO#lI%orX!-e<}6R;Mpy_Q2#lcGoJ3GRfTb14)WJZ<xd>%Zi7-
z7Sqk|TD!?7-jT)LZ7Z3=TW0>rda`jxhLXBBgnC=RsABRw>w#-zISFhJ6{b&)>K*#N
zl)GfvB|f?F{l>K2HsX85<KO;qb;9~u2TfclIrH_kt|?Txeq3TytR<|nWX4@IUP_GA
zYwEeVgviaV)93iJ_XhM?tmh}^wV-XLe^6?!yOu+7gDh2RiMlhr8HI7Fl=FtRHOdw&
z@@sB%NyYkeQ#0so<4B`AsZ4c!lQnHNxBDBmb^bi59{igQ2LxVcIYyrua5%-~9!D#K
zlwzmy&XjX?Huts`tXZ%8scx)pFT1~h*p|k?^_GIMlov}&oh4r~Y6q~DYjhv*kT9cc
zDpTz;n&I@?SVY56TH7T+!EGJrKgi8WO>4Q`QjI+F>g9+()OPD%`N*TCwzeVEOf~2_
zO|LLjaH?f2JMA?=K0;z^d4<sHj8{=s4|WHOWyXqByH$(XGEXpRR5)1FJQitIJjE%!
zJ|SGV%*bjeTez9FIiXo^*pZXIvSd?tqBB{(hZ)tGO<62?o13of4H>Eo?Yi^T<0VH2
z-B=Jq`MIymUQ9ka7pWKNe2D#5eU-U4<kCE&*nfarN$se{y&ar<iF|a7uc;+eC~uL~
zUt&Mh%M`=-?y}n}daJExqhyu!cWjDiQ>#y-Yv9n|c>jv4ziaYjq$b}`<juEC)?^o?
zo?rEf^133g>7$`{D^|ZbO66))PMphimuQ#Z@yNsJ9Qq3PX$)xGB%b<vCe2-+I~2xe
zC&0#EdBc>~cze?>8Sd|9N&HUSW4y&`KKH%Sf1I7&H%mi#>-SW0Q~%qK^-~XZ2bQIj
zMe%6WAKI<C+cewY+v6Uaik*i&c$ImN^Cs!1Hjt%H9NsP&ct`cbhx}Z(T)9@tR{kxj
zGPl+?NDT5$jtv%0s@w3|T<l}-Bk$wrJ5r@g7ijs0+S+<|6?-Ln9{ZSqq~S}0nOzA6
z@di`|cMJ#ac|I673^7bFbb3&9<ivy72R08*8#*4jm=dNZV;HVmYutI;N6FiY-aBlF
zb2PDMPi24GKm4RLrFf*GPjc1`*2gw{Y?y3fIXU`K{?VS)in{TSRdo;Q<T9nQv_70~
zVzoE6Pp<c=GO+dB{Up|~TBJWMEJ)ig%)qW#`_cPrSrMaN?N1A83K%BUJcSAk3fC2C
z7pi-oUeI0Wn+u(DUw@fOD8Myf3spPShoD12*+HA>{-jH0zt2I*AuQz}S>U88H7#a-
z%FNNLoWib5{H<imX_p!nX*V$~natfI5&<=wH59flYUXOP```B;m^e0}M0@p8%)z9C
zw<{j=mW#h~$+Su6J16M<km6ycQM=LB%q<yDGKMn7TKHQiS~8w~emvXW_%Yhi^u)#T
zFoyxVysE7B(MyGK3Y|XT3#Zq{E8i=p3f2y02-6O;s^qIYyVLuw_1)jJ1BB!R6Ah~h
zdat=npEvLGYM9LoxgGMVFZP{N7Hd{qR`74h)l%{Al1*jYUuiXketOn<`pwPHNyA08
z-fxsE13u{ueS34H_Qrt8*q(lyo<F+XUJX6Zdz#n$qT*uwNSS%*zM+7bnR%A^$+=y#
z@4shGdk@|nis^3}l=(c?Rb0Jvijd}(wQwx4NU`OXdA!Y8{EdM9L0w}pUfEswax;3D
zPhOTy+s;D2eqB4Or*BfJeD}*|VvMZp&xD7S^7UqVzHZ&_%+$>!#T35%G*67`dDZ$`
zk+<ko3&W<vL_&&WIz5V9eeAAzX<BJ2SG#8DoVqZqn`qc}HUHtaFW-KjnWpeMpn1T%
z>?3(6<)3WV_~dr5@^&1v&wXss-FNUyNDk}G!;v~YJGSV&=Bwn}s_j!t^Xa3@9ewg6
z=hHP^TH3a>*~#meQ#vJ%y%rq^PcKN1zPnHToW`Hg(n+Hc=5hPBS<pV96cr~IpSIPs
z3$o#=POQ0F<M5u(dZ%47<1QMBOTX<owuf@h0*{1VguQ+CyPlDdofV}$b7uVRmS<iB
zhAnDNsfDHwrI%kAy&!eLY5c@^I2-@+pDxenSdo@o>r?kT8=PV+qtC{;i8PrM+0K4l
z_h{q1@d3djM(48{ObzoLT%Rs|QFc^w6mR;G+vs_Ew$*sZs>zMTfi+8n(`2l;C|fN{
z%}g@C)UCWDG&c12(87da>5;a{>{w&3^j8IGIlbNjd6QK#(wzq05-;|+opXvi(UK&b
zFI*;^_ssK@f<e~NqFgU))4^wr>ue96t#~-1{->yMtMO>NQtP(%-3A-=|50?rcOd%a
z*uxJGof<uwB*z+>pPp=*Jkv4iuaI;xe%fJFxMluKvPI{}!s5KC<9bIyhA#TvEgoiU
zN17IdhyD!moSKWAV_17>Z7}^ot}hPG4)(GcXY(_d&O}LvZB>_d8QGgW)|wy`KP@8>
zuMzh9?S|Vr#X5_NjoDo%ZdRI9CisZWSQ^u2rsr>T_^m;%CAvfUBUfjKFVCQgU9h9-
zyYT4!T=MtN<@865-_QAw_Tkc9yILUy2{$9x^F3y*&m-Qay}vl{>3!$m)xj@?j9%Rh
z2a7`nUKAG8nz_7i9nX2!x54<Rach>G`RS3fgC}R-cHfOQuk2{tceiNT@qLrr+`x1)
z*H?B~_x6Fgip~BqSsjV)PnH^gnC7kBur~P2#F_oiZ@3Lqw7+~I@!hDivP0A-<!j1#
z>g)2>WG$aFTrQk)Qo|B$6Z~K53z<gWJUQ;-dtv{vnb^5}7tM<q-B(6#-1y}4sVPas
zLvBuU@|5kZLF0<!?g}zKi{(y9#Z#KEwZe)Ri(?lGC(~Qs1w0x)?xuZ7N5Sjnf~Rf4
zOlWI@vXhC^i_VIo%?WRP!+noWmG&xER(6Gdw|in|w?wRoo%o^tSRi)!^CkBQHGM6D
z@Z|gN+t086d(ZTCDsB0I##zne-0}z14L@&WM0_Iq^5Y406(s`?vf;LDCO<L8HKoC4
zZRZ|Eh^NqZ7OctK_i@J4ZrX!_nvq-4k02xeR|oWLN0$!w?~%Vv+UfnatDf@nAcfn4
z|A@WGp;b>@KG<KCzxprqBm9H>w%TwgXsECL{BLO4t9o?^QeNja3;qv-CWlvlens;V
zT|QrOf&`@XWXf@eYW0~{*BmDg5}(~bkU(lr&0Zzx86CO$HC<;Di+Y<(5F}iZa*6Jp
z`X^B&*uV5k)m)??NC<>u`I17D(s!(WO}&(KHsd=95^}0z6pK1JDa5<_dUA_e@ZZ=^
zMv#<Ks^)Dw0h-mX>B91Q{-7aNzjkU#3OXc;M3E>GMWRR)i6T)XibRnp5=Ej&6p12H
zB#K0lC=x}YNEC@8Q6!2)kth=V|0KG!ojYb_F932yu7C4fkth;HqDT~pB2grYM3E>G
zMWRR)i6T)XibRnp5=Ej&6p12HB#K0lC=x}YNEC^ZkSM=jf!fMm0OX2X|K_<OQ6!2)
zkth;HqDT~pB2gszpG<V$`H`P<*$AbhON|tkOo>S=D>_XcCO%J6v+@+2He4h%+e|B4
zHB6GmS4QWk3nXb+h<5QmAt{9t<r2Mq(o!h&yIt)=())mcOf^oGr1t?p0SZun0u-PC
z1t>rP3Q&Lo6rcbFC_w*hP?=jgoGW_)fQwoI3Q&Lo6rcbFC_n)UP=Epypa2CZKmiI+
zfC3bt00k&O0SZun0u-RXKInm7AHn~b3jkcy3Q&Lo6rcbFC_n)UP=Epypa2CZKmiI+
zfC3bt00k&O0s2o5teV1T2!db?oO04)WG($H@bibml$<uxBu%*Upq8zYCuzdnV>aig
z7LvyMb#3e7pCD<xUx|5%o-JvE<ARshm$cc27nj#}fuyN<T+8blPSVsofC3bt0R5{#
zA3e%By|NboxTqDN00k&O0SZun0u-PC1t>rP3Q&Lo6rcbFC_n)UP=Epypa2CZKmq#e
zgWji<%vjkA09@1xP=Epypa2CZKmiI+fC3bt00k&O0SZun0u-PC1t>rP3Q&Lo6rce8
z^+64!uTih;1pqE;1t>rP3Q&Ol^`PA6M`8(r2vUmmZKB@B96c3SVy;N?U^+$qiJ2#Y
zBnhXIJeV%E`crKdNy33752gzg58wZSBw<F92h%ZD$uAC*lq^623Q&Lo6rcbFC_n)U
zP=Epypa2CZKmiI+fC3bt0R3N}#o_~ID|-Qei&_B+P=Epypa2CZKmiI+fC3bt00k&O
z0SZun0u-PC1t>rP3Q&Lo6rjI8Xnu_0kCnXuz(uV91t>rP3Q&Lo6rcbFC_n)UP=Epy
zpa2EvUk;k>SV=(;1a;Y59v^i`*Q6_k(XDp*nC=HqfC3bt00k&O0SeH67xcEtTA!7@
z0Ki4900k&O0SZun0u-PC1t>rP3Q&Lo6rcbFC_n)UP=Epypa2CZKmiKSUmrB@(4M>h
zbS?mJQ7b?J3Q&Lo6rcbFC_n)UP=Epypa2CZKmiI+fC3bt00k&O0SZun0`!*z6`7jO
zU)c))+`m_?+~-GPiRCY)qf3nxmQ0CB%&R^N!}7~ft<_)sM2hL<mqY5XJ5C!e1r7Dp
z*SDEgmgHe~&QTX8g?LwAUklMLl84=u==Ik>i6UWr3zyfI)M0l31t>rP3Q&Lo6rcbF
zC_n)UP=Epypa2CZKmiI+fC3bt00k&O0s8BKo>q{sTG<N#T+|9sfC3bt00k&O0SZun
z0u-PC1t>rP3Q&Lo6rcbFC_n)UP=EpypaA{#L4B*Xd#~&T04{0;C_n)UP=Epypa2CZ
zKmiKSzY(ZIQy2|F5R8FSPFjqtrJn`<^@B0s?ms@@4xj)9C_n)UP=Epypa2CZKmiI+
zfc|Yjhr-hz5(K@*&%FS^MXdk@C_n)UP=Epypa2CZKmkfJs6W9l#_nZ)KWOpizc*7q
MsI8K9z~cA+0+w6?^#A|>
new file mode 100644
--- /dev/null
+++ b/modules/libpr0n/test/browser/browser_bug666317.js
@@ -0,0 +1,62 @@
+waitForExplicitFinish();
+
+let pageSource =
+  '<html><body>' +
+    '<img id="testImg" src="' + TESTROOT + 'big.png">' +
+  '</body></html>';
+
+let oldDiscardingPref, oldTab, newTab;
+let prefBranch = Cc["@mozilla.org/preferences-service;1"]
+                   .getService(Ci.nsIPrefService)
+                   .getBranch('image.mem.');
+
+function isImgDecoded() {
+  let img = gBrowser.getBrowserForTab(newTab).contentWindow
+            .document.getElementById('testImg');
+  img.QueryInterface(Ci.nsIImageLoadingContent);
+  let request = img.getRequest(Ci.nsIImageLoadingContent.CURRENT_REQUEST);
+  return request.imageStatus & Ci.imgIRequest.STATUS_FRAME_COMPLETE ? true : false;
+}
+
+// Ensure that the image is decoded by drawing it to a canvas.
+function forceDecodeImg() {
+  let doc = gBrowser.getBrowserForTab(newTab).contentWindow.document;
+  let img = doc.getElementById('testImg');
+  let canvas = doc.createElement('canvas');
+  let ctx = canvas.getContext('2d');
+  ctx.drawImage(img, 0, 0);
+}
+
+function test() {
+  // Enable the discarding pref.
+  oldDiscardingPref = prefBranch.getBoolPref('discardable');
+  prefBranch.setBoolPref('discardable', true);
+
+  // Create and focus a new tab.
+  oldTab = gBrowser.selectedTab;
+  newTab = gBrowser.addTab('data:text/html,' + pageSource);
+  gBrowser.selectedTab = newTab;
+
+  // Run step2 after the tab loads.
+  gBrowser.getBrowserForTab(newTab)
+          .addEventListener("pageshow", step2 );
+}
+
+function step2() {
+  // Check that the image is decoded.
+  forceDecodeImg();
+  ok(isImgDecoded(), 'Image should initially be decoded.');
+
+  // Focus the old tab, then fire a memory-pressure notification.  This should
+  // cause the decoded image in the new tab to be discarded.
+  gBrowser.selectedTab = oldTab;
+  var os = Cc["@mozilla.org/observer-service;1"]
+             .getService(Ci.nsIObserverService);
+  os.notifyObservers(null, 'memory-pressure', 'heap-minimize');
+  ok(!isImgDecoded(), 'Image should be discarded.');
+
+  // And we're done.
+  gBrowser.removeTab(newTab);
+  prefBranch.setBoolPref('discardable', oldDiscardingPref);
+  finish();
+}