/image/src/imgRequest.cpp

http://github.com/zpao/v8monkey · C++ · 1247 lines · 818 code · 222 blank · 207 comment · 144 complexity · 040f609e986a801b571f645aaac4b1f4 MD5 · raw file

  1. /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  2. *
  3. * ***** BEGIN LICENSE BLOCK *****
  4. * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  5. *
  6. * The contents of this file are subject to the Mozilla Public License Version
  7. * 1.1 (the "License"); you may not use this file except in compliance with
  8. * the License. You may obtain a copy of the License at
  9. * http://www.mozilla.org/MPL/
  10. *
  11. * Software distributed under the License is distributed on an "AS IS" basis,
  12. * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  13. * for the specific language governing rights and limitations under the
  14. * License.
  15. *
  16. * The Original Code is mozilla.org code.
  17. *
  18. * The Initial Developer of the Original Code is
  19. * Netscape Communications Corporation.
  20. * Portions created by the Initial Developer are Copyright (C) 2001
  21. * the Initial Developer. All Rights Reserved.
  22. *
  23. * Contributor(s):
  24. * Stuart Parmenter <stuart@mozilla.com>
  25. * Bobby Holley <bobbyholley@gmail.com>
  26. *
  27. * Alternatively, the contents of this file may be used under the terms of
  28. * either the GNU General Public License Version 2 or later (the "GPL"), or
  29. * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  30. * in which case the provisions of the GPL or the LGPL are applicable instead
  31. * of those above. If you wish to allow use of your version of this file only
  32. * under the terms of either the GPL or the LGPL, and not to allow others to
  33. * use your version of this file under the terms of the MPL, indicate your
  34. * decision by deleting the provisions above and replace them with the notice
  35. * and other provisions required by the GPL or the LGPL. If you do not delete
  36. * the provisions above, a recipient may use your version of this file under
  37. * the terms of any one of the MPL, the GPL or the LGPL.
  38. *
  39. * ***** END LICENSE BLOCK ***** */
  40. #include "imgRequest.h"
  41. #include "ImageLogging.h"
  42. /* We end up pulling in windows.h because we eventually hit gfxWindowsSurface;
  43. * windows.h defines LoadImage, so we have to #undef it or imgLoader::LoadImage
  44. * gets changed.
  45. * This #undef needs to be in multiple places because we don't always pull
  46. * headers in in the same order.
  47. */
  48. #undef LoadImage
  49. #include "imgLoader.h"
  50. #include "imgRequestProxy.h"
  51. #include "RasterImage.h"
  52. #include "VectorImage.h"
  53. #include "imgILoader.h"
  54. #include "netCore.h"
  55. #include "nsIChannel.h"
  56. #include "nsICachingChannel.h"
  57. #include "nsILoadGroup.h"
  58. #include "nsIInputStream.h"
  59. #include "nsIMultiPartChannel.h"
  60. #include "nsIHttpChannel.h"
  61. #include "nsIComponentManager.h"
  62. #include "nsIInterfaceRequestorUtils.h"
  63. #include "nsIServiceManager.h"
  64. #include "nsISupportsPrimitives.h"
  65. #include "nsIScriptSecurityManager.h"
  66. #include "nsICacheVisitor.h"
  67. #include "nsString.h"
  68. #include "nsXPIDLString.h"
  69. #include "plstr.h" // PL_strcasestr(...)
  70. #include "nsNetUtil.h"
  71. #include "nsIProtocolHandler.h"
  72. #include "mozilla/Preferences.h"
  73. #include "DiscardTracker.h"
  74. #include "nsAsyncRedirectVerifyHelper.h"
  75. #define SVG_MIMETYPE "image/svg+xml"
  76. using namespace mozilla;
  77. using namespace mozilla::image;
  78. static bool gInitializedPrefCaches = false;
  79. static bool gDecodeOnDraw = false;
  80. static bool gDiscardable = false;
  81. static void
  82. InitPrefCaches()
  83. {
  84. Preferences::AddBoolVarCache(&gDiscardable, "image.mem.discardable");
  85. Preferences::AddBoolVarCache(&gDecodeOnDraw, "image.mem.decodeondraw");
  86. gInitializedPrefCaches = true;
  87. }
  88. #if defined(PR_LOGGING)
  89. PRLogModuleInfo *gImgLog = PR_NewLogModule("imgRequest");
  90. #endif
  91. NS_IMPL_ISUPPORTS8(imgRequest,
  92. imgIDecoderObserver, imgIContainerObserver,
  93. nsIStreamListener, nsIRequestObserver,
  94. nsISupportsWeakReference,
  95. nsIChannelEventSink,
  96. nsIInterfaceRequestor,
  97. nsIAsyncVerifyRedirectCallback)
  98. imgRequest::imgRequest() :
  99. mValidator(nsnull), mImageSniffers("image-sniffing-services"),
  100. mInnerWindowId(0), mCORSMode(imgIRequest::CORS_NONE),
  101. mDecodeRequested(false), mIsMultiPartChannel(false), mGotData(false),
  102. mIsInCache(false)
  103. {
  104. // Register our pref observers if we haven't yet.
  105. if (NS_UNLIKELY(!gInitializedPrefCaches)) {
  106. InitPrefCaches();
  107. }
  108. }
  109. imgRequest::~imgRequest()
  110. {
  111. if (mURI) {
  112. nsCAutoString spec;
  113. mURI->GetSpec(spec);
  114. LOG_FUNC_WITH_PARAM(gImgLog, "imgRequest::~imgRequest()", "keyuri", spec.get());
  115. } else
  116. LOG_FUNC(gImgLog, "imgRequest::~imgRequest()");
  117. }
  118. nsresult imgRequest::Init(nsIURI *aURI,
  119. nsIURI *aCurrentURI,
  120. nsIRequest *aRequest,
  121. nsIChannel *aChannel,
  122. imgCacheEntry *aCacheEntry,
  123. void *aLoadId,
  124. nsIPrincipal* aLoadingPrincipal,
  125. PRInt32 aCORSMode)
  126. {
  127. LOG_FUNC(gImgLog, "imgRequest::Init");
  128. NS_ABORT_IF_FALSE(!mImage, "Multiple calls to init");
  129. NS_ABORT_IF_FALSE(aURI, "No uri");
  130. NS_ABORT_IF_FALSE(aCurrentURI, "No current uri");
  131. NS_ABORT_IF_FALSE(aRequest, "No request");
  132. NS_ABORT_IF_FALSE(aChannel, "No channel");
  133. mProperties = do_CreateInstance("@mozilla.org/properties;1");
  134. mStatusTracker = new imgStatusTracker(nsnull);
  135. mURI = aURI;
  136. mCurrentURI = aCurrentURI;
  137. mRequest = aRequest;
  138. mChannel = aChannel;
  139. mTimedChannel = do_QueryInterface(mChannel);
  140. mLoadingPrincipal = aLoadingPrincipal;
  141. mCORSMode = aCORSMode;
  142. mChannel->GetNotificationCallbacks(getter_AddRefs(mPrevChannelSink));
  143. NS_ASSERTION(mPrevChannelSink != this,
  144. "Initializing with a channel that already calls back to us!");
  145. mChannel->SetNotificationCallbacks(this);
  146. mCacheEntry = aCacheEntry;
  147. SetLoadId(aLoadId);
  148. return NS_OK;
  149. }
  150. imgStatusTracker&
  151. imgRequest::GetStatusTracker()
  152. {
  153. if (mImage) {
  154. NS_ABORT_IF_FALSE(!mStatusTracker,
  155. "Should have given mStatusTracker to mImage");
  156. return mImage->GetStatusTracker();
  157. } else {
  158. NS_ABORT_IF_FALSE(mStatusTracker,
  159. "Should have mStatusTracker until we create mImage");
  160. return *mStatusTracker;
  161. }
  162. }
  163. void imgRequest::SetCacheEntry(imgCacheEntry *entry)
  164. {
  165. mCacheEntry = entry;
  166. }
  167. bool imgRequest::HasCacheEntry() const
  168. {
  169. return mCacheEntry != nsnull;
  170. }
  171. nsresult imgRequest::AddProxy(imgRequestProxy *proxy)
  172. {
  173. NS_PRECONDITION(proxy, "null imgRequestProxy passed in");
  174. LOG_SCOPE_WITH_PARAM(gImgLog, "imgRequest::AddProxy", "proxy", proxy);
  175. // If we're empty before adding, we have to tell the loader we now have
  176. // proxies.
  177. if (mObservers.IsEmpty()) {
  178. NS_ABORT_IF_FALSE(mURI, "Trying to SetHasProxies without key uri.");
  179. imgLoader::SetHasProxies(mURI);
  180. }
  181. // If we don't have any current observers, we should restart any animation.
  182. if (mImage && !HaveProxyWithObserver(proxy) && proxy->HasObserver()) {
  183. LOG_MSG(gImgLog, "imgRequest::AddProxy", "resetting animation");
  184. mImage->ResetAnimation();
  185. }
  186. proxy->SetPrincipal(mPrincipal);
  187. return mObservers.AppendElementUnlessExists(proxy) ?
  188. NS_OK : NS_ERROR_OUT_OF_MEMORY;
  189. }
  190. nsresult imgRequest::RemoveProxy(imgRequestProxy *proxy, nsresult aStatus, bool aNotify)
  191. {
  192. LOG_SCOPE_WITH_PARAM(gImgLog, "imgRequest::RemoveProxy", "proxy", proxy);
  193. // This will remove our animation consumers, so after removing
  194. // this proxy, we don't end up without proxies with observers, but still
  195. // have animation consumers.
  196. proxy->ClearAnimationConsumers();
  197. if (!mObservers.RemoveElement(proxy)) {
  198. // Not one of our proxies; we're done
  199. return NS_OK;
  200. }
  201. // Let the status tracker do its thing before we potentially call Cancel()
  202. // below, because Cancel() may result in OnStopRequest being called back
  203. // before Cancel() returns, leaving the image in a different state then the
  204. // one it was in at this point.
  205. imgStatusTracker& statusTracker = GetStatusTracker();
  206. statusTracker.EmulateRequestFinished(proxy, aStatus, !aNotify);
  207. if (mObservers.IsEmpty()) {
  208. // If we have no observers, there's nothing holding us alive. If we haven't
  209. // been cancelled and thus removed from the cache, tell the image loader so
  210. // we can be evicted from the cache.
  211. if (mCacheEntry) {
  212. NS_ABORT_IF_FALSE(mURI, "Removing last observer without key uri.");
  213. imgLoader::SetHasNoProxies(mURI, mCacheEntry);
  214. }
  215. #if defined(PR_LOGGING)
  216. else {
  217. nsCAutoString spec;
  218. mURI->GetSpec(spec);
  219. LOG_MSG_WITH_PARAM(gImgLog, "imgRequest::RemoveProxy no cache entry", "uri", spec.get());
  220. }
  221. #endif
  222. /* If |aStatus| is a failure code, then cancel the load if it is still in progress.
  223. Otherwise, let the load continue, keeping 'this' in the cache with no observers.
  224. This way, if a proxy is destroyed without calling cancel on it, it won't leak
  225. and won't leave a bad pointer in mObservers.
  226. */
  227. if (statusTracker.IsLoading() && NS_FAILED(aStatus)) {
  228. LOG_MSG(gImgLog, "imgRequest::RemoveProxy", "load in progress. canceling");
  229. this->Cancel(NS_BINDING_ABORTED);
  230. }
  231. /* break the cycle from the cache entry. */
  232. mCacheEntry = nsnull;
  233. }
  234. // If a proxy is removed for a reason other than its owner being
  235. // changed, remove the proxy from the loadgroup.
  236. if (aStatus != NS_IMAGELIB_CHANGING_OWNER)
  237. proxy->RemoveFromLoadGroup(true);
  238. return NS_OK;
  239. }
  240. void imgRequest::CancelAndAbort(nsresult aStatus)
  241. {
  242. LOG_SCOPE(gImgLog, "imgRequest::CancelAndAbort");
  243. Cancel(aStatus);
  244. // It's possible for the channel to fail to open after we've set our
  245. // notification callbacks. In that case, make sure to break the cycle between
  246. // the channel and us, because it won't.
  247. if (mChannel) {
  248. mChannel->SetNotificationCallbacks(mPrevChannelSink);
  249. mPrevChannelSink = nsnull;
  250. }
  251. }
  252. void imgRequest::Cancel(nsresult aStatus)
  253. {
  254. /* The Cancel() method here should only be called by this class. */
  255. LOG_SCOPE(gImgLog, "imgRequest::Cancel");
  256. imgStatusTracker& statusTracker = GetStatusTracker();
  257. statusTracker.RecordCancel();
  258. RemoveFromCache();
  259. if (mRequest && statusTracker.IsLoading())
  260. mRequest->Cancel(aStatus);
  261. }
  262. nsresult imgRequest::GetURI(nsIURI **aURI)
  263. {
  264. LOG_FUNC(gImgLog, "imgRequest::GetURI");
  265. if (mURI) {
  266. *aURI = mURI;
  267. NS_ADDREF(*aURI);
  268. return NS_OK;
  269. }
  270. return NS_ERROR_FAILURE;
  271. }
  272. nsresult imgRequest::GetSecurityInfo(nsISupports **aSecurityInfo)
  273. {
  274. LOG_FUNC(gImgLog, "imgRequest::GetSecurityInfo");
  275. // Missing security info means this is not a security load
  276. // i.e. it is not an error when security info is missing
  277. NS_IF_ADDREF(*aSecurityInfo = mSecurityInfo);
  278. return NS_OK;
  279. }
  280. void imgRequest::RemoveFromCache()
  281. {
  282. LOG_SCOPE(gImgLog, "imgRequest::RemoveFromCache");
  283. if (mIsInCache) {
  284. // mCacheEntry is nulled out when we have no more observers.
  285. if (mCacheEntry)
  286. imgLoader::RemoveFromCache(mCacheEntry);
  287. else
  288. imgLoader::RemoveFromCache(mURI);
  289. }
  290. mCacheEntry = nsnull;
  291. }
  292. bool imgRequest::HaveProxyWithObserver(imgRequestProxy* aProxyToIgnore) const
  293. {
  294. nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers);
  295. imgRequestProxy* proxy;
  296. while (iter.HasMore()) {
  297. proxy = iter.GetNext();
  298. if (proxy == aProxyToIgnore) {
  299. continue;
  300. }
  301. if (proxy->HasObserver()) {
  302. return true;
  303. }
  304. }
  305. return false;
  306. }
  307. PRInt32 imgRequest::Priority() const
  308. {
  309. PRInt32 priority = nsISupportsPriority::PRIORITY_NORMAL;
  310. nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(mRequest);
  311. if (p)
  312. p->GetPriority(&priority);
  313. return priority;
  314. }
  315. void imgRequest::AdjustPriority(imgRequestProxy *proxy, PRInt32 delta)
  316. {
  317. // only the first proxy is allowed to modify the priority of this image load.
  318. //
  319. // XXX(darin): this is probably not the most optimal algorithm as we may want
  320. // to increase the priority of requests that have a lot of proxies. the key
  321. // concern though is that image loads remain lower priority than other pieces
  322. // of content such as link clicks, CSS, and JS.
  323. //
  324. if (mObservers.SafeElementAt(0, nsnull) != proxy)
  325. return;
  326. nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(mRequest);
  327. if (p)
  328. p->AdjustPriority(delta);
  329. }
  330. void imgRequest::SetIsInCache(bool incache)
  331. {
  332. LOG_FUNC_WITH_PARAM(gImgLog, "imgRequest::SetIsCacheable", "incache", incache);
  333. mIsInCache = incache;
  334. }
  335. void imgRequest::UpdateCacheEntrySize()
  336. {
  337. if (mCacheEntry) {
  338. mCacheEntry->SetDataSize(mImage->GetDataSize());
  339. #ifdef DEBUG_joe
  340. nsCAutoString url;
  341. mURI->GetSpec(url);
  342. printf("CACHEPUT: %d %s %d\n", time(NULL), url.get(), imageSize);
  343. #endif
  344. }
  345. }
  346. void imgRequest::SetCacheValidation(imgCacheEntry* aCacheEntry, nsIRequest* aRequest)
  347. {
  348. /* get the expires info */
  349. if (aCacheEntry) {
  350. nsCOMPtr<nsICachingChannel> cacheChannel(do_QueryInterface(aRequest));
  351. if (cacheChannel) {
  352. nsCOMPtr<nsISupports> cacheToken;
  353. cacheChannel->GetCacheToken(getter_AddRefs(cacheToken));
  354. if (cacheToken) {
  355. nsCOMPtr<nsICacheEntryInfo> entryDesc(do_QueryInterface(cacheToken));
  356. if (entryDesc) {
  357. PRUint32 expiration;
  358. /* get the expiration time from the caching channel's token */
  359. entryDesc->GetExpirationTime(&expiration);
  360. // Expiration time defaults to 0. We set the expiration time on our
  361. // entry if it hasn't been set yet.
  362. if (aCacheEntry->GetExpiryTime() == 0)
  363. aCacheEntry->SetExpiryTime(expiration);
  364. }
  365. }
  366. }
  367. // Determine whether the cache entry must be revalidated when we try to use it.
  368. // Currently, only HTTP specifies this information...
  369. nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aRequest));
  370. if (httpChannel) {
  371. bool bMustRevalidate = false;
  372. httpChannel->IsNoStoreResponse(&bMustRevalidate);
  373. if (!bMustRevalidate) {
  374. httpChannel->IsNoCacheResponse(&bMustRevalidate);
  375. }
  376. if (!bMustRevalidate) {
  377. nsCAutoString cacheHeader;
  378. httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("Cache-Control"),
  379. cacheHeader);
  380. if (PL_strcasestr(cacheHeader.get(), "must-revalidate")) {
  381. bMustRevalidate = true;
  382. }
  383. }
  384. // Cache entries default to not needing to validate. We ensure that
  385. // multiple calls to this function don't override an earlier decision to
  386. // validate by making validation a one-way decision.
  387. if (bMustRevalidate)
  388. aCacheEntry->SetMustValidate(bMustRevalidate);
  389. }
  390. // We always need to validate file URIs.
  391. nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
  392. if (channel) {
  393. nsCOMPtr<nsIURI> uri;
  394. channel->GetURI(getter_AddRefs(uri));
  395. bool isfile = false;
  396. uri->SchemeIs("file", &isfile);
  397. if (isfile)
  398. aCacheEntry->SetMustValidate(isfile);
  399. }
  400. }
  401. }
  402. nsresult
  403. imgRequest::LockImage()
  404. {
  405. return mImage->LockImage();
  406. }
  407. nsresult
  408. imgRequest::UnlockImage()
  409. {
  410. return mImage->UnlockImage();
  411. }
  412. nsresult
  413. imgRequest::RequestDecode()
  414. {
  415. // If we've initialized our image, we can request a decode.
  416. if (mImage) {
  417. return mImage->RequestDecode();
  418. }
  419. // Otherwise, flag to do it when we get the image
  420. mDecodeRequested = true;
  421. return NS_OK;
  422. }
  423. /** imgIContainerObserver methods **/
  424. /* [noscript] void frameChanged (in imgIRequest request,
  425. in imgIContainer container,
  426. in nsIntRect dirtyRect); */
  427. NS_IMETHODIMP imgRequest::FrameChanged(imgIRequest *request,
  428. imgIContainer *container,
  429. const nsIntRect *dirtyRect)
  430. {
  431. LOG_SCOPE(gImgLog, "imgRequest::FrameChanged");
  432. NS_ABORT_IF_FALSE(mImage,
  433. "FrameChanged callback before we've created our image");
  434. mImage->GetStatusTracker().RecordFrameChanged(container, dirtyRect);
  435. nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers);
  436. while (iter.HasMore()) {
  437. mImage->GetStatusTracker().SendFrameChanged(iter.GetNext(), container, dirtyRect);
  438. }
  439. return NS_OK;
  440. }
  441. /** imgIDecoderObserver methods **/
  442. /* void onStartDecode (in imgIRequest request); */
  443. NS_IMETHODIMP imgRequest::OnStartDecode(imgIRequest *request)
  444. {
  445. LOG_SCOPE(gImgLog, "imgRequest::OnStartDecode");
  446. NS_ABORT_IF_FALSE(mImage,
  447. "OnStartDecode callback before we've created our image");
  448. mImage->GetStatusTracker().RecordStartDecode();
  449. nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers);
  450. while (iter.HasMore()) {
  451. mImage->GetStatusTracker().SendStartDecode(iter.GetNext());
  452. }
  453. /* In the case of streaming jpegs, it is possible to get multiple OnStartDecodes which
  454. indicates the beginning of a new decode.
  455. The cache entry's size therefore needs to be reset to 0 here. If we do not do this,
  456. the code in imgRequest::OnStopFrame will continue to increase the data size cumulatively.
  457. */
  458. if (mCacheEntry)
  459. mCacheEntry->SetDataSize(0);
  460. return NS_OK;
  461. }
  462. NS_IMETHODIMP imgRequest::OnStartRequest(imgIRequest *aRequest)
  463. {
  464. NS_NOTREACHED("imgRequest(imgIDecoderObserver)::OnStartRequest");
  465. return NS_OK;
  466. }
  467. /* void onStartContainer (in imgIRequest request, in imgIContainer image); */
  468. NS_IMETHODIMP imgRequest::OnStartContainer(imgIRequest *request, imgIContainer *image)
  469. {
  470. LOG_SCOPE(gImgLog, "imgRequest::OnStartContainer");
  471. NS_ASSERTION(image, "imgRequest::OnStartContainer called with a null image!");
  472. if (!image) return NS_ERROR_UNEXPECTED;
  473. NS_ABORT_IF_FALSE(mImage,
  474. "OnStartContainer callback before we've created our image");
  475. NS_ABORT_IF_FALSE(image == mImage,
  476. "OnStartContainer callback from an image we don't own");
  477. mImage->GetStatusTracker().RecordStartContainer(image);
  478. nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers);
  479. while (iter.HasMore()) {
  480. mImage->GetStatusTracker().SendStartContainer(iter.GetNext(), image);
  481. }
  482. return NS_OK;
  483. }
  484. /* void onStartFrame (in imgIRequest request, in unsigned long frame); */
  485. NS_IMETHODIMP imgRequest::OnStartFrame(imgIRequest *request,
  486. PRUint32 frame)
  487. {
  488. LOG_SCOPE(gImgLog, "imgRequest::OnStartFrame");
  489. NS_ABORT_IF_FALSE(mImage,
  490. "OnStartFrame callback before we've created our image");
  491. mImage->GetStatusTracker().RecordStartFrame(frame);
  492. nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers);
  493. while (iter.HasMore()) {
  494. mImage->GetStatusTracker().SendStartFrame(iter.GetNext(), frame);
  495. }
  496. return NS_OK;
  497. }
  498. /* [noscript] void onDataAvailable (in imgIRequest request, in boolean aCurrentFrame, [const] in nsIntRect rect); */
  499. NS_IMETHODIMP imgRequest::OnDataAvailable(imgIRequest *request,
  500. bool aCurrentFrame,
  501. const nsIntRect * rect)
  502. {
  503. LOG_SCOPE(gImgLog, "imgRequest::OnDataAvailable");
  504. NS_ABORT_IF_FALSE(mImage,
  505. "OnDataAvailable callback before we've created our image");
  506. mImage->GetStatusTracker().RecordDataAvailable(aCurrentFrame, rect);
  507. nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers);
  508. while (iter.HasMore()) {
  509. mImage->GetStatusTracker().SendDataAvailable(iter.GetNext(), aCurrentFrame, rect);
  510. }
  511. return NS_OK;
  512. }
  513. /* void onStopFrame (in imgIRequest request, in unsigned long frame); */
  514. NS_IMETHODIMP imgRequest::OnStopFrame(imgIRequest *request,
  515. PRUint32 frame)
  516. {
  517. LOG_SCOPE(gImgLog, "imgRequest::OnStopFrame");
  518. NS_ABORT_IF_FALSE(mImage,
  519. "OnStopFrame callback before we've created our image");
  520. mImage->GetStatusTracker().RecordStopFrame(frame);
  521. nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers);
  522. while (iter.HasMore()) {
  523. mImage->GetStatusTracker().SendStopFrame(iter.GetNext(), frame);
  524. }
  525. return NS_OK;
  526. }
  527. /* void onStopContainer (in imgIRequest request, in imgIContainer image); */
  528. NS_IMETHODIMP imgRequest::OnStopContainer(imgIRequest *request,
  529. imgIContainer *image)
  530. {
  531. LOG_SCOPE(gImgLog, "imgRequest::OnStopContainer");
  532. NS_ABORT_IF_FALSE(mImage,
  533. "OnDataContainer callback before we've created our image");
  534. mImage->GetStatusTracker().RecordStopContainer(image);
  535. nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers);
  536. while (iter.HasMore()) {
  537. mImage->GetStatusTracker().SendStopContainer(iter.GetNext(), image);
  538. }
  539. return NS_OK;
  540. }
  541. /* void onStopDecode (in imgIRequest request, in nsresult status, in wstring statusArg); */
  542. NS_IMETHODIMP imgRequest::OnStopDecode(imgIRequest *aRequest,
  543. nsresult aStatus,
  544. const PRUnichar *aStatusArg)
  545. {
  546. LOG_SCOPE(gImgLog, "imgRequest::OnStopDecode");
  547. NS_ABORT_IF_FALSE(mImage,
  548. "OnDataDecode callback before we've created our image");
  549. // We finished the decode, and thus have the decoded frames. Update the cache
  550. // entry size to take this into account.
  551. UpdateCacheEntrySize();
  552. mImage->GetStatusTracker().RecordStopDecode(aStatus, aStatusArg);
  553. nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers);
  554. while (iter.HasMore()) {
  555. mImage->GetStatusTracker().SendStopDecode(iter.GetNext(), aStatus,
  556. aStatusArg);
  557. }
  558. // RasterImage and everything below it is completely correct and
  559. // bulletproof about its handling of decoder notifications.
  560. // Unfortunately, here and above we have to make some gross and
  561. // inappropriate use of things to get things to work without
  562. // completely overhauling the decoder observer interface (this will,
  563. // thankfully, happen in bug 505385). From imgRequest and above (for
  564. // the time being), OnStopDecode is just a companion to OnStopRequest
  565. // that signals success or failure of the _load_ (not the _decode_).
  566. // Within imgStatusTracker, we ignore OnStopDecode notifications from the
  567. // decoder and RasterImage and generate our own every time we send
  568. // OnStopRequest. From within SendStopDecode, we actually send
  569. // OnStopContainer. For more information, see bug 435296.
  570. return NS_OK;
  571. }
  572. NS_IMETHODIMP imgRequest::OnStopRequest(imgIRequest *aRequest,
  573. bool aLastPart)
  574. {
  575. NS_NOTREACHED("imgRequest(imgIDecoderObserver)::OnStopRequest");
  576. return NS_OK;
  577. }
  578. /* void onDiscard (in imgIRequest request); */
  579. NS_IMETHODIMP imgRequest::OnDiscard(imgIRequest *aRequest)
  580. {
  581. NS_ABORT_IF_FALSE(mImage,
  582. "OnDiscard callback before we've created our image");
  583. mImage->GetStatusTracker().RecordDiscard();
  584. // Update the cache entry size, since we just got rid of frame data
  585. UpdateCacheEntrySize();
  586. nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers);
  587. while (iter.HasMore()) {
  588. mImage->GetStatusTracker().SendDiscard(iter.GetNext());
  589. }
  590. return NS_OK;
  591. }
  592. NS_IMETHODIMP imgRequest::OnImageIsAnimated(imgIRequest *aRequest)
  593. {
  594. NS_ABORT_IF_FALSE(mImage,
  595. "OnImageIsAnimated callback before we've created our image");
  596. mImage->GetStatusTracker().RecordImageIsAnimated();
  597. nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers);
  598. while (iter.HasMore()) {
  599. mImage->GetStatusTracker().SendImageIsAnimated(iter.GetNext());
  600. }
  601. return NS_OK;
  602. }
  603. /** nsIRequestObserver methods **/
  604. /* void onStartRequest (in nsIRequest request, in nsISupports ctxt); */
  605. NS_IMETHODIMP imgRequest::OnStartRequest(nsIRequest *aRequest, nsISupports *ctxt)
  606. {
  607. LOG_SCOPE(gImgLog, "imgRequest::OnStartRequest");
  608. // Figure out if we're multipart
  609. nsCOMPtr<nsIMultiPartChannel> mpchan(do_QueryInterface(aRequest));
  610. if (mpchan)
  611. mIsMultiPartChannel = true;
  612. // If we're not multipart, we shouldn't have an image yet
  613. NS_ABORT_IF_FALSE(mIsMultiPartChannel || !mImage,
  614. "Already have an image for non-multipart request");
  615. // If we're multipart, and our image is initialized, fix things up for another round
  616. if (mIsMultiPartChannel && mImage) {
  617. if (mImage->GetType() == imgIContainer::TYPE_RASTER) {
  618. // Inform the RasterImage that we have new source data
  619. static_cast<RasterImage*>(mImage.get())->NewSourceData();
  620. } else { // imageType == imgIContainer::TYPE_VECTOR
  621. nsCOMPtr<nsIStreamListener> imageAsStream = do_QueryInterface(mImage);
  622. NS_ABORT_IF_FALSE(imageAsStream,
  623. "SVG-typed Image failed QI to nsIStreamListener");
  624. imageAsStream->OnStartRequest(aRequest, ctxt);
  625. }
  626. }
  627. /*
  628. * If mRequest is null here, then we need to set it so that we'll be able to
  629. * cancel it if our Cancel() method is called. Note that this can only
  630. * happen for multipart channels. We could simply not null out mRequest for
  631. * non-last parts, if GetIsLastPart() were reliable, but it's not. See
  632. * https://bugzilla.mozilla.org/show_bug.cgi?id=339610
  633. */
  634. if (!mRequest) {
  635. NS_ASSERTION(mpchan,
  636. "We should have an mRequest here unless we're multipart");
  637. nsCOMPtr<nsIChannel> chan;
  638. mpchan->GetBaseChannel(getter_AddRefs(chan));
  639. mRequest = chan;
  640. }
  641. imgStatusTracker& statusTracker = GetStatusTracker();
  642. statusTracker.RecordStartRequest();
  643. nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
  644. if (channel)
  645. channel->GetSecurityInfo(getter_AddRefs(mSecurityInfo));
  646. nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers);
  647. while (iter.HasMore()) {
  648. statusTracker.SendStartRequest(iter.GetNext());
  649. }
  650. /* Get our principal */
  651. nsCOMPtr<nsIChannel> chan(do_QueryInterface(aRequest));
  652. if (chan) {
  653. nsCOMPtr<nsIScriptSecurityManager> secMan =
  654. do_GetService("@mozilla.org/scriptsecuritymanager;1");
  655. if (secMan) {
  656. nsresult rv = secMan->GetChannelPrincipal(chan,
  657. getter_AddRefs(mPrincipal));
  658. if (NS_FAILED(rv)) {
  659. return rv;
  660. }
  661. // Tell all of our proxies that we have a principal.
  662. nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers);
  663. while (iter.HasMore()) {
  664. iter.GetNext()->SetPrincipal(mPrincipal);
  665. }
  666. }
  667. }
  668. SetCacheValidation(mCacheEntry, aRequest);
  669. // Shouldn't we be dead already if this gets hit? Probably multipart/x-mixed-replace...
  670. if (mObservers.IsEmpty()) {
  671. this->Cancel(NS_IMAGELIB_ERROR_FAILURE);
  672. }
  673. return NS_OK;
  674. }
  675. /* void onStopRequest (in nsIRequest request, in nsISupports ctxt, in nsresult status); */
  676. NS_IMETHODIMP imgRequest::OnStopRequest(nsIRequest *aRequest, nsISupports *ctxt, nsresult status)
  677. {
  678. LOG_FUNC(gImgLog, "imgRequest::OnStopRequest");
  679. bool lastPart = true;
  680. nsCOMPtr<nsIMultiPartChannel> mpchan(do_QueryInterface(aRequest));
  681. if (mpchan)
  682. mpchan->GetIsLastPart(&lastPart);
  683. // XXXldb What if this is a non-last part of a multipart request?
  684. // xxx before we release our reference to mRequest, lets
  685. // save the last status that we saw so that the
  686. // imgRequestProxy will have access to it.
  687. if (mRequest) {
  688. mRequest = nsnull; // we no longer need the request
  689. }
  690. // stop holding a ref to the channel, since we don't need it anymore
  691. if (mChannel) {
  692. mChannel->SetNotificationCallbacks(mPrevChannelSink);
  693. mPrevChannelSink = nsnull;
  694. mChannel = nsnull;
  695. }
  696. // Tell the image that it has all of the source data. Note that this can
  697. // trigger a failure, since the image might be waiting for more non-optional
  698. // data and this is the point where we break the news that it's not coming.
  699. if (mImage) {
  700. nsresult rv;
  701. if (mImage->GetType() == imgIContainer::TYPE_RASTER) {
  702. // Notify the image
  703. rv = static_cast<RasterImage*>(mImage.get())->SourceDataComplete();
  704. } else { // imageType == imgIContainer::TYPE_VECTOR
  705. nsCOMPtr<nsIStreamListener> imageAsStream = do_QueryInterface(mImage);
  706. NS_ABORT_IF_FALSE(imageAsStream,
  707. "SVG-typed Image failed QI to nsIStreamListener");
  708. rv = imageAsStream->OnStopRequest(aRequest, ctxt, status);
  709. }
  710. // If we got an error in the SourceDataComplete() / OnStopRequest() call,
  711. // we don't want to proceed as if nothing bad happened. However, we also
  712. // want to give precedence to failure status codes from necko, since
  713. // presumably they're more meaningful.
  714. if (NS_FAILED(rv) && NS_SUCCEEDED(status))
  715. status = rv;
  716. }
  717. imgStatusTracker& statusTracker = GetStatusTracker();
  718. statusTracker.RecordStopRequest(lastPart, status);
  719. // If the request went through, update the cache entry size. Otherwise,
  720. // cancel the request, which removes us from the cache.
  721. if (mImage && NS_SUCCEEDED(status)) {
  722. // We update the cache entry size here because this is where we finish
  723. // loading compressed source data, which is part of our size calculus.
  724. UpdateCacheEntrySize();
  725. }
  726. else {
  727. // stops animations, removes from cache
  728. this->Cancel(status);
  729. }
  730. /* notify the kids */
  731. nsTObserverArray<imgRequestProxy*>::ForwardIterator srIter(mObservers);
  732. while (srIter.HasMore()) {
  733. statusTracker.SendStopRequest(srIter.GetNext(), lastPart, status);
  734. }
  735. mTimedChannel = nsnull;
  736. return NS_OK;
  737. }
  738. /* prototype for these defined below */
  739. static NS_METHOD sniff_mimetype_callback(nsIInputStream* in, void* closure, const char* fromRawSegment,
  740. PRUint32 toOffset, PRUint32 count, PRUint32 *writeCount);
  741. /** nsIStreamListener methods **/
  742. /* void onDataAvailable (in nsIRequest request, in nsISupports ctxt, in nsIInputStream inStr, in unsigned long sourceOffset, in unsigned long count); */
  743. NS_IMETHODIMP imgRequest::OnDataAvailable(nsIRequest *aRequest, nsISupports *ctxt, nsIInputStream *inStr, PRUint32 sourceOffset, PRUint32 count)
  744. {
  745. LOG_SCOPE_WITH_PARAM(gImgLog, "imgRequest::OnDataAvailable", "count", count);
  746. NS_ASSERTION(aRequest, "imgRequest::OnDataAvailable -- no request!");
  747. nsresult rv;
  748. PRUint16 imageType;
  749. if (mGotData) {
  750. imageType = mImage->GetType();
  751. } else {
  752. LOG_SCOPE(gImgLog, "imgRequest::OnDataAvailable |First time through... finding mimetype|");
  753. mGotData = true;
  754. /* look at the first few bytes and see if we can tell what the data is from that
  755. * since servers tend to lie. :(
  756. */
  757. PRUint32 out;
  758. inStr->ReadSegments(sniff_mimetype_callback, this, count, &out);
  759. #ifdef NS_DEBUG
  760. /* NS_WARNING if the content type from the channel isn't the same if the sniffing */
  761. #endif
  762. nsCOMPtr<nsIChannel> chan(do_QueryInterface(aRequest));
  763. if (mContentType.IsEmpty()) {
  764. LOG_SCOPE(gImgLog, "imgRequest::OnDataAvailable |sniffing of mimetype failed|");
  765. rv = NS_ERROR_FAILURE;
  766. if (chan) {
  767. rv = chan->GetContentType(mContentType);
  768. }
  769. if (NS_FAILED(rv)) {
  770. PR_LOG(gImgLog, PR_LOG_ERROR,
  771. ("[this=%p] imgRequest::OnDataAvailable -- Content type unavailable from the channel\n",
  772. this));
  773. this->Cancel(NS_IMAGELIB_ERROR_FAILURE);
  774. return NS_BINDING_ABORTED;
  775. }
  776. LOG_MSG(gImgLog, "imgRequest::OnDataAvailable", "Got content type from the channel");
  777. }
  778. /* now we have mimetype, so we can infer the image type that we want */
  779. if (mContentType.EqualsLiteral(SVG_MIMETYPE)) {
  780. mImage = new VectorImage(mStatusTracker.forget());
  781. } else {
  782. mImage = new RasterImage(mStatusTracker.forget());
  783. }
  784. mImage->SetInnerWindowID(mInnerWindowId);
  785. imageType = mImage->GetType();
  786. // Notify any imgRequestProxys that are observing us that we have an Image.
  787. nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers);
  788. while (iter.HasMore()) {
  789. iter.GetNext()->SetImage(mImage);
  790. }
  791. /* set our mimetype as a property */
  792. nsCOMPtr<nsISupportsCString> contentType(do_CreateInstance("@mozilla.org/supports-cstring;1"));
  793. if (contentType) {
  794. contentType->SetData(mContentType);
  795. mProperties->Set("type", contentType);
  796. }
  797. /* set our content disposition as a property */
  798. nsCAutoString disposition;
  799. if (chan) {
  800. chan->GetContentDispositionHeader(disposition);
  801. }
  802. if (!disposition.IsEmpty()) {
  803. nsCOMPtr<nsISupportsCString> contentDisposition(do_CreateInstance("@mozilla.org/supports-cstring;1"));
  804. if (contentDisposition) {
  805. contentDisposition->SetData(disposition);
  806. mProperties->Set("content-disposition", contentDisposition);
  807. }
  808. }
  809. LOG_MSG_WITH_PARAM(gImgLog, "imgRequest::OnDataAvailable", "content type", mContentType.get());
  810. //
  811. // Figure out our Image initialization flags
  812. //
  813. // We default to the static globals
  814. bool isDiscardable = gDiscardable;
  815. bool doDecodeOnDraw = gDecodeOnDraw;
  816. // We want UI to be as snappy as possible and not to flicker. Disable discarding
  817. // and decode-on-draw for chrome URLS
  818. bool isChrome = false;
  819. rv = mURI->SchemeIs("chrome", &isChrome);
  820. if (NS_SUCCEEDED(rv) && isChrome)
  821. isDiscardable = doDecodeOnDraw = false;
  822. // We don't want resources like the "loading" icon to be discardable or
  823. // decode-on-draw either.
  824. bool isResource = false;
  825. rv = mURI->SchemeIs("resource", &isResource);
  826. if (NS_SUCCEEDED(rv) && isResource)
  827. isDiscardable = doDecodeOnDraw = false;
  828. // For multipart/x-mixed-replace, we basically want a direct channel to the
  829. // decoder. Disable both for this case as well.
  830. if (mIsMultiPartChannel)
  831. isDiscardable = doDecodeOnDraw = false;
  832. // We have all the information we need
  833. PRUint32 imageFlags = Image::INIT_FLAG_NONE;
  834. if (isDiscardable)
  835. imageFlags |= Image::INIT_FLAG_DISCARDABLE;
  836. if (doDecodeOnDraw)
  837. imageFlags |= Image::INIT_FLAG_DECODE_ON_DRAW;
  838. if (mIsMultiPartChannel)
  839. imageFlags |= Image::INIT_FLAG_MULTIPART;
  840. // Get our URI string
  841. nsCAutoString uriString;
  842. rv = mURI->GetSpec(uriString);
  843. if (NS_FAILED(rv))
  844. uriString.Assign("<unknown image URI>");
  845. // Initialize the image that we created above. For RasterImages, this
  846. // instantiates a decoder behind the scenes, so if we don't have a decoder
  847. // for this mimetype we'll find out about it here.
  848. rv = mImage->Init(this, mContentType.get(), uriString.get(), imageFlags);
  849. if (NS_FAILED(rv)) { // Probably bad mimetype
  850. this->Cancel(rv);
  851. return NS_BINDING_ABORTED;
  852. }
  853. if (imageType == imgIContainer::TYPE_RASTER) {
  854. /* Use content-length as a size hint for http channels. */
  855. nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aRequest));
  856. if (httpChannel) {
  857. nsCAutoString contentLength;
  858. rv = httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("content-length"),
  859. contentLength);
  860. if (NS_SUCCEEDED(rv)) {
  861. PRInt32 len = contentLength.ToInteger(&rv);
  862. // Pass anything usable on so that the RasterImage can preallocate
  863. // its source buffer
  864. if (len > 0) {
  865. PRUint32 sizeHint = (PRUint32) len;
  866. sizeHint = NS_MIN<PRUint32>(sizeHint, 20000000); /* Bound by something reasonable */
  867. RasterImage* rasterImage = static_cast<RasterImage*>(mImage.get());
  868. rv = rasterImage->SetSourceSizeHint(sizeHint);
  869. if (NS_FAILED(rv)) {
  870. // Flush memory, try to get some back, and try again
  871. rv = nsMemory::HeapMinimize(true);
  872. rv |= rasterImage->SetSourceSizeHint(sizeHint);
  873. // If we've still failed at this point, things are going downhill
  874. if (NS_FAILED(rv)) {
  875. NS_WARNING("About to hit OOM in imagelib!");
  876. }
  877. }
  878. }
  879. }
  880. }
  881. }
  882. if (imageType == imgIContainer::TYPE_RASTER) {
  883. // If we were waiting on the image to do something, now's our chance.
  884. if (mDecodeRequested) {
  885. mImage->RequestDecode();
  886. }
  887. } else { // imageType == imgIContainer::TYPE_VECTOR
  888. nsCOMPtr<nsIStreamListener> imageAsStream = do_QueryInterface(mImage);
  889. NS_ABORT_IF_FALSE(imageAsStream,
  890. "SVG-typed Image failed QI to nsIStreamListener");
  891. imageAsStream->OnStartRequest(aRequest, nsnull);
  892. }
  893. }
  894. if (imageType == imgIContainer::TYPE_RASTER) {
  895. // WriteToRasterImage always consumes everything it gets
  896. // if it doesn't run out of memory
  897. PRUint32 bytesRead;
  898. rv = inStr->ReadSegments(RasterImage::WriteToRasterImage,
  899. static_cast<void*>(mImage),
  900. count, &bytesRead);
  901. NS_ABORT_IF_FALSE(bytesRead == count || mImage->HasError(),
  902. "WriteToRasterImage should consume everything or the image must be in error!");
  903. } else { // imageType == imgIContainer::TYPE_VECTOR
  904. nsCOMPtr<nsIStreamListener> imageAsStream = do_QueryInterface(mImage);
  905. rv = imageAsStream->OnDataAvailable(aRequest, ctxt, inStr,
  906. sourceOffset, count);
  907. }
  908. if (NS_FAILED(rv)) {
  909. PR_LOG(gImgLog, PR_LOG_WARNING,
  910. ("[this=%p] imgRequest::OnDataAvailable -- "
  911. "copy to RasterImage failed\n", this));
  912. this->Cancel(NS_IMAGELIB_ERROR_FAILURE);
  913. return NS_BINDING_ABORTED;
  914. }
  915. return NS_OK;
  916. }
  917. static NS_METHOD sniff_mimetype_callback(nsIInputStream* in,
  918. void* closure,
  919. const char* fromRawSegment,
  920. PRUint32 toOffset,
  921. PRUint32 count,
  922. PRUint32 *writeCount)
  923. {
  924. imgRequest *request = static_cast<imgRequest*>(closure);
  925. NS_ASSERTION(request, "request is null!");
  926. if (count > 0)
  927. request->SniffMimeType(fromRawSegment, count);
  928. *writeCount = 0;
  929. return NS_ERROR_FAILURE;
  930. }
  931. void
  932. imgRequest::SniffMimeType(const char *buf, PRUint32 len)
  933. {
  934. imgLoader::GetMimeTypeFromContent(buf, len, mContentType);
  935. // The vast majority of the time, imgLoader will find a gif/jpeg/png image
  936. // and fill mContentType with the sniffed MIME type.
  937. if (!mContentType.IsEmpty())
  938. return;
  939. // When our sniffing fails, we want to query registered image decoders
  940. // to see if they can identify the image. If we always trusted the server
  941. // to send the right MIME, images sent as text/plain would not be rendered.
  942. const nsCOMArray<nsIContentSniffer>& sniffers = mImageSniffers.GetEntries();
  943. PRUint32 length = sniffers.Count();
  944. for (PRUint32 i = 0; i < length; ++i) {
  945. nsresult rv =
  946. sniffers[i]->GetMIMETypeFromContent(nsnull, (const PRUint8 *) buf, len, mContentType);
  947. if (NS_SUCCEEDED(rv) && !mContentType.IsEmpty()) {
  948. return;
  949. }
  950. }
  951. }
  952. /** nsIInterfaceRequestor methods **/
  953. NS_IMETHODIMP
  954. imgRequest::GetInterface(const nsIID & aIID, void **aResult)
  955. {
  956. if (!mPrevChannelSink || aIID.Equals(NS_GET_IID(nsIChannelEventSink)))
  957. return QueryInterface(aIID, aResult);
  958. NS_ASSERTION(mPrevChannelSink != this,
  959. "Infinite recursion - don't keep track of channel sinks that are us!");
  960. return mPrevChannelSink->GetInterface(aIID, aResult);
  961. }
  962. /** nsIChannelEventSink methods **/
  963. NS_IMETHODIMP
  964. imgRequest::AsyncOnChannelRedirect(nsIChannel *oldChannel,
  965. nsIChannel *newChannel, PRUint32 flags,
  966. nsIAsyncVerifyRedirectCallback *callback)
  967. {
  968. NS_ASSERTION(mRequest && mChannel, "Got a channel redirect after we nulled out mRequest!");
  969. NS_ASSERTION(mChannel == oldChannel, "Got a channel redirect for an unknown channel!");
  970. NS_ASSERTION(newChannel, "Got a redirect to a NULL channel!");
  971. SetCacheValidation(mCacheEntry, oldChannel);
  972. // Prepare for callback
  973. mRedirectCallback = callback;
  974. mNewRedirectChannel = newChannel;
  975. nsCOMPtr<nsIChannelEventSink> sink(do_GetInterface(mPrevChannelSink));
  976. if (sink) {
  977. nsresult rv = sink->AsyncOnChannelRedirect(oldChannel, newChannel, flags,
  978. this);
  979. if (NS_FAILED(rv)) {
  980. mRedirectCallback = nsnull;
  981. mNewRedirectChannel = nsnull;
  982. }
  983. return rv;
  984. }
  985. (void) OnRedirectVerifyCallback(NS_OK);
  986. return NS_OK;
  987. }
  988. NS_IMETHODIMP
  989. imgRequest::OnRedirectVerifyCallback(nsresult result)
  990. {
  991. NS_ASSERTION(mRedirectCallback, "mRedirectCallback not set in callback");
  992. NS_ASSERTION(mNewRedirectChannel, "mNewRedirectChannel not set in callback");
  993. if (NS_FAILED(result)) {
  994. mRedirectCallback->OnRedirectVerifyCallback(result);
  995. mRedirectCallback = nsnull;
  996. mNewRedirectChannel = nsnull;
  997. return NS_OK;
  998. }
  999. mChannel = mNewRedirectChannel;
  1000. mTimedChannel = do_QueryInterface(mChannel);
  1001. mNewRedirectChannel = nsnull;
  1002. #if defined(PR_LOGGING)
  1003. nsCAutoString oldspec;
  1004. if (mCurrentURI)
  1005. mCurrentURI->GetSpec(oldspec);
  1006. LOG_MSG_WITH_PARAM(gImgLog, "imgRequest::OnChannelRedirect", "old", oldspec.get());
  1007. #endif
  1008. // make sure we have a protocol that returns data rather than opens
  1009. // an external application, e.g. mailto:
  1010. mChannel->GetURI(getter_AddRefs(mCurrentURI));
  1011. bool doesNotReturnData = false;
  1012. nsresult rv =
  1013. NS_URIChainHasFlags(mCurrentURI, nsIProtocolHandler::URI_DOES_NOT_RETURN_DATA,
  1014. &doesNotReturnData);
  1015. if (NS_SUCCEEDED(rv) && doesNotReturnData)
  1016. rv = NS_ERROR_ABORT;
  1017. if (NS_FAILED(rv)) {
  1018. mRedirectCallback->OnRedirectVerifyCallback(rv);
  1019. mRedirectCallback = nsnull;
  1020. return NS_OK;
  1021. }
  1022. mRedirectCallback->OnRedirectVerifyCallback(NS_OK);
  1023. mRedirectCallback = nsnull;
  1024. return NS_OK;
  1025. }