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 :
38 : #include "nsComponentManagerUtils.h"
39 : #include "nsITimer.h"
40 : #include "RasterImage.h"
41 : #include "DiscardTracker.h"
42 : #include "mozilla/Preferences.h"
43 :
44 : namespace mozilla {
45 : namespace image {
46 :
47 : static bool sInitialized = false;
48 : static bool sTimerOn = false;
49 : static PRUint32 sMinDiscardTimeoutMs = 10000; /* Default if pref unreadable. */
50 : static nsITimer *sTimer = nsnull;
51 : static struct DiscardTrackerNode sHead, sSentinel, sTail;
52 :
53 : /*
54 : * Puts an image in the back of the tracker queue. If the image is already
55 : * in the tracker, this removes it first.
56 : */
57 : nsresult
58 1 : DiscardTracker::Reset(DiscardTrackerNode *node)
59 : {
60 : nsresult rv;
61 : #ifdef DEBUG
62 1 : bool isSentinel = (node == &sSentinel);
63 :
64 : // Sanity check the node.
65 1 : NS_ABORT_IF_FALSE(isSentinel || node->curr, "Node doesn't point to anything!");
66 :
67 : // We should not call this function if we can't discard
68 1 : NS_ABORT_IF_FALSE(isSentinel || node->curr->CanDiscard(),
69 : "trying to reset discarding but can't discard!");
70 :
71 : // As soon as an image becomes animated it is set non-discardable
72 1 : NS_ABORT_IF_FALSE(isSentinel || !node->curr->mAnim,
73 : "Trying to reset discarding on animated image!");
74 : #endif
75 :
76 : // Initialize the first time through
77 1 : if (NS_UNLIKELY(!sInitialized)) {
78 1 : rv = Initialize();
79 1 : NS_ENSURE_SUCCESS(rv, rv);
80 : }
81 :
82 : // Remove the node if it's in the list.
83 1 : Remove(node);
84 :
85 : // Append it to the list.
86 1 : node->prev = sTail.prev;
87 1 : node->next = &sTail;
88 1 : node->prev->next = sTail.prev = node;
89 :
90 : // Make sure the timer is running
91 1 : rv = TimerOn();
92 1 : NS_ENSURE_SUCCESS(rv,rv);
93 :
94 1 : return NS_OK;
95 : }
96 :
97 : /*
98 : * Removes a node from the tracker. No-op if the node is currently untracked.
99 : */
100 : void
101 29 : DiscardTracker::Remove(DiscardTrackerNode *node)
102 : {
103 29 : NS_ABORT_IF_FALSE(node != nsnull, "Can't pass null node");
104 :
105 : // If we're not in a list, we have nothing to do.
106 29 : if ((node->prev == nsnull) || (node->next == nsnull)) {
107 28 : NS_ABORT_IF_FALSE(node->prev == node->next,
108 : "Node is half in a list!");
109 28 : return;
110 : }
111 :
112 : // Connect around ourselves
113 1 : node->prev->next = node->next;
114 1 : node->next->prev = node->prev;
115 :
116 : // Clean up the node we removed.
117 1 : node->prev = node->next = nsnull;
118 : }
119 :
120 : /*
121 : * Discard all the images we're tracking.
122 : */
123 : void
124 0 : DiscardTracker::DiscardAll()
125 : {
126 0 : if (!sInitialized)
127 0 : return;
128 :
129 : // Remove the sentinel from the list so that the only elements in the list
130 : // which don't track an image are the head and tail.
131 0 : Remove(&sSentinel);
132 :
133 : // Discard all tracked images.
134 0 : for (DiscardTrackerNode *node = sHead.next;
135 : node != &sTail; node = sHead.next) {
136 0 : NS_ABORT_IF_FALSE(node->curr, "empty node!");
137 0 : Remove(node);
138 0 : node->curr->Discard();
139 : }
140 :
141 : // Add the sentinel back to the (now empty) list.
142 0 : Reset(&sSentinel);
143 :
144 : // Because the sentinel is the only element in the list, the next timer event
145 : // would be a no-op. Disable the timer as an optimization.
146 0 : TimerOff();
147 : }
148 :
149 : static int
150 0 : DiscardTimeoutChangedCallback(const char* aPref, void *aClosure)
151 : {
152 0 : DiscardTracker::ReloadTimeout();
153 0 : return 0;
154 : }
155 :
156 : /**
157 : * Initialize the tracker.
158 : */
159 : nsresult
160 1 : DiscardTracker::Initialize()
161 : {
162 : nsresult rv;
163 :
164 : // Set up the list. Head<->Sentinel<->Tail
165 1 : sHead.curr = sTail.curr = sSentinel.curr = nsnull;
166 1 : sHead.prev = sTail.next = nsnull;
167 1 : sHead.next = sTail.prev = &sSentinel;
168 1 : sSentinel.prev = &sHead;
169 1 : sSentinel.next = &sTail;
170 :
171 : // Watch the timeout pref for changes.
172 : Preferences::RegisterCallback(DiscardTimeoutChangedCallback,
173 1 : DISCARD_TIMEOUT_PREF);
174 :
175 1 : ReloadTimeout();
176 :
177 : // Create and start the timer
178 2 : nsCOMPtr<nsITimer> t = do_CreateInstance("@mozilla.org/timer;1");
179 1 : NS_ENSURE_TRUE(t, NS_ERROR_OUT_OF_MEMORY);
180 1 : t.forget(&sTimer);
181 1 : rv = TimerOn();
182 1 : NS_ENSURE_SUCCESS(rv, rv);
183 :
184 : // Mark us as initialized
185 1 : sInitialized = true;
186 :
187 1 : return NS_OK;
188 : }
189 :
190 : /**
191 : * Shut down the tracker, deallocating the timer.
192 : */
193 : void
194 93 : DiscardTracker::Shutdown()
195 : {
196 93 : if (sTimer) {
197 1 : sTimer->Cancel();
198 1 : NS_RELEASE(sTimer);
199 1 : sTimer = nsnull;
200 : }
201 93 : }
202 :
203 : /**
204 : * Read the discard timeout from about:config.
205 : */
206 : void
207 1 : DiscardTracker::ReloadTimeout()
208 : {
209 : nsresult rv;
210 :
211 : // read the timeout pref
212 : PRInt32 discardTimeout;
213 1 : rv = Preferences::GetInt(DISCARD_TIMEOUT_PREF, &discardTimeout);
214 :
215 : // If we got something bogus, return
216 1 : if (!NS_SUCCEEDED(rv) || discardTimeout <= 0)
217 0 : return;
218 :
219 : // If the value didn't change, return
220 1 : if ((PRUint32) discardTimeout == sMinDiscardTimeoutMs)
221 1 : return;
222 :
223 : // Update the value
224 0 : sMinDiscardTimeoutMs = (PRUint32) discardTimeout;
225 :
226 : // If the timer's on, restart the clock to make changes take effect
227 0 : if (sTimerOn) {
228 0 : TimerOff();
229 0 : TimerOn();
230 : }
231 : }
232 :
233 : /**
234 : * Enables the timer. No-op if the timer is already running.
235 : */
236 : nsresult
237 2 : DiscardTracker::TimerOn()
238 : {
239 : // Nothing to do if the timer's already on.
240 2 : if (sTimerOn)
241 1 : return NS_OK;
242 1 : sTimerOn = true;
243 :
244 : // Activate
245 : return sTimer->InitWithFuncCallback(TimerCallback,
246 : nsnull,
247 : sMinDiscardTimeoutMs,
248 1 : nsITimer::TYPE_REPEATING_SLACK);
249 : }
250 :
251 : /*
252 : * Disables the timer. No-op if the timer isn't running.
253 : */
254 : void
255 0 : DiscardTracker::TimerOff()
256 : {
257 : // Nothing to do if the timer's already off.
258 0 : if (!sTimerOn)
259 0 : return;
260 0 : sTimerOn = false;
261 :
262 : // Deactivate
263 0 : sTimer->Cancel();
264 : }
265 :
266 : /**
267 : * Routine activated when the timer fires. This discards everything
268 : * in front of sentinel, and resets the sentinel to the back of the
269 : * list.
270 : */
271 : void
272 0 : DiscardTracker::TimerCallback(nsITimer *aTimer, void *aClosure)
273 : {
274 : DiscardTrackerNode *node;
275 :
276 : // Remove and discard everything before the sentinel
277 0 : for (node = sSentinel.prev; node != &sHead; node = sSentinel.prev) {
278 0 : NS_ABORT_IF_FALSE(node->curr, "empty node!");
279 0 : Remove(node);
280 0 : node->curr->Discard();
281 : }
282 :
283 : // Append the sentinel to the back of the list
284 0 : Reset(&sSentinel);
285 :
286 : // If there's nothing in front of the sentinel, the next callback
287 : // is guaranteed to be a no-op. Disable the timer as an optimization.
288 0 : if (sSentinel.prev == &sHead)
289 0 : TimerOff();
290 0 : }
291 :
292 : } // namespace image
293 : } // namespace mozilla
|