LCOV - code coverage report
Current view: top level - dom/fetch - FetchDriver.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 196 450 43.6 %
Date: 2017-07-14 16:53:18 Functions: 19 25 76.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include "mozilla/DebugOnly.h"
       8             : #include "mozilla/dom/FetchDriver.h"
       9             : 
      10             : #include "nsIAsyncVerifyRedirectCallback.h"
      11             : #include "nsIDocument.h"
      12             : #include "nsIInputStream.h"
      13             : #include "nsIOutputStream.h"
      14             : #include "nsIHttpChannel.h"
      15             : #include "nsIHttpChannelInternal.h"
      16             : #include "nsIScriptSecurityManager.h"
      17             : #include "nsISupportsPriority.h"
      18             : #include "nsIThreadRetargetableRequest.h"
      19             : #include "nsIUploadChannel2.h"
      20             : #include "nsIInterfaceRequestorUtils.h"
      21             : #include "nsIPipe.h"
      22             : 
      23             : #include "nsContentPolicyUtils.h"
      24             : #include "nsDataHandler.h"
      25             : #include "nsHostObjectProtocolHandler.h"
      26             : #include "nsNetUtil.h"
      27             : #include "nsPrintfCString.h"
      28             : #include "nsStreamUtils.h"
      29             : #include "nsStringStream.h"
      30             : #include "nsHttpChannel.h"
      31             : 
      32             : #include "mozilla/dom/File.h"
      33             : #include "mozilla/dom/workers/Workers.h"
      34             : #include "mozilla/EventStateManager.h"
      35             : #include "mozilla/ipc/PBackgroundSharedTypes.h"
      36             : #include "mozilla/Unused.h"
      37             : 
      38             : #include "Fetch.h"
      39             : #include "FetchUtil.h"
      40             : #include "InternalRequest.h"
      41             : #include "InternalResponse.h"
      42             : 
      43             : namespace mozilla {
      44             : namespace dom {
      45             : 
      46          31 : NS_IMPL_ISUPPORTS(FetchDriver,
      47             :                   nsIStreamListener, nsIChannelEventSink, nsIInterfaceRequestor,
      48             :                   nsIThreadRetargetableStreamListener)
      49             : 
      50           1 : FetchDriver::FetchDriver(InternalRequest* aRequest, nsIPrincipal* aPrincipal,
      51             :                          nsILoadGroup* aLoadGroup, nsIEventTarget* aMainThreadEventTarget,
      52           1 :                          bool aIsTrackingFetch)
      53             :   : mPrincipal(aPrincipal)
      54             :   , mLoadGroup(aLoadGroup)
      55             :   , mRequest(aRequest)
      56             :   , mMainThreadEventTarget(aMainThreadEventTarget)
      57             :   , mIsTrackingFetch(aIsTrackingFetch)
      58             : #ifdef DEBUG
      59             :   , mResponseAvailableCalled(false)
      60           1 :   , mFetchCalled(false)
      61             : #endif
      62             : {
      63           1 :   MOZ_ASSERT(aRequest);
      64           1 :   MOZ_ASSERT(aPrincipal);
      65           1 :   MOZ_ASSERT(aMainThreadEventTarget);
      66           1 : }
      67             : 
      68           3 : FetchDriver::~FetchDriver()
      69             : {
      70             :   // We assert this since even on failures, we should call
      71             :   // FailWithNetworkError().
      72           1 :   MOZ_ASSERT(mResponseAvailableCalled);
      73           3 : }
      74             : 
      75             : nsresult
      76           1 : FetchDriver::Fetch(FetchSignal* aSignal, FetchDriverObserver* aObserver)
      77             : {
      78           1 :   workers::AssertIsOnMainThread();
      79             : #ifdef DEBUG
      80           1 :   MOZ_ASSERT(!mFetchCalled);
      81           1 :   mFetchCalled = true;
      82             : #endif
      83             : 
      84           1 :   mObserver = aObserver;
      85             : 
      86           1 :   Telemetry::Accumulate(Telemetry::SERVICE_WORKER_REQUEST_PASSTHROUGH,
      87           2 :                         mRequest->WasCreatedByFetchEvent());
      88             : 
      89             :   // FIXME(nsm): Deal with HSTS.
      90             : 
      91           1 :   MOZ_RELEASE_ASSERT(!mRequest->IsSynchronous(),
      92             :                      "Synchronous fetch not supported");
      93             : 
      94             : 
      95           2 :   UniquePtr<mozilla::ipc::PrincipalInfo> principalInfo(new mozilla::ipc::PrincipalInfo());
      96           1 :   nsresult rv = PrincipalToPrincipalInfo(mPrincipal, principalInfo.get());
      97           1 :   if (NS_WARN_IF(NS_FAILED(rv))) {
      98           0 :     return rv;
      99             :   }
     100             : 
     101           1 :   mRequest->SetPrincipalInfo(Move(principalInfo));
     102             : 
     103             :   // If the signal is aborted, it's time to inform the observer and terminate
     104             :   // the operation.
     105           1 :   if (aSignal) {
     106           0 :     if (aSignal->Aborted()) {
     107           0 :       Aborted();
     108           0 :       return NS_OK;
     109             :     }
     110             : 
     111           0 :     Follow(aSignal);
     112             :   }
     113             : 
     114           1 :   if (NS_FAILED(HttpFetch())) {
     115           0 :     FailWithNetworkError();
     116             :   }
     117             : 
     118             :   // Any failure is handled by FailWithNetworkError notifying the aObserver.
     119           1 :   return NS_OK;
     120             : }
     121             : 
     122             : // This function implements the "HTTP Fetch" algorithm from the Fetch spec.
     123             : // Functionality is often split between here, the CORS listener proxy and the
     124             : // Necko HTTP implementation.
     125             : nsresult
     126           1 : FetchDriver::HttpFetch()
     127             : {
     128           1 :   MOZ_ASSERT(NS_IsMainThread());
     129             : 
     130             :   // Step 1. "Let response be null."
     131           1 :   mResponse = nullptr;
     132             :   nsresult rv;
     133             : 
     134           2 :   nsCOMPtr<nsIIOService> ios = do_GetIOService(&rv);
     135           1 :   NS_ENSURE_SUCCESS(rv, rv);
     136             : 
     137           2 :   nsAutoCString url;
     138           1 :   mRequest->GetURL(url);
     139           2 :   nsCOMPtr<nsIURI> uri;
     140           1 :   rv = NS_NewURI(getter_AddRefs(uri), url, nullptr, nullptr, ios);
     141           1 :   NS_ENSURE_SUCCESS(rv, rv);
     142             : 
     143             :   // Unsafe requests aren't allowed with when using no-core mode.
     144           2 :   if (mRequest->Mode() == RequestMode::No_cors &&
     145           1 :       mRequest->UnsafeRequest() &&
     146           0 :       (!mRequest->HasSimpleMethod() ||
     147           0 :        !mRequest->Headers()->HasOnlySimpleHeaders())) {
     148           0 :     MOZ_ASSERT(false, "The API should have caught this");
     149             :     return NS_ERROR_DOM_BAD_URI;
     150             :   }
     151             : 
     152             :   // non-GET requests aren't allowed for blob.
     153           1 :   if (IsBlobURI(uri)) {
     154           0 :     nsAutoCString method;
     155           0 :     mRequest->GetMethod(method);
     156           0 :     if (!method.EqualsLiteral("GET")) {
     157           0 :       return NS_ERROR_DOM_NETWORK_ERR;
     158             :     }
     159             :   }
     160             : 
     161             :   // Step 2 deals with letting ServiceWorkers intercept requests. This is
     162             :   // handled by Necko after the channel is opened.
     163             :   // FIXME(nsm): Bug 1119026: The channel's skip service worker flag should be
     164             :   // set based on the Request's flag.
     165             : 
     166             :   // Step 3.1 "If the CORS preflight flag is set and one of these conditions is
     167             :   // true..." is handled by the CORS proxy.
     168             :   //
     169             :   // Step 3.2 "Set request's skip service worker flag." This isn't required
     170             :   // since Necko will fall back to the network if the ServiceWorker does not
     171             :   // respond with a valid Response.
     172             :   //
     173             :   // NS_StartCORSPreflight() will automatically kick off the original request
     174             :   // if it succeeds, so we need to have everything setup for the original
     175             :   // request too.
     176             : 
     177             :   // Step 3.3 "Let credentials flag be set if one of
     178             :   //  - request's credentials mode is "include"
     179             :   //  - request's credentials mode is "same-origin" and either the CORS flag
     180             :   //    is unset or response tainting is "opaque"
     181             :   // is true, and unset otherwise."
     182             : 
     183             :   // Set skip serviceworker flag.
     184             :   // While the spec also gates on the client being a ServiceWorker, we can't
     185             :   // infer that here. Instead we rely on callers to set the flag correctly.
     186           1 :   const nsLoadFlags bypassFlag = mRequest->SkipServiceWorker() ?
     187           1 :                                  nsIChannel::LOAD_BYPASS_SERVICE_WORKER : 0;
     188             : 
     189           1 :   nsSecurityFlags secFlags = nsILoadInfo::SEC_ABOUT_BLANK_INHERITS;
     190           1 :   if (mRequest->Mode() == RequestMode::Cors) {
     191           1 :     secFlags |= nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS;
     192           0 :   } else if (mRequest->Mode() == RequestMode::Same_origin ||
     193           0 :              mRequest->Mode() == RequestMode::Navigate) {
     194           0 :     secFlags |= nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS;
     195           0 :   } else if (mRequest->Mode() == RequestMode::No_cors) {
     196           0 :     secFlags |= nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS;
     197             :   } else {
     198           0 :     MOZ_ASSERT_UNREACHABLE("Unexpected request mode!");
     199             :     return NS_ERROR_UNEXPECTED;
     200             :   }
     201             : 
     202           1 :   if (mRequest->GetRedirectMode() != RequestRedirect::Follow) {
     203           0 :     secFlags |= nsILoadInfo::SEC_DONT_FOLLOW_REDIRECTS;
     204             :   }
     205             : 
     206             :   // This is handles the use credentials flag in "HTTP
     207             :   // network or cache fetch" in the spec and decides whether to transmit
     208             :   // cookies and other identifying information.
     209           1 :   if (mRequest->GetCredentialsMode() == RequestCredentials::Include) {
     210           0 :     secFlags |= nsILoadInfo::SEC_COOKIES_INCLUDE;
     211           1 :   } else if (mRequest->GetCredentialsMode() == RequestCredentials::Omit) {
     212           1 :     secFlags |= nsILoadInfo::SEC_COOKIES_OMIT;
     213           0 :   } else if (mRequest->GetCredentialsMode() == RequestCredentials::Same_origin) {
     214           0 :     secFlags |= nsILoadInfo::SEC_COOKIES_SAME_ORIGIN;
     215             :   } else {
     216           0 :     MOZ_ASSERT_UNREACHABLE("Unexpected credentials mode!");
     217             :     return NS_ERROR_UNEXPECTED;
     218             :   }
     219             : 
     220             :   // From here on we create a channel and set its properties with the
     221             :   // information from the InternalRequest. This is an implementation detail.
     222           1 :   MOZ_ASSERT(mLoadGroup);
     223           2 :   nsCOMPtr<nsIChannel> chan;
     224             : 
     225             :   nsLoadFlags loadFlags = nsIRequest::LOAD_NORMAL |
     226           1 :     bypassFlag | nsIChannel::LOAD_CLASSIFY_URI;
     227           1 :   if (mDocument) {
     228           0 :     MOZ_ASSERT(mDocument->NodePrincipal() == mPrincipal);
     229           0 :     rv = NS_NewChannel(getter_AddRefs(chan),
     230             :                        uri,
     231             :                        mDocument,
     232             :                        secFlags,
     233             :                        mRequest->ContentPolicyType(),
     234             :                        mLoadGroup,
     235             :                        nullptr, /* aCallbacks */
     236             :                        loadFlags,
     237           0 :                        ios);
     238             :   } else {
     239           1 :     rv = NS_NewChannel(getter_AddRefs(chan),
     240             :                        uri,
     241             :                        mPrincipal,
     242             :                        secFlags,
     243             :                        mRequest->ContentPolicyType(),
     244             :                        mLoadGroup,
     245             :                        nullptr, /* aCallbacks */
     246             :                        loadFlags,
     247             :                        ios);
     248             :   }
     249           1 :   NS_ENSURE_SUCCESS(rv, rv);
     250             : 
     251           1 :   mLoadGroup = nullptr;
     252             : 
     253             :   // Insert ourselves into the notification callbacks chain so we can set
     254             :   // headers on redirects.
     255             : #ifdef DEBUG
     256             :   {
     257           2 :     nsCOMPtr<nsIInterfaceRequestor> notificationCallbacks;
     258           1 :     chan->GetNotificationCallbacks(getter_AddRefs(notificationCallbacks));
     259           1 :     MOZ_ASSERT(!notificationCallbacks);
     260             :   }
     261             : #endif
     262           1 :   chan->SetNotificationCallbacks(this);
     263             : 
     264           2 :   nsCOMPtr<nsIClassOfService> cos(do_QueryInterface(chan));
     265             :   // Mark channel as urgent-start if the Fetch is triggered by user input
     266             :   // events.
     267           1 :   if (cos && EventStateManager::IsHandlingUserInput()) {
     268           0 :     cos->AddClassFlags(nsIClassOfService::UrgentStart);
     269             :   }
     270             : 
     271             :   // Step 3.5 begins "HTTP network or cache fetch".
     272             :   // HTTP network or cache fetch
     273             :   // ---------------------------
     274             :   // Step 1 "Let HTTPRequest..." The channel is the HTTPRequest.
     275           2 :   nsCOMPtr<nsIHttpChannel> httpChan = do_QueryInterface(chan);
     276           1 :   if (httpChan) {
     277             :     // Copy the method.
     278           0 :     nsAutoCString method;
     279           0 :     mRequest->GetMethod(method);
     280           0 :     rv = httpChan->SetRequestMethod(method);
     281           0 :     NS_ENSURE_SUCCESS(rv, rv);
     282             : 
     283             :     // Set the same headers.
     284           0 :     SetRequestHeaders(httpChan);
     285             : 
     286           0 :     net::ReferrerPolicy net_referrerPolicy = mRequest->GetEnvironmentReferrerPolicy();
     287             :     // Step 6 of
     288             :     // https://fetch.spec.whatwg.org/#main-fetch
     289             :     // If request's referrer policy is the empty string and request's client is
     290             :     // non-null, then set request's referrer policy to request's client's
     291             :     // associated referrer policy.
     292             :     // Basically, "client" is not in our implementation, we use
     293             :     // EnvironmentReferrerPolicy of the worker or document context
     294           0 :     if (mRequest->ReferrerPolicy_() == ReferrerPolicy::_empty) {
     295           0 :       mRequest->SetReferrerPolicy(net_referrerPolicy);
     296             :     }
     297             :     // Step 7 of
     298             :     // https://fetch.spec.whatwg.org/#main-fetch
     299             :     // If request’s referrer policy is the empty string,
     300             :     // then set request’s referrer policy to "no-referrer-when-downgrade".
     301           0 :     if (mRequest->ReferrerPolicy_() == ReferrerPolicy::_empty) {
     302             :       net::ReferrerPolicy referrerPolicy =
     303           0 :         static_cast<net::ReferrerPolicy>(NS_GetDefaultReferrerPolicy());
     304           0 :       mRequest->SetReferrerPolicy(referrerPolicy);
     305             :     }
     306             : 
     307           0 :     rv = FetchUtil::SetRequestReferrer(mPrincipal,
     308             :                                        mDocument,
     309             :                                        httpChan,
     310             :                                        mRequest);
     311           0 :     NS_ENSURE_SUCCESS(rv, rv);
     312             : 
     313             :     // Bug 1120722 - Authorization will be handled later.
     314             :     // Auth may require prompting, we don't support it yet.
     315             :     // The next patch in this same bug prevents this from aborting the request.
     316             :     // Credentials checks for CORS are handled by nsCORSListenerProxy,
     317             : 
     318           0 :     nsCOMPtr<nsIHttpChannelInternal> internalChan = do_QueryInterface(httpChan);
     319             : 
     320             :     // Conversion between enumerations is safe due to static asserts in
     321             :     // dom/workers/ServiceWorkerManager.cpp
     322           0 :     rv = internalChan->SetCorsMode(static_cast<uint32_t>(mRequest->Mode()));
     323           0 :     MOZ_ASSERT(NS_SUCCEEDED(rv));
     324           0 :     rv = internalChan->SetRedirectMode(static_cast<uint32_t>(mRequest->GetRedirectMode()));
     325           0 :     MOZ_ASSERT(NS_SUCCEEDED(rv));
     326           0 :     mRequest->MaybeSkipCacheIfPerformingRevalidation();
     327           0 :     rv = internalChan->SetFetchCacheMode(static_cast<uint32_t>(mRequest->GetCacheMode()));
     328           0 :     MOZ_ASSERT(NS_SUCCEEDED(rv));
     329           0 :     rv = internalChan->SetIntegrityMetadata(mRequest->GetIntegrity());
     330           0 :     MOZ_ASSERT(NS_SUCCEEDED(rv));
     331             :   }
     332             : 
     333             :   // Step 5. Proxy authentication will be handled by Necko.
     334             : 
     335             :   // Continue setting up 'HTTPRequest'. Content-Type and body data.
     336           2 :   nsCOMPtr<nsIUploadChannel2> uploadChan = do_QueryInterface(chan);
     337           1 :   if (uploadChan) {
     338           0 :     nsAutoCString contentType;
     339           0 :     ErrorResult result;
     340           0 :     mRequest->Headers()->GetFirst(NS_LITERAL_CSTRING("content-type"), contentType, result);
     341             :     // We don't actually expect "result" to have failed here: that only happens
     342             :     // for invalid header names.  But if for some reason it did, just propagate
     343             :     // it out.
     344           0 :     if (result.Failed()) {
     345           0 :       return result.StealNSResult();
     346             :     }
     347             : 
     348             :     // Now contentType is the header that was set in mRequest->Headers(), or a
     349             :     // void string if no header was set.
     350             : #ifdef DEBUG
     351             :     bool hasContentTypeHeader =
     352           0 :       mRequest->Headers()->Has(NS_LITERAL_CSTRING("content-type"), result);
     353           0 :     MOZ_ASSERT(!result.Failed());
     354           0 :     MOZ_ASSERT_IF(!hasContentTypeHeader, contentType.IsVoid());
     355             : #endif // DEBUG
     356             : 
     357           0 :     nsCOMPtr<nsIInputStream> bodyStream;
     358           0 :     mRequest->GetBody(getter_AddRefs(bodyStream));
     359           0 :     if (bodyStream) {
     360           0 :       nsAutoCString method;
     361           0 :       mRequest->GetMethod(method);
     362           0 :       rv = uploadChan->ExplicitSetUploadStream(bodyStream, contentType, -1, method, false /* aStreamHasHeaders */);
     363           0 :       NS_ENSURE_SUCCESS(rv, rv);
     364             :     }
     365             :   }
     366             : 
     367             :   // If preflight is required, start a "CORS preflight fetch"
     368             :   // https://fetch.spec.whatwg.org/#cors-preflight-fetch-0. All the
     369             :   // implementation is handled by the http channel calling into
     370             :   // nsCORSListenerProxy. We just inform it which unsafe headers are included
     371             :   // in the request.
     372           1 :   if (mRequest->Mode() == RequestMode::Cors) {
     373           2 :     AutoTArray<nsCString, 5> unsafeHeaders;
     374           1 :     mRequest->Headers()->GetUnsafeHeaders(unsafeHeaders);
     375           2 :     nsCOMPtr<nsILoadInfo> loadInfo = chan->GetLoadInfo();
     376           1 :     if (loadInfo) {
     377           1 :       loadInfo->SetCorsPreflightInfo(unsafeHeaders, false);
     378             :     }
     379             :   }
     380             : 
     381           1 :   if (mIsTrackingFetch && nsContentUtils::IsLowerNetworkPriority()) {
     382           0 :     nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(chan);
     383           0 :     if (p) {
     384           0 :       p->SetPriority(nsISupportsPriority::PRIORITY_LOWEST);
     385             :     }
     386             :   }
     387             : 
     388           1 :   rv = chan->AsyncOpen2(this);
     389           1 :   NS_ENSURE_SUCCESS(rv, rv);
     390             : 
     391             :   // Step 4 onwards of "HTTP Fetch" is handled internally by Necko.
     392             : 
     393           1 :   mChannel = chan;
     394           1 :   return NS_OK;
     395             : }
     396             : already_AddRefed<InternalResponse>
     397           1 : FetchDriver::BeginAndGetFilteredResponse(InternalResponse* aResponse,
     398             :                                          bool aFoundOpaqueRedirect)
     399             : {
     400           1 :   MOZ_ASSERT(aResponse);
     401           2 :   AutoTArray<nsCString, 4> reqURLList;
     402           1 :   mRequest->GetURLListWithoutFragment(reqURLList);
     403           1 :   MOZ_ASSERT(!reqURLList.IsEmpty());
     404           1 :   aResponse->SetURLList(reqURLList);
     405           2 :   RefPtr<InternalResponse> filteredResponse;
     406           1 :   if (aFoundOpaqueRedirect) {
     407           0 :     filteredResponse = aResponse->OpaqueRedirectResponse();
     408             :   } else {
     409           1 :     switch (mRequest->GetResponseTainting()) {
     410             :       case LoadTainting::Basic:
     411           1 :         filteredResponse = aResponse->BasicResponse();
     412           1 :         break;
     413             :       case LoadTainting::CORS:
     414           0 :         filteredResponse = aResponse->CORSResponse();
     415           0 :         break;
     416             :       case LoadTainting::Opaque:
     417           0 :         filteredResponse = aResponse->OpaqueResponse();
     418           0 :         break;
     419             :       default:
     420           0 :         MOZ_CRASH("Unexpected case");
     421             :     }
     422             :   }
     423             : 
     424           1 :   MOZ_ASSERT(filteredResponse);
     425           1 :   MOZ_ASSERT(mObserver);
     426           2 :   if (filteredResponse->Type() == ResponseType::Error ||
     427           1 :       mRequest->GetIntegrity().IsEmpty()) {
     428           1 :     mObserver->OnResponseAvailable(filteredResponse);
     429             :   #ifdef DEBUG
     430           1 :     mResponseAvailableCalled = true;
     431             :   #endif
     432             :   }
     433             : 
     434           2 :   return filteredResponse.forget();
     435             : }
     436             : 
     437             : void
     438           0 : FetchDriver::FailWithNetworkError()
     439             : {
     440           0 :   workers::AssertIsOnMainThread();
     441           0 :   RefPtr<InternalResponse> error = InternalResponse::NetworkError();
     442           0 :   if (mObserver) {
     443           0 :     mObserver->OnResponseAvailable(error);
     444             : #ifdef DEBUG
     445           0 :     mResponseAvailableCalled = true;
     446             : #endif
     447           0 :     mObserver->OnResponseEnd(FetchDriverObserver::eByNetworking);
     448           0 :     mObserver = nullptr;
     449             :   }
     450             : 
     451           0 :   mChannel = nullptr;
     452           0 : }
     453             : 
     454             : NS_IMETHODIMP
     455           1 : FetchDriver::OnStartRequest(nsIRequest* aRequest,
     456             :                             nsISupports* aContext)
     457             : {
     458           1 :   workers::AssertIsOnMainThread();
     459             : 
     460             :   // Note, this can be called multiple times if we are doing an opaqueredirect.
     461             :   // In that case we will get a simulated OnStartRequest() and then the real
     462             :   // channel will call in with an errored OnStartRequest().
     463             : 
     464             :   nsresult rv;
     465           1 :   aRequest->GetStatus(&rv);
     466           1 :   if (NS_FAILED(rv)) {
     467           0 :     FailWithNetworkError();
     468           0 :     return rv;
     469             :   }
     470             : 
     471             :   // We should only get to the following code once.
     472           1 :   MOZ_ASSERT(!mPipeOutputStream);
     473           1 :   MOZ_ASSERT(mObserver);
     474             : 
     475           2 :   RefPtr<InternalResponse> response;
     476           2 :   nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
     477           2 :   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequest);
     478             : 
     479             :   // On a successful redirect we perform the following substeps of HTTP Fetch,
     480             :   // step 5, "redirect status", step 11.
     481             : 
     482           1 :   bool foundOpaqueRedirect = false;
     483             : 
     484           1 :   int64_t contentLength = InternalResponse::UNKNOWN_BODY_SIZE;
     485           1 :   rv = channel->GetContentLength(&contentLength);
     486           1 :   MOZ_ASSERT_IF(NS_FAILED(rv), contentLength == InternalResponse::UNKNOWN_BODY_SIZE);
     487             : 
     488           1 :   if (httpChannel) {
     489             :     uint32_t responseStatus;
     490           0 :     rv = httpChannel->GetResponseStatus(&responseStatus);
     491           0 :     MOZ_ASSERT(NS_SUCCEEDED(rv));
     492             : 
     493           0 :     if (mozilla::net::nsHttpChannel::IsRedirectStatus(responseStatus)) {
     494           0 :       if (mRequest->GetRedirectMode() == RequestRedirect::Error) {
     495           0 :         FailWithNetworkError();
     496           0 :         return NS_BINDING_FAILED;
     497             :       }
     498           0 :       if (mRequest->GetRedirectMode() == RequestRedirect::Manual) {
     499           0 :         foundOpaqueRedirect = true;
     500             :       }
     501             :     }
     502             : 
     503           0 :     nsAutoCString statusText;
     504           0 :     rv = httpChannel->GetResponseStatusText(statusText);
     505           0 :     MOZ_ASSERT(NS_SUCCEEDED(rv));
     506             : 
     507           0 :     response = new InternalResponse(responseStatus, statusText);
     508             : 
     509           0 :     response->Headers()->FillResponseHeaders(httpChannel);
     510             : 
     511             :     // If Content-Encoding or Transfer-Encoding headers are set, then the actual
     512             :     // Content-Length (which refer to the decoded data) is obscured behind the encodings.
     513           0 :     ErrorResult result;
     514           0 :     if (response->Headers()->Has(NS_LITERAL_CSTRING("content-encoding"), result) ||
     515           0 :         response->Headers()->Has(NS_LITERAL_CSTRING("transfer-encoding"), result)) {
     516             :       NS_WARNING("Cannot know response Content-Length due to presence of Content-Encoding "
     517           0 :                  "or Transfer-Encoding headers.");
     518           0 :       contentLength = InternalResponse::UNKNOWN_BODY_SIZE;
     519             :     }
     520           0 :     MOZ_ASSERT(!result.Failed());
     521             :   } else {
     522           2 :     response = new InternalResponse(200, NS_LITERAL_CSTRING("OK"));
     523             : 
     524           2 :     ErrorResult result;
     525           2 :     nsAutoCString contentType;
     526           1 :     rv = channel->GetContentType(contentType);
     527           1 :     if (NS_SUCCEEDED(rv) && !contentType.IsEmpty()) {
     528           2 :       nsAutoCString contentCharset;
     529           1 :       channel->GetContentCharset(contentCharset);
     530           1 :       if (NS_SUCCEEDED(rv) && !contentCharset.IsEmpty()) {
     531           1 :         contentType += NS_LITERAL_CSTRING(";charset=") + contentCharset;
     532             :       }
     533             : 
     534           3 :       response->Headers()->Append(NS_LITERAL_CSTRING("Content-Type"),
     535             :                                   contentType,
     536           2 :                                   result);
     537           1 :       MOZ_ASSERT(!result.Failed());
     538             :     }
     539             : 
     540           1 :     if (contentLength > 0) {
     541           2 :       nsAutoCString contentLenStr;
     542           1 :       contentLenStr.AppendInt(contentLength);
     543           3 :       response->Headers()->Append(NS_LITERAL_CSTRING("Content-Length"),
     544             :                                   contentLenStr,
     545           2 :                                   result);
     546           1 :       MOZ_ASSERT(!result.Failed());
     547             :     }
     548             :   }
     549             : 
     550             :   // We open a pipe so that we can immediately set the pipe's read end as the
     551             :   // response's body. Setting the segment size to UINT32_MAX means that the
     552             :   // pipe has infinite space. The nsIChannel will continue to buffer data in
     553             :   // xpcom events even if we block on a fixed size pipe.  It might be possible
     554             :   // to suspend the channel and then resume when there is space available, but
     555             :   // for now use an infinite pipe to avoid blocking.
     556           2 :   nsCOMPtr<nsIInputStream> pipeInputStream;
     557           1 :   rv = NS_NewPipe(getter_AddRefs(pipeInputStream),
     558           2 :                   getter_AddRefs(mPipeOutputStream),
     559             :                   0, /* default segment size */
     560             :                   UINT32_MAX /* infinite pipe */,
     561             :                   true /* non-blocking input, otherwise you deadlock */,
     562             :                   false /* blocking output, since the pipe is 'in'finite */ );
     563           1 :   if (NS_WARN_IF(NS_FAILED(rv))) {
     564           0 :     FailWithNetworkError();
     565             :     // Cancel request.
     566           0 :     return rv;
     567             :   }
     568           1 :   response->SetBody(pipeInputStream, contentLength);
     569             : 
     570           1 :   response->InitChannelInfo(channel);
     571             : 
     572           2 :   nsCOMPtr<nsIURI> channelURI;
     573           1 :   rv = channel->GetURI(getter_AddRefs(channelURI));
     574           1 :   if (NS_WARN_IF(NS_FAILED(rv))) {
     575           0 :     FailWithNetworkError();
     576             :     // Cancel request.
     577           0 :     return rv;
     578             :   }
     579             : 
     580           2 :   nsCOMPtr<nsILoadInfo> loadInfo;
     581           1 :   rv = channel->GetLoadInfo(getter_AddRefs(loadInfo));
     582           1 :   if (NS_WARN_IF(NS_FAILED(rv))) {
     583           0 :     FailWithNetworkError();
     584           0 :     return rv;
     585             :   }
     586             : 
     587             :   // Propagate any tainting from the channel back to our response here.  This
     588             :   // step is not reflected in the spec because the spec is written such that
     589             :   // FetchEvent.respondWith() just passes the already-tainted Response back to
     590             :   // the outer fetch().  In gecko, however, we serialize the Response through
     591             :   // the channel and must regenerate the tainting from the channel in the
     592             :   // interception case.
     593           1 :   mRequest->MaybeIncreaseResponseTainting(loadInfo->GetTainting());
     594             : 
     595             :   // Resolves fetch() promise which may trigger code running in a worker.  Make
     596             :   // sure the Response is fully initialized before calling this.
     597           1 :   mResponse = BeginAndGetFilteredResponse(response, foundOpaqueRedirect);
     598             : 
     599             :   // From "Main Fetch" step 17: SRI-part1.
     600           3 :   if (mResponse->Type() != ResponseType::Error &&
     601           1 :       !mRequest->GetIntegrity().IsEmpty() &&
     602           0 :       mSRIMetadata.IsEmpty()) {
     603           0 :     nsIConsoleReportCollector* aReporter = nullptr;
     604           0 :     if (mObserver) {
     605           0 :       aReporter = mObserver->GetReporter();
     606             :     }
     607             : 
     608           0 :     nsAutoCString sourceUri;
     609           0 :     if (mDocument && mDocument->GetDocumentURI()) {
     610           0 :       mDocument->GetDocumentURI()->GetAsciiSpec(sourceUri);
     611           0 :     } else if (!mWorkerScript.IsEmpty()) {
     612           0 :       sourceUri.Assign(mWorkerScript);
     613             :     }
     614           0 :     SRICheck::IntegrityMetadata(mRequest->GetIntegrity(), sourceUri,
     615           0 :                                 aReporter, &mSRIMetadata);
     616             :     mSRIDataVerifier = new SRICheckDataVerifier(mSRIMetadata, sourceUri,
     617           0 :                                                 aReporter);
     618             : 
     619             :     // Do not retarget off main thread when using SRI API.
     620           0 :     return NS_OK;
     621             :   }
     622             : 
     623           2 :   nsCOMPtr<nsIEventTarget> sts = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv);
     624           1 :   if (NS_WARN_IF(NS_FAILED(rv))) {
     625           0 :     FailWithNetworkError();
     626             :     // Cancel request.
     627           0 :     return rv;
     628             :   }
     629             : 
     630             :   // Try to retarget off main thread.
     631           2 :   if (nsCOMPtr<nsIThreadRetargetableRequest> rr = do_QueryInterface(aRequest)) {
     632           1 :     Unused << NS_WARN_IF(NS_FAILED(rr->RetargetDeliveryTo(sts)));
     633             :   }
     634           1 :   return NS_OK;
     635             : }
     636             : 
     637             : namespace {
     638             : 
     639             : // Runnable to call the observer OnDataAvailable on the main-thread.
     640           3 : class DataAvailableRunnable final : public Runnable
     641             : {
     642             :   RefPtr<FetchDriverObserver> mObserver;
     643             : 
     644             : public:
     645           1 :   explicit DataAvailableRunnable(FetchDriverObserver* aObserver)
     646           1 :     : Runnable("dom::DataAvailableRunnable")
     647           1 :     , mObserver(aObserver)
     648             :   {
     649           1 :      MOZ_ASSERT(aObserver);
     650           1 :   }
     651             : 
     652             :   NS_IMETHOD
     653           1 :   Run() override
     654             :   {
     655           1 :     mObserver->OnDataAvailable();
     656           1 :     mObserver = nullptr;
     657           1 :     return NS_OK;
     658             :   }
     659             : };
     660             : 
     661             : struct SRIVerifierAndOutputHolder {
     662           0 :   SRIVerifierAndOutputHolder(SRICheckDataVerifier* aVerifier,
     663             :                              nsIOutputStream* aOutputStream)
     664           0 :     : mVerifier(aVerifier)
     665           0 :     , mOutputStream(aOutputStream)
     666           0 :   {}
     667             : 
     668             :   SRICheckDataVerifier* mVerifier;
     669             :   nsIOutputStream* mOutputStream;
     670             : 
     671             : private:
     672             :   SRIVerifierAndOutputHolder() = delete;
     673             : };
     674             : 
     675             : // Just like NS_CopySegmentToStream, but also sends the data into an
     676             : // SRICheckDataVerifier.
     677             : nsresult
     678           0 : CopySegmentToStreamAndSRI(nsIInputStream* aInStr,
     679             :                           void* aClosure,
     680             :                           const char* aBuffer,
     681             :                           uint32_t aOffset,
     682             :                           uint32_t aCount,
     683             :                           uint32_t* aCountWritten)
     684             : {
     685           0 :   auto holder = static_cast<SRIVerifierAndOutputHolder*>(aClosure);
     686           0 :   MOZ_DIAGNOSTIC_ASSERT(holder && holder->mVerifier && holder->mOutputStream,
     687             :                         "Bogus holder");
     688             :   nsresult rv =
     689           0 :     holder->mVerifier->Update(aCount,
     690           0 :                               reinterpret_cast<const uint8_t*>(aBuffer));
     691           0 :   NS_ENSURE_SUCCESS(rv, rv);
     692             : 
     693             :   // The rest is just like NS_CopySegmentToStream.
     694           0 :   *aCountWritten = 0;
     695           0 :   while (aCount) {
     696           0 :     uint32_t n = 0;
     697           0 :     rv = holder->mOutputStream->Write(aBuffer, aCount, &n);
     698           0 :     if (NS_FAILED(rv)) {
     699           0 :       return rv;
     700             :     }
     701           0 :     aBuffer += n;
     702           0 :     aCount -= n;
     703           0 :     *aCountWritten += n;
     704             :   }
     705           0 :   return NS_OK;
     706             : }
     707             : 
     708             : } // anonymous namespace
     709             : 
     710             : NS_IMETHODIMP
     711           1 : FetchDriver::OnDataAvailable(nsIRequest* aRequest,
     712             :                              nsISupports* aContext,
     713             :                              nsIInputStream* aInputStream,
     714             :                              uint64_t aOffset,
     715             :                              uint32_t aCount)
     716             : {
     717             :   // NB: This can be called on any thread!  But we're guaranteed that it is
     718             :   // called between OnStartRequest and OnStopRequest, so we don't need to worry
     719             :   // about races.
     720             : 
     721           1 :   if (mObserver) {
     722           1 :     if (NS_IsMainThread()) {
     723           0 :       mObserver->OnDataAvailable();
     724             :     } else {
     725           3 :       RefPtr<Runnable> runnable = new DataAvailableRunnable(mObserver);
     726             :       nsresult rv =
     727           1 :         mMainThreadEventTarget->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
     728           1 :       if (NS_WARN_IF(NS_FAILED(rv))) {
     729           0 :         return rv;
     730             :       }
     731             :     }
     732             :   }
     733             : 
     734             :   uint32_t aRead;
     735           1 :   MOZ_ASSERT(mResponse);
     736           1 :   MOZ_ASSERT(mPipeOutputStream);
     737             : 
     738             :   // From "Main Fetch" step 17: SRI-part2.
     739           2 :   if (mResponse->Type() != ResponseType::Error &&
     740           1 :       !mRequest->GetIntegrity().IsEmpty()) {
     741           0 :     MOZ_ASSERT(mSRIDataVerifier);
     742             : 
     743           0 :     SRIVerifierAndOutputHolder holder(mSRIDataVerifier, mPipeOutputStream);
     744             :     nsresult rv = aInputStream->ReadSegments(CopySegmentToStreamAndSRI,
     745           0 :                                              &holder, aCount, &aRead);
     746           0 :     return rv;
     747             :   }
     748             : 
     749             :   nsresult rv = aInputStream->ReadSegments(NS_CopySegmentToStream,
     750             :                                            mPipeOutputStream,
     751           1 :                                            aCount, &aRead);
     752           1 :   return rv;
     753             : }
     754             : 
     755             : NS_IMETHODIMP
     756           1 : FetchDriver::OnStopRequest(nsIRequest* aRequest,
     757             :                            nsISupports* aContext,
     758             :                            nsresult aStatusCode)
     759             : {
     760           1 :   workers::AssertIsOnMainThread();
     761           1 :   if (NS_FAILED(aStatusCode)) {
     762           0 :     nsCOMPtr<nsIAsyncOutputStream> outputStream = do_QueryInterface(mPipeOutputStream);
     763           0 :     if (outputStream) {
     764           0 :       outputStream->CloseWithStatus(NS_BINDING_FAILED);
     765             :     }
     766             : 
     767             :     // We proceed as usual here, since we've already created a successful response
     768             :     // from OnStartRequest.
     769             :   } else {
     770           1 :     MOZ_ASSERT(mResponse);
     771           1 :     MOZ_ASSERT(!mResponse->IsError());
     772             : 
     773             :     // From "Main Fetch" step 17: SRI-part3.
     774           2 :     if (mResponse->Type() != ResponseType::Error &&
     775           1 :         !mRequest->GetIntegrity().IsEmpty()) {
     776           0 :       MOZ_ASSERT(mSRIDataVerifier);
     777             : 
     778           0 :       nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
     779             : 
     780           0 :       nsIConsoleReportCollector* aReporter = nullptr;
     781           0 :       if (mObserver) {
     782           0 :         aReporter = mObserver->GetReporter();
     783             :       }
     784             : 
     785           0 :       nsAutoCString sourceUri;
     786           0 :       if (mDocument && mDocument->GetDocumentURI()) {
     787           0 :         mDocument->GetDocumentURI()->GetAsciiSpec(sourceUri);
     788           0 :       } else if (!mWorkerScript.IsEmpty()) {
     789           0 :         sourceUri.Assign(mWorkerScript);
     790             :       }
     791           0 :       nsresult rv = mSRIDataVerifier->Verify(mSRIMetadata, channel, sourceUri,
     792           0 :                                              aReporter);
     793           0 :       if (NS_FAILED(rv)) {
     794           0 :         FailWithNetworkError();
     795             :         // Cancel request.
     796           0 :         return rv;
     797             :       }
     798             :     }
     799             : 
     800           1 :     if (mPipeOutputStream) {
     801           1 :       mPipeOutputStream->Close();
     802             :     }
     803             :   }
     804             : 
     805           1 :   if (mObserver) {
     806           2 :     if (mResponse->Type() != ResponseType::Error &&
     807           1 :         !mRequest->GetIntegrity().IsEmpty()) {
     808             :       //From "Main Fetch" step 23: Process response.
     809           0 :       MOZ_ASSERT(mResponse);
     810           0 :       mObserver->OnResponseAvailable(mResponse);
     811             :       #ifdef DEBUG
     812           0 :         mResponseAvailableCalled = true;
     813             :       #endif
     814             :     }
     815             : 
     816           1 :     mObserver->OnResponseEnd(FetchDriverObserver::eByNetworking);
     817           1 :     mObserver = nullptr;
     818             :   }
     819             : 
     820           1 :   mChannel = nullptr;
     821           1 :   return NS_OK;
     822             : }
     823             : 
     824             : NS_IMETHODIMP
     825           0 : FetchDriver::AsyncOnChannelRedirect(nsIChannel* aOldChannel,
     826             :                                     nsIChannel* aNewChannel,
     827             :                                     uint32_t aFlags,
     828             :                                     nsIAsyncVerifyRedirectCallback *aCallback)
     829             : {
     830           0 :   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aNewChannel);
     831           0 :   if (httpChannel) {
     832           0 :     SetRequestHeaders(httpChannel);
     833             :   }
     834             : 
     835           0 :   nsCOMPtr<nsIHttpChannel> oldHttpChannel = do_QueryInterface(aOldChannel);
     836           0 :   nsAutoCString tRPHeaderCValue;
     837           0 :   if (oldHttpChannel) {
     838           0 :     Unused << oldHttpChannel->GetResponseHeader(NS_LITERAL_CSTRING("referrer-policy"),
     839           0 :                                                 tRPHeaderCValue);
     840             :   }
     841             : 
     842             :   // "HTTP-redirect fetch": step 14 "Append locationURL to request's URL list."
     843           0 :   nsCOMPtr<nsIURI> uri;
     844           0 :   MOZ_ALWAYS_SUCCEEDS(aNewChannel->GetURI(getter_AddRefs(uri)));
     845             : 
     846           0 :   nsCOMPtr<nsIURI> uriClone;
     847           0 :   nsresult rv = uri->CloneIgnoringRef(getter_AddRefs(uriClone));
     848           0 :   if(NS_WARN_IF(NS_FAILED(rv))){
     849           0 :     return rv;
     850             :   }
     851           0 :   nsCString spec;
     852           0 :   rv = uriClone->GetSpec(spec);
     853           0 :   if(NS_WARN_IF(NS_FAILED(rv))){
     854           0 :     return rv;
     855             :   }
     856           0 :   nsCString fragment;
     857           0 :   rv = uri->GetRef(fragment);
     858           0 :   if(NS_WARN_IF(NS_FAILED(rv))){
     859           0 :     return rv;
     860             :   }
     861             : 
     862           0 :   mRequest->AddURL(spec, fragment);
     863           0 :   NS_ConvertUTF8toUTF16 tRPHeaderValue(tRPHeaderCValue);
     864             :   // updates request’s associated referrer policy according to the
     865             :   // Referrer-Policy header (if any).
     866           0 :   if (!tRPHeaderValue.IsEmpty()) {
     867             :     net::ReferrerPolicy net_referrerPolicy =
     868           0 :       nsContentUtils::GetReferrerPolicyFromHeader(tRPHeaderValue);
     869           0 :     if (net_referrerPolicy != net::RP_Unset) {
     870           0 :       mRequest->SetReferrerPolicy(net_referrerPolicy);
     871             :       // Should update channel's referrer policy
     872           0 :       if (httpChannel) {
     873           0 :         rv = FetchUtil::SetRequestReferrer(mPrincipal,
     874             :                                            mDocument,
     875             :                                            httpChannel,
     876           0 :                                            mRequest);
     877           0 :         NS_ENSURE_SUCCESS(rv, rv);
     878             :       }
     879             :     }
     880             :   }
     881             : 
     882           0 :   aCallback->OnRedirectVerifyCallback(NS_OK);
     883           0 :   return NS_OK;
     884             : }
     885             : 
     886             : NS_IMETHODIMP
     887           1 : FetchDriver::CheckListenerChain()
     888             : {
     889           1 :   return NS_OK;
     890             : }
     891             : 
     892             : NS_IMETHODIMP
     893           3 : FetchDriver::GetInterface(const nsIID& aIID, void **aResult)
     894             : {
     895           3 :   if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) {
     896           0 :     *aResult = static_cast<nsIChannelEventSink*>(this);
     897           0 :     NS_ADDREF_THIS();
     898           0 :     return NS_OK;
     899             :   }
     900           3 :   if (aIID.Equals(NS_GET_IID(nsIStreamListener))) {
     901           0 :     *aResult = static_cast<nsIStreamListener*>(this);
     902           0 :     NS_ADDREF_THIS();
     903           0 :     return NS_OK;
     904             :   }
     905           3 :   if (aIID.Equals(NS_GET_IID(nsIRequestObserver))) {
     906           0 :     *aResult = static_cast<nsIRequestObserver*>(this);
     907           0 :     NS_ADDREF_THIS();
     908           0 :     return NS_OK;
     909             :   }
     910             : 
     911           3 :   return QueryInterface(aIID, aResult);
     912             : }
     913             : 
     914             : void
     915           1 : FetchDriver::SetDocument(nsIDocument* aDocument)
     916             : {
     917             :   // Cannot set document after Fetch() has been called.
     918           1 :   MOZ_ASSERT(!mFetchCalled);
     919           1 :   mDocument = aDocument;
     920           1 : }
     921             : 
     922             : void
     923           0 : FetchDriver::SetRequestHeaders(nsIHttpChannel* aChannel) const
     924             : {
     925           0 :   MOZ_ASSERT(aChannel);
     926             : 
     927           0 :   AutoTArray<InternalHeaders::Entry, 5> headers;
     928           0 :   mRequest->Headers()->GetEntries(headers);
     929           0 :   bool hasAccept = false;
     930           0 :   for (uint32_t i = 0; i < headers.Length(); ++i) {
     931           0 :     if (!hasAccept && headers[i].mName.EqualsLiteral("accept")) {
     932           0 :       hasAccept = true;
     933             :     }
     934           0 :     if (headers[i].mValue.IsEmpty()) {
     935           0 :       DebugOnly<nsresult> rv = aChannel->SetEmptyRequestHeader(headers[i].mName);
     936           0 :       MOZ_ASSERT(NS_SUCCEEDED(rv));
     937             :     } else {
     938             :       DebugOnly<nsresult> rv =
     939           0 :         aChannel->SetRequestHeader(headers[i].mName, headers[i].mValue,
     940           0 :                                    false /* merge */);
     941           0 :       MOZ_ASSERT(NS_SUCCEEDED(rv));
     942             :     }
     943             :   }
     944             : 
     945           0 :   if (!hasAccept) {
     946             :     DebugOnly<nsresult> rv =
     947           0 :       aChannel->SetRequestHeader(NS_LITERAL_CSTRING("accept"),
     948           0 :                                  NS_LITERAL_CSTRING("*/*"),
     949           0 :                                  false /* merge */);
     950           0 :     MOZ_ASSERT(NS_SUCCEEDED(rv));
     951             :   }
     952             : 
     953           0 :   if (mRequest->ForceOriginHeader()) {
     954           0 :     nsAutoString origin;
     955           0 :     if (NS_SUCCEEDED(nsContentUtils::GetUTFOrigin(mPrincipal, origin))) {
     956             :       DebugOnly<nsresult> rv =
     957           0 :         aChannel->SetRequestHeader(NS_LITERAL_CSTRING("origin"),
     958           0 :                                    NS_ConvertUTF16toUTF8(origin),
     959           0 :                                    false /* merge */);
     960           0 :       MOZ_ASSERT(NS_SUCCEEDED(rv));
     961             :     }
     962             :   }
     963           0 : }
     964             : 
     965             : void
     966           0 : FetchDriver::Aborted()
     967             : {
     968           0 :   if (mObserver) {
     969             :   #ifdef DEBUG
     970           0 :     mResponseAvailableCalled = true;
     971             :   #endif
     972           0 :     mObserver->OnResponseEnd(FetchDriverObserver::eAborted);
     973           0 :     mObserver = nullptr;
     974             :   }
     975             : 
     976           0 :   if (mChannel) {
     977           0 :     mChannel->Cancel(NS_BINDING_ABORTED);
     978           0 :     mChannel = nullptr;
     979             :   }
     980           0 : }
     981             : 
     982             : } // namespace dom
     983             : } // namespace mozilla

Generated by: LCOV version 1.13