LCOV - code coverage report
Current view: directory - netwerk/base/src - nsAsyncRedirectVerifyHelper.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 102 85 83.3 %
Date: 2012-06-02 Functions: 19 18 94.7 %

       1                 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
       2                 : /* ***** BEGIN LICENSE BLOCK *****
       3                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       4                 :  *
       5                 :  * The contents of this file are subject to the Mozilla Public License Version
       6                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       7                 :  * the License. You may obtain a copy of the License at
       8                 :  * http://www.mozilla.org/MPL/
       9                 :  *
      10                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      11                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      12                 :  * for the specific language governing rights and limitations under the
      13                 :  * License.
      14                 :  *
      15                 :  * The Original Code is mozilla.org networking code.
      16                 :  *
      17                 :  * The Initial Developer of the Original Code is
      18                 :  * Mozilla Corporation
      19                 :  * Portions created by the Initial Developer are Copyright (C) 2009
      20                 :  * the Initial Developer. All Rights Reserved.
      21                 :  *
      22                 :  * Contributor(s):
      23                 :  *    Honza Bambas <honzab@firemni.cz>
      24                 :  *    Bjarne Geir Herland <bjarne@runitsoft.com>
      25                 :  *
      26                 :  * Alternatively, the contents of this file may be used under the terms of
      27                 :  * either the GNU General Public License Version 2 or later (the "GPL"), or
      28                 :  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      29                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      30                 :  * of those above. If you wish to allow use of your version of this file only
      31                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      32                 :  * use your version of this file under the terms of the MPL, indicate your
      33                 :  * decision by deleting the provisions above and replace them with the notice
      34                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      35                 :  * the provisions above, a recipient may use your version of this file under
      36                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      37                 :  *
      38                 :  * ***** END LICENSE BLOCK ***** */
      39                 : 
      40                 : #ifdef MOZ_LOGGING
      41                 : #define FORCE_PR_LOG
      42                 : #endif
      43                 : 
      44                 : #include "prlog.h"
      45                 : #include "nsAsyncRedirectVerifyHelper.h"
      46                 : #include "nsThreadUtils.h"
      47                 : #include "nsNetUtil.h"
      48                 : 
      49                 : #include "nsIOService.h"
      50                 : #include "nsIChannel.h"
      51                 : #include "nsIHttpChannelInternal.h"
      52                 : #include "nsIAsyncVerifyRedirectCallback.h"
      53                 : 
      54                 : #ifdef PR_LOGGING
      55            1464 : static PRLogModuleInfo *gLog = PR_NewLogModule("nsRedirect");
      56                 : #define LOG(args) PR_LOG(gLog, PR_LOG_DEBUG, args)
      57                 : #else
      58                 : #define LOG(args)
      59                 : #endif
      60                 : 
      61            1703 : NS_IMPL_THREADSAFE_ISUPPORTS2(nsAsyncRedirectVerifyHelper,
      62                 :                               nsIAsyncVerifyRedirectCallback,
      63                 :                               nsIRunnable)
      64                 : 
      65             620 : class nsAsyncVerifyRedirectCallbackEvent : public nsRunnable {
      66                 : public:
      67             155 :     nsAsyncVerifyRedirectCallbackEvent(nsIAsyncVerifyRedirectCallback *cb,
      68                 :                                        nsresult result)
      69             155 :         : mCallback(cb), mResult(result) {
      70             155 :     }
      71                 : 
      72             155 :     NS_IMETHOD Run()
      73                 :     {
      74             155 :         LOG(("nsAsyncVerifyRedirectCallbackEvent::Run() "
      75                 :              "callback to %p with result %x",
      76                 :              mCallback.get(), mResult));
      77             155 :        (void) mCallback->OnRedirectVerifyCallback(mResult);
      78             155 :        return NS_OK;
      79                 :     }
      80                 : private:
      81                 :     nsCOMPtr<nsIAsyncVerifyRedirectCallback> mCallback;
      82                 :     nsresult mResult;
      83                 : };
      84                 : 
      85             155 : nsAsyncRedirectVerifyHelper::nsAsyncRedirectVerifyHelper()
      86                 :     : mCallbackInitiated(false),
      87                 :       mExpectedCallbacks(0),
      88             155 :       mResult(NS_OK)
      89                 : {
      90             155 : }
      91                 : 
      92             310 : nsAsyncRedirectVerifyHelper::~nsAsyncRedirectVerifyHelper()
      93                 : {
      94             155 :     NS_ASSERTION(NS_FAILED(mResult) || mExpectedCallbacks == 0,
      95                 :                  "Did not receive all required callbacks!");
      96             155 : }
      97                 : 
      98                 : nsresult
      99             155 : nsAsyncRedirectVerifyHelper::Init(nsIChannel* oldChan, nsIChannel* newChan,
     100                 :                                   PRUint32 flags, bool synchronize)
     101                 : {
     102             155 :     LOG(("nsAsyncRedirectVerifyHelper::Init() "
     103                 :          "oldChan=%p newChan=%p", oldChan, newChan));
     104             155 :     mOldChan           = oldChan;
     105             155 :     mNewChan           = newChan;
     106             155 :     mFlags             = flags;
     107             155 :     mCallbackThread    = do_GetCurrentThread();
     108                 : 
     109             155 :     if (synchronize)
     110               0 :       mWaitingForRedirectCallback = true;
     111                 : 
     112                 :     nsresult rv;
     113             155 :     rv = NS_DispatchToMainThread(this);
     114             155 :     NS_ENSURE_SUCCESS(rv, rv);
     115                 : 
     116             155 :     if (synchronize) {
     117               0 :       nsIThread *thread = NS_GetCurrentThread();
     118               0 :       while (mWaitingForRedirectCallback) {
     119               0 :         if (!NS_ProcessNextEvent(thread)) {
     120               0 :           return NS_ERROR_UNEXPECTED;
     121                 :         }
     122                 :       }
     123                 :     }
     124                 : 
     125             155 :     return NS_OK;
     126                 : }
     127                 : 
     128                 : NS_IMETHODIMP
     129             371 : nsAsyncRedirectVerifyHelper::OnRedirectVerifyCallback(nsresult result)
     130                 : {
     131             371 :     LOG(("nsAsyncRedirectVerifyHelper::OnRedirectVerifyCallback() "
     132                 :          "result=%x expectedCBs=%u mResult=%x",
     133                 :          result, mExpectedCallbacks, mResult));
     134                 : 
     135             371 :     --mExpectedCallbacks;
     136                 : 
     137                 :     // If response indicates failure we may call back immediately
     138             371 :     if (NS_FAILED(result)) {
     139                 :         // We chose to store the first failure-value (as opposed to the last)
     140              15 :         if (NS_SUCCEEDED(mResult))
     141              15 :             mResult = result;
     142                 : 
     143                 :         // If InitCallback() has been called, just invoke the callback and
     144                 :         // return. Otherwise it will be invoked from InitCallback()
     145              15 :         if (mCallbackInitiated) {
     146               0 :             ExplicitCallback(mResult);
     147               0 :             return NS_OK;
     148                 :         }
     149                 :     }
     150                 : 
     151                 :     // If the expected-counter is in balance and InitCallback() was called, all
     152                 :     // sinks have agreed that the redirect is ok and we can invoke our callback
     153             371 :     if (mCallbackInitiated && mExpectedCallbacks == 0) {
     154               0 :         ExplicitCallback(mResult);
     155                 :     }
     156                 : 
     157             371 :     return NS_OK;
     158                 : }
     159                 : 
     160                 : nsresult
     161             371 : nsAsyncRedirectVerifyHelper::DelegateOnChannelRedirect(nsIChannelEventSink *sink,
     162                 :                                                        nsIChannel *oldChannel,
     163                 :                                                        nsIChannel *newChannel,
     164                 :                                                        PRUint32 flags)
     165                 : {
     166             371 :     LOG(("nsAsyncRedirectVerifyHelper::DelegateOnChannelRedirect() "
     167                 :          "sink=%p expectedCBs=%u mResult=%x",
     168                 :          sink, mExpectedCallbacks, mResult));
     169                 : 
     170             371 :     ++mExpectedCallbacks;
     171                 : 
     172             371 :     if (IsOldChannelCanceled()) {
     173               0 :         LOG(("  old channel has been canceled, cancel the redirect by "
     174                 :              "emulating OnRedirectVerifyCallback..."));
     175               0 :         (void) OnRedirectVerifyCallback(NS_BINDING_ABORTED);
     176               0 :         return NS_BINDING_ABORTED;
     177                 :     }
     178                 : 
     179                 :     nsresult rv =
     180             371 :         sink->AsyncOnChannelRedirect(oldChannel, newChannel, flags, this);
     181                 : 
     182             371 :     LOG(("  result=%x expectedCBs=%u", rv, mExpectedCallbacks));
     183                 : 
     184                 :     // If the sink returns failure from this call the redirect is vetoed. We
     185                 :     // emulate a callback from the sink in this case in order to perform all
     186                 :     // the necessary logic.
     187             371 :     if (NS_FAILED(rv)) {
     188              15 :         LOG(("  emulating OnRedirectVerifyCallback..."));
     189              15 :         (void) OnRedirectVerifyCallback(rv);
     190                 :     }
     191                 : 
     192             371 :     return rv;  // Return the actual status since our caller may need it
     193                 : }
     194                 : 
     195                 : void
     196             155 : nsAsyncRedirectVerifyHelper::ExplicitCallback(nsresult result)
     197                 : {
     198             155 :     LOG(("nsAsyncRedirectVerifyHelper::ExplicitCallback() "
     199                 :          "result=%x expectedCBs=%u mCallbackInitiated=%u mResult=%x",
     200                 :          result, mExpectedCallbacks, mCallbackInitiated, mResult));
     201                 : 
     202                 :     nsCOMPtr<nsIAsyncVerifyRedirectCallback>
     203             310 :         callback(do_QueryInterface(mOldChan));
     204                 : 
     205             155 :     if (!callback || !mCallbackThread) {
     206               0 :         LOG(("nsAsyncRedirectVerifyHelper::ExplicitCallback() "
     207                 :              "callback=%p mCallbackThread=%p", callback.get(), mCallbackThread.get()));
     208                 :         return;
     209                 :     }
     210                 : 
     211             155 :     mCallbackInitiated = false;  // reset to ensure only one callback
     212             155 :     mWaitingForRedirectCallback = false;
     213                 : 
     214                 :     // Now, dispatch the callback on the event-target which called Init()
     215                 :     nsRefPtr<nsIRunnable> event =
     216             465 :         new nsAsyncVerifyRedirectCallbackEvent(callback, result);
     217             155 :     if (!event) {
     218                 :         NS_WARNING("nsAsyncRedirectVerifyHelper::ExplicitCallback() "
     219               0 :                    "failed creating callback event!");
     220                 :         return;
     221                 :     }
     222             155 :     nsresult rv = mCallbackThread->Dispatch(event, NS_DISPATCH_NORMAL);
     223             155 :     if (NS_FAILED(rv)) {
     224                 :         NS_WARNING("nsAsyncRedirectVerifyHelper::ExplicitCallback() "
     225               0 :                    "failed dispatching callback event!");
     226                 :     } else {
     227             155 :         LOG(("nsAsyncRedirectVerifyHelper::ExplicitCallback() "
     228                 :              "dispatched callback event=%p", event.get()));
     229                 :     }
     230                 :    
     231                 : }
     232                 : 
     233                 : void
     234             152 : nsAsyncRedirectVerifyHelper::InitCallback()
     235                 : {
     236             152 :     LOG(("nsAsyncRedirectVerifyHelper::InitCallback() "
     237                 :          "expectedCBs=%d mResult=%x", mExpectedCallbacks, mResult));
     238                 : 
     239             152 :     mCallbackInitiated = true;
     240                 : 
     241                 :     // Invoke the callback if we are done
     242             152 :     if (mExpectedCallbacks == 0)
     243             152 :         ExplicitCallback(mResult);
     244             152 : }
     245                 : 
     246                 : NS_IMETHODIMP
     247             155 : nsAsyncRedirectVerifyHelper::Run()
     248                 : {
     249                 :     /* If the channel got canceled after it fired AsyncOnChannelRedirect
     250                 :      * and before we got here, mostly because docloader load has been canceled,
     251                 :      * we must completely ignore this notification and prevent any further
     252                 :      * notification.
     253                 :      */
     254             155 :     if (IsOldChannelCanceled()) {
     255               0 :         ExplicitCallback(NS_BINDING_ABORTED);
     256               0 :         return NS_OK;
     257                 :     }
     258                 : 
     259                 :     // First, the global observer
     260             155 :     NS_ASSERTION(gIOService, "Must have an IO service at this point");
     261             155 :     LOG(("nsAsyncRedirectVerifyHelper::Run() calling gIOService..."));
     262                 :     nsresult rv = gIOService->AsyncOnChannelRedirect(mOldChan, mNewChan,
     263             155 :                                                      mFlags, this);
     264             155 :     if (NS_FAILED(rv)) {
     265               3 :         ExplicitCallback(rv);
     266               3 :         return NS_OK;
     267                 :     }
     268                 : 
     269                 :     // Now, the per-channel observers
     270             304 :     nsCOMPtr<nsIChannelEventSink> sink;
     271             152 :     NS_QueryNotificationCallbacks(mOldChan, sink);
     272             152 :     if (sink) {
     273              62 :         LOG(("nsAsyncRedirectVerifyHelper::Run() calling sink..."));
     274              62 :         rv = DelegateOnChannelRedirect(sink, mOldChan, mNewChan, mFlags);
     275                 :     }
     276                 : 
     277                 :     // All invocations to AsyncOnChannelRedirect has been done - call
     278                 :     // InitCallback() to flag this
     279             152 :     InitCallback();
     280             152 :     return NS_OK;
     281                 : }
     282                 : 
     283                 : bool
     284             526 : nsAsyncRedirectVerifyHelper::IsOldChannelCanceled()
     285                 : {
     286                 :     bool canceled;
     287                 :     nsCOMPtr<nsIHttpChannelInternal> oldChannelInternal =
     288            1052 :         do_QueryInterface(mOldChan);
     289             526 :     if (oldChannelInternal) {
     290             526 :         oldChannelInternal->GetCanceled(&canceled);
     291             526 :         if (canceled)
     292               0 :             return true;
     293                 :     }
     294                 : 
     295             526 :     return false;
     296            4392 : }

Generated by: LCOV version 1.7