Back to index

lightning-sunbird  0.9+nobinonly
nsWyciwygChannel.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
00002  *
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 Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * 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  * Netscape Communications Corporation.
00020  * Portions created by the Initial Developer are Copyright (C) 1998
00021  * the Initial Developer. All Rights Reserved.
00022  *
00023  * Contributor(s):
00024  *   Radha Kulkarni(radha@netscape.com)
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either of the GNU General Public License Version 2 or later (the "GPL"),
00028  * or 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 MPL, 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 MPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK ***** */
00039 
00040 #include "nsWyciwygChannel.h"
00041 #include "nsIServiceManager.h"
00042 #include "nsILoadGroup.h"
00043 #include "nsIScriptSecurityManager.h"
00044 #include "nsNetUtil.h"
00045 #include "nsContentUtils.h"
00046 #include "nsICacheService.h"
00047 #include "nsICacheSession.h"
00048 
00049 
00050 PRLogModuleInfo * gWyciwygLog = nsnull;
00051 
00052 #define wyciwyg_TYPE "text/html"
00053 #define LOG(args)  PR_LOG(gWyciwygLog, 4, args)
00054 
00055 // nsWyciwygChannel methods 
00056 nsWyciwygChannel::nsWyciwygChannel()
00057   : mStatus(NS_OK),
00058     mIsPending(PR_FALSE),
00059     mContentLength(-1),
00060     mLoadFlags(LOAD_NORMAL)
00061 {
00062 }
00063 
00064 nsWyciwygChannel::~nsWyciwygChannel() 
00065 {
00066 }
00067 
00068 NS_IMPL_ISUPPORTS6(nsWyciwygChannel,
00069                    nsIChannel,
00070                    nsIRequest,
00071                    nsIStreamListener,
00072                    nsIRequestObserver,
00073                    nsICacheListener, 
00074                    nsIWyciwygChannel)
00075 
00076 nsresult
00077 nsWyciwygChannel::Init(nsIURI* uri)
00078 {
00079   NS_ENSURE_ARG_POINTER(uri);
00080   mURI = uri;
00081   return NS_OK;
00082 }
00083 
00085 // nsIRequest methods:
00087 
00088 NS_IMETHODIMP
00089 nsWyciwygChannel::GetName(nsACString &aName)
00090 {
00091   return mURI->GetSpec(aName);
00092 }
00093  
00094 NS_IMETHODIMP
00095 nsWyciwygChannel::IsPending(PRBool *aIsPending)
00096 {
00097   *aIsPending = mIsPending;
00098   return NS_OK;
00099 }
00100 
00101 NS_IMETHODIMP
00102 nsWyciwygChannel::GetStatus(nsresult *aStatus)
00103 {
00104   if (NS_SUCCEEDED(mStatus) && mPump)
00105     mPump->GetStatus(aStatus);
00106   else
00107     *aStatus = mStatus;
00108   return NS_OK;
00109 }
00110 
00111 NS_IMETHODIMP
00112 nsWyciwygChannel::Cancel(nsresult status)
00113 {
00114   mStatus = status;
00115   if (mPump)
00116     mPump->Cancel(status);
00117   // else we're waiting for OnCacheEntryAvailable
00118   return NS_OK;
00119 }
00120  
00121 NS_IMETHODIMP
00122 nsWyciwygChannel::Suspend()
00123 {
00124   if (mPump)
00125     mPump->Suspend();
00126   // XXX else, we'll ignore this ... and that's probably bad!
00127   return NS_OK;
00128 }
00129  
00130 NS_IMETHODIMP
00131 nsWyciwygChannel::Resume()
00132 {
00133   if (mPump)
00134     mPump->Resume();
00135   // XXX else, we'll ignore this ... and that's probably bad!
00136   return NS_OK;
00137 }
00138 
00139 NS_IMETHODIMP
00140 nsWyciwygChannel::GetLoadGroup(nsILoadGroup* *aLoadGroup)
00141 {
00142   *aLoadGroup = mLoadGroup;
00143   NS_IF_ADDREF(*aLoadGroup);
00144   return NS_OK;
00145 }
00146 
00147 NS_IMETHODIMP
00148 nsWyciwygChannel::SetLoadGroup(nsILoadGroup* aLoadGroup)
00149 {
00150   mLoadGroup = aLoadGroup;
00151   return NS_OK;
00152 }
00153 
00154 NS_IMETHODIMP
00155 nsWyciwygChannel::SetLoadFlags(PRUint32 aLoadFlags)
00156 {
00157   mLoadFlags = aLoadFlags;
00158   return NS_OK;
00159 }
00160 
00161 NS_IMETHODIMP
00162 nsWyciwygChannel::GetLoadFlags(PRUint32 * aLoadFlags)
00163 {
00164   *aLoadFlags = mLoadFlags;
00165   return NS_OK;
00166 }
00167 
00169 // nsIChannel methods:
00171 
00172 NS_IMETHODIMP
00173 nsWyciwygChannel::GetOriginalURI(nsIURI* *aURI)
00174 {
00175   // Let's hope this isn't called before mOriginalURI is set or we will
00176   // return the full wyciwyg URI for our originalURI  :S
00177   NS_ASSERTION(mOriginalURI, "nsWyciwygChannel::GetOriginalURI - mOriginalURI not set!\n");
00178 
00179   *aURI = mOriginalURI ? mOriginalURI : mURI;
00180   NS_IF_ADDREF(*aURI);
00181   return NS_OK;
00182 }
00183 
00184 NS_IMETHODIMP
00185 nsWyciwygChannel::SetOriginalURI(nsIURI* aURI)
00186 {
00187   mOriginalURI = aURI;
00188   return NS_OK;
00189 }
00190 
00191 NS_IMETHODIMP
00192 nsWyciwygChannel::GetURI(nsIURI* *aURI)
00193 {
00194   *aURI = mURI;
00195   NS_IF_ADDREF(*aURI);
00196   return NS_OK;
00197 }
00198 
00199 NS_IMETHODIMP
00200 nsWyciwygChannel::GetOwner(nsISupports **aOwner)
00201 {
00202   nsresult rv = NS_OK;
00203 
00204   if (!mOwner) {
00205     // Create codebase principal with URI of original document, not our URI
00206 
00207     // without an owner or an original URI!
00208     NS_ENSURE_TRUE(mOriginalURI, NS_ERROR_FAILURE);
00209 
00210     nsCOMPtr<nsIPrincipal> principal;
00211     nsIScriptSecurityManager *secMan = nsContentUtils::GetSecurityManager();
00212     rv = secMan->GetCodebasePrincipal(mOriginalURI, getter_AddRefs(principal));
00213     if (NS_SUCCEEDED(rv)) {
00214       mOwner = principal;
00215     }
00216   }
00217 
00218   NS_IF_ADDREF(*aOwner = mOwner);
00219   return rv;
00220 }
00221 
00222 NS_IMETHODIMP
00223 nsWyciwygChannel::SetOwner(nsISupports* aOwner)
00224 {
00225   mOwner = aOwner;
00226   return NS_OK;
00227 }
00228 
00229 NS_IMETHODIMP
00230 nsWyciwygChannel::GetNotificationCallbacks(nsIInterfaceRequestor* *aCallbacks)
00231 {
00232   *aCallbacks = mCallbacks.get();
00233   NS_IF_ADDREF(*aCallbacks);
00234   return NS_OK;
00235 }
00236 
00237 NS_IMETHODIMP
00238 nsWyciwygChannel::SetNotificationCallbacks(nsIInterfaceRequestor* aNotificationCallbacks)
00239 {
00240   mCallbacks = aNotificationCallbacks;
00241   mProgressSink = do_GetInterface(mCallbacks);
00242   return NS_OK;
00243 }
00244 
00245 NS_IMETHODIMP 
00246 nsWyciwygChannel::GetSecurityInfo(nsISupports * *aSecurityInfo)
00247 {
00248   NS_IF_ADDREF(*aSecurityInfo = mSecurityInfo);
00249 
00250   return NS_OK;
00251 }
00252 
00253 NS_IMETHODIMP
00254 nsWyciwygChannel::GetContentType(nsACString &aContentType)
00255 {
00256   aContentType.AssignLiteral(wyciwyg_TYPE);
00257   return NS_OK;
00258 }
00259 
00260 NS_IMETHODIMP
00261 nsWyciwygChannel::SetContentType(const nsACString &aContentType)
00262 {
00263   return NS_ERROR_NOT_IMPLEMENTED;
00264 }
00265 
00266 NS_IMETHODIMP
00267 nsWyciwygChannel::GetContentCharset(nsACString &aContentCharset)
00268 {
00269   aContentCharset.Assign("UTF-16");
00270   return NS_OK;
00271 }
00272 
00273 NS_IMETHODIMP
00274 nsWyciwygChannel::SetContentCharset(const nsACString &aContentCharset)
00275 {
00276   return NS_ERROR_NOT_IMPLEMENTED;
00277 }
00278 
00279 NS_IMETHODIMP
00280 nsWyciwygChannel::GetContentLength(PRInt32 *aContentLength)
00281 {
00282   return NS_ERROR_NOT_IMPLEMENTED;
00283 }
00284 
00285 NS_IMETHODIMP
00286 nsWyciwygChannel::SetContentLength(PRInt32 aContentLength)
00287 {
00288   mContentLength = aContentLength;
00289 
00290   return NS_OK;
00291 }
00292 
00293 NS_IMETHODIMP
00294 nsWyciwygChannel::Open(nsIInputStream ** aReturn)
00295 {
00296   return NS_ERROR_NOT_IMPLEMENTED;
00297 }
00298 
00299 NS_IMETHODIMP
00300 nsWyciwygChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *ctx)
00301 {
00302   LOG(("nsWyciwygChannel::AsyncOpen [this=%x]\n", this));
00303 
00304   NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS);
00305   NS_ENSURE_ARG_POINTER(listener);
00306 
00307   nsCAutoString spec;
00308   mURI->GetSpec(spec);
00309 
00310   // open a cache entry for this channel...
00311   PRBool delayed = PR_FALSE;
00312   nsresult rv = OpenCacheEntry(spec, nsICache::ACCESS_READ, &delayed);        
00313   if (NS_FAILED(rv)) {
00314     LOG(("nsWyciwygChannel::OpenCacheEntry failed [rv=%x]\n", rv));
00315     return rv;
00316   }
00317 
00318   if (!delayed) {
00319     rv = ReadFromCache();
00320     if (NS_FAILED(rv)) {
00321       LOG(("nsWyciwygChannel::ReadFromCache failed [rv=%x]\n", rv));
00322       return rv;
00323     }
00324   }
00325 
00326   mIsPending = PR_TRUE;
00327   mListener = listener;
00328   mListenerContext = ctx;
00329 
00330   if (mLoadGroup)
00331     mLoadGroup->AddRequest(this, nsnull);
00332 
00333   return NS_OK;
00334 }
00335 
00337 // nsIWyciwygChannel
00339 
00340 NS_IMETHODIMP
00341 nsWyciwygChannel::WriteToCacheEntry(const nsAString &aData)
00342 {
00343   nsresult rv;
00344 
00345   if (!mCacheEntry) {
00346     nsCAutoString spec;
00347     rv = mURI->GetAsciiSpec(spec);
00348     if (NS_FAILED(rv)) return rv;
00349     rv = OpenCacheEntry(spec, nsICache::ACCESS_WRITE);
00350     if (NS_FAILED(rv)) return rv;
00351   }
00352 
00353   if (mSecurityInfo) {
00354     mCacheEntry->SetSecurityInfo(mSecurityInfo);
00355   }
00356 
00357   PRUint32 out;
00358   if (!mCacheOutputStream) {
00359     // Get the outputstream from the cache entry.
00360     rv = mCacheEntry->OpenOutputStream(0, getter_AddRefs(mCacheOutputStream));    
00361     if (NS_FAILED(rv)) return rv;
00362 
00363     // Write out a Byte Order Mark, so that we'll know if the data is
00364     // BE or LE when we go to read it.
00365     PRUnichar bom = 0xFEFF;
00366     rv = mCacheOutputStream->Write((char *)&bom, sizeof(bom), &out);
00367     if (NS_FAILED(rv)) return rv;
00368   }
00369 
00370   return mCacheOutputStream->Write((char *)PromiseFlatString(aData).get(),
00371                                    aData.Length() * sizeof(PRUnichar), &out);
00372 }
00373 
00374 
00375 NS_IMETHODIMP
00376 nsWyciwygChannel::CloseCacheEntry(nsresult reason)
00377 {
00378   if (mCacheEntry) {
00379     LOG(("nsWyciwygChannel::CloseCacheEntry [this=%x ]", this));
00380     mCacheOutputStream = 0;
00381     mCacheInputStream = 0;
00382 
00383     if (NS_FAILED(reason))
00384       mCacheEntry->Doom();
00385 
00386     mCacheEntry = 0;
00387   }
00388   return NS_OK;
00389 }
00390 
00391 NS_IMETHODIMP
00392 nsWyciwygChannel::SetSecurityInfo(nsISupports *aSecurityInfo)
00393 {
00394   mSecurityInfo = aSecurityInfo;
00395 
00396   return NS_OK;
00397 }
00398 
00400 // nsICachelistener
00402 NS_IMETHODIMP
00403 nsWyciwygChannel::OnCacheEntryAvailable(nsICacheEntryDescriptor * aCacheEntry, nsCacheAccessMode aMode, nsresult aStatus)
00404 {
00405   LOG(("nsWyciwygChannel::OnCacheEntryAvailable [this=%x entry=%x "
00406        "access=%x status=%x]\n", this, aCacheEntry, aMode, aStatus));
00407 
00408   // if the channel's already fired onStopRequest, 
00409   // then we should ignore this event.
00410   if (!mIsPending)
00411     return NS_OK;
00412 
00413   // otherwise, we have to handle this event.
00414   if (NS_SUCCEEDED(aStatus))
00415     mCacheEntry = aCacheEntry;
00416   else if (NS_SUCCEEDED(mStatus))
00417     mStatus = aStatus;
00418 
00419   nsresult rv;
00420   if (NS_FAILED(mStatus)) {
00421     LOG(("channel was canceled [this=%x status=%x]\n", this, mStatus));
00422     rv = mStatus;
00423   }
00424   else { // advance to the next state...
00425     rv = ReadFromCache();
00426   }
00427 
00428   // a failure from Connect means that we have to abort the channel.
00429   if (NS_FAILED(rv)) {
00430     CloseCacheEntry(rv);
00431 
00432     if (mListener) {
00433       mListener->OnStartRequest(this, mListenerContext);
00434       mListener->OnStopRequest(this, mListenerContext, mStatus);
00435       mListener = 0;
00436       mListenerContext = 0;
00437     }
00438 
00439     mIsPending = PR_FALSE;
00440 
00441     // Remove ourselves from the load group.
00442     if (mLoadGroup)
00443       mLoadGroup->RemoveRequest(this, nsnull, mStatus);
00444   }
00445 
00446   return NS_OK;
00447 }
00448 
00449 //-----------------------------------------------------------------------------
00450 // nsWyciwygChannel::nsIStreamListener
00451 //-----------------------------------------------------------------------------
00452 
00453 NS_IMETHODIMP
00454 nsWyciwygChannel::OnDataAvailable(nsIRequest *request, nsISupports *ctx,
00455                                   nsIInputStream *input,
00456                                   PRUint32 offset, PRUint32 count)
00457 {
00458   LOG(("nsWyciwygChannel::OnDataAvailable [this=%x request=%x offset=%u count=%u]\n",
00459       this, request, offset, count));
00460 
00461   nsresult rv;
00462   
00463   rv = mListener->OnDataAvailable(this, mListenerContext, input, offset, count);
00464 
00465   // XXX handle 64-bit stuff for real
00466   if (mProgressSink && NS_SUCCEEDED(rv) && !(mLoadFlags & LOAD_BACKGROUND))
00467     mProgressSink->OnProgress(this, nsnull, nsUint64(offset + count),
00468                               nsUint64(mContentLength));
00469 
00470   return rv; // let the pump cancel on failure
00471 }
00472 
00474 // nsIRequestObserver
00476 
00477 NS_IMETHODIMP
00478 nsWyciwygChannel::OnStartRequest(nsIRequest *request, nsISupports *ctx)
00479 {
00480   LOG(("nsWyciwygChannel::OnStartRequest [this=%x request=%x\n",
00481       this, request));
00482 
00483   return mListener->OnStartRequest(this, mListenerContext);
00484 }
00485 
00486 
00487 NS_IMETHODIMP
00488 nsWyciwygChannel::OnStopRequest(nsIRequest *request, nsISupports *ctx, nsresult status)
00489 {
00490   LOG(("nsWyciwygChannel::OnStopRequest [this=%x request=%x status=%d\n",
00491       this, request, status));
00492 
00493   if (NS_SUCCEEDED(mStatus))
00494     mStatus = status;
00495 
00496   mListener->OnStopRequest(this, mListenerContext, mStatus);
00497   mListener = 0;
00498   mListenerContext = 0;
00499 
00500   if (mLoadGroup)
00501     mLoadGroup->RemoveRequest(this, nsnull, mStatus);
00502 
00503   CloseCacheEntry(mStatus);
00504   mPump = 0;
00505   mIsPending = PR_FALSE;
00506 
00507   // Drop notification callbacks to prevent cycles.
00508   mCallbacks = 0;
00509   mProgressSink = 0;
00510 
00511   return NS_OK;
00512 }
00513 
00515 // Helper functions
00517 
00518 nsresult
00519 nsWyciwygChannel::OpenCacheEntry(const nsACString & aCacheKey,
00520                                  nsCacheAccessMode aAccessMode,
00521                                  PRBool * aDelayFlag)
00522 {
00523   nsresult rv = NS_ERROR_FAILURE;
00524   // Get cache service
00525   nsCOMPtr<nsICacheService> cacheService =
00526     do_GetService(NS_CACHESERVICE_CONTRACTID, &rv);
00527   NS_ENSURE_SUCCESS(rv, rv);
00528 
00529   nsXPIDLCString spec;    
00530   nsAutoString newURIString;    
00531   nsCOMPtr<nsICacheSession> cacheSession;
00532 
00533   // honor security settings
00534   nsCacheStoragePolicy storagePolicy;
00535   if (mLoadFlags & INHIBIT_PERSISTENT_CACHING)
00536     storagePolicy = nsICache::STORE_IN_MEMORY;
00537   else
00538     storagePolicy = nsICache::STORE_ANYWHERE;
00539  
00540   // Open a stream based cache session.
00541   rv = cacheService->CreateSession("wyciwyg", storagePolicy, PR_TRUE,
00542                                    getter_AddRefs(cacheSession));
00543   if (!cacheSession) 
00544     return NS_ERROR_FAILURE;
00545 
00546   /* we'll try to synchronously open the cache entry... however, it
00547    * may be in use and not yet validated, in which case we'll try
00548    * asynchronously opening the cache entry.
00549    */
00550     
00551   rv = cacheSession->OpenCacheEntry(aCacheKey, aAccessMode, PR_FALSE,
00552                                     getter_AddRefs(mCacheEntry));
00553 
00554   if (rv == NS_ERROR_CACHE_WAIT_FOR_VALIDATION) {
00555     // access to the cache entry has been denied. Let's try opening it
00556     // async.
00557     rv = cacheSession->AsyncOpenCacheEntry(aCacheKey, aAccessMode, this);
00558     if (NS_FAILED(rv)) 
00559       return rv;
00560     if (aDelayFlag)
00561       *aDelayFlag = PR_TRUE;
00562   }
00563   else if (rv == NS_OK) {
00564     LOG(("nsWyciwygChannel::OpenCacheEntry got cache entry \n"));
00565   }
00566 
00567   return rv;
00568 }
00569 
00570 nsresult
00571 nsWyciwygChannel::ReadFromCache()
00572 {
00573   LOG(("nsWyciwygChannel::ReadFromCache [this=%x] ", this));
00574 
00575   NS_ENSURE_TRUE(mCacheEntry, NS_ERROR_FAILURE);
00576   nsresult rv;
00577 
00578   // Get the stored security info
00579   mCacheEntry->GetSecurityInfo(getter_AddRefs(mSecurityInfo));
00580 
00581   // Get a transport to the cached data...
00582   rv = mCacheEntry->OpenInputStream(0, getter_AddRefs(mCacheInputStream));
00583   if (NS_FAILED(rv))
00584     return rv;
00585   NS_ENSURE_TRUE(mCacheInputStream, NS_ERROR_UNEXPECTED);
00586 
00587   rv = NS_NewInputStreamPump(getter_AddRefs(mPump), mCacheInputStream);
00588   if (NS_FAILED(rv)) return rv;
00589 
00590   // Pump the cache data downstream
00591   return mPump->AsyncRead(this, nsnull);
00592 }
00593 
00594 // vim: ts=2 sw=2