Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/uriloader/prefetch/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 29 kB image not shown  

Quelle  nsPrefetchService.cpp   Sprache: C

 
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


#include "nsPrefetchService.h"

#include "mozilla/AsyncEventDispatcher.h"
#include "mozilla/Attributes.h"
#include "mozilla/CORSMode.h"
#include "mozilla/Components.h"
#include "mozilla/dom/ClientInfo.h"
#include "mozilla/dom/HTMLLinkElement.h"
#include "mozilla/dom/ServiceWorkerDescriptor.h"
#include "mozilla/Preferences.h"
#include "ReferrerInfo.h"

#include "nsIObserverService.h"
#include "nsIWebProgress.h"
#include "nsICacheInfoChannel.h"
#include "nsIHttpChannel.h"
#include "nsIURL.h"
#include "nsISupportsPriority.h"
#include "nsNetUtil.h"
#include "nsString.h"
#include "nsReadableUtils.h"
#include "nsStreamUtils.h"
#include "prtime.h"
#include "mozilla/Logging.h"
#include "nsIAsyncVerifyRedirectCallback.h"
#include "nsINode.h"
#include "mozilla/dom/Document.h"
#include "nsContentUtils.h"
#include "mozilla/AsyncEventDispatcher.h"
#include "nsICachingChannel.h"
#include "nsHttp.h"

using namespace mozilla;
using namespace mozilla::dom;

//
// To enable logging (see mozilla/Logging.h for full details):
//
//    set MOZ_LOG=nsPrefetch:5
//    set MOZ_LOG_FILE=prefetch.log
//
// this enables LogLevel::Debug level information and places all output in
// the file prefetch.log
//
static LazyLogModule gPrefetchLog("nsPrefetch");

#undef LOG
#define LOG(args) MOZ_LOG(gPrefetchLog, mozilla::LogLevel::Debug, args)

#undef LOG_ENABLED
#define LOG_ENABLED() MOZ_LOG_TEST(gPrefetchLog, mozilla::LogLevel::Debug)

#define PREFETCH_PREF "network.prefetch-next"
#define PARALLELISM_PREF "network.prefetch-next.parallelism"
#define AGGRESSIVE_PREF "network.prefetch-next.aggressive"

//-----------------------------------------------------------------------------
// nsPrefetchNode <public>
//-----------------------------------------------------------------------------

nsPrefetchNode::nsPrefetchNode(nsPrefetchService* aService, nsIURI* aURI,
                               nsIReferrerInfo* aReferrerInfo, nsINode* aSource,
                               nsContentPolicyType aPolicyType, bool aPreload)
    : mURI(aURI),
      mReferrerInfo(aReferrerInfo),
      mPolicyType(aPolicyType),
      mPreload(aPreload),
      mService(aService),
      mChannel(nullptr),
      mBytesRead(0),
      mShouldFireLoadEvent(false) {
  nsWeakPtr source = do_GetWeakReference(aSource);
  mSources.AppendElement(source);
}

nsresult nsPrefetchNode::OpenChannel() {
  if (mSources.IsEmpty()) {
    // Don't attempt to prefetch if we don't have a source node
    // (which should never happen).
    return NS_ERROR_FAILURE;
  }
  nsCOMPtr<nsINode> source;
  while (!mSources.IsEmpty() &&
         !(source = do_QueryReferent(mSources.ElementAt(0)))) {
    // If source is null remove it.
    // (which should never happen).
    mSources.RemoveElementAt(0);
  }

  if (!source) {
    // Don't attempt to prefetch if we don't have a source node
    // (which should never happen).

    return NS_ERROR_FAILURE;
  }
  nsCOMPtr<nsILoadGroup> loadGroup = source->OwnerDoc()->GetDocumentLoadGroup();
  CORSMode corsMode = CORS_NONE;
  if (auto* link = dom::HTMLLinkElement::FromNode(source)) {
    corsMode = link->GetCORSMode();
  }

  uint32_t securityFlags;
  if (corsMode == CORS_NONE) {
    securityFlags = nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_INHERITS_SEC_CONTEXT;
  } else {
    securityFlags = nsILoadInfo::SEC_REQUIRE_CORS_INHERITS_SEC_CONTEXT;
    if (corsMode == CORS_USE_CREDENTIALS) {
      securityFlags |= nsILoadInfo::SEC_COOKIES_INCLUDE;
    }
  }
  nsresult rv = NS_NewChannelInternal(
      getter_AddRefs(mChannel), mURI, source, source->NodePrincipal(),
      nullptr,  // aTriggeringPrincipal
      Maybe<ClientInfo>(), Maybe<ServiceWorkerDescriptor>(), securityFlags,
      mPolicyType, source->OwnerDoc()->CookieJarSettings(),
      nullptr,    // aPerformanceStorage
      loadGroup,  // aLoadGroup
      this,       // aCallbacks
      nsIRequest::LOAD_BACKGROUND | nsICachingChannel::LOAD_ONLY_IF_MODIFIED);

  NS_ENSURE_SUCCESS(rv, rv);

  // configure HTTP specific stuff
  nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel);
  if (httpChannel) {
    DebugOnly<nsresult> success = httpChannel->SetReferrerInfo(mReferrerInfo);
    MOZ_ASSERT(NS_SUCCEEDED(success));

    // https://fetch.spec.whatwg.org/#http-sec-purpose
    success =
        httpChannel->SetRequestHeader("Sec-Purpose"_ns, "prefetch"_ns, false);
    MOZ_ASSERT(NS_SUCCEEDED(success));
  }

  // Reduce the priority of prefetch network requests.
  nsCOMPtr<nsISupportsPriority> priorityChannel = do_QueryInterface(mChannel);
  if (priorityChannel) {
    priorityChannel->AdjustPriority(nsISupportsPriority::PRIORITY_LOWEST);
  }

  rv = mChannel->AsyncOpen(this);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    // Drop the ref to the channel, because we don't want to end up with
    // cycles through it.
    mChannel = nullptr;
  }
  return rv;
}

nsresult nsPrefetchNode::CancelChannel(nsresult error) {
  mChannel->Cancel(error);
  mChannel = nullptr;

  return NS_OK;
}

//-----------------------------------------------------------------------------
// nsPrefetchNode::nsISupports
//-----------------------------------------------------------------------------

NS_IMPL_ISUPPORTS(nsPrefetchNode, nsIRequestObserver, nsIStreamListener,
                  nsIInterfaceRequestor, nsIChannelEventSink,
                  nsIRedirectResultListener)

//-----------------------------------------------------------------------------
// nsPrefetchNode::nsIStreamListener
//-----------------------------------------------------------------------------

NS_IMETHODIMP
nsPrefetchNode::OnStartRequest(nsIRequest* aRequest) {
  nsresult rv;

  nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequest, &rv);
  if (NS_FAILED(rv)) return rv;

  // if the load is cross origin without CORS, or the CORS access is rejected,
  // always fire load event to avoid leaking site information.
  nsCOMPtr<nsILoadInfo> loadInfo = httpChannel->LoadInfo();
  mShouldFireLoadEvent =
      loadInfo->GetTainting() == LoadTainting::Opaque ||
      (loadInfo->GetTainting() == LoadTainting::CORS &&
       (NS_FAILED(httpChannel->GetStatus(&rv)) || NS_FAILED(rv)));

  // no need to prefetch http error page
  bool requestSucceeded;
  if (NS_FAILED(httpChannel->GetRequestSucceeded(&requestSucceeded)) ||
      !requestSucceeded) {
    return NS_BINDING_ABORTED;
  }

  nsCOMPtr<nsICacheInfoChannel> cacheInfoChannel =
      do_QueryInterface(aRequest, &rv);
  if (NS_FAILED(rv)) return rv;

  // no need to prefetch a document that is already in the cache
  bool fromCache;
  if (NS_SUCCEEDED(cacheInfoChannel->IsFromCache(&fromCache)) && fromCache) {
    LOG(("document is already in the cache; canceling prefetch\n"));
    // although it's canceled we still want to fire load event
    mShouldFireLoadEvent = true;
    return NS_BINDING_ABORTED;
  }

  //
  // no need to prefetch a document that must be requested fresh each
  // and every time.
  //
  uint32_t expTime;
  if (NS_SUCCEEDED(cacheInfoChannel->GetCacheTokenExpirationTime(&expTime))) {
    if (mozilla::net::NowInSeconds() >= expTime) {
      LOG(
          ("document cannot be reused from cache; "
           "canceling prefetch\n"));
      return NS_BINDING_ABORTED;
    }
  }

  return NS_OK;
}

NS_IMETHODIMP
nsPrefetchNode::OnDataAvailable(nsIRequest* aRequest, nsIInputStream* aStream,
                                uint64_t aOffset, uint32_t aCount) {
  uint32_t bytesRead = 0;
  aStream->ReadSegments(NS_DiscardSegment, nullptr, aCount, &bytesRead);
  mBytesRead += bytesRead;
  LOG(("prefetched %u bytes [offset=%" PRIu64 "]\n", bytesRead, aOffset));
  return NS_OK;
}

NS_IMETHODIMP
nsPrefetchNode::OnStopRequest(nsIRequest* aRequest, nsresult aStatus) {
  LOG(("done prefetching [status=%" PRIx32 "]\n",
       static_cast<uint32_t>(aStatus)));

  if (mBytesRead == 0 && aStatus == NS_OK && mChannel) {
    // we didn't need to read (because LOAD_ONLY_IF_MODIFIED was
    // specified), but the object should report loadedSize as if it
    // did.
    mChannel->GetContentLength(&mBytesRead);
  }

  mService->NotifyLoadCompleted(this);
  mService->DispatchEvent(this, mShouldFireLoadEvent || NS_SUCCEEDED(aStatus));
  mService->RemoveNodeAndMaybeStartNextPrefetchURI(this);
  return NS_OK;
}

//-----------------------------------------------------------------------------
// nsPrefetchNode::nsIInterfaceRequestor
//-----------------------------------------------------------------------------

NS_IMETHODIMP
nsPrefetchNode::GetInterface(const nsIID& aIID, void** aResult) {
  if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) {
    NS_ADDREF_THIS();
    *aResult = static_cast<nsIChannelEventSink*>(this);
    return NS_OK;
  }

  if (aIID.Equals(NS_GET_IID(nsIRedirectResultListener))) {
    NS_ADDREF_THIS();
    *aResult = static_cast<nsIRedirectResultListener*>(this);
    return NS_OK;
  }

  return NS_ERROR_NO_INTERFACE;
}

//-----------------------------------------------------------------------------
// nsPrefetchNode::nsIChannelEventSink
//-----------------------------------------------------------------------------

NS_IMETHODIMP
nsPrefetchNode::AsyncOnChannelRedirect(
    nsIChannel* aOldChannel, nsIChannel* aNewChannel, uint32_t aFlags,
    nsIAsyncVerifyRedirectCallback* callback) {
  nsCOMPtr<nsIURI> newURI;
  nsresult rv = aNewChannel->GetURI(getter_AddRefs(newURI));
  if (NS_FAILED(rv)) return rv;

  if (!newURI->SchemeIs("http") && !newURI->SchemeIs("https")) {
    LOG(("rejected: URL is not of type http/https\n"));
    return NS_ERROR_ABORT;
  }

  // HTTP request headers are not automatically forwarded to the new channel.
  nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aNewChannel);
  NS_ENSURE_STATE(httpChannel);

  // https://fetch.spec.whatwg.org/#http-sec-purpose
  rv = httpChannel->SetRequestHeader("Sec-Purpose"_ns, "prefetch"_ns, false);
  MOZ_ASSERT(NS_SUCCEEDED(rv));

  // Assign to mChannel after we get notification about success of the
  // redirect in OnRedirectResult.
  mRedirectChannel = aNewChannel;

  callback->OnRedirectVerifyCallback(NS_OK);
  return NS_OK;
}

//-----------------------------------------------------------------------------
// nsPrefetchNode::nsIRedirectResultListener
//-----------------------------------------------------------------------------

NS_IMETHODIMP
nsPrefetchNode::OnRedirectResult(nsresult status) {
  if (NS_SUCCEEDED(status) && mRedirectChannel) mChannel = mRedirectChannel;

  mRedirectChannel = nullptr;

  return NS_OK;
}

//-----------------------------------------------------------------------------
// nsPrefetchService <public>
//-----------------------------------------------------------------------------

nsPrefetchService::nsPrefetchService()
    : mMaxParallelism(6),
      mStopCount(0),
      mHaveProcessed(false),
      mPrefetchDisabled(true),
      mAggressive(false) {}

nsPrefetchService::~nsPrefetchService() {
  Preferences::RemoveObserver(this, PREFETCH_PREF);
  Preferences::RemoveObserver(this, PARALLELISM_PREF);
  Preferences::RemoveObserver(this, AGGRESSIVE_PREF);
  // cannot reach destructor if prefetch in progress (listener owns reference
  // to this service)
  EmptyPrefetchQueue();
}

nsresult nsPrefetchService::Init() {
  nsresult rv;

  // read prefs and hook up pref observer
  mPrefetchDisabled = !Preferences::GetBool(PREFETCH_PREF, !mPrefetchDisabled);
  Preferences::AddWeakObserver(this, PREFETCH_PREF);

  mMaxParallelism = Preferences::GetInt(PARALLELISM_PREF, mMaxParallelism);
  if (mMaxParallelism < 1) {
    mMaxParallelism = 1;
  }
  Preferences::AddWeakObserver(this, PARALLELISM_PREF);

  mAggressive = Preferences::GetBool(AGGRESSIVE_PREF, false);
  Preferences::AddWeakObserver(this, AGGRESSIVE_PREF);

  // Observe xpcom-shutdown event
  nsCOMPtr<nsIObserverService> observerService =
      mozilla::services::GetObserverService();
  if (!observerService) return NS_ERROR_FAILURE;

  rv = observerService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, true);
  NS_ENSURE_SUCCESS(rv, rv);

  if (!mPrefetchDisabled) {
    AddProgressListener();
  }

  return NS_OK;
}

void nsPrefetchService::RemoveNodeAndMaybeStartNextPrefetchURI(
    nsPrefetchNode* aFinished) {
  if (aFinished) {
    mCurrentNodes.RemoveElement(aFinished);
  }

  if ((!mStopCount && mHaveProcessed) || mAggressive) {
    ProcessNextPrefetchURI();
  }
}

void nsPrefetchService::ProcessNextPrefetchURI() {
  if (mCurrentNodes.Length() >= static_cast<uint32_t>(mMaxParallelism)) {
    // We already have enough prefetches going on, so hold off
    // for now.
    return;
  }

  nsresult rv;

  do {
    if (mPrefetchQueue.empty()) {
      break;
    }
    RefPtr<nsPrefetchNode> node = std::move(mPrefetchQueue.front());
    mPrefetchQueue.pop_front();

    if (LOG_ENABLED()) {
      LOG(("ProcessNextPrefetchURI [%s]\n",
           node->mURI->GetSpecOrDefault().get()));
    }

    //
    // if opening the channel fails (e.g. security check returns an error),
    // send an error event and then just skip to the next uri
    //
    rv = node->OpenChannel();
    if (NS_SUCCEEDED(rv)) {
      mCurrentNodes.AppendElement(node);
    } else {
      DispatchEvent(node, false);
    }
  } while (NS_FAILED(rv));
}

void nsPrefetchService::NotifyLoadRequested(nsPrefetchNode* node) {
  nsCOMPtr<nsIObserverService> observerService =
      mozilla::services::GetObserverService();
  if (!observerService) return;

  observerService->NotifyObservers(
      static_cast<nsIStreamListener*>(node),
      (node->mPreload) ? "preload-load-requested" : "prefetch-load-requested",
      nullptr);
}

void nsPrefetchService::NotifyLoadCompleted(nsPrefetchNode* node) {
  nsCOMPtr<nsIObserverService> observerService =
      mozilla::services::GetObserverService();
  if (!observerService) return;

  observerService->NotifyObservers(
      static_cast<nsIStreamListener*>(node),
      (node->mPreload) ? "preload-load-completed" : "prefetch-load-completed",
      nullptr);
}

void nsPrefetchService::DispatchEvent(nsPrefetchNode* node, bool aSuccess) {
  for (uint32_t i = 0; i < node->mSources.Length(); i++) {
    nsCOMPtr<nsINode> domNode = do_QueryReferent(node->mSources.ElementAt(i));
    if (domNode && domNode->IsInComposedDoc()) {
      // We don't dispatch synchronously since |node| might be in a DocGroup
      // that we're not allowed to touch. (Our network request happens in the
      // DocGroup of one of the mSources nodes--not necessarily this one).
      RefPtr<AsyncEventDispatcher> dispatcher = new AsyncEventDispatcher(
          domNode, aSuccess ? u"load"_ns : u"error"_ns, CanBubble::eNo);
      dispatcher->RequireNodeInDocument();
      dispatcher->PostDOMEvent();
    }
  }
}

//-----------------------------------------------------------------------------
// nsPrefetchService <private>
//-----------------------------------------------------------------------------

void nsPrefetchService::AddProgressListener() {
  // Register as an observer for the document loader
  nsCOMPtr<nsIWebProgress> progress = components::DocLoader::Service();
  if (progress)
    progress->AddProgressListener(this, nsIWebProgress::NOTIFY_STATE_DOCUMENT);
}

void nsPrefetchService::RemoveProgressListener() {
  // Register as an observer for the document loader
  nsCOMPtr<nsIWebProgress> progress = components::DocLoader::Service();
  if (progress) progress->RemoveProgressListener(this);
}

nsresult nsPrefetchService::EnqueueURI(nsIURI* aURI,
                                       nsIReferrerInfo* aReferrerInfo,
                                       nsINode* aSource,
                                       nsPrefetchNode** aNode) {
  RefPtr<nsPrefetchNode> node = new nsPrefetchNode(
      this, aURI, aReferrerInfo, aSource, nsIContentPolicy::TYPE_OTHER, false);
  mPrefetchQueue.push_back(node);
  node.forget(aNode);
  return NS_OK;
}

void nsPrefetchService::EmptyPrefetchQueue() {
  while (!mPrefetchQueue.empty()) {
    mPrefetchQueue.pop_back();
  }
}

void nsPrefetchService::StartPrefetching() {
  //
  // at initialization time we might miss the first DOCUMENT START
  // notification, so we have to be careful to avoid letting our
  // stop count go negative.
  //
  if (mStopCount > 0) mStopCount--;

  LOG(("StartPrefetching [stopcount=%d]\n", mStopCount));

  // only start prefetching after we've received enough DOCUMENT
  // STOP notifications.  we do this inorder to defer prefetching
  // until after all sub-frames have finished loading.
  if (!mStopCount) {
    mHaveProcessed = true;
    while (!mPrefetchQueue.empty() &&
           mCurrentNodes.Length() < static_cast<uint32_t>(mMaxParallelism)) {
      ProcessNextPrefetchURI();
    }
  }
}

void nsPrefetchService::StopPrefetching() {
  mStopCount++;

  LOG(("StopPrefetching [stopcount=%d]\n", mStopCount));

  // When we start a load, we need to stop all prefetches that has been
  // added by the old load, therefore call StopAll only at the moment we
  // switch to a new page load (i.e. mStopCount == 1).
  // TODO: do not stop prefetches that are relevant for the new load.
  if (mStopCount == 1) {
    StopAll();
  }
}

void nsPrefetchService::StopCurrentPrefetchsPreloads(bool aPreload) {
  for (int32_t i = mCurrentNodes.Length() - 1; i >= 0; --i) {
    if (mCurrentNodes[i]->mPreload == aPreload) {
      mCurrentNodes[i]->CancelChannel(NS_BINDING_ABORTED);
      mCurrentNodes.RemoveElementAt(i);
    }
  }

  if (!aPreload) {
    EmptyPrefetchQueue();
  }
}

void nsPrefetchService::StopAll() {
  for (uint32_t i = 0; i < mCurrentNodes.Length(); ++i) {
    mCurrentNodes[i]->CancelChannel(NS_BINDING_ABORTED);
  }
  mCurrentNodes.Clear();
  EmptyPrefetchQueue();
}

nsresult nsPrefetchService::CheckURIScheme(nsIURI* aURI,
                                           nsIReferrerInfo* aReferrerInfo) {
  //
  // XXX we should really be asking the protocol handler if it supports
  // caching, so we can determine if there is any value to prefetching.
  // for now, we'll only prefetch http and https links since we know that's
  // the most common case.
  //
  if (!aURI->SchemeIs("http") && !aURI->SchemeIs("https")) {
    LOG(("rejected: URL is not of type http/https\n"));
    return NS_ERROR_ABORT;
  }

  //
  // the referrer URI must be http:
  //
  nsCOMPtr<nsIURI> referrer = aReferrerInfo->GetOriginalReferrer();
  if (!referrer) {
    return NS_ERROR_ABORT;
  }

  if (!referrer->SchemeIs("http") && !referrer->SchemeIs("https")) {
    LOG(("rejected: referrer URL is neither http nor https\n"));
    return NS_ERROR_ABORT;
  }

  return NS_OK;
}

//-----------------------------------------------------------------------------
// nsPrefetchService::nsISupports
//-----------------------------------------------------------------------------

NS_IMPL_ISUPPORTS(nsPrefetchService, nsIPrefetchService, nsIWebProgressListener,
                  nsIObserver, nsISupportsWeakReference)

//-----------------------------------------------------------------------------
// nsPrefetchService::nsIPrefetchService
//-----------------------------------------------------------------------------

nsresult nsPrefetchService::Preload(nsIURI* aURI,
                                    nsIReferrerInfo* aReferrerInfo,
                                    nsINode* aSource,
                                    nsContentPolicyType aPolicyType) {
  NS_ENSURE_ARG_POINTER(aURI);
  NS_ENSURE_ARG_POINTER(aReferrerInfo);
  if (LOG_ENABLED()) {
    LOG(("PreloadURI [%s]\n", aURI->GetSpecOrDefault().get()));
  }

  LOG(("rejected: preload service is deprecated\n"));
  return NS_ERROR_ABORT;
}

nsresult nsPrefetchService::Prefetch(nsIURI* aURI,
                                     nsIReferrerInfo* aReferrerInfo,
                                     nsINode* aSource, bool aExplicit) {
  NS_ENSURE_ARG_POINTER(aURI);
  NS_ENSURE_ARG_POINTER(aReferrerInfo);

  if (LOG_ENABLED()) {
    LOG(("PrefetchURI [%s]\n", aURI->GetSpecOrDefault().get()));
  }

  if (mPrefetchDisabled) {
    LOG(("rejected: prefetch service is disabled\n"));
    return NS_ERROR_ABORT;
  }

  nsresult rv = CheckURIScheme(aURI, aReferrerInfo);
  if (NS_FAILED(rv)) {
    return rv;
  }

  // XXX we might want to either leverage nsIProtocolHandler::protocolFlags
  // or possibly nsIRequest::loadFlags to determine if this URI should be
  // prefetched.
  //

  // skip URLs that contain query strings, except URLs for which prefetching
  // has been explicitly requested.
  if (!aExplicit) {
    nsCOMPtr<nsIURL> url(do_QueryInterface(aURI, &rv));
    if (NS_FAILED(rv)) return rv;
    nsAutoCString query;
    rv = url->GetQuery(query);
    if (NS_FAILED(rv) || !query.IsEmpty()) {
      LOG(("rejected: URL has a query string\n"));
      return NS_ERROR_ABORT;
    }
  }

  //
  // Check whether it is being prefetched.
  //
  for (uint32_t i = 0; i < mCurrentNodes.Length(); ++i) {
    bool equals;
    if (NS_SUCCEEDED(mCurrentNodes[i]->mURI->Equals(aURI, &equals)) && equals) {
      nsWeakPtr source = do_GetWeakReference(aSource);
      if (mCurrentNodes[i]->mSources.IndexOf(source) ==
          mCurrentNodes[i]->mSources.NoIndex) {
        LOG(
            ("URL is already being prefetched, add a new reference "
             "document\n"));
        mCurrentNodes[i]->mSources.AppendElement(source);
        return NS_OK;
      } else {
        LOG(("URL is already being prefetched by this document"));
        return NS_ERROR_ABORT;
      }
    }
  }

  //
  // Check whether it is on the prefetch queue.
  //
  for (std::deque<RefPtr<nsPrefetchNode>>::iterator nodeIt =
           mPrefetchQueue.begin();
       nodeIt != mPrefetchQueue.end(); nodeIt++) {
    bool equals;
    RefPtr<nsPrefetchNode> node = nodeIt->get();
    if (NS_SUCCEEDED(node->mURI->Equals(aURI, &equals)) && equals) {
      nsWeakPtr source = do_GetWeakReference(aSource);
      if (node->mSources.IndexOf(source) == node->mSources.NoIndex) {
        LOG(
            ("URL is already being prefetched, add a new reference "
             "document\n"));
        node->mSources.AppendElement(do_GetWeakReference(aSource));
        return NS_OK;
      } else {
        LOG(("URL is already being prefetched by this document"));
        return NS_ERROR_ABORT;
      }
    }
  }

  RefPtr<nsPrefetchNode> enqueuedNode;
  rv = EnqueueURI(aURI, aReferrerInfo, aSource, getter_AddRefs(enqueuedNode));
  NS_ENSURE_SUCCESS(rv, rv);

  NotifyLoadRequested(enqueuedNode);

  // if there are no pages loading, kick off the request immediately
  if ((!mStopCount && mHaveProcessed) || mAggressive) {
    ProcessNextPrefetchURI();
  }

  return NS_OK;
}

NS_IMETHODIMP
nsPrefetchService::CancelPrefetchPreloadURI(nsIURI* aURI, nsINode* aSource) {
  NS_ENSURE_ARG_POINTER(aURI);

  if (LOG_ENABLED()) {
    LOG(("CancelPrefetchURI [%s]\n", aURI->GetSpecOrDefault().get()));
  }

  //
  // look in current prefetches
  //
  for (uint32_t i = 0; i < mCurrentNodes.Length(); ++i) {
    bool equals;
    if (NS_SUCCEEDED(mCurrentNodes[i]->mURI->Equals(aURI, &equals)) && equals) {
      nsWeakPtr source = do_GetWeakReference(aSource);
      if (mCurrentNodes[i]->mSources.IndexOf(source) !=
          mCurrentNodes[i]->mSources.NoIndex) {
        mCurrentNodes[i]->mSources.RemoveElement(source);
        if (mCurrentNodes[i]->mSources.IsEmpty()) {
          mCurrentNodes[i]->CancelChannel(NS_BINDING_ABORTED);
          mCurrentNodes.RemoveElementAt(i);
        }
        return NS_OK;
      }
      return NS_ERROR_FAILURE;
    }
  }

  //
  // look into the prefetch queue
  //
  for (std::deque<RefPtr<nsPrefetchNode>>::iterator nodeIt =
           mPrefetchQueue.begin();
       nodeIt != mPrefetchQueue.end(); nodeIt++) {
    bool equals;
    RefPtr<nsPrefetchNode> node = nodeIt->get();
    if (NS_SUCCEEDED(node->mURI->Equals(aURI, &equals)) && equals) {
      nsWeakPtr source = do_GetWeakReference(aSource);
      if (node->mSources.IndexOf(source) != node->mSources.NoIndex) {
#ifdef DEBUG
        int32_t inx = node->mSources.IndexOf(source);
        nsCOMPtr<nsINode> domNode =
            do_QueryReferent(node->mSources.ElementAt(inx));
        MOZ_ASSERT(domNode);
#endif

        node->mSources.RemoveElement(source);
        if (node->mSources.IsEmpty()) {
          mPrefetchQueue.erase(nodeIt);
        }
        return NS_OK;
      }
      return NS_ERROR_FAILURE;
    }
  }

  // not found!
  return NS_ERROR_FAILURE;
}

NS_IMETHODIMP
nsPrefetchService::PreloadURI(nsIURI* aURI, nsIReferrerInfo* aReferrerInfo,
                              nsINode* aSource,
                              nsContentPolicyType aPolicyType) {
  return Preload(aURI, aReferrerInfo, aSource, aPolicyType);
}

NS_IMETHODIMP
nsPrefetchService::PrefetchURI(nsIURI* aURI, nsIReferrerInfo* aReferrerInfo,
                               nsINode* aSource, bool aExplicit) {
  return Prefetch(aURI, aReferrerInfo, aSource, aExplicit);
}

NS_IMETHODIMP
nsPrefetchService::HasMoreElements(bool* aHasMore) {
  *aHasMore = (mCurrentNodes.Length() || !mPrefetchQueue.empty());
  return NS_OK;
}

//-----------------------------------------------------------------------------
// nsPrefetchService::nsIWebProgressListener
//-----------------------------------------------------------------------------

NS_IMETHODIMP
nsPrefetchService::OnProgressChange(nsIWebProgress* aProgress,
                                    nsIRequest* aRequest,
                                    int32_t curSelfProgress,
                                    int32_t maxSelfProgress,
                                    int32_t curTotalProgress,
                                    int32_t maxTotalProgress) {
  MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
  return NS_OK;
}

NS_IMETHODIMP
nsPrefetchService::OnStateChange(nsIWebProgress* aWebProgress,
                                 nsIRequest* aRequest,
                                 uint32_t progressStateFlags,
                                 nsresult aStatus) {
  if (progressStateFlags & STATE_IS_DOCUMENT) {
    if (progressStateFlags & STATE_STOP)
      StartPrefetching();
    else if (progressStateFlags & STATE_START)
      StopPrefetching();
  }

  return NS_OK;
}

NS_IMETHODIMP
nsPrefetchService::OnLocationChange(nsIWebProgress* aWebProgress,
                                    nsIRequest* aRequest, nsIURI* location,
                                    uint32_t aFlags) {
  MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
  return NS_OK;
}

NS_IMETHODIMP
nsPrefetchService::OnStatusChange(nsIWebProgress* aWebProgress,
                                  nsIRequest* aRequest, nsresult aStatus,
                                  const char16_t* aMessage) {
  MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
  return NS_OK;
}

NS_IMETHODIMP
nsPrefetchService::OnSecurityChange(nsIWebProgress* aWebProgress,
                                    nsIRequest* aRequest, uint32_t aState) {
  MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
  return NS_OK;
}

NS_IMETHODIMP
nsPrefetchService::OnContentBlockingEvent(nsIWebProgress* aWebProgress,
                                          nsIRequest* aRequest,
                                          uint32_t aEvent) {
  MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
  return NS_OK;
}

//-----------------------------------------------------------------------------
// nsPrefetchService::nsIObserver
//-----------------------------------------------------------------------------

NS_IMETHODIMP
nsPrefetchService::Observe(nsISupports* aSubject, const char* aTopic,
                           const char16_t* aData) {
  LOG(("nsPrefetchService::Observe [topic=%s]\n", aTopic));

  if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
    StopAll();
    mPrefetchDisabled = true;
  } else if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
    const nsCString converted = NS_ConvertUTF16toUTF8(aData);
    const char* pref = converted.get();
    if (!strcmp(pref, PREFETCH_PREF)) {
      if (Preferences::GetBool(PREFETCH_PREF, false)) {
        if (mPrefetchDisabled) {
          LOG(("enabling prefetching\n"));
          mPrefetchDisabled = false;
          AddProgressListener();
        }
      } else {
        if (!mPrefetchDisabled) {
          LOG(("disabling prefetching\n"));
          StopCurrentPrefetchsPreloads(false);
          mPrefetchDisabled = true;
          RemoveProgressListener();
        }
      }
    } else if (!strcmp(pref, PARALLELISM_PREF)) {
      mMaxParallelism = Preferences::GetInt(PARALLELISM_PREF, mMaxParallelism);
      if (mMaxParallelism < 1) {
        mMaxParallelism = 1;
      }
      // If our parallelism has increased, go ahead and kick off enough
      // prefetches to fill up our allowance. If we're now over our
      // allowance, we'll just silently let some of them finish to get
      // back below our limit.
      while (((!mStopCount && mHaveProcessed) || mAggressive) &&
             !mPrefetchQueue.empty() &&
             mCurrentNodes.Length() < static_cast<uint32_t>(mMaxParallelism)) {
        ProcessNextPrefetchURI();
      }
    } else if (!strcmp(pref, AGGRESSIVE_PREF)) {
      mAggressive = Preferences::GetBool(AGGRESSIVE_PREF, false);
      // in aggressive mode, start prefetching immediately
      if (mAggressive) {
        while (mStopCount && !mPrefetchQueue.empty() &&
               mCurrentNodes.Length() <
                   static_cast<uint32_t>(mMaxParallelism)) {
          ProcessNextPrefetchURI();
        }
      }
    }
  }

  return NS_OK;
}

// vim: ts=4 sw=2 expandtab

89%


¤ Dauer der Verarbeitung: 0.18 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung ist noch experimentell.