1 : //* -*- Mode: C++; tab-width: 8; 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 Url Classifier code
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Google Inc.
19 : * Portions created by the Initial Developer are Copyright (C) 2006
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Tony Chang <tony@ponderer.org> (original author)
24 : *
25 : * Alternatively, the contents of this file may be used under the terms of
26 : * either the GNU General Public License Version 2 or later (the "GPL"), or
27 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 : * in which case the provisions of the GPL or the LGPL are applicable instead
29 : * of those above. If you wish to allow use of your version of this file only
30 : * under the terms of either the GPL or the LGPL, and not to allow others to
31 : * use your version of this file under the terms of the MPL, indicate your
32 : * decision by deleting the provisions above and replace them with the notice
33 : * and other provisions required by the GPL or the LGPL. If you do not delete
34 : * the provisions above, a recipient may use your version of this file under
35 : * the terms of any one of the MPL, the GPL or the LGPL.
36 : *
37 : * ***** END LICENSE BLOCK ***** */
38 :
39 : #include "nsCRT.h"
40 : #include "nsIHttpChannel.h"
41 : #include "nsIObserverService.h"
42 : #include "nsIStringStream.h"
43 : #include "nsIUploadChannel.h"
44 : #include "nsIURI.h"
45 : #include "nsIUrlClassifierDBService.h"
46 : #include "nsStreamUtils.h"
47 : #include "nsStringStream.h"
48 : #include "nsToolkitCompsCID.h"
49 : #include "nsUrlClassifierStreamUpdater.h"
50 : #include "prlog.h"
51 :
52 : static const char* gQuitApplicationMessage = "quit-application";
53 :
54 : // NSPR_LOG_MODULES=UrlClassifierStreamUpdater:5
55 : #if defined(PR_LOGGING)
56 : static const PRLogModuleInfo *gUrlClassifierStreamUpdaterLog = nsnull;
57 : #define LOG(args) PR_LOG(gUrlClassifierStreamUpdaterLog, PR_LOG_DEBUG, args)
58 : #else
59 : #define LOG(args)
60 : #endif
61 :
62 :
63 : ///////////////////////////////////////////////////////////////////////////////
64 : // nsIUrlClassiferStreamUpdater implementation
65 : // Handles creating/running the stream listener
66 :
67 8 : nsUrlClassifierStreamUpdater::nsUrlClassifierStreamUpdater()
68 : : mIsUpdating(false), mInitialized(false), mDownloadError(false),
69 8 : mBeganStream(false), mUpdateUrl(nsnull), mChannel(nsnull)
70 : {
71 : #if defined(PR_LOGGING)
72 8 : if (!gUrlClassifierStreamUpdaterLog)
73 8 : gUrlClassifierStreamUpdaterLog = PR_NewLogModule("UrlClassifierStreamUpdater");
74 : #endif
75 :
76 8 : }
77 :
78 2731 : NS_IMPL_THREADSAFE_ISUPPORTS9(nsUrlClassifierStreamUpdater,
79 : nsIUrlClassifierStreamUpdater,
80 : nsIUrlClassifierUpdateObserver,
81 : nsIRequestObserver,
82 : nsIStreamListener,
83 : nsIObserver,
84 : nsIBadCertListener2,
85 : nsISSLErrorListener,
86 : nsIInterfaceRequestor,
87 : nsITimerCallback)
88 :
89 : /**
90 : * Clear out the update.
91 : */
92 : void
93 82 : nsUrlClassifierStreamUpdater::DownloadDone()
94 : {
95 82 : LOG(("nsUrlClassifierStreamUpdater::DownloadDone [this=%p]", this));
96 82 : mIsUpdating = false;
97 :
98 82 : mPendingUpdates.Clear();
99 82 : mDownloadError = false;
100 82 : mSuccessCallback = nsnull;
101 82 : mUpdateErrorCallback = nsnull;
102 82 : mDownloadErrorCallback = nsnull;
103 82 : }
104 :
105 : ///////////////////////////////////////////////////////////////////////////////
106 : // nsIUrlClassifierStreamUpdater implementation
107 :
108 : NS_IMETHODIMP
109 0 : nsUrlClassifierStreamUpdater::GetUpdateUrl(nsACString & aUpdateUrl)
110 : {
111 0 : if (mUpdateUrl) {
112 0 : mUpdateUrl->GetSpec(aUpdateUrl);
113 : } else {
114 0 : aUpdateUrl.Truncate();
115 : }
116 0 : return NS_OK;
117 : }
118 :
119 : NS_IMETHODIMP
120 88 : nsUrlClassifierStreamUpdater::SetUpdateUrl(const nsACString & aUpdateUrl)
121 : {
122 88 : LOG(("Update URL is %s\n", PromiseFlatCString(aUpdateUrl).get()));
123 :
124 88 : nsresult rv = NS_NewURI(getter_AddRefs(mUpdateUrl), aUpdateUrl);
125 88 : NS_ENSURE_SUCCESS(rv, rv);
126 :
127 88 : return NS_OK;
128 : }
129 :
130 : nsresult
131 99 : nsUrlClassifierStreamUpdater::FetchUpdate(nsIURI *aUpdateUrl,
132 : const nsACString & aRequestBody,
133 : const nsACString & aStreamTable,
134 : const nsACString & aServerMAC)
135 : {
136 : nsresult rv;
137 : PRUint32 loadFlags = nsIChannel::INHIBIT_CACHING |
138 99 : nsIChannel::LOAD_BYPASS_CACHE;
139 99 : rv = NS_NewChannel(getter_AddRefs(mChannel), aUpdateUrl, nsnull, nsnull, this,
140 99 : loadFlags);
141 99 : NS_ENSURE_SUCCESS(rv, rv);
142 :
143 99 : mBeganStream = false;
144 :
145 99 : if (!aRequestBody.IsEmpty()) {
146 0 : rv = AddRequestBody(aRequestBody);
147 0 : NS_ENSURE_SUCCESS(rv, rv);
148 : }
149 :
150 : // Set the appropriate content type for file/data URIs, for unit testing
151 : // purposes.
152 : bool match;
153 198 : if ((NS_SUCCEEDED(aUpdateUrl->SchemeIs("file", &match)) && match) ||
154 99 : (NS_SUCCEEDED(aUpdateUrl->SchemeIs("data", &match)) && match)) {
155 97 : mChannel->SetContentType(NS_LITERAL_CSTRING("application/vnd.google.safebrowsing-update"));
156 : }
157 :
158 : // Make the request
159 99 : rv = mChannel->AsyncOpen(this, nsnull);
160 99 : NS_ENSURE_SUCCESS(rv, rv);
161 :
162 99 : mStreamTable = aStreamTable;
163 99 : mServerMAC = aServerMAC;
164 :
165 99 : return NS_OK;
166 : }
167 :
168 : nsresult
169 17 : nsUrlClassifierStreamUpdater::FetchUpdate(const nsACString & aUpdateUrl,
170 : const nsACString & aRequestBody,
171 : const nsACString & aStreamTable,
172 : const nsACString & aServerMAC)
173 : {
174 34 : nsCOMPtr<nsIURI> uri;
175 17 : nsresult rv = NS_NewURI(getter_AddRefs(uri), aUpdateUrl);
176 17 : NS_ENSURE_SUCCESS(rv, rv);
177 :
178 17 : LOG(("Fetching update from %s\n", PromiseFlatCString(aUpdateUrl).get()));
179 :
180 17 : return FetchUpdate(uri, aRequestBody, aStreamTable, aServerMAC);
181 : }
182 :
183 : NS_IMETHODIMP
184 88 : nsUrlClassifierStreamUpdater::DownloadUpdates(
185 : const nsACString &aRequestTables,
186 : const nsACString &aRequestBody,
187 : const nsACString &aClientKey,
188 : nsIUrlClassifierCallback *aSuccessCallback,
189 : nsIUrlClassifierCallback *aUpdateErrorCallback,
190 : nsIUrlClassifierCallback *aDownloadErrorCallback,
191 : bool *_retval)
192 : {
193 88 : NS_ENSURE_ARG(aSuccessCallback);
194 88 : NS_ENSURE_ARG(aUpdateErrorCallback);
195 88 : NS_ENSURE_ARG(aDownloadErrorCallback);
196 :
197 88 : if (mIsUpdating) {
198 6 : LOG(("already updating, skipping update"));
199 6 : *_retval = false;
200 6 : return NS_OK;
201 : }
202 :
203 82 : if (!mUpdateUrl) {
204 0 : NS_ERROR("updateUrl not set");
205 0 : return NS_ERROR_NOT_INITIALIZED;
206 : }
207 :
208 : nsresult rv;
209 :
210 82 : if (!mInitialized) {
211 : // Add an observer for shutdown so we can cancel any pending list
212 : // downloads. quit-application is the same event that the download
213 : // manager listens for and uses to cancel pending downloads.
214 : nsCOMPtr<nsIObserverService> observerService =
215 8 : mozilla::services::GetObserverService();
216 4 : if (!observerService)
217 0 : return NS_ERROR_FAILURE;
218 :
219 4 : observerService->AddObserver(this, gQuitApplicationMessage, false);
220 :
221 4 : mDBService = do_GetService(NS_URLCLASSIFIERDBSERVICE_CONTRACTID, &rv);
222 4 : NS_ENSURE_SUCCESS(rv, rv);
223 :
224 8 : mInitialized = true;
225 : }
226 :
227 82 : rv = mDBService->BeginUpdate(this, aRequestTables, aClientKey);
228 82 : if (rv == NS_ERROR_NOT_AVAILABLE) {
229 0 : LOG(("already updating, skipping update"));
230 0 : *_retval = false;
231 0 : return NS_OK;
232 82 : } else if (NS_FAILED(rv)) {
233 0 : return rv;
234 : }
235 :
236 82 : mSuccessCallback = aSuccessCallback;
237 82 : mUpdateErrorCallback = aUpdateErrorCallback;
238 82 : mDownloadErrorCallback = aDownloadErrorCallback;
239 :
240 82 : mIsUpdating = true;
241 82 : *_retval = true;
242 :
243 :
244 82 : return FetchUpdate(mUpdateUrl, aRequestBody, EmptyCString(), EmptyCString());
245 : }
246 :
247 : ///////////////////////////////////////////////////////////////////////////////
248 : // nsIUrlClassifierUpdateObserver implementation
249 :
250 : NS_IMETHODIMP
251 17 : nsUrlClassifierStreamUpdater::UpdateUrlRequested(const nsACString &aUrl,
252 : const nsACString &aTable,
253 : const nsACString &aServerMAC)
254 : {
255 17 : LOG(("Queuing requested update from %s\n", PromiseFlatCString(aUrl).get()));
256 :
257 17 : PendingUpdate *update = mPendingUpdates.AppendElement();
258 17 : if (!update)
259 0 : return NS_ERROR_OUT_OF_MEMORY;
260 :
261 : // Allow data: and file: urls for unit testing purposes, otherwise assume http
262 38 : if (StringBeginsWith(aUrl, NS_LITERAL_CSTRING("data:")) ||
263 21 : StringBeginsWith(aUrl, NS_LITERAL_CSTRING("file:"))) {
264 15 : update->mUrl = aUrl;
265 : } else {
266 2 : update->mUrl = NS_LITERAL_CSTRING("http://") + aUrl;
267 : }
268 17 : update->mTable = aTable;
269 17 : update->mServerMAC = aServerMAC;
270 :
271 17 : return NS_OK;
272 : }
273 :
274 : NS_IMETHODIMP
275 0 : nsUrlClassifierStreamUpdater::RekeyRequested()
276 : {
277 : nsCOMPtr<nsIObserverService> observerService =
278 0 : mozilla::services::GetObserverService();
279 :
280 0 : if (!observerService)
281 0 : return NS_ERROR_FAILURE;
282 :
283 0 : return observerService->NotifyObservers(static_cast<nsIUrlClassifierStreamUpdater*>(this),
284 : "url-classifier-rekey-requested",
285 0 : nsnull);
286 : }
287 :
288 : nsresult
289 17 : nsUrlClassifierStreamUpdater::FetchNext()
290 : {
291 17 : if (mPendingUpdates.Length() == 0) {
292 0 : return NS_OK;
293 : }
294 :
295 17 : PendingUpdate &update = mPendingUpdates[0];
296 17 : LOG(("Fetching update url: %s\n", update.mUrl.get()));
297 17 : nsresult rv = FetchUpdate(update.mUrl, EmptyCString(),
298 34 : update.mTable, update.mServerMAC);
299 17 : if (NS_FAILED(rv)) {
300 0 : LOG(("Error fetching update url: %s\n", update.mUrl.get()));
301 : // We can commit the urls that we've applied so far. This is
302 : // probably a transient server problem, so trigger backoff.
303 0 : mDownloadErrorCallback->HandleEvent(EmptyCString());
304 0 : mDownloadError = true;
305 0 : mDBService->FinishUpdate();
306 0 : return rv;
307 : }
308 :
309 17 : mPendingUpdates.RemoveElementAt(0);
310 :
311 17 : return NS_OK;
312 : }
313 :
314 : NS_IMETHODIMP
315 97 : nsUrlClassifierStreamUpdater::StreamFinished(nsresult status,
316 : PRUint32 requestedDelay)
317 : {
318 97 : LOG(("nsUrlClassifierStreamUpdater::StreamFinished [%x, %d]", status, requestedDelay));
319 97 : if (NS_FAILED(status) || mPendingUpdates.Length() == 0) {
320 : // We're done.
321 80 : mDBService->FinishUpdate();
322 80 : return NS_OK;
323 : }
324 :
325 : // Wait the requested amount of time before starting a new stream.
326 : nsresult rv;
327 17 : mTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
328 17 : if (NS_SUCCEEDED(rv)) {
329 17 : rv = mTimer->InitWithCallback(this, requestedDelay,
330 17 : nsITimer::TYPE_ONE_SHOT);
331 : }
332 :
333 17 : if (NS_FAILED(rv)) {
334 0 : NS_WARNING("Unable to initialize timer, fetching next safebrowsing item immediately");
335 0 : return FetchNext();
336 : }
337 :
338 17 : return NS_OK;
339 : }
340 :
341 : NS_IMETHODIMP
342 81 : nsUrlClassifierStreamUpdater::UpdateSuccess(PRUint32 requestedTimeout)
343 : {
344 81 : LOG(("nsUrlClassifierStreamUpdater::UpdateSuccess [this=%p]", this));
345 81 : if (mPendingUpdates.Length() != 0) {
346 0 : NS_WARNING("Didn't fetch all safebrowsing update redirects");
347 : }
348 :
349 : // DownloadDone() clears mSuccessCallback, so we save it off here.
350 162 : nsCOMPtr<nsIUrlClassifierCallback> successCallback = mDownloadError ? nsnull : mSuccessCallback.get();
351 81 : DownloadDone();
352 :
353 162 : nsCAutoString strTimeout;
354 81 : strTimeout.AppendInt(requestedTimeout);
355 81 : if (successCallback) {
356 81 : successCallback->HandleEvent(strTimeout);
357 : }
358 :
359 81 : return NS_OK;
360 : }
361 :
362 : NS_IMETHODIMP
363 1 : nsUrlClassifierStreamUpdater::UpdateError(PRUint32 result)
364 : {
365 1 : LOG(("nsUrlClassifierStreamUpdater::UpdateError [this=%p]", this));
366 :
367 : // DownloadDone() clears mUpdateErrorCallback, so we save it off here.
368 2 : nsCOMPtr<nsIUrlClassifierCallback> errorCallback = mDownloadError ? nsnull : mUpdateErrorCallback.get();
369 :
370 1 : DownloadDone();
371 :
372 2 : nsCAutoString strResult;
373 1 : strResult.AppendInt(result);
374 1 : if (errorCallback) {
375 1 : errorCallback->HandleEvent(strResult);
376 : }
377 :
378 1 : return NS_OK;
379 : }
380 :
381 : nsresult
382 0 : nsUrlClassifierStreamUpdater::AddRequestBody(const nsACString &aRequestBody)
383 : {
384 : nsresult rv;
385 : nsCOMPtr<nsIStringInputStream> strStream =
386 0 : do_CreateInstance(NS_STRINGINPUTSTREAM_CONTRACTID, &rv);
387 0 : NS_ENSURE_SUCCESS(rv, rv);
388 :
389 0 : rv = strStream->SetData(aRequestBody.BeginReading(),
390 0 : aRequestBody.Length());
391 0 : NS_ENSURE_SUCCESS(rv, rv);
392 :
393 0 : nsCOMPtr<nsIUploadChannel> uploadChannel = do_QueryInterface(mChannel, &rv);
394 0 : NS_ENSURE_SUCCESS(rv, rv);
395 :
396 0 : rv = uploadChannel->SetUploadStream(strStream,
397 0 : NS_LITERAL_CSTRING("text/plain"),
398 0 : -1);
399 0 : NS_ENSURE_SUCCESS(rv, rv);
400 :
401 0 : nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel, &rv);
402 0 : NS_ENSURE_SUCCESS(rv, rv);
403 :
404 0 : rv = httpChannel->SetRequestMethod(NS_LITERAL_CSTRING("POST"));
405 0 : NS_ENSURE_SUCCESS(rv, rv);
406 :
407 0 : return NS_OK;
408 : }
409 :
410 :
411 : ///////////////////////////////////////////////////////////////////////////////
412 : // nsIStreamListenerObserver implementation
413 :
414 : NS_IMETHODIMP
415 99 : nsUrlClassifierStreamUpdater::OnStartRequest(nsIRequest *request,
416 : nsISupports* context)
417 : {
418 : nsresult rv;
419 99 : bool downloadError = false;
420 198 : nsCAutoString strStatus;
421 99 : nsresult status = NS_OK;
422 :
423 : // Only update if we got http success header
424 198 : nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(request);
425 99 : if (httpChannel) {
426 2 : rv = httpChannel->GetStatus(&status);
427 2 : NS_ENSURE_SUCCESS(rv, rv);
428 :
429 2 : if (NS_ERROR_CONNECTION_REFUSED == status ||
430 : NS_ERROR_NET_TIMEOUT == status) {
431 : // Assume we're overloading the server and trigger backoff.
432 0 : downloadError = true;
433 : }
434 :
435 2 : if (NS_SUCCEEDED(status)) {
436 0 : bool succeeded = false;
437 0 : rv = httpChannel->GetRequestSucceeded(&succeeded);
438 0 : NS_ENSURE_SUCCESS(rv, rv);
439 :
440 0 : if (!succeeded) {
441 : // 404 or other error, pass error status back
442 0 : LOG(("HTTP request returned failure code."));
443 :
444 0 : rv = httpChannel->GetResponseStatus(&status);
445 0 : NS_ENSURE_SUCCESS(rv, rv);
446 :
447 0 : strStatus.AppendInt(status);
448 0 : downloadError = true;
449 : }
450 : }
451 : }
452 :
453 99 : if (downloadError) {
454 0 : mDownloadErrorCallback->HandleEvent(strStatus);
455 0 : mDownloadError = true;
456 0 : status = NS_ERROR_ABORT;
457 99 : } else if (NS_SUCCEEDED(status)) {
458 97 : mBeganStream = true;
459 97 : rv = mDBService->BeginStream(mStreamTable, mServerMAC);
460 97 : NS_ENSURE_SUCCESS(rv, rv);
461 : }
462 :
463 99 : mStreamTable.Truncate();
464 99 : mServerMAC.Truncate();
465 :
466 99 : return status;
467 : }
468 :
469 : NS_IMETHODIMP
470 97 : nsUrlClassifierStreamUpdater::OnDataAvailable(nsIRequest *request,
471 : nsISupports* context,
472 : nsIInputStream *aIStream,
473 : PRUint32 aSourceOffset,
474 : PRUint32 aLength)
475 : {
476 97 : if (!mDBService)
477 0 : return NS_ERROR_NOT_INITIALIZED;
478 :
479 97 : LOG(("OnDataAvailable (%d bytes)", aLength));
480 :
481 : nsresult rv;
482 :
483 : // Copy the data into a nsCString
484 194 : nsCString chunk;
485 97 : rv = NS_ConsumeStream(aIStream, aLength, chunk);
486 97 : NS_ENSURE_SUCCESS(rv, rv);
487 :
488 : //LOG(("Chunk (%d): %s\n\n", chunk.Length(), chunk.get()));
489 :
490 97 : rv = mDBService->UpdateStream(chunk);
491 97 : NS_ENSURE_SUCCESS(rv, rv);
492 :
493 97 : return NS_OK;
494 : }
495 :
496 : NS_IMETHODIMP
497 99 : nsUrlClassifierStreamUpdater::OnStopRequest(nsIRequest *request, nsISupports* context,
498 : nsresult aStatus)
499 : {
500 99 : if (!mDBService)
501 0 : return NS_ERROR_NOT_INITIALIZED;
502 :
503 99 : LOG(("OnStopRequest (status %x)", aStatus));
504 :
505 : nsresult rv;
506 :
507 99 : if (NS_SUCCEEDED(aStatus)) {
508 : // Success, finish this stream and move on to the next.
509 97 : rv = mDBService->FinishStream();
510 2 : } else if (mBeganStream) {
511 : // We began this stream and couldn't finish it. We have to cancel the
512 : // update, it's not in a consistent state.
513 0 : rv = mDBService->CancelUpdate();
514 : } else {
515 : // The fetch failed, but we didn't start the stream (probably a
516 : // server or connection error). We can commit what we've applied
517 : // so far, and request again later.
518 2 : rv = mDBService->FinishUpdate();
519 : }
520 :
521 99 : mChannel = nsnull;
522 :
523 99 : return rv;
524 : }
525 :
526 : ///////////////////////////////////////////////////////////////////////////////
527 : // nsIObserver implementation
528 :
529 : NS_IMETHODIMP
530 0 : nsUrlClassifierStreamUpdater::Observe(nsISupports *aSubject, const char *aTopic,
531 : const PRUnichar *aData)
532 : {
533 0 : if (nsCRT::strcmp(aTopic, gQuitApplicationMessage) == 0) {
534 0 : if (mIsUpdating && mChannel) {
535 0 : LOG(("Cancel download"));
536 : nsresult rv;
537 0 : rv = mChannel->Cancel(NS_ERROR_ABORT);
538 0 : NS_ENSURE_SUCCESS(rv, rv);
539 0 : mIsUpdating = false;
540 0 : mChannel = nsnull;
541 : }
542 0 : if (mTimer) {
543 0 : mTimer->Cancel();
544 0 : mTimer = nsnull;
545 : }
546 : }
547 0 : return NS_OK;
548 : }
549 :
550 : ///////////////////////////////////////////////////////////////////////////////
551 : // nsIBadCertListener2 implementation
552 :
553 : NS_IMETHODIMP
554 0 : nsUrlClassifierStreamUpdater::NotifyCertProblem(nsIInterfaceRequestor *socketInfo,
555 : nsISSLStatus *status,
556 : const nsACString &targetSite,
557 : bool *_retval)
558 : {
559 0 : *_retval = true;
560 0 : return NS_OK;
561 : }
562 :
563 : ///////////////////////////////////////////////////////////////////////////////
564 : // nsISSLErrorListener implementation
565 :
566 : NS_IMETHODIMP
567 0 : nsUrlClassifierStreamUpdater::NotifySSLError(nsIInterfaceRequestor *socketInfo,
568 : PRInt32 error,
569 : const nsACString &targetSite,
570 : bool *_retval)
571 : {
572 0 : *_retval = true;
573 0 : return NS_OK;
574 : }
575 :
576 : ///////////////////////////////////////////////////////////////////////////////
577 : // nsIInterfaceRequestor implementation
578 :
579 : NS_IMETHODIMP
580 4 : nsUrlClassifierStreamUpdater::GetInterface(const nsIID & eventSinkIID, void* *_retval)
581 : {
582 4 : return QueryInterface(eventSinkIID, _retval);
583 : }
584 :
585 :
586 : ///////////////////////////////////////////////////////////////////////////////
587 : // nsITimerCallback implementation
588 : NS_IMETHODIMP
589 17 : nsUrlClassifierStreamUpdater::Notify(nsITimer *timer)
590 : {
591 17 : LOG(("nsUrlClassifierStreamUpdater::Notify [%p]", this));
592 :
593 17 : mTimer = nsnull;
594 :
595 : // Start the update process up again.
596 17 : FetchNext();
597 :
598 17 : return NS_OK;
599 : }
600 :
|