~ubuntu-branches/ubuntu/precise/kompozer/precise

« back to all changes in this revision

Viewing changes to mozilla/modules/libpr0n/src/imgLoader.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Anthony Yarusso
  • Date: 2007-08-27 01:11:03 UTC
  • Revision ID: james.westby@ubuntu.com-20070827011103-2jgf4s6532gqu2ka
Tags: upstream-0.7.10
ImportĀ upstreamĀ versionĀ 0.7.10

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 
2
 *
 
3
 * The contents of this file are subject to the Mozilla Public
 
4
 * License Version 1.1 (the "License"); you may not use this file
 
5
 * except in compliance with the License. You may obtain a copy of
 
6
 * the License at http://www.mozilla.org/MPL/
 
7
 * 
 
8
 * Software distributed under the License is distributed on an "AS
 
9
 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
 
10
 * implied. See the License for the specific language governing
 
11
 * rights and limitations under the License.
 
12
 * 
 
13
 * The Original Code is mozilla.org code.
 
14
 * 
 
15
 * The Initial Developer of the Original Code is Netscape
 
16
 * Communications Corporation.  Portions created by Netscape are
 
17
 * Copyright (C) 2001 Netscape Communications Corporation.
 
18
 * All Rights Reserved.
 
19
 * 
 
20
 * Contributor(s):
 
21
 *   Stuart Parmenter <pavlov@netscape.com>
 
22
 */
 
23
 
 
24
#include "imgLoader.h"
 
25
 
 
26
#include "nsCOMPtr.h"
 
27
 
 
28
#include "nsNetUtil.h"
 
29
#include "nsIHttpChannel.h"
 
30
#include "nsICachingChannel.h"
 
31
#include "nsIProxyObjectManager.h"
 
32
#include "nsIServiceManager.h"
 
33
#include "nsXPIDLString.h"
 
34
#include "nsCRT.h"
 
35
 
 
36
#include "netCore.h"
 
37
 
 
38
#include "imgCache.h"
 
39
#include "imgRequest.h"
 
40
#include "imgRequestProxy.h"
 
41
 
 
42
#include "ImageErrors.h"
 
43
#include "ImageLogging.h"
 
44
 
 
45
#include "nsIComponentRegistrar.h"
 
46
 
 
47
// we want to explore making the document own the load group
 
48
// so we can associate the document URI with the load group.
 
49
// until this point, we have an evil hack:
 
50
#include "nsIHttpChannelInternal.h"  
 
51
 
 
52
#if defined(DEBUG_pavlov) || defined(DEBUG_timeless)
 
53
#include "nsISimpleEnumerator.h"
 
54
#include "nsXPCOM.h"
 
55
#include "nsISupportsPrimitives.h"
 
56
#include "nsXPIDLString.h"
 
57
#include "nsComponentManagerUtils.h"
 
58
 
 
59
static void PrintImageDecoders()
 
60
{
 
61
  nsCOMPtr<nsIComponentRegistrar> compMgr;
 
62
  if (NS_FAILED(NS_GetComponentRegistrar(getter_AddRefs(compMgr))) || !compMgr)
 
63
    return;
 
64
  nsCOMPtr<nsISimpleEnumerator> enumer;
 
65
  if (NS_FAILED(compMgr->EnumerateContractIDs(getter_AddRefs(enumer))) || !enumer)
 
66
    return;
 
67
  
 
68
  nsCString str;
 
69
  nsCOMPtr<nsISupports> s;
 
70
  PRBool more = PR_FALSE;
 
71
  while (NS_SUCCEEDED(enumer->HasMoreElements(&more)) && more) {
 
72
    enumer->GetNext(getter_AddRefs(s));
 
73
    if (s) {
 
74
      nsCOMPtr<nsISupportsCString> ss(do_QueryInterface(s));
 
75
 
 
76
      nsCAutoString xcs;
 
77
      ss->GetData(xcs);
 
78
 
 
79
      NS_NAMED_LITERAL_CSTRING(decoderContract, "@mozilla.org/image/decoder;2?type=");
 
80
 
 
81
      if (StringBeginsWith(xcs, decoderContract)) {
 
82
        printf("Have decoder for mime type: %s\n", xcs.get()+decoderContract.Length());
 
83
      }
 
84
    }
 
85
  }
 
86
}
 
87
#endif
 
88
 
 
89
NS_IMPL_ISUPPORTS2(imgLoader, imgILoader, nsIContentSniffer)
 
90
 
 
91
imgLoader::imgLoader()
 
92
{
 
93
  /* member initializers and constructor code */
 
94
#ifdef DEBUG_pavlov
 
95
  PrintImageDecoders();
 
96
#endif
 
97
}
 
98
 
 
99
imgLoader::~imgLoader()
 
100
{
 
101
  /* destructor code */
 
102
}
 
103
 
 
104
#define LOAD_FLAGS_CACHE_MASK    (nsIRequest::LOAD_BYPASS_CACHE | \
 
105
                                  nsIRequest::LOAD_FROM_CACHE)
 
106
 
 
107
#define LOAD_FLAGS_VALIDATE_MASK (nsIRequest::VALIDATE_ALWAYS |   \
 
108
                                  nsIRequest::VALIDATE_NEVER |    \
 
109
                                  nsIRequest::VALIDATE_ONCE_PER_SESSION)
 
110
 
 
111
 
 
112
static PRBool RevalidateEntry(nsICacheEntryDescriptor *aEntry,
 
113
                              nsLoadFlags aFlags,
 
114
                              PRBool aHasExpired)
 
115
{
 
116
  PRBool bValidateEntry = PR_FALSE;
 
117
 
 
118
  NS_ASSERTION(!(aFlags & nsIRequest::LOAD_BYPASS_CACHE),
 
119
               "MUST not revalidate when BYPASS_CACHE is specified.");
 
120
 
 
121
  if (aFlags & nsIRequest::VALIDATE_ALWAYS) {
 
122
    bValidateEntry = PR_TRUE;
 
123
  }
 
124
  //
 
125
  // The cache entry has expired...  Determine whether the stale cache
 
126
  // entry can be used without validation...
 
127
  //
 
128
  else if (aHasExpired) {
 
129
    //
 
130
    // VALIDATE_NEVER and VALIDATE_ONCE_PER_SESSION allow stale cache
 
131
    // entries to be used unless they have been explicitly marked to
 
132
    // indicate that revalidation is necessary.
 
133
    //
 
134
    if (aFlags & (nsIRequest::VALIDATE_NEVER | 
 
135
                  nsIRequest::VALIDATE_ONCE_PER_SESSION)) 
 
136
    {
 
137
      nsXPIDLCString value;
 
138
 
 
139
      aEntry->GetMetaDataElement("MustValidateIfExpired",
 
140
                                 getter_Copies(value));
 
141
      if (PL_strcmp(value, "true")) {
 
142
        bValidateEntry = PR_TRUE;
 
143
      }
 
144
    }
 
145
    //
 
146
    // LOAD_FROM_CACHE allows a stale cache entry to be used... Otherwise,
 
147
    // the entry must be revalidated.
 
148
    //
 
149
    else if (!(aFlags & nsIRequest::LOAD_FROM_CACHE)) {
 
150
      bValidateEntry = PR_TRUE;
 
151
    }
 
152
  }
 
153
 
 
154
  return bValidateEntry;
 
155
}
 
156
 
 
157
 
 
158
static nsresult NewImageChannel(nsIChannel **aResult,
 
159
                                nsIURI *aURI,
 
160
                                nsIURI *aInitialDocumentURI,
 
161
                                nsIURI *aReferringURI,
 
162
                                nsILoadGroup *aLoadGroup, nsLoadFlags aLoadFlags)
 
163
{
 
164
  nsresult rv;
 
165
  nsCOMPtr<nsIChannel> newChannel;
 
166
  nsCOMPtr<nsIHttpChannel> newHttpChannel;
 
167
 
 
168
  nsCOMPtr<nsIInterfaceRequestor> callbacks;
 
169
 
 
170
  if (aLoadGroup) {
 
171
    // Get the notification callbacks from the load group for the new channel.
 
172
    //
 
173
    // XXX: This is not exactly correct, because the network request could be
 
174
    //      referenced by multiple windows...  However, the new channel needs
 
175
    //      something.  So, using the 'first' notification callbacks is better
 
176
    //      than nothing...
 
177
    //
 
178
    aLoadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
 
179
  }
 
180
 
 
181
  // Pass in a NULL loadgroup because this is the underlying network request.
 
182
  // This request may be referenced by several proxy image requests (psossibly
 
183
  // in different documents).
 
184
  // If all of the proxy requests are canceled then this request should be
 
185
  // canceled too.
 
186
  //
 
187
  rv = NS_NewChannel(aResult,
 
188
                     aURI,        // URI 
 
189
                     nsnull,      // Cached IOService
 
190
                     nsnull,      // LoadGroup
 
191
                     callbacks,   // Notification Callbacks
 
192
                     aLoadFlags);
 
193
  if (NS_FAILED(rv))
 
194
    return rv;
 
195
 
 
196
  // Initialize HTTP-specific attributes
 
197
  newHttpChannel = do_QueryInterface(*aResult);
 
198
  if (newHttpChannel) {
 
199
    newHttpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Accept"),
 
200
                                     NS_LITERAL_CSTRING("image/png,*/*;q=0.5"),
 
201
                                     PR_FALSE);
 
202
 
 
203
    nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal = do_QueryInterface(newHttpChannel);
 
204
    NS_ENSURE_TRUE(httpChannelInternal, NS_ERROR_UNEXPECTED);
 
205
    httpChannelInternal->SetDocumentURI(aInitialDocumentURI);
 
206
    newHttpChannel->SetReferrer(aReferringURI);
 
207
  }
 
208
 
 
209
  return NS_OK;
 
210
}
 
211
 
 
212
/* imgIRequest loadImage (in nsIURI aURI, in nsIURI initialDocumentURI, in nsILoadGroup aLoadGroup, in imgIDecoderObserver aObserver, in nsISupports aCX, in nsLoadFlags aLoadFlags, in nsISupports cacheKey, in imgIRequest aRequest); */
 
213
 
 
214
NS_IMETHODIMP imgLoader::LoadImage(nsIURI *aURI, 
 
215
                                   nsIURI *aInitialDocumentURI,
 
216
                                   nsIURI *aReferrerURI,
 
217
                                   nsILoadGroup *aLoadGroup,
 
218
                                   imgIDecoderObserver *aObserver,
 
219
                                   nsISupports *aCX,
 
220
                                   nsLoadFlags aLoadFlags,
 
221
                                   nsISupports *cacheKey,
 
222
                                   imgIRequest *aRequest,
 
223
                                   imgIRequest **_retval)
 
224
{
 
225
  NS_ASSERTION(aURI, "imgLoader::LoadImage -- NULL URI pointer");
 
226
 
 
227
  // CreateNewProxyForRequest treats _retval as inout - null out
 
228
  // to make sure the passed value doesn't affect the behavior of
 
229
  // this method
 
230
  *_retval = nsnull;
 
231
 
 
232
  if (!aURI)
 
233
    return NS_ERROR_NULL_POINTER;
 
234
 
 
235
#if defined(PR_LOGGING)
 
236
  nsCAutoString spec;
 
237
  aURI->GetAsciiSpec(spec);
 
238
  LOG_SCOPE_WITH_PARAM(gImgLog, "imgLoader::LoadImage", "aURI", spec.get());
 
239
#endif
 
240
 
 
241
  // This is an owning reference that must be released.
 
242
  imgRequest *request = nsnull;
 
243
 
 
244
  nsresult rv;
 
245
  nsLoadFlags requestFlags = nsIRequest::LOAD_NORMAL;
 
246
 
 
247
  // Get the default load flags from the loadgroup (if possible)...
 
248
  if (aLoadGroup) {
 
249
    aLoadGroup->GetLoadFlags(&requestFlags);
 
250
  }
 
251
  //
 
252
  // Merge the default load flags with those passed in via aLoadFlags.
 
253
  // Currently, *only* the caching, validation and background load flags
 
254
  // are merged...
 
255
  //
 
256
  // The flags in aLoadFlags take precidence over the default flags!
 
257
  //
 
258
  if (aLoadFlags & LOAD_FLAGS_CACHE_MASK) {
 
259
    // Override the default caching flags...
 
260
    requestFlags = (requestFlags & ~LOAD_FLAGS_CACHE_MASK) |
 
261
                   (aLoadFlags & LOAD_FLAGS_CACHE_MASK);
 
262
  }
 
263
  if (aLoadFlags & LOAD_FLAGS_VALIDATE_MASK) {
 
264
    // Override the default validation flags...
 
265
    requestFlags = (requestFlags & ~LOAD_FLAGS_VALIDATE_MASK) |
 
266
                   (aLoadFlags & LOAD_FLAGS_VALIDATE_MASK);
 
267
  }
 
268
  if (aLoadFlags & nsIRequest::LOAD_BACKGROUND) {
 
269
    // Propagate background loading...
 
270
    requestFlags |= nsIRequest::LOAD_BACKGROUND;
 
271
  }
 
272
 
 
273
  nsCOMPtr<nsICacheEntryDescriptor> entry;
 
274
  PRBool bCanCacheRequest = PR_TRUE;
 
275
  PRBool bHasExpired      = PR_FALSE;
 
276
  PRBool bValidateRequest = PR_FALSE;
 
277
 
 
278
  PRBool addToLoadGroup   = PR_TRUE;
 
279
 
 
280
  // XXX For now ignore the cache key. We will need it in the future
 
281
  // for correctly dealing with image load requests that are a result
 
282
  // of post data.
 
283
  imgCache::Get(aURI, &bHasExpired,
 
284
                &request, getter_AddRefs(entry)); // addrefs request
 
285
 
 
286
  if (request && entry) {
 
287
 
 
288
    // request's null out their mCacheEntry when all proxy's are removed.
 
289
    // If we are about to add a new one back, go ahead and re-set the cache
 
290
    // entry so it can be used.
 
291
    if (!request->mCacheEntry) {
 
292
      request->mCacheEntry = entry;
 
293
    }
 
294
 
 
295
    // If the request's loadId is the same as the aCX, then it is ok to use
 
296
    // this one because it has already been validated for this context.
 
297
    //
 
298
    // XXX: nsnull seems to be a 'special' key value that indicates that NO
 
299
    //      validation is required.
 
300
    //
 
301
    void *key = (void*)aCX;
 
302
    if (request->mLoadId != key) {
 
303
 
 
304
      // LOAD_BYPASS_CACHE - Always re-fetch
 
305
      if (requestFlags & nsIRequest::LOAD_BYPASS_CACHE) {
 
306
        // doom cache entry; be sure to break the reference cycle between the
 
307
        // request and cache entry.  NOTE: the request might not own the cache
 
308
        // entry at this point, so we explicitly Doom |entry| just in case.
 
309
        entry->Doom();
 
310
        entry = nsnull;
 
311
        request->RemoveFromCache();
 
312
        NS_RELEASE(request);
 
313
      } else {
 
314
        // Determine whether the cache entry must be revalidated...
 
315
        bValidateRequest = RevalidateEntry(entry, requestFlags, bHasExpired);
 
316
 
 
317
        PR_LOG(gImgLog, PR_LOG_DEBUG,
 
318
               ("imgLoader::LoadImage validating cache entry. " 
 
319
                "bValidateRequest = %d", bValidateRequest));
 
320
      }
 
321
 
 
322
    }
 
323
#if defined(PR_LOGGING)
 
324
    else if (!key) {
 
325
      PR_LOG(gImgLog, PR_LOG_DEBUG,
 
326
             ("imgLoader::LoadImage BYPASSING cache validation for %s " 
 
327
              "because of NULL LoadID", spec.get()));
 
328
    }
 
329
#endif
 
330
  }
 
331
 
 
332
  //
 
333
  // Get the current EventQueue...  This is used as a cacheId to prevent
 
334
  // sharing requests which are being loaded across multiple event queues...
 
335
  //
 
336
  nsCOMPtr<nsIEventQueueService> eventQService;
 
337
  nsCOMPtr<nsIEventQueue> activeQ;
 
338
 
 
339
  eventQService = do_GetService(NS_EVENTQUEUESERVICE_CONTRACTID, &rv);
 
340
  if (NS_FAILED(rv)) {
 
341
    NS_IF_RELEASE(request);
 
342
    return rv;
 
343
  }
 
344
 
 
345
  rv = eventQService->ResolveEventQueue(NS_CURRENT_EVENTQ,
 
346
                                        getter_AddRefs(activeQ));
 
347
  if (NS_FAILED(rv)) {
 
348
    NS_IF_RELEASE(request);
 
349
    return rv;
 
350
  }
 
351
 
 
352
  void *cacheId = activeQ.get();
 
353
  if (request && !request->IsReusable(cacheId)) {
 
354
    //
 
355
    // The current request is still being loaded and lives on a different
 
356
    // event queue.
 
357
    //
 
358
    // Since its event queue is NOT active, do not reuse this imgRequest !!
 
359
    // Instead, force a new request to be created but DO NOT allow it to be
 
360
    // cached!
 
361
    //
 
362
    PR_LOG(gImgLog, PR_LOG_DEBUG,
 
363
           ("[this=%p] imgLoader::LoadImage -- DANGER!! Unable to use cached "
 
364
            "imgRequest [request=%p]\n", this, request));
 
365
 
 
366
    entry = nsnull;
 
367
    NS_RELEASE(request);
 
368
 
 
369
    bCanCacheRequest = PR_FALSE;
 
370
  }
 
371
 
 
372
  //
 
373
  // Time to load the request... There are 3 possible cases:
 
374
  // =======================================================
 
375
  //   1. There is no cached request (ie. nothing was found in the cache).
 
376
  //
 
377
  //   2. There is a cached request that must be validated.
 
378
  //
 
379
  //   3. There is a valid cached request.
 
380
  //
 
381
  if (request && bValidateRequest) {
 
382
    /* Case #2: the cache request cache must be revalidated. */
 
383
    LOG_SCOPE(gImgLog, "imgLoader::LoadImage |cache hit| must validate");
 
384
 
 
385
    // now we need to insert a new channel request object inbetween the real
 
386
    // request and the proxy that basically delays loading the image until it
 
387
    // gets a 304 or figures out that this needs to be a new request
 
388
 
 
389
    if (request->mValidator) {
 
390
      rv = CreateNewProxyForRequest(request, aLoadGroup, aObserver,
 
391
                                    requestFlags, aRequest, _retval);
 
392
 
 
393
      if (*_retval)
 
394
        request->mValidator->AddProxy(NS_STATIC_CAST(imgRequestProxy*, *_retval));
 
395
 
 
396
      NS_RELEASE(request);
 
397
      return rv;
 
398
 
 
399
    } else {
 
400
      nsCOMPtr<nsIChannel> newChannel;
 
401
      rv = NewImageChannel(getter_AddRefs(newChannel),
 
402
                           aURI,
 
403
                           aInitialDocumentURI,
 
404
                           aReferrerURI,
 
405
                           aLoadGroup,
 
406
                           requestFlags);
 
407
      if (NS_FAILED(rv)) {
 
408
        NS_RELEASE(request);
 
409
        return NS_ERROR_FAILURE;
 
410
      }
 
411
 
 
412
      nsCOMPtr<nsICachingChannel> cacheChan(do_QueryInterface(newChannel));
 
413
 
 
414
      if (cacheChan) {
 
415
        // since this channel supports nsICachingChannel, we can ask it
 
416
        // to only stream us data if the data comes off the net.
 
417
        PRUint32 loadFlags;
 
418
        if (NS_SUCCEEDED(newChannel->GetLoadFlags(&loadFlags)))
 
419
            newChannel->SetLoadFlags(loadFlags | nsICachingChannel::LOAD_ONLY_IF_MODIFIED);
 
420
 
 
421
      }
 
422
      nsCOMPtr<imgIRequest> req;
 
423
      rv = CreateNewProxyForRequest(request, aLoadGroup, aObserver,
 
424
                                    requestFlags, aRequest, getter_AddRefs(req));
 
425
      if (NS_FAILED(rv)) {
 
426
        NS_RELEASE(request);
 
427
        return rv;
 
428
      }
 
429
 
 
430
      imgCacheValidator *hvc = new imgCacheValidator(request, aCX);
 
431
      if (!hvc) {
 
432
        NS_RELEASE(request);
 
433
        return NS_ERROR_OUT_OF_MEMORY;
 
434
      }
 
435
 
 
436
      NS_ADDREF(hvc);
 
437
      request->mValidator = hvc;
 
438
 
 
439
      hvc->AddProxy(NS_STATIC_CAST(imgRequestProxy*,
 
440
                                   NS_STATIC_CAST(imgIRequest*, req.get())));
 
441
 
 
442
      rv = newChannel->AsyncOpen(NS_STATIC_CAST(nsIStreamListener *, hvc), nsnull);
 
443
      if (NS_SUCCEEDED(rv))
 
444
        NS_ADDREF(*_retval = req.get());
 
445
 
 
446
      NS_RELEASE(hvc);
 
447
 
 
448
      NS_RELEASE(request);
 
449
 
 
450
      return rv;
 
451
    }
 
452
  } else if (!request) {
 
453
    /* Case #1: no request from the cache.  do a new load */
 
454
    LOG_SCOPE(gImgLog, "imgLoader::LoadImage |cache miss|");
 
455
 
 
456
    nsCOMPtr<nsIChannel> newChannel;
 
457
    rv = NewImageChannel(getter_AddRefs(newChannel),
 
458
                         aURI,
 
459
                         aInitialDocumentURI,
 
460
                         aReferrerURI,
 
461
                         aLoadGroup,
 
462
                         requestFlags);
 
463
    if (NS_FAILED(rv))
 
464
      return NS_ERROR_FAILURE;
 
465
 
 
466
    NS_NEWXPCOM(request, imgRequest);
 
467
    if (!request) return NS_ERROR_OUT_OF_MEMORY;
 
468
 
 
469
    NS_ADDREF(request);
 
470
 
 
471
    PR_LOG(gImgLog, PR_LOG_DEBUG,
 
472
           ("[this=%p] imgLoader::LoadImage -- Created new imgRequest [request=%p]\n", this, request));
 
473
 
 
474
    // Add the new request into the imgCache if its cachable...
 
475
    if (bCanCacheRequest) {
 
476
      imgCache::Put(aURI, request, getter_AddRefs(entry));
 
477
    }
 
478
 
 
479
    request->Init(newChannel, entry, cacheId, aCX);
 
480
 
 
481
    // create the proxy listener
 
482
    ProxyListener *pl = new ProxyListener(NS_STATIC_CAST(nsIStreamListener *, request));
 
483
    if (!pl) {
 
484
      NS_RELEASE(request);
 
485
      return NS_ERROR_OUT_OF_MEMORY;
 
486
    }
 
487
 
 
488
    NS_ADDREF(pl);
 
489
 
 
490
    PR_LOG(gImgLog, PR_LOG_DEBUG,
 
491
           ("[this=%p] imgLoader::LoadImage -- Calling channel->AsyncOpen()\n", this));
 
492
 
 
493
    nsresult openRes;
 
494
    openRes = newChannel->AsyncOpen(NS_STATIC_CAST(nsIStreamListener *, pl), nsnull);
 
495
 
 
496
    NS_RELEASE(pl);
 
497
 
 
498
    if (NS_FAILED(openRes)) {
 
499
      NS_RELEASE(request);
 
500
      return openRes;
 
501
    }
 
502
 
 
503
  } else {
 
504
    /* Case #3: request found in cache.  use it */
 
505
    // XXX: Should this be executed if an expired cache entry does not have a caching channel??
 
506
    LOG_MSG_WITH_PARAM(gImgLog, 
 
507
                       "imgLoader::LoadImage |cache hit|", "request", request);
 
508
 
 
509
    // Update the request's LoadId
 
510
    request->SetLoadId(aCX);
 
511
 
 
512
    // request is already loaded, no need to add anything to the
 
513
    // loadgroup.
 
514
    addToLoadGroup = PR_FALSE;
 
515
  }
 
516
 
 
517
  LOG_MSG(gImgLog, "imgLoader::LoadImage", "creating proxy request.");
 
518
 
 
519
  rv = CreateNewProxyForRequest(request, aLoadGroup, aObserver,
 
520
                                requestFlags, aRequest, _retval);
 
521
 
 
522
  imgRequestProxy *proxy = (imgRequestProxy *)*_retval;
 
523
 
 
524
  if (addToLoadGroup) {
 
525
    proxy->AddToLoadGroup();
 
526
  }
 
527
 
 
528
  // if we have to validate the request, then we will send the
 
529
  // notifications later.
 
530
  if (!bValidateRequest) {
 
531
    request->NotifyProxyListener(proxy);
 
532
  }
 
533
 
 
534
  NS_RELEASE(request);
 
535
 
 
536
  return rv;
 
537
}
 
538
 
 
539
/* imgIRequest loadImageWithChannel(in nsIChannel channel, in imgIDecoderObserver aObserver, in nsISupports cx, out nsIStreamListener); */
 
540
NS_IMETHODIMP imgLoader::LoadImageWithChannel(nsIChannel *channel, imgIDecoderObserver *aObserver, nsISupports *aCX, nsIStreamListener **listener, imgIRequest **_retval)
 
541
{
 
542
  NS_ASSERTION(channel, "imgLoader::LoadImageWithChannel -- NULL channel pointer");
 
543
 
 
544
  // CreateNewProxyForRequest treats _retval as inout - null out
 
545
  // to make sure the passed value doesn't affect the behavior of
 
546
  // this method
 
547
  *_retval = nsnull;
 
548
 
 
549
  nsresult rv;
 
550
  imgRequest *request = nsnull;
 
551
 
 
552
  nsCOMPtr<nsIURI> uri;
 
553
  channel->GetURI(getter_AddRefs(uri));
 
554
 
 
555
  nsCOMPtr<nsICacheEntryDescriptor> entry;
 
556
  PRBool bHasExpired;
 
557
 
 
558
  imgCache::Get(uri, &bHasExpired, &request, getter_AddRefs(entry)); // addrefs request
 
559
 
 
560
  nsLoadFlags requestFlags = nsIRequest::LOAD_NORMAL;
 
561
 
 
562
  channel->GetLoadFlags(&requestFlags);
 
563
 
 
564
  if (request) {
 
565
    PRBool bUseCacheCopy = PR_TRUE;
 
566
 
 
567
    // LOAD_BYPASS_CACHE - Always re-fetch
 
568
    if (requestFlags & nsIRequest::LOAD_BYPASS_CACHE) {
 
569
      bUseCacheCopy = PR_FALSE;
 
570
    }
 
571
    else if (RevalidateEntry(entry, requestFlags, bHasExpired)) {
 
572
      nsCOMPtr<nsICachingChannel> cacheChan(do_QueryInterface(channel));
 
573
      if (cacheChan) {
 
574
        cacheChan->IsFromCache(&bUseCacheCopy);
 
575
      } else {
 
576
        bUseCacheCopy = PR_FALSE;
 
577
      }
 
578
    }
 
579
 
 
580
    if (!bUseCacheCopy) {
 
581
      // doom cache entry; be sure to break the reference cycle between the
 
582
      // request and cache entry.  NOTE: the request might not own the cache
 
583
      // entry at this point, so we explicitly Doom |entry| just in case.
 
584
      entry->Doom();
 
585
      entry = nsnull;
 
586
      request->RemoveFromCache();
 
587
      NS_RELEASE(request);
 
588
    }
 
589
  }
 
590
 
 
591
  nsCOMPtr<nsILoadGroup> loadGroup;
 
592
  channel->GetLoadGroup(getter_AddRefs(loadGroup));
 
593
 
 
594
  if (request) {
 
595
    // we have this in our cache already.. cancel the current (document) load
 
596
 
 
597
    /* XXX If |*listener| is null when we return here, the caller should 
 
598
       probably cancel the channel instead of us doing it here.
 
599
    */
 
600
    channel->Cancel(NS_IMAGELIB_ERROR_LOAD_ABORTED); // this should fire an OnStopRequest
 
601
 
 
602
    *listener = nsnull; // give them back a null nsIStreamListener
 
603
  } else {
 
604
    //
 
605
    // Get the current EventQueue...  This is used as a cacheId to prevent
 
606
    // sharing requests which are being loaded across multiple event queues...
 
607
    //
 
608
    nsCOMPtr<nsIEventQueueService> eventQService;
 
609
    nsCOMPtr<nsIEventQueue> activeQ;
 
610
 
 
611
    eventQService = do_GetService(NS_EVENTQUEUESERVICE_CONTRACTID, &rv);
 
612
    if (NS_FAILED(rv)) 
 
613
      return rv;
 
614
        
 
615
    rv = eventQService->ResolveEventQueue(NS_CURRENT_EVENTQ, getter_AddRefs(activeQ));
 
616
    if (NS_FAILED(rv))
 
617
      return rv;
 
618
 
 
619
    NS_NEWXPCOM(request, imgRequest);
 
620
    if (!request) return NS_ERROR_OUT_OF_MEMORY;
 
621
 
 
622
    NS_ADDREF(request);
 
623
 
 
624
    imgCache::Put(uri, request, getter_AddRefs(entry));
 
625
 
 
626
    request->Init(channel, entry, activeQ.get(), aCX);
 
627
 
 
628
    ProxyListener *pl = new ProxyListener(NS_STATIC_CAST(nsIStreamListener *, request));
 
629
    if (!pl) {
 
630
      NS_RELEASE(request);
 
631
      return NS_ERROR_OUT_OF_MEMORY;
 
632
    }
 
633
 
 
634
    NS_ADDREF(pl);
 
635
 
 
636
    *listener = NS_STATIC_CAST(nsIStreamListener*, pl);
 
637
    NS_ADDREF(*listener);
 
638
 
 
639
    NS_RELEASE(pl);
 
640
  }
 
641
 
 
642
  // XXX: It looks like the wrong load flags are being passed in...
 
643
  requestFlags &= 0xFFFF;
 
644
 
 
645
  rv = CreateNewProxyForRequest(request, loadGroup, aObserver,
 
646
                                requestFlags, nsnull, _retval);
 
647
  request->NotifyProxyListener(NS_STATIC_CAST(imgRequestProxy*, *_retval));
 
648
 
 
649
  NS_RELEASE(request);
 
650
 
 
651
  return rv;
 
652
}
 
653
 
 
654
 
 
655
nsresult
 
656
imgLoader::CreateNewProxyForRequest(imgRequest *aRequest, nsILoadGroup *aLoadGroup,
 
657
                                    imgIDecoderObserver *aObserver,
 
658
                                    nsLoadFlags aLoadFlags, imgIRequest *aProxyRequest,
 
659
                                    imgIRequest **_retval)
 
660
{
 
661
  LOG_SCOPE_WITH_PARAM(gImgLog, "imgLoader::CreateNewProxyForRequest", "imgRequest", aRequest);
 
662
 
 
663
  /* XXX If we move decoding onto separate threads, we should save off the
 
664
     calling thread here and pass it off to |proxyRequest| so that it call
 
665
     proxy calls to |aObserver|.
 
666
   */
 
667
 
 
668
  imgRequestProxy *proxyRequest;
 
669
  if (aProxyRequest) {
 
670
    proxyRequest = NS_STATIC_CAST(imgRequestProxy *, aProxyRequest);
 
671
  } else {
 
672
    NS_NEWXPCOM(proxyRequest, imgRequestProxy);
 
673
    if (!proxyRequest) return NS_ERROR_OUT_OF_MEMORY;
 
674
  }
 
675
  NS_ADDREF(proxyRequest);
 
676
 
 
677
  /* It is important to call |SetLoadFlags()| before calling |Init()| because
 
678
     |Init()| adds the request to the loadgroup.
 
679
   */
 
680
  proxyRequest->SetLoadFlags(aLoadFlags);
 
681
 
 
682
  // init adds itself to imgRequest's list of observers
 
683
  nsresult rv = proxyRequest->Init(aRequest, aLoadGroup, aObserver);
 
684
  if (NS_FAILED(rv)) {
 
685
    NS_RELEASE(proxyRequest);
 
686
    return rv;
 
687
  }
 
688
 
 
689
  if (*_retval) {
 
690
    (*_retval)->Cancel(NS_IMAGELIB_ERROR_LOAD_ABORTED);
 
691
    NS_RELEASE(*_retval);
 
692
  }
 
693
  *_retval = NS_STATIC_CAST(imgIRequest*, proxyRequest);
 
694
  NS_ADDREF(*_retval);
 
695
 
 
696
  NS_RELEASE(proxyRequest);
 
697
 
 
698
  return NS_OK;
 
699
}
 
700
 
 
701
NS_IMETHODIMP imgLoader::SupportImageWithMimeType(const char* aMimeType, PRBool *_retval)
 
702
{
 
703
  *_retval = PR_FALSE;
 
704
  nsCOMPtr<nsIComponentRegistrar> reg;
 
705
  nsresult rv = NS_GetComponentRegistrar(getter_AddRefs(reg));
 
706
  if (NS_FAILED(rv))
 
707
    return rv;
 
708
  nsCAutoString mimeType(aMimeType);
 
709
  ToLowerCase(mimeType);
 
710
  nsCAutoString decoderId(NS_LITERAL_CSTRING("@mozilla.org/image/decoder;2?type=") + mimeType);
 
711
  return reg->IsContractIDRegistered(decoderId.get(),  _retval);
 
712
}
 
713
 
 
714
NS_IMETHODIMP imgLoader::GetMIMETypeFromContent(const PRUint8* aContents, PRUint32 aLength, nsACString& aContentType)
 
715
{
 
716
  return GetMimeTypeFromContent((const char*)aContents, aLength, aContentType);
 
717
}
 
718
 
 
719
/* static */
 
720
nsresult imgLoader::GetMimeTypeFromContent(const char* aContents, PRUint32 aLength, nsACString& aContentType)
 
721
{
 
722
  /* Is it a GIF? */
 
723
  if (aLength >= 4 && !nsCRT::strncmp(aContents, "GIF8", 4))  {
 
724
    aContentType.Assign(NS_LITERAL_CSTRING("image/gif"));
 
725
  }
 
726
 
 
727
  /* or a PNG? */
 
728
  else if (aLength >= 4 && ((unsigned char)aContents[0]==0x89 &&
 
729
                   (unsigned char)aContents[1]==0x50 &&
 
730
                   (unsigned char)aContents[2]==0x4E &&
 
731
                   (unsigned char)aContents[3]==0x47))
 
732
  { 
 
733
    aContentType.Assign(NS_LITERAL_CSTRING("image/png"));
 
734
  }
 
735
 
 
736
  /* maybe a JPEG (JFIF)? */
 
737
  /* JFIF files start with SOI APP0 but older files can start with SOI DQT
 
738
   * so we test for SOI followed by any marker, i.e. FF D8 FF
 
739
   * this will also work for SPIFF JPEG files if they appear in the future.
 
740
   *
 
741
   * (JFIF is 0XFF 0XD8 0XFF 0XE0 <skip 2> 0X4A 0X46 0X49 0X46 0X00)
 
742
   */
 
743
  else if (aLength >= 3 &&
 
744
     ((unsigned char)aContents[0])==0xFF &&
 
745
     ((unsigned char)aContents[1])==0xD8 &&
 
746
     ((unsigned char)aContents[2])==0xFF)
 
747
  {
 
748
    aContentType.Assign(NS_LITERAL_CSTRING("image/jpeg"));
 
749
  }
 
750
 
 
751
  /* or how about ART? */
 
752
  /* ART begins with JG (4A 47). Major version offset 2.
 
753
   * Minor version offset 3. Offset 4 must be NULL.
 
754
   */
 
755
  else if (aLength >= 5 &&
 
756
   ((unsigned char) aContents[0])==0x4a &&
 
757
   ((unsigned char) aContents[1])==0x47 &&
 
758
   ((unsigned char) aContents[4])==0x00 )
 
759
  {
 
760
    aContentType.Assign(NS_LITERAL_CSTRING("image/x-jg"));
 
761
  }
 
762
 
 
763
  else if (aLength >= 2 && !nsCRT::strncmp(aContents, "BM", 2)) {
 
764
    aContentType.Assign(NS_LITERAL_CSTRING("image/bmp"));
 
765
  }
 
766
 
 
767
  // ICOs always begin with a 2-byte 0 followed by a 2-byte 1.
 
768
  else if (aLength >= 4 && !memcmp(aContents, "\000\000\001\000", 4)) {
 
769
    aContentType.Assign(NS_LITERAL_CSTRING("image/x-icon"));
 
770
  }
 
771
 
 
772
  else if (aLength >= 8 && !nsCRT::strncmp(aContents, "#define ", 8)) {
 
773
    aContentType.Assign(NS_LITERAL_CSTRING("image/x-xbitmap"));
 
774
  }
 
775
  else {
 
776
    /* none of the above?  I give up */
 
777
    return NS_ERROR_NOT_AVAILABLE;
 
778
  }
 
779
 
 
780
  return NS_OK;
 
781
}
 
782
 
 
783
/**
 
784
 * proxy stream listener class used to handle multipart/x-mixed-replace
 
785
 */
 
786
 
 
787
#include "nsIRequest.h"
 
788
#include "nsIStreamConverterService.h"
 
789
#include "nsXPIDLString.h"
 
790
 
 
791
NS_IMPL_ISUPPORTS2(ProxyListener, nsIStreamListener, nsIRequestObserver)
 
792
 
 
793
ProxyListener::ProxyListener(nsIStreamListener *dest) :
 
794
  mDestListener(dest)
 
795
{
 
796
  /* member initializers and constructor code */
 
797
}
 
798
 
 
799
ProxyListener::~ProxyListener()
 
800
{
 
801
  /* destructor code */
 
802
}
 
803
 
 
804
 
 
805
/** nsIRequestObserver methods **/
 
806
 
 
807
/* void onStartRequest (in nsIRequest request, in nsISupports ctxt); */
 
808
NS_IMETHODIMP ProxyListener::OnStartRequest(nsIRequest *aRequest, nsISupports *ctxt)
 
809
{
 
810
  if (!mDestListener)
 
811
    return NS_ERROR_FAILURE;
 
812
 
 
813
  nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
 
814
  if (channel) {
 
815
    nsCAutoString contentType;
 
816
    nsresult rv = channel->GetContentType(contentType);
 
817
 
 
818
    if (!contentType.IsEmpty()) {
 
819
     /* If multipart/x-mixed-replace content, we'll insert a MIME decoder
 
820
        in the pipeline to handle the content and pass it along to our
 
821
        original listener.
 
822
      */
 
823
      if (NS_LITERAL_CSTRING("multipart/x-mixed-replace").Equals(contentType)) {
 
824
 
 
825
        nsCOMPtr<nsIStreamConverterService> convServ(do_GetService("@mozilla.org/streamConverters;1", &rv));
 
826
        if (NS_SUCCEEDED(rv)) {
 
827
          nsCOMPtr<nsIStreamListener> toListener(mDestListener);
 
828
          nsCOMPtr<nsIStreamListener> fromListener;
 
829
 
 
830
          rv = convServ->AsyncConvertData(NS_LITERAL_STRING("multipart/x-mixed-replace").get(),
 
831
                                          NS_LITERAL_STRING("*/*").get(),
 
832
                                          toListener,
 
833
                                          nsnull,
 
834
                                          getter_AddRefs(fromListener));
 
835
          if (NS_SUCCEEDED(rv))
 
836
            mDestListener = fromListener;
 
837
        }
 
838
      }
 
839
    }
 
840
  }
 
841
 
 
842
  return mDestListener->OnStartRequest(aRequest, ctxt);
 
843
}
 
844
 
 
845
/* void onStopRequest (in nsIRequest request, in nsISupports ctxt, in nsresult status); */
 
846
NS_IMETHODIMP ProxyListener::OnStopRequest(nsIRequest *aRequest, nsISupports *ctxt, nsresult status)
 
847
{
 
848
  if (!mDestListener)
 
849
    return NS_ERROR_FAILURE;
 
850
 
 
851
  return mDestListener->OnStopRequest(aRequest, ctxt, status);
 
852
}
 
853
 
 
854
/** nsIStreamListener methods **/
 
855
 
 
856
/* void onDataAvailable (in nsIRequest request, in nsISupports ctxt, in nsIInputStream inStr, in unsigned long sourceOffset, in unsigned long count); */
 
857
NS_IMETHODIMP ProxyListener::OnDataAvailable(nsIRequest *aRequest, nsISupports *ctxt, nsIInputStream *inStr, PRUint32 sourceOffset, PRUint32 count)
 
858
{
 
859
  if (!mDestListener)
 
860
    return NS_ERROR_FAILURE;
 
861
 
 
862
  return mDestListener->OnDataAvailable(aRequest, ctxt, inStr, sourceOffset, count);
 
863
}
 
864
 
 
865
 
 
866
 
 
867
 
 
868
 
 
869
/**
 
870
 * http validate class.  check a channel for a 304
 
871
 */
 
872
 
 
873
NS_IMPL_ISUPPORTS2(imgCacheValidator, nsIStreamListener, nsIRequestObserver)
 
874
 
 
875
imgCacheValidator::imgCacheValidator(imgRequest *request, void *aContext) :
 
876
  mContext(aContext)
 
877
{
 
878
  /* member initializers and constructor code */
 
879
 
 
880
  mRequest = request;
 
881
  NS_ADDREF(mRequest);
 
882
}
 
883
 
 
884
imgCacheValidator::~imgCacheValidator()
 
885
{
 
886
  /* destructor code */
 
887
  if (mRequest) {
 
888
    mRequest->mValidator = nsnull;
 
889
    NS_RELEASE(mRequest);
 
890
  }
 
891
}
 
892
 
 
893
void imgCacheValidator::AddProxy(imgRequestProxy *aProxy)
 
894
{
 
895
  // aProxy needs to be in the loadgroup since we're validating from
 
896
  // the network.
 
897
  aProxy->AddToLoadGroup();
 
898
 
 
899
  mProxies.AppendElement(aProxy);
 
900
}
 
901
 
 
902
/** nsIRequestObserver methods **/
 
903
 
 
904
/* void onStartRequest (in nsIRequest request, in nsISupports ctxt); */
 
905
NS_IMETHODIMP imgCacheValidator::OnStartRequest(nsIRequest *aRequest, nsISupports *ctxt)
 
906
{
 
907
  nsCOMPtr<nsICachingChannel> cacheChan(do_QueryInterface(aRequest));
 
908
  if (cacheChan) {
 
909
    PRBool isFromCache;
 
910
    if (NS_SUCCEEDED(cacheChan->IsFromCache(&isFromCache)) && isFromCache) {
 
911
 
 
912
      PRUint32 count;
 
913
      mProxies.Count(&count);
 
914
      for (PRInt32 i = count-1; i>=0; i--) {
 
915
        imgRequestProxy *proxy;
 
916
        mProxies.GetElementAt(i, (nsISupports**)&proxy);
 
917
        mRequest->NotifyProxyListener(proxy);
 
918
        NS_RELEASE(proxy);
 
919
      }
 
920
 
 
921
      mRequest->SetLoadId(mContext);
 
922
      mRequest->mValidator = nsnull;
 
923
 
 
924
      NS_RELEASE(mRequest); // assigns null
 
925
 
 
926
      return NS_OK;
 
927
    }
 
928
  }
 
929
  // fun stuff.
 
930
  nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
 
931
  nsCOMPtr<nsICacheEntryDescriptor> entry;
 
932
  nsCOMPtr<nsIURI> uri;
 
933
 
 
934
  // Doom the old request's cache entry
 
935
  mRequest->RemoveFromCache();
 
936
 
 
937
  mRequest->GetURI(getter_AddRefs(uri));
 
938
 
 
939
  mRequest->mValidator = nsnull;
 
940
  NS_RELEASE(mRequest); // assigns null
 
941
 
 
942
  nsresult rv;
 
943
  nsCOMPtr<nsIEventQueueService> eventQService = do_GetService(NS_EVENTQUEUESERVICE_CONTRACTID, &rv);
 
944
  if (NS_FAILED(rv)) return rv;
 
945
 
 
946
  nsCOMPtr<nsIEventQueue> activeQ;
 
947
  rv = eventQService->ResolveEventQueue(NS_CURRENT_EVENTQ, getter_AddRefs(activeQ));
 
948
  if (NS_FAILED(rv)) return rv;
 
949
 
 
950
  imgRequest *request;
 
951
  NS_NEWXPCOM(request, imgRequest);
 
952
  if (!request) return NS_ERROR_OUT_OF_MEMORY;
 
953
  NS_ADDREF(request);
 
954
 
 
955
  imgCache::Put(uri, request, getter_AddRefs(entry));
 
956
 
 
957
  request->Init(channel, entry, activeQ.get(), mContext);
 
958
 
 
959
  ProxyListener *pl = new ProxyListener(NS_STATIC_CAST(nsIStreamListener *, request));
 
960
  if (!pl) {
 
961
    NS_RELEASE(request);
 
962
    return NS_ERROR_OUT_OF_MEMORY;
 
963
  }
 
964
 
 
965
  mDestListener = NS_STATIC_CAST(nsIStreamListener*, pl);
 
966
 
 
967
  PRUint32 count;
 
968
  mProxies.Count(&count);
 
969
  for (PRInt32 i = count-1; i>=0; i--) {
 
970
    imgRequestProxy *proxy;
 
971
    mProxies.GetElementAt(i, (nsISupports**)&proxy);
 
972
    proxy->ChangeOwner(request);
 
973
    request->NotifyProxyListener(proxy);
 
974
    NS_RELEASE(proxy);
 
975
  }
 
976
 
 
977
  NS_RELEASE(request);
 
978
 
 
979
  if (!mDestListener)
 
980
    return NS_OK;
 
981
 
 
982
  return mDestListener->OnStartRequest(aRequest, ctxt);
 
983
}
 
984
 
 
985
/* void onStopRequest (in nsIRequest request, in nsISupports ctxt, in nsresult status); */
 
986
NS_IMETHODIMP imgCacheValidator::OnStopRequest(nsIRequest *aRequest, nsISupports *ctxt, nsresult status)
 
987
{
 
988
  if (!mDestListener)
 
989
    return NS_OK;
 
990
 
 
991
  return mDestListener->OnStopRequest(aRequest, ctxt, status);
 
992
}
 
993
 
 
994
/** nsIStreamListener methods **/
 
995
 
 
996
 
 
997
// XXX see bug 113959
 
998
static NS_METHOD dispose_of_data(nsIInputStream* in, void* closure,
 
999
                                 const char* fromRawSegment, PRUint32 toOffset,
 
1000
                                 PRUint32 count, PRUint32 *writeCount)
 
1001
{
 
1002
  *writeCount = count;
 
1003
  return NS_OK;
 
1004
}
 
1005
 
 
1006
/* void onDataAvailable (in nsIRequest request, in nsISupports ctxt, in nsIInputStream inStr, in unsigned long sourceOffset, in unsigned long count); */
 
1007
NS_IMETHODIMP imgCacheValidator::OnDataAvailable(nsIRequest *aRequest, nsISupports *ctxt, nsIInputStream *inStr, PRUint32 sourceOffset, PRUint32 count)
 
1008
{
 
1009
#ifdef DEBUG
 
1010
  nsCOMPtr<nsICachingChannel> cacheChan(do_QueryInterface(aRequest));
 
1011
  if (cacheChan) {
 
1012
    PRBool isFromCache;
 
1013
    if (NS_SUCCEEDED(cacheChan->IsFromCache(&isFromCache)) && isFromCache)
 
1014
      NS_ERROR("OnDataAvailable not suppressed by LOAD_ONLY_IF_MODIFIED load flag");
 
1015
  }
 
1016
#endif
 
1017
 
 
1018
  if (!mDestListener) {
 
1019
    // XXX see bug 113959
 
1020
    PRUint32 _retval;
 
1021
    inStr->ReadSegments(dispose_of_data, nsnull, count, &_retval);
 
1022
    return NS_OK;
 
1023
  }
 
1024
 
 
1025
  return mDestListener->OnDataAvailable(aRequest, ctxt, inStr, sourceOffset, count);
 
1026
}