/image/src/DiscardTracker.cpp

http://github.com/zpao/v8monkey · C++ · 293 lines · 149 code · 45 blank · 99 comment · 26 complexity · e4ba92d674999750f621f9cfbf8859f4 MD5 · raw file

  1. /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  2. ***** BEGIN LICENSE BLOCK *****
  3. * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  4. *
  5. * The contents of this file are subject to the Mozilla Public License Version
  6. * 1.1 (the "License"); you may not use this file except in compliance with
  7. * the License. You may obtain a copy of the License at
  8. * http://www.mozilla.org/MPL/
  9. *
  10. * Software distributed under the License is distributed on an "AS IS" basis,
  11. * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  12. * for the specific language governing rights and limitations under the
  13. * License.
  14. *
  15. * The Original Code is mozilla.org code.
  16. *
  17. * The Initial Developer of the Original Code is Mozilla Foundation.
  18. * Portions created by the Initial Developer are Copyright (C) 2010
  19. * the Initial Developer. All Rights Reserved.
  20. *
  21. * Contributor(s):
  22. * Bobby Holley <bobbyholley@gmail.com>
  23. *
  24. * Alternatively, the contents of this file may be used under the terms of
  25. * either the GNU General Public License Version 2 or later (the "GPL"), or
  26. * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  27. * in which case the provisions of the GPL or the LGPL are applicable instead
  28. * of those above. If you wish to allow use of your version of this file only
  29. * under the terms of either the GPL or the LGPL, and not to allow others to
  30. * use your version of this file under the terms of the MPL, indicate your
  31. * decision by deleting the provisions above and replace them with the notice
  32. * and other provisions required by the GPL or the LGPL. If you do not delete
  33. * the provisions above, a recipient may use your version of this file under
  34. * the terms of any one of the MPL, the GPL or the LGPL.
  35. *
  36. * ***** END LICENSE BLOCK ***** */
  37. #include "nsComponentManagerUtils.h"
  38. #include "nsITimer.h"
  39. #include "RasterImage.h"
  40. #include "DiscardTracker.h"
  41. #include "mozilla/Preferences.h"
  42. namespace mozilla {
  43. namespace image {
  44. static bool sInitialized = false;
  45. static bool sTimerOn = false;
  46. static PRUint32 sMinDiscardTimeoutMs = 10000; /* Default if pref unreadable. */
  47. static nsITimer *sTimer = nsnull;
  48. static struct DiscardTrackerNode sHead, sSentinel, sTail;
  49. /*
  50. * Puts an image in the back of the tracker queue. If the image is already
  51. * in the tracker, this removes it first.
  52. */
  53. nsresult
  54. DiscardTracker::Reset(DiscardTrackerNode *node)
  55. {
  56. nsresult rv;
  57. #ifdef DEBUG
  58. bool isSentinel = (node == &sSentinel);
  59. // Sanity check the node.
  60. NS_ABORT_IF_FALSE(isSentinel || node->curr, "Node doesn't point to anything!");
  61. // We should not call this function if we can't discard
  62. NS_ABORT_IF_FALSE(isSentinel || node->curr->CanDiscard(),
  63. "trying to reset discarding but can't discard!");
  64. // As soon as an image becomes animated it is set non-discardable
  65. NS_ABORT_IF_FALSE(isSentinel || !node->curr->mAnim,
  66. "Trying to reset discarding on animated image!");
  67. #endif
  68. // Initialize the first time through
  69. if (NS_UNLIKELY(!sInitialized)) {
  70. rv = Initialize();
  71. NS_ENSURE_SUCCESS(rv, rv);
  72. }
  73. // Remove the node if it's in the list.
  74. Remove(node);
  75. // Append it to the list.
  76. node->prev = sTail.prev;
  77. node->next = &sTail;
  78. node->prev->next = sTail.prev = node;
  79. // Make sure the timer is running
  80. rv = TimerOn();
  81. NS_ENSURE_SUCCESS(rv,rv);
  82. return NS_OK;
  83. }
  84. /*
  85. * Removes a node from the tracker. No-op if the node is currently untracked.
  86. */
  87. void
  88. DiscardTracker::Remove(DiscardTrackerNode *node)
  89. {
  90. NS_ABORT_IF_FALSE(node != nsnull, "Can't pass null node");
  91. // If we're not in a list, we have nothing to do.
  92. if ((node->prev == nsnull) || (node->next == nsnull)) {
  93. NS_ABORT_IF_FALSE(node->prev == node->next,
  94. "Node is half in a list!");
  95. return;
  96. }
  97. // Connect around ourselves
  98. node->prev->next = node->next;
  99. node->next->prev = node->prev;
  100. // Clean up the node we removed.
  101. node->prev = node->next = nsnull;
  102. }
  103. /*
  104. * Discard all the images we're tracking.
  105. */
  106. void
  107. DiscardTracker::DiscardAll()
  108. {
  109. if (!sInitialized)
  110. return;
  111. // Remove the sentinel from the list so that the only elements in the list
  112. // which don't track an image are the head and tail.
  113. Remove(&sSentinel);
  114. // Discard all tracked images.
  115. for (DiscardTrackerNode *node = sHead.next;
  116. node != &sTail; node = sHead.next) {
  117. NS_ABORT_IF_FALSE(node->curr, "empty node!");
  118. Remove(node);
  119. node->curr->Discard();
  120. }
  121. // Add the sentinel back to the (now empty) list.
  122. Reset(&sSentinel);
  123. // Because the sentinel is the only element in the list, the next timer event
  124. // would be a no-op. Disable the timer as an optimization.
  125. TimerOff();
  126. }
  127. static int
  128. DiscardTimeoutChangedCallback(const char* aPref, void *aClosure)
  129. {
  130. DiscardTracker::ReloadTimeout();
  131. return 0;
  132. }
  133. /**
  134. * Initialize the tracker.
  135. */
  136. nsresult
  137. DiscardTracker::Initialize()
  138. {
  139. nsresult rv;
  140. // Set up the list. Head<->Sentinel<->Tail
  141. sHead.curr = sTail.curr = sSentinel.curr = nsnull;
  142. sHead.prev = sTail.next = nsnull;
  143. sHead.next = sTail.prev = &sSentinel;
  144. sSentinel.prev = &sHead;
  145. sSentinel.next = &sTail;
  146. // Watch the timeout pref for changes.
  147. Preferences::RegisterCallback(DiscardTimeoutChangedCallback,
  148. DISCARD_TIMEOUT_PREF);
  149. ReloadTimeout();
  150. // Create and start the timer
  151. nsCOMPtr<nsITimer> t = do_CreateInstance("@mozilla.org/timer;1");
  152. NS_ENSURE_TRUE(t, NS_ERROR_OUT_OF_MEMORY);
  153. t.forget(&sTimer);
  154. rv = TimerOn();
  155. NS_ENSURE_SUCCESS(rv, rv);
  156. // Mark us as initialized
  157. sInitialized = true;
  158. return NS_OK;
  159. }
  160. /**
  161. * Shut down the tracker, deallocating the timer.
  162. */
  163. void
  164. DiscardTracker::Shutdown()
  165. {
  166. if (sTimer) {
  167. sTimer->Cancel();
  168. NS_RELEASE(sTimer);
  169. sTimer = nsnull;
  170. }
  171. }
  172. /**
  173. * Read the discard timeout from about:config.
  174. */
  175. void
  176. DiscardTracker::ReloadTimeout()
  177. {
  178. nsresult rv;
  179. // read the timeout pref
  180. PRInt32 discardTimeout;
  181. rv = Preferences::GetInt(DISCARD_TIMEOUT_PREF, &discardTimeout);
  182. // If we got something bogus, return
  183. if (!NS_SUCCEEDED(rv) || discardTimeout <= 0)
  184. return;
  185. // If the value didn't change, return
  186. if ((PRUint32) discardTimeout == sMinDiscardTimeoutMs)
  187. return;
  188. // Update the value
  189. sMinDiscardTimeoutMs = (PRUint32) discardTimeout;
  190. // If the timer's on, restart the clock to make changes take effect
  191. if (sTimerOn) {
  192. TimerOff();
  193. TimerOn();
  194. }
  195. }
  196. /**
  197. * Enables the timer. No-op if the timer is already running.
  198. */
  199. nsresult
  200. DiscardTracker::TimerOn()
  201. {
  202. // Nothing to do if the timer's already on.
  203. if (sTimerOn)
  204. return NS_OK;
  205. sTimerOn = true;
  206. // Activate
  207. return sTimer->InitWithFuncCallback(TimerCallback,
  208. nsnull,
  209. sMinDiscardTimeoutMs,
  210. nsITimer::TYPE_REPEATING_SLACK);
  211. }
  212. /*
  213. * Disables the timer. No-op if the timer isn't running.
  214. */
  215. void
  216. DiscardTracker::TimerOff()
  217. {
  218. // Nothing to do if the timer's already off.
  219. if (!sTimerOn)
  220. return;
  221. sTimerOn = false;
  222. // Deactivate
  223. sTimer->Cancel();
  224. }
  225. /**
  226. * Routine activated when the timer fires. This discards everything
  227. * in front of sentinel, and resets the sentinel to the back of the
  228. * list.
  229. */
  230. void
  231. DiscardTracker::TimerCallback(nsITimer *aTimer, void *aClosure)
  232. {
  233. DiscardTrackerNode *node;
  234. // Remove and discard everything before the sentinel
  235. for (node = sSentinel.prev; node != &sHead; node = sSentinel.prev) {
  236. NS_ABORT_IF_FALSE(node->curr, "empty node!");
  237. Remove(node);
  238. node->curr->Discard();
  239. }
  240. // Append the sentinel to the back of the list
  241. Reset(&sSentinel);
  242. // If there's nothing in front of the sentinel, the next callback
  243. // is guaranteed to be a no-op. Disable the timer as an optimization.
  244. if (sSentinel.prev == &sHead)
  245. TimerOff();
  246. }
  247. } // namespace image
  248. } // namespace mozilla