Back to index

lightning-sunbird  0.9+nobinonly
nsImageLoadingContent.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 // vim: ft=cpp tw=78 sw=2 et ts=2
00003 /* ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License
00007  * Version 1.1 (the "License"); you may not use this file except in
00008  * compliance with the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is mozilla.org code.
00017  *
00018  * The Initial Developer of the Original Code is 
00019  * Boris Zbarsky <bzbarsky@mit.edu>.
00020  * Portions created by the Initial Developer are Copyright (C) 2003
00021  * the Initial Developer. All Rights Reserved.
00022  *
00023  * Contributor(s):
00024  *
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either the GNU General Public License Version 2 or later (the "GPL"), or
00028  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the NPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the NPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK ***** */
00039 
00040 #include "nsImageLoadingContent.h"
00041 #include "nsAutoPtr.h"
00042 #include "nsContentErrors.h"
00043 #include "nsIContent.h"
00044 #include "nsIDocument.h"
00045 #include "nsINodeInfo.h"
00046 #include "nsIScriptGlobalObject.h"
00047 #include "nsIDOMWindow.h"
00048 #include "nsServiceManagerUtils.h"
00049 #include "nsContentPolicyUtils.h"
00050 #include "nsIURI.h"
00051 #include "nsILoadGroup.h"
00052 #include "imgIContainer.h"
00053 #include "gfxIImageFrame.h"
00054 #include "imgILoader.h"
00055 #include "plevent.h"
00056 #include "nsIEventQueueService.h"
00057 #include "nsIEventQueue.h"
00058 #include "nsNetUtil.h"
00059 
00060 #include "nsPresContext.h"
00061 #include "nsIPresShell.h"
00062 #include "nsGUIEvent.h"
00063 #include "nsDummyLayoutRequest.h"
00064 
00065 #include "nsIChannel.h"
00066 #include "nsIStreamListener.h"
00067 
00068 #include "nsLayoutAtoms.h"
00069 #include "nsIFrame.h"
00070 #include "nsIDOMNode.h"
00071 
00072 #include "nsContentUtils.h"
00073 #include "nsIContentPolicy.h"
00074 #include "nsContentPolicyUtils.h"
00075 #include "nsDOMClassInfo.h"
00076 
00077 #ifdef DEBUG_chb
00078 static void PrintReqURL(imgIRequest* req) {
00079   if (!req) {
00080     printf("(null req)\n");
00081     return;
00082   }
00083 
00084   nsCOMPtr<nsIURI> uri;
00085   req->GetURI(getter_AddRefs(uri));
00086   if (!uri) {
00087     printf("(null uri)\n");
00088     return;
00089   }
00090 
00091   nsCAutoString spec;
00092   uri->GetSpec(spec);
00093   printf("spec='%s'\n", spec.get());
00094 }
00095 #endif /* DEBUG_chb */
00096 
00097 
00098 nsImageLoadingContent::nsImageLoadingContent()
00099   : mObserverList(nsnull),
00100     mImageBlockingStatus(nsIContentPolicy::ACCEPT),
00101     mRootRefCount(0),
00102     mLoadingEnabled(PR_TRUE)
00103 {
00104   if (!nsContentUtils::GetImgLoader())
00105     mLoadingEnabled = PR_FALSE;
00106 }
00107 
00108 void
00109 nsImageLoadingContent::DestroyImageLoadingContent()
00110 {
00111   // Cancel our requests so they won't hold stale refs to us
00112   if (mCurrentRequest) {
00113     mCurrentRequest->Cancel(NS_ERROR_FAILURE);
00114     mCurrentRequest = nsnull;
00115   }
00116   if (mPendingRequest) {
00117     mPendingRequest->Cancel(NS_ERROR_FAILURE);
00118     mPendingRequest = nsnull;
00119   }
00120 
00121   // This can actually fire for multipart/x-mixed-replace, since if the
00122   // load is canceled between parts (e.g., by cancelling the load
00123   // group), we won't get any notification.  See bug 321054 comment 31
00124   // and bug 339610.  *If* that multipart/x-mixed-replace image has
00125   // event handlers, we won't even get to this warning; we'll leak
00126   // instead.
00127   NS_WARN_IF_FALSE(mRootRefCount == 0,
00128                    "unbalanced handler preservation refcount");
00129   if (mRootRefCount != 0) {
00130     mRootRefCount = 1;
00131     UnpreserveLoadHandlers();
00132   }
00133 }
00134 
00135 nsImageLoadingContent::~nsImageLoadingContent()
00136 {
00137   NS_ASSERTION(!mCurrentRequest && !mPendingRequest,
00138                "DestroyImageLoadingContent not called");
00139   NS_ASSERTION(!mObserverList.mObserver && !mObserverList.mNext,
00140                "Observers still registered?");
00141 }
00142 
00143 // Macro to call some func on each observer.  This handles observers
00144 // removing themselves.
00145 #define LOOP_OVER_OBSERVERS(func_)                                       \
00146   PR_BEGIN_MACRO                                                         \
00147     for (ImageObserver* observer = &mObserverList, *next; observer;      \
00148          observer = next) {                                              \
00149       next = observer->mNext;                                            \
00150       if (observer->mObserver) {                                         \
00151         observer->mObserver->func_;                                      \
00152       }                                                                  \
00153     }                                                                    \
00154   PR_END_MACRO
00155 
00156 
00157 /*
00158  * imgIContainerObserver impl
00159  */
00160 NS_IMETHODIMP
00161 nsImageLoadingContent::FrameChanged(imgIContainer* aContainer,
00162                                     gfxIImageFrame* aFrame,
00163                                     nsRect* aDirtyRect)
00164 {
00165   LOOP_OVER_OBSERVERS(FrameChanged(aContainer, aFrame, aDirtyRect));
00166   return NS_OK;
00167 }
00168             
00169 /*
00170  * imgIDecoderObserver impl
00171  */
00172 NS_IMETHODIMP
00173 nsImageLoadingContent::OnStartRequest(imgIRequest* aRequest)
00174 {
00175   return NS_OK;
00176 }
00177 
00178 NS_IMETHODIMP
00179 nsImageLoadingContent::OnStartDecode(imgIRequest* aRequest)
00180 {
00181   LOOP_OVER_OBSERVERS(OnStartDecode(aRequest));
00182   return NS_OK;
00183 }
00184 
00185 NS_IMETHODIMP
00186 nsImageLoadingContent::OnStartContainer(imgIRequest* aRequest,
00187                                         imgIContainer* aContainer)
00188 {
00189   LOOP_OVER_OBSERVERS(OnStartContainer(aRequest, aContainer));
00190   return NS_OK;    
00191 }
00192 
00193 NS_IMETHODIMP
00194 nsImageLoadingContent::OnStartFrame(imgIRequest* aRequest,
00195                                     gfxIImageFrame* aFrame)
00196 {
00197   LOOP_OVER_OBSERVERS(OnStartFrame(aRequest, aFrame));
00198   return NS_OK;    
00199 }
00200 
00201 NS_IMETHODIMP
00202 nsImageLoadingContent::OnDataAvailable(imgIRequest* aRequest,
00203                                        gfxIImageFrame* aFrame,
00204                                        const nsRect* aRect)
00205 {
00206   LOOP_OVER_OBSERVERS(OnDataAvailable(aRequest, aFrame, aRect));
00207   return NS_OK;
00208 }
00209 
00210 NS_IMETHODIMP
00211 nsImageLoadingContent::OnStopFrame(imgIRequest* aRequest,
00212                                    gfxIImageFrame* aFrame)
00213 {
00214   LOOP_OVER_OBSERVERS(OnStopFrame(aRequest, aFrame));
00215   return NS_OK;
00216 }
00217 
00218 NS_IMETHODIMP
00219 nsImageLoadingContent::OnStopContainer(imgIRequest* aRequest,
00220                                        imgIContainer* aContainer)
00221 {
00222   LOOP_OVER_OBSERVERS(OnStopContainer(aRequest, aContainer));
00223   return NS_OK;
00224 }
00225 
00226 NS_IMETHODIMP
00227 nsImageLoadingContent::OnStopDecode(imgIRequest* aRequest,
00228                                     nsresult aStatus,
00229                                     const PRUnichar* aStatusArg)
00230 {
00231   NS_PRECONDITION(aRequest == mCurrentRequest || aRequest == mPendingRequest,
00232                   "Unknown request");
00233   LOOP_OVER_OBSERVERS(OnStopDecode(aRequest, aStatus, aStatusArg));
00234 
00235   if (aRequest == mPendingRequest) {
00236     mCurrentRequest->Cancel(NS_ERROR_IMAGE_SRC_CHANGED);
00237     mPendingRequest.swap(mCurrentRequest);
00238     mPendingRequest = nsnull;
00239   }
00240 
00241   // XXXldb What's the difference between when OnStopDecode and OnStopRequest
00242   // fire?  Should we do this work there instead?  Should they just be the
00243   // same?
00244 
00245   if (NS_SUCCEEDED(aStatus)) {
00246     FireEvent(NS_LITERAL_STRING("load"));
00247   } else {
00248     FireEvent(NS_LITERAL_STRING("error"));
00249   }
00250 
00251   return NS_OK;
00252 }
00253 
00254 NS_IMETHODIMP
00255 nsImageLoadingContent::OnStopRequest(imgIRequest* aRequest, PRBool aLastPart)
00256 {
00257   if (aLastPart)
00258     UnpreserveLoadHandlers();
00259 
00260   return NS_OK;
00261 }
00262 
00263 /*
00264  * nsIImageLoadingContent impl
00265  */
00266 
00267 NS_IMETHODIMP
00268 nsImageLoadingContent::GetLoadingEnabled(PRBool *aLoadingEnabled)
00269 {
00270   *aLoadingEnabled = mLoadingEnabled;
00271   return NS_OK;
00272 }
00273 
00274 NS_IMETHODIMP
00275 nsImageLoadingContent::SetLoadingEnabled(PRBool aLoadingEnabled)
00276 {
00277   if (nsContentUtils::GetImgLoader())
00278     mLoadingEnabled = aLoadingEnabled;
00279   return NS_OK;
00280 }
00281 
00282 NS_IMETHODIMP
00283 nsImageLoadingContent::GetImageBlockingStatus(PRInt16* aStatus)
00284 {
00285   NS_PRECONDITION(aStatus, "Null out param");
00286   *aStatus = mImageBlockingStatus;
00287   return NS_OK;
00288 }
00289 
00290 NS_IMETHODIMP
00291 nsImageLoadingContent::AddObserver(imgIDecoderObserver* aObserver)
00292 {
00293   NS_ENSURE_ARG_POINTER(aObserver);
00294 
00295   if (!mObserverList.mObserver) {
00296     mObserverList.mObserver = aObserver;
00297     // Don't touch the linking of the list!
00298     return NS_OK;
00299   }
00300 
00301   // otherwise we have to create a new entry
00302 
00303   ImageObserver* observer = &mObserverList;
00304   while (observer->mNext) {
00305     observer = observer->mNext;
00306   }
00307 
00308   observer->mNext = new ImageObserver(aObserver);
00309   if (! observer->mNext) {
00310     return NS_ERROR_OUT_OF_MEMORY;
00311   }
00312 
00313   return NS_OK;
00314 }
00315 
00316 NS_IMETHODIMP
00317 nsImageLoadingContent::RemoveObserver(imgIDecoderObserver* aObserver)
00318 {
00319   NS_ENSURE_ARG_POINTER(aObserver);
00320 
00321   if (mObserverList.mObserver == aObserver) {
00322     mObserverList.mObserver = nsnull;
00323     // Don't touch the linking of the list!
00324     return NS_OK;
00325   }
00326 
00327   // otherwise have to find it and splice it out
00328   ImageObserver* observer = &mObserverList;
00329   while (observer->mNext && observer->mNext->mObserver != aObserver) {
00330     observer = observer->mNext;
00331   }
00332 
00333   // At this point, we are pointing to the list element whose mNext is
00334   // the right observer (assuming of course that mNext is not null)
00335   if (observer->mNext) {
00336     // splice it out
00337     ImageObserver* oldObserver = observer->mNext;
00338     observer->mNext = oldObserver->mNext;
00339     oldObserver->mNext = nsnull;  // so we don't destroy them all
00340     delete oldObserver;
00341   }
00342 #ifdef DEBUG
00343   else {
00344     NS_WARNING("Asked to remove non-existent observer");
00345   }
00346 #endif
00347   return NS_OK;
00348 }
00349 
00350 NS_IMETHODIMP
00351 nsImageLoadingContent::GetRequest(PRInt32 aRequestType,
00352                                   imgIRequest** aRequest)
00353 {
00354   switch(aRequestType) {
00355   case CURRENT_REQUEST:
00356     *aRequest = mCurrentRequest;
00357     break;
00358   case PENDING_REQUEST:
00359     *aRequest = mPendingRequest;
00360     break;
00361   default:
00362     NS_ERROR("Unknown request type");
00363     *aRequest = nsnull;
00364     return NS_ERROR_UNEXPECTED;
00365   }
00366   
00367   NS_IF_ADDREF(*aRequest);
00368   return NS_OK;
00369 }
00370 
00371 
00372 NS_IMETHODIMP
00373 nsImageLoadingContent::GetRequestType(imgIRequest* aRequest,
00374                                       PRInt32* aRequestType)
00375 {
00376   NS_PRECONDITION(aRequestType, "Null out param");
00377   
00378   if (aRequest == mCurrentRequest) {
00379     *aRequestType = CURRENT_REQUEST;
00380     return NS_OK;
00381   }
00382 
00383   if (aRequest == mPendingRequest) {
00384     *aRequestType = PENDING_REQUEST;
00385     return NS_OK;
00386   }
00387 
00388   *aRequestType = UNKNOWN_REQUEST;
00389   NS_ERROR("Unknown request");
00390   return NS_ERROR_UNEXPECTED;
00391 }
00392 
00393 NS_IMETHODIMP
00394 nsImageLoadingContent::GetCurrentURI(nsIURI** aURI)
00395 {
00396   if (mCurrentRequest)
00397     return mCurrentRequest->GetURI(aURI);
00398 
00399   NS_IF_ADDREF(*aURI = mCurrentURI);
00400   return NS_OK;
00401 }
00402 
00403 NS_IMETHODIMP
00404 nsImageLoadingContent::LoadImageWithChannel(nsIChannel* aChannel,
00405                                             nsIStreamListener** aListener)
00406 {
00407   NS_PRECONDITION(aListener, "null out param");
00408   
00409   NS_ENSURE_ARG_POINTER(aChannel);
00410 
00411   if (!nsContentUtils::GetImgLoader())
00412     return NS_ERROR_NULL_POINTER;
00413 
00414   // XXX what should we do with content policies here, if anything?
00415   // Shouldn't that be done before the start of the load?
00416   
00417   nsCOMPtr<nsIDocument> doc = GetOurDocument();
00418   if (!doc) {
00419     // Don't bother
00420     return NS_OK;
00421   }
00422 
00423   PreserveLoadHandlers();
00424 
00425   // Null out our mCurrentURI, in case we have no image requests right now.
00426   mCurrentURI = nsnull;
00427   
00428   CancelImageRequests(NS_ERROR_IMAGE_SRC_CHANGED, PR_FALSE,
00429                       nsIContentPolicy::ACCEPT);
00430 
00431   nsCOMPtr<imgIRequest> & req = mCurrentRequest ? mPendingRequest : mCurrentRequest;
00432 
00433   nsresult rv = nsContentUtils::GetImgLoader()->LoadImageWithChannel(aChannel, this, doc, aListener, getter_AddRefs(req));
00434 
00435   if (NS_FAILED(rv))
00436     UnpreserveLoadHandlers();
00437 
00438   return rv;
00439 }
00440 
00441 // XXX This should be a protected method, not an interface method!!!
00442 NS_IMETHODIMP
00443 nsImageLoadingContent::ImageURIChanged(const nsAString& aNewURI) {
00444   return ImageURIChanged(aNewURI, PR_TRUE);
00445 }
00446 
00447 /*
00448  * Non-interface methods
00449  */
00450 nsresult
00451 nsImageLoadingContent::ImageURIChanged(const nsAString& aNewURI,
00452                                        PRBool aForce)
00453 {
00454   if (!mLoadingEnabled) {
00455     return NS_OK;
00456   }
00457 
00458   // First, get a document (needed for security checks and the like)
00459   nsCOMPtr<nsIDocument> doc = GetOurDocument();
00460   if (!doc) {
00461     // No reason to bother, I think...
00462     return NS_OK;
00463   }
00464 
00465   nsresult rv;   // XXXbz Should failures in this method fire onerror?
00466 
00467   nsCOMPtr<nsIURI> imageURI;
00468   rv = StringToURI(aNewURI, doc, getter_AddRefs(imageURI));
00469   NS_ENSURE_SUCCESS(rv, rv);
00470 
00471   // Skip the URI equality check if our current image was blocked.  If
00472   // that happened, we really do want to try loading again.
00473   if (!aForce && NS_CP_ACCEPTED(mImageBlockingStatus)) {
00474     nsCOMPtr<nsIURI> currentURI;
00475     GetCurrentURI(getter_AddRefs(currentURI));
00476     PRBool equal;
00477     if (currentURI &&
00478         NS_SUCCEEDED(currentURI->Equals(imageURI, &equal)) &&
00479         equal) {
00480       // Nothing to do here.
00481       return NS_OK;
00482     }
00483   }
00484 
00485   // Remember the URL of this request, in case someone asks us for it later
00486   // But this only matters if we are affecting the current request
00487   if (!mCurrentRequest)
00488     mCurrentURI = imageURI;
00489   
00490   // If we'll be loading a new image, we want to cancel our existing
00491   // requests; the question is what reason to pass in.  If everything
00492   // is going smoothly, that reason should be
00493   // NS_ERROR_IMAGE_SRC_CHANGED so that our frame (if any) will know
00494   // not to show the broken image icon.  If the load is blocked by the
00495   // content policy or security manager, we will want to cancel with
00496   // the error code from those.
00497 
00498   PRInt16 newImageStatus;
00499   PRBool loadImage = nsContentUtils::CanLoadImage(imageURI,
00500                                                   NS_STATIC_CAST(nsIImageLoadingContent*, this),
00501                                                   doc,
00502                                                   &newImageStatus);
00503   NS_ASSERTION(loadImage || !NS_CP_ACCEPTED(newImageStatus),
00504                "CanLoadImage lied");
00505 
00506   nsresult cancelResult = loadImage ? NS_ERROR_IMAGE_SRC_CHANGED
00507                                     : NS_ERROR_IMAGE_BLOCKED;
00508 
00509   CancelImageRequests(cancelResult, PR_FALSE, newImageStatus);
00510 
00511   if (!loadImage) {
00512     // Don't actually load anything!  This was blocked by CanLoadImage.
00513     return NS_OK;
00514   }
00515 
00516   PreserveLoadHandlers();
00517 
00518   nsCOMPtr<imgIRequest> & req = mCurrentRequest ? mPendingRequest : mCurrentRequest;
00519 
00520   nsCOMPtr<nsIContent> thisContent = do_QueryInterface(NS_STATIC_CAST(nsIImageLoadingContent*, this), &rv);
00521   NS_ENSURE_TRUE(thisContent, rv);
00522 
00523   // It may be that one of our frames has replaced itself with alt text... This
00524   // would only have happened if our mCurrentRequest had issues, and we would
00525   // have set it to null by now in that case.  Have to save that information
00526   // here, since LoadImage may clobber the value of mCurrentRequest.  On the
00527   // other hand, if we've never had an observer, we know there aren't any frames
00528   // that have changed to alt text on us yet.
00529   PRBool mayNeedReframe = thisContent->MayHaveFrame() && !mCurrentRequest;
00530   
00531   rv = nsContentUtils::LoadImage(imageURI, doc, doc->GetDocumentURI(),
00532                                  this, nsIRequest::LOAD_NORMAL,
00533                                  getter_AddRefs(req));
00534   if (NS_FAILED(rv)) {
00535     UnpreserveLoadHandlers();
00536   }
00537 
00538   // If we now have a current request, we don't need to store the URI, since
00539   // we can get it off the request. Release it.
00540   if (mCurrentRequest) {
00541     mCurrentURI = nsnull;
00542   }
00543 
00544   if (!mayNeedReframe) {
00545     // We're all set
00546     return NS_OK;
00547   }
00548 
00549   // Only continue if we're in a document -- that would mean we're a useful
00550   // chunk of the content model and _may_ have a frame.  This should eliminate
00551   // things like SetAttr calls during the parsing process, as well as things
00552   // like setting src on |new Image()|-type things.
00553   if (!thisContent->IsInDoc()) {
00554     return NS_OK;
00555   }
00556 
00557   // OK, now for each PresShell, see whether we have a frame -- this tends to
00558   // be expensive, which is why it's the last check....  If we have a frame
00559   // and it's not of the right type, reframe it.
00560   PRInt32 numShells = doc->GetNumberOfShells();
00561   for (PRInt32 i = 0; i < numShells; ++i) {
00562     nsIPresShell *shell = doc->GetShellAt(i);
00563     if (shell) {
00564       nsIFrame* frame = nsnull;
00565       shell->GetPrimaryFrameFor(thisContent, &frame);
00566       if (frame) {
00567         // XXXbz I don't like this one bit... we really need a better way of
00568         // doing the CantRenderReplacedElement stuff.. In particular, it needs
00569         // to be easily detectable.  For example, I suspect that this code will
00570         // fail for <object> in the current CantRenderReplacedElement
00571         // implementation...
00572         nsIAtom* frameType = frame->GetType();
00573         if (frameType != nsLayoutAtoms::imageFrame &&
00574             frameType != nsLayoutAtoms::imageControlFrame &&
00575             frameType != nsLayoutAtoms::objectFrame) {
00576           shell->RecreateFramesFor(thisContent);
00577         }
00578       }
00579     }
00580   }
00581 
00582   return NS_OK;
00583 }
00584 
00585 void
00586 nsImageLoadingContent::CancelImageRequests()
00587 {
00588   // Make sure to null out mCurrentURI here, so we no longer look like an image
00589   mCurrentURI = nsnull;
00590   CancelImageRequests(NS_BINDING_ABORTED, PR_TRUE, nsIContentPolicy::ACCEPT);
00591 }
00592 
00593 void
00594 nsImageLoadingContent::CancelImageRequests(nsresult aReason,
00595                                            PRBool   aEvenIfSizeAvailable,
00596                                            PRInt16  aNewImageStatus)
00597 {
00598   // Cancel the pending request, if any
00599   if (mPendingRequest) {
00600     mPendingRequest->Cancel(aReason);
00601     mPendingRequest = nsnull;
00602   }
00603 
00604   // Cancel the current request if it has not progressed enough to
00605   // have a size yet
00606   if (mCurrentRequest) {
00607     PRUint32 loadStatus = imgIRequest::STATUS_ERROR;
00608     mCurrentRequest->GetImageStatus(&loadStatus);
00609 
00610     NS_ASSERTION(NS_CP_ACCEPTED(mImageBlockingStatus),
00611                  "Have current request but blocked image?");
00612     
00613     if (aEvenIfSizeAvailable ||
00614         !(loadStatus & imgIRequest::STATUS_SIZE_AVAILABLE)) {
00615       // The new image is going to become the current request.  Make sure to
00616       // set mImageBlockingStatus _before_ we cancel the request... if we set
00617       // it after, things that are watching the mCurrentRequest will get wrong
00618       // data.
00619       mImageBlockingStatus = aNewImageStatus;
00620       mCurrentRequest->Cancel(aReason);
00621       mCurrentRequest = nsnull;
00622     }
00623   } else {
00624     // No current request so the new image status will become the
00625     // status of the current request
00626     mImageBlockingStatus = aNewImageStatus;
00627   }
00628 
00629   // Note that the only way we could have avoided setting the image blocking
00630   // status above is if we have a current request and have kept it as the
00631   // current request.  In that case, we want to leave our old status, since the
00632   // status corresponds to the current request.  Even if we plan to do a
00633   // pending request load, having an mCurrentRequest means that our current
00634   // status is not a REJECT_* status, and doing the load shouldn't change that.
00635   // XXXbz there is an issue here if different ACCEPT statuses are used, but...
00636 }
00637 
00638 nsIDocument*
00639 nsImageLoadingContent::GetOurDocument()
00640 {
00641   nsCOMPtr<nsIContent> thisContent = do_QueryInterface(NS_STATIC_CAST(nsIImageLoadingContent*, this));
00642   NS_ENSURE_TRUE(thisContent, nsnull);
00643 
00644   return thisContent->GetOwnerDoc();
00645 }
00646 
00647 nsresult
00648 nsImageLoadingContent::StringToURI(const nsAString& aSpec,
00649                                    nsIDocument* aDocument,
00650                                    nsIURI** aURI)
00651 {
00652   NS_PRECONDITION(aDocument, "Must have a document");
00653   NS_PRECONDITION(aURI, "Null out param");
00654 
00655   // (1) Get the base URI
00656   nsCOMPtr<nsIContent> thisContent = do_QueryInterface(NS_STATIC_CAST(nsIImageLoadingContent*, this));
00657   NS_ASSERTION(thisContent, "An image loading content must be an nsIContent");
00658   nsCOMPtr<nsIURI> baseURL = thisContent->GetBaseURI();
00659 
00660   // (2) Get the charset
00661   const nsACString &charset = aDocument->GetDocumentCharacterSet();
00662 
00663   // (3) Construct the silly thing
00664   return NS_NewURI(aURI,
00665                    aSpec,
00666                    charset.IsEmpty() ? nsnull : PromiseFlatCString(charset).get(),
00667                    baseURL,
00668                    nsContentUtils::GetIOServiceWeakRef());
00669 }
00670 
00671 
00675 MOZ_DECL_CTOR_COUNTER(ImageEvent)
00676 
00677 class nsImageLoadingContent::Event : public PLEvent
00678 {
00679 public:
00680   Event(nsPresContext* aPresContext, nsImageLoadingContent* aContent,
00681              const nsAString& aMessage, nsIDocument* aDocument)
00682     : mPresContext(aPresContext),
00683       mContent(aContent),
00684       mMessage(aMessage),
00685       mDocument(aDocument)
00686   {
00687     MOZ_COUNT_CTOR(nsImageLoadingContent::Event);
00688     PL_InitEvent(this, aContent, Handle, Destroy);
00689   }
00690   ~Event()
00691   {
00692     MOZ_COUNT_DTOR(nsImageLoadingContent::Event);
00693     mDocument->UnblockOnload();
00694     mContent->UnpreserveLoadHandlers();
00695   }
00696   
00697   PR_STATIC_CALLBACK(void*) Handle(PLEvent* aEvent);
00698   PR_STATIC_CALLBACK(void) Destroy(PLEvent* aEvent);
00699 
00700   nsCOMPtr<nsPresContext> mPresContext;
00701   nsRefPtr<nsImageLoadingContent> mContent;
00702   nsString mMessage;
00703   // Need to hold on to the document in case our event outlives document
00704   // teardown... Wantto be able to get back to the document even if the
00705   // prescontext and content can't.
00706   nsCOMPtr<nsIDocument> mDocument;
00707 };
00708 
00709 /* static */ void * PR_CALLBACK
00710 nsImageLoadingContent::Event::Handle(PLEvent* aEvent)
00711 {
00712   nsImageLoadingContent::Event* evt =
00713     NS_STATIC_CAST(nsImageLoadingContent::Event*, aEvent);
00714   nsEventStatus estatus = nsEventStatus_eIgnore;
00715   PRUint32 eventMsg;
00716 
00717   if (evt->mMessage.EqualsLiteral("load")) {
00718     eventMsg = NS_IMAGE_LOAD;
00719   } else {
00720     eventMsg = NS_IMAGE_ERROR;
00721   }
00722 
00723   nsCOMPtr<nsIContent> ourContent = do_QueryInterface(NS_STATIC_CAST(nsIImageLoadingContent*, evt->mContent));
00724 
00725   nsEvent event(PR_TRUE, eventMsg);
00726   ourContent->HandleDOMEvent(evt->mPresContext, &event, nsnull,
00727                              NS_EVENT_FLAG_INIT, &estatus);
00728 
00729   return nsnull;
00730 }
00731 
00732 /* static */ void PR_CALLBACK
00733 nsImageLoadingContent::Event::Destroy(PLEvent* aEvent)
00734 {
00735   nsImageLoadingContent::Event* evt =
00736     NS_STATIC_CAST(nsImageLoadingContent::Event*, aEvent);
00737   delete evt;
00738 }
00739 
00740 nsresult
00741 nsImageLoadingContent::FireEvent(const nsAString& aEventType)
00742 {
00743   // We have to fire the event asynchronously so that we won't go into infinite
00744   // loops in cases when onLoad handlers reset the src and the new src is in
00745   // cache.
00746 
00747   nsCOMPtr<nsIDocument> document = GetOurDocument();
00748   if (!document) {
00749     // no use to fire events if there is no document....
00750     return NS_OK;
00751   }                                                                             
00752   nsresult rv;
00753   nsCOMPtr<nsIEventQueueService> eventQService =
00754     do_GetService("@mozilla.org/event-queue-service;1", &rv);
00755   NS_ENSURE_TRUE(eventQService, rv);
00756 
00757   nsCOMPtr<nsIEventQueue> eventQ;
00758   // Use the UI thread event queue (though we should not be getting called from
00759   // off the UI thread in any case....)
00760   rv = eventQService->GetSpecialEventQueue(nsIEventQueueService::UI_THREAD_EVENT_QUEUE,
00761                                            getter_AddRefs(eventQ));
00762   NS_ENSURE_TRUE(eventQ, rv);
00763 
00764   nsIPresShell *shell = document->GetShellAt(0);
00765   NS_ENSURE_TRUE(shell, NS_ERROR_FAILURE);
00766 
00767   nsPresContext *presContext = shell->GetPresContext();
00768   NS_ENSURE_TRUE(presContext, NS_ERROR_FAILURE);
00769 
00770   nsImageLoadingContent::Event* evt =
00771     new Event(presContext, this, aEventType, document);
00772   NS_ENSURE_TRUE(evt, NS_ERROR_OUT_OF_MEMORY);
00773 
00774   // Block onload for our event.  Since we unblock in the event destructor, we
00775   // want to block now, even if posting will fail.
00776   document->BlockOnload();
00777   PreserveLoadHandlers();
00778   
00779   rv = eventQ->PostEvent(evt);
00780 
00781   if (NS_FAILED(rv)) {
00782     PL_DestroyEvent(evt);
00783   }
00784 
00785   return rv;
00786 }
00787 
00788 void
00789 nsImageLoadingContent::PreserveLoadHandlers()
00790 {
00791   ++mRootRefCount;
00792   NS_LOG_ADDREF(&mRootRefCount, mRootRefCount,
00793                 "nsImageLoadingContent::mRootRefCount", sizeof(mRootRefCount));
00794   if (mRootRefCount == 1) {
00795     nsCOMPtr<nsIDOMGCParticipant> part = do_QueryInterface(NS_STATIC_CAST(nsIImageLoadingContent*, this));
00796     nsresult rv = nsDOMClassInfo::SetExternallyReferenced(part);
00797     // The worst that will happen if we ignore this failure is that
00798     // onload or onerror will fail to fire.  I suppose we could fire
00799     // onerror now as a result of that, but the only reason it would
00800     // actually fail is out-of-memory, and it seems silly to bother and
00801     // unlikely to work in that case.
00802     NS_ASSERTION(NS_SUCCEEDED(rv), "ignoring failure to root participant");
00803   }
00804 }
00805 
00806 void
00807 nsImageLoadingContent::UnpreserveLoadHandlers()
00808 {
00809   NS_ASSERTION(mRootRefCount != 0,
00810                "load handler preservation refcount underflow");
00811   --mRootRefCount;
00812   NS_LOG_RELEASE(&mRootRefCount, mRootRefCount,
00813                  "nsImageLoadingContent::mRootRefCount");
00814   if (mRootRefCount == 0) {
00815     nsCOMPtr<nsIDOMGCParticipant> part = do_QueryInterface(NS_STATIC_CAST(nsIImageLoadingContent*, this));
00816     nsDOMClassInfo::UnsetExternallyReferenced(part);
00817   }
00818 }