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

Quelle  BlobURLProtocolHandler.cpp   Sprache: C

 
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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 "BlobURLProtocolHandler.h"
#include "BlobURLChannel.h"
#include "mozilla/dom/BlobURL.h"

#include "mozilla/dom/ChromeUtils.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/dom/ContentParent.h"
#include "mozilla/dom/Exceptions.h"
#include "mozilla/dom/BlobImpl.h"
#include "mozilla/dom/IPCBlobUtils.h"
#include "mozilla/dom/MediaSource.h"
#include "mozilla/ipc/IPCStreamUtils.h"
#include "mozilla/AppShutdown.h"
#include "mozilla/BasePrincipal.h"
#include "mozilla/LoadInfo.h"
#include "mozilla/Maybe.h"
#include "mozilla/NullPrincipal.h"
#include "mozilla/OriginAttributes.h"
#include "mozilla/Preferences.h"
#include "mozilla/SchedulerGroup.h"
#include "mozilla/ScopeExit.h"
#include "nsClassHashtable.h"
#include "nsContentUtils.h"
#include "nsError.h"
#include "nsIAsyncShutdown.h"
#include "nsIDUtils.h"
#include "nsIException.h"  // for nsIStackFrame
#include "nsIMemoryReporter.h"
#include "nsIPrincipal.h"
#include "nsIUUIDGenerator.h"
#include "nsNetUtil.h"
#include "nsReadableUtils.h"

#define RELEASING_TIMER 5000

namespace mozilla {

using namespace ipc;

namespace dom {

// -----------------------------------------------------------------------
// Hash table
struct DataInfo {
  enum ObjectType { eBlobImpl, eMediaSource };

  DataInfo(mozilla::dom::BlobImpl* aBlobImpl, nsIPrincipal* aPrincipal,
           const nsCString& aPartitionKey)
      : mObjectType(eBlobImpl),
        mBlobImpl(aBlobImpl),
        mPrincipal(aPrincipal),
        mPartitionKey(aPartitionKey),
        mRevoked(false) {
    MOZ_ASSERT(aPrincipal);
  }

  DataInfo(MediaSource* aMediaSource, nsIPrincipal* aPrincipal,
           const nsCString& aPartitionKey)
      : mObjectType(eMediaSource),
        mMediaSource(aMediaSource),
        mPrincipal(aPrincipal),
        mPartitionKey(aPartitionKey),
        mRevoked(false) {
    MOZ_ASSERT(aPrincipal);
  }

  ObjectType mObjectType;

  RefPtr<BlobImpl> mBlobImpl;
  RefPtr<MediaSource> mMediaSource;

  nsCOMPtr<nsIPrincipal> mPrincipal;

  nsCString mPartitionKey;

  nsCString mStack;

  // When a blobURL is revoked, we keep it alive for RELEASING_TIMER
  // milliseconds in order to support pending operations such as navigation,
  // download and so on.
  bool mRevoked;
};

// The mutex is locked whenever gDataTable is changed, or if gDataTable
// is accessed off-main-thread.
static StaticMutex sMutex MOZ_UNANNOTATED;

// All changes to gDataTable must happen on the main thread, while locking
// sMutex. Reading from gDataTable on the main thread may happen without
// locking, since no changes are possible. Reading it from another thread
// must also lock sMutex to prevent data races.
static nsClassHashtable<nsCStringHashKey, mozilla::dom::DataInfo>* gDataTable;

static mozilla::dom::DataInfo* GetDataInfo(const nsACString& aUri,
                                           bool aAlsoIfRevoked = false) {
  if (!gDataTable) {
    return nullptr;
  }

  // Let's remove any fragment from this URI.
  int32_t fragmentPos = aUri.FindChar('#');

  mozilla::dom::DataInfo* res;
  if (fragmentPos < 0) {
    res = gDataTable->Get(aUri);
  } else {
    res = gDataTable->Get(StringHead(aUri, fragmentPos));
  }

  if (!aAlsoIfRevoked && res && res->mRevoked) {
    return nullptr;
  }

  return res;
}

static mozilla::dom::DataInfo* GetDataInfoFromURI(nsIURI* aURI,
                                                  bool aAlsoIfRevoked = false) {
  if (!aURI) {
    return nullptr;
  }

  nsCString spec;
  nsresult rv = aURI->GetSpec(spec);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return nullptr;
  }

  return GetDataInfo(spec, aAlsoIfRevoked);
}

// Memory reporting for the hash table.
void BroadcastBlobURLRegistration(const nsACString& aURI,
                                  mozilla::dom::BlobImpl* aBlobImpl,
                                  nsIPrincipal* aPrincipal,
                                  const nsCString& aPartitionKey) {
  MOZ_ASSERT(NS_IsMainThread());
  MOZ_ASSERT(aBlobImpl);
  MOZ_ASSERT(aPrincipal);

  if (XRE_IsParentProcess()) {
    dom::ContentParent::BroadcastBlobURLRegistration(aURI, aBlobImpl,
                                                     aPrincipal, aPartitionKey);
    return;
  }

  IPCBlob ipcBlob;
  nsresult rv = IPCBlobUtils::Serialize(aBlobImpl, ipcBlob);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return;
  }

  dom::ContentChild* cc = dom::ContentChild::GetSingleton();
  (void)NS_WARN_IF(!cc->SendStoreAndBroadcastBlobURLRegistration(
      nsCString(aURI), ipcBlob, aPrincipal, aPartitionKey));
}

void BroadcastBlobURLUnregistration(const nsCString& aURI,
                                    nsIPrincipal* aPrincipal) {
  MOZ_ASSERT(NS_IsMainThread());

  if (XRE_IsParentProcess()) {
    dom::ContentParent::BroadcastBlobURLUnregistration(aURI, aPrincipal);
    return;
  }

  dom::ContentChild* cc = dom::ContentChild::GetSingleton();
  if (cc) {
    (void)NS_WARN_IF(
        !cc->SendUnstoreAndBroadcastBlobURLUnregistration(aURI, aPrincipal));
  }
}

class BlobURLsReporter final : public nsIMemoryReporter {
 public:
  NS_DECL_ISUPPORTS

  NS_IMETHOD CollectReports(nsIHandleReportCallback* aCallback,
                            nsISupports* aData, bool aAnonymize) override {
    MOZ_ASSERT(NS_IsMainThread(),
               "without locking gDataTable is main-thread only");
    if (!gDataTable) {
      return NS_OK;
    }

    nsTHashMap<nsPtrHashKey<mozilla::dom::BlobImpl>, uint32_t> refCounts;

    // Determine number of URLs per mozilla::dom::BlobImpl, to handle the case
    // where it's > 1.
    for (const auto& entry : *gDataTable) {
      if (entry.GetWeak()->mObjectType != mozilla::dom::DataInfo::eBlobImpl) {
        continue;
      }

      mozilla::dom::BlobImpl* blobImpl = entry.GetWeak()->mBlobImpl;
      MOZ_ASSERT(blobImpl);

      refCounts.LookupOrInsert(blobImpl, 0) += 1;
    }

    for (const auto& entry : *gDataTable) {
      nsCStringHashKey::KeyType key = entry.GetKey();
      mozilla::dom::DataInfo* info = entry.GetWeak();

      if (entry.GetWeak()->mObjectType == mozilla::dom::DataInfo::eBlobImpl) {
        mozilla::dom::BlobImpl* blobImpl = entry.GetWeak()->mBlobImpl;
        MOZ_ASSERT(blobImpl);

        constexpr auto desc =
            "A blob URL allocated with URL.createObjectURL; the referenced "
            "blob cannot be freed until all URLs for it have been explicitly "
            "invalidated with URL.revokeObjectURL."_ns;
        nsAutoCString path, url, owner, specialDesc;
        uint64_t size = 0;
        uint32_t refCount = 1;
        DebugOnly<bool> blobImplWasCounted;

        blobImplWasCounted = refCounts.Get(blobImpl, &refCount);
        MOZ_ASSERT(blobImplWasCounted);
        MOZ_ASSERT(refCount > 0);

        bool isMemoryFile = blobImpl->IsMemoryFile();

        if (isMemoryFile) {
          ErrorResult rv;
          size = blobImpl->GetSize(rv);
          if (NS_WARN_IF(rv.Failed())) {
            rv.SuppressException();
            size = 0;
          }
        }

        path = isMemoryFile ? "memory-blob-urls/" : "file-blob-urls/";
        BuildPath(path, key, info, aAnonymize);

        if (refCount > 1) {
          nsAutoCString addrStr;

          addrStr = "0x";
          addrStr.AppendInt((uint64_t)(mozilla::dom::BlobImpl*)blobImpl, 16);

          path += " ";
          path.AppendInt(refCount);
          path += "@";
          path += addrStr;

          specialDesc = desc;
          specialDesc += "\n\nNOTE: This blob (address ";
          specialDesc += addrStr;
          specialDesc += ") has ";
          specialDesc.AppendInt(refCount);
          specialDesc += " URLs.";
          if (isMemoryFile) {
            specialDesc += " Its size is divided ";
            specialDesc += refCount > 2 ? "among" : "between";
            specialDesc += " them in this report.";
          }
        }

        const nsACString& descString =
            specialDesc.IsEmpty() ? static_cast<const nsACString&>(desc)
                                  : static_cast<const nsACString&>(specialDesc);
        if (isMemoryFile) {
          aCallback->Callback(""_ns, path, KIND_OTHER, UNITS_BYTES,
                              size / refCount, descString, aData);
        } else {
          aCallback->Callback(""_ns, path, KIND_OTHER, UNITS_COUNT, 1,
                              descString, aData);
        }
        continue;
      }

      // Just report the path for the MediaSource.
      nsAutoCString path;
      path = "media-source-urls/";
      BuildPath(path, key, info, aAnonymize);

      constexpr auto desc =
          "An object URL allocated with URL.createObjectURL; the referenced "
          "data cannot be freed until all URLs for it have been explicitly "
          "invalidated with URL.revokeObjectURL."_ns;

      aCallback->Callback(""_ns, path, KIND_OTHER, UNITS_COUNT, 1, desc, aData);
    }

    return NS_OK;
  }

  // Initialize info->mStack to record JS stack info, if enabled.
  // The string generated here is used in ReportCallback, below.
  static void GetJSStackForBlob(mozilla::dom::DataInfo* aInfo) {
    nsCString& stack = aInfo->mStack;
    MOZ_ASSERT(stack.IsEmpty());
    const uint32_t maxFrames =
        Preferences::GetUint("memory.blob_report.stack_frames");

    if (maxFrames == 0) {
      return;
    }

    nsCOMPtr<nsIStackFrame> frame = dom::GetCurrentJSStack(maxFrames);

    nsAutoCString origin;

    aInfo->mPrincipal->GetPrePath(origin);

    // If we got a frame, we better have a current JSContext.  This is cheating
    // a bit; ideally we'd have our caller pass in a JSContext, or have
    // GetCurrentJSStack() hand out the JSContext it found.
    JSContext* cx = frame ? nsContentUtils::GetCurrentJSContext() : nullptr;

    while (frame) {
      nsCString fileName;
      frame->GetFilename(cx, fileName);

      int32_t lineNumber = frame->GetLineNumber(cx);

      if (!fileName.IsEmpty()) {
        stack += "js(";
        if (!origin.IsEmpty()) {
          // Make the file name root-relative for conciseness if possible.
          const char* originData;
          uint32_t originLen;

          originLen = origin.GetData(&originData);
          // If fileName starts with origin + "/", cut up to that "/".
          if (fileName.Length() >= originLen + 1 &&
              memcmp(fileName.get(), originData, originLen) == 0 &&
              fileName[originLen] == '/') {
            fileName.Cut(0, originLen);
          }
        }
        fileName.ReplaceChar('/''\\');
        stack += fileName;
        if (lineNumber > 0) {
          stack += ", line=";
          stack.AppendInt(lineNumber);
        }
        stack += ")/";
      }

      frame = frame->GetCaller(cx);
    }
  }

 private:
  ~BlobURLsReporter() = default;

  static void BuildPath(nsAutoCString& path, nsCStringHashKey::KeyType aKey,
                        mozilla::dom::DataInfo* aInfo, bool anonymize) {
    nsAutoCString url, owner;
    aInfo->mPrincipal->GetAsciiSpec(owner);
    if (!owner.IsEmpty()) {
      owner.ReplaceChar('/''\\');
      path += "owner(";
      if (anonymize) {
        path += "";
      } else {
        path += owner;
      }
      path += ")";
    } else {
      path += "owner unknown";
    }
    path += "/";
    if (anonymize) {
      path += "";
    } else {
      path += aInfo->mStack;
    }
    url = aKey;
    url.ReplaceChar('/''\\');
    if (anonymize) {
      path += "";
    } else {
      path += url;
    }
  }
};

NS_IMPL_ISUPPORTS(BlobURLsReporter, nsIMemoryReporter)

class ReleasingTimerHolder final : public Runnable,
                                   public nsITimerCallback,
                                   public nsIAsyncShutdownBlocker {
 public:
  NS_DECL_ISUPPORTS_INHERITED

  static void Create(const nsACString& aURI) {
    MOZ_ASSERT(NS_IsMainThread());

    RefPtr<ReleasingTimerHolder> holder = new ReleasingTimerHolder(aURI);

    // BlobURLProtocolHandler::RemoveDataEntry potentially happens late. We are
    // prepared to RevokeUri synchronously if we run after XPCOMWillShutdown,
    // but we need at least to be able to dispatch to the main thread here.
    auto raii = MakeScopeExit([holder] { holder->CancelTimerAndRevokeURI(); });

    nsresult rv = SchedulerGroup::Dispatch(holder.forget());
    NS_ENSURE_SUCCESS_VOID(rv);

    raii.release();
  }

  // Runnable interface

  NS_IMETHOD
  Run() override {
    RefPtr<ReleasingTimerHolder> self = this;
    auto raii = MakeScopeExit([self] { self->CancelTimerAndRevokeURI(); });

    nsresult rv = NS_NewTimerWithCallback(
        getter_AddRefs(mTimer), this, RELEASING_TIMER, nsITimer::TYPE_ONE_SHOT);
    NS_ENSURE_SUCCESS(rv, NS_OK);

    nsCOMPtr<nsIAsyncShutdownClient> phase = GetShutdownPhase();
    NS_ENSURE_TRUE(!!phase, NS_OK);

    rv = phase->AddBlocker(this, NS_LITERAL_STRING_FROM_CSTRING(__FILE__),
                           __LINE__, u"ReleasingTimerHolder shutdown"_ns);
    NS_ENSURE_SUCCESS(rv, NS_OK);

    raii.release();
    return NS_OK;
  }

  // nsITimerCallback interface

  NS_IMETHOD
  Notify(nsITimer* aTimer) override {
    RevokeURI();
    return NS_OK;
  }

#ifdef MOZ_COLLECTING_RUNNABLE_TELEMETRY
  using nsINamed::GetName;
#endif

  // nsIAsyncShutdownBlocker interface

  NS_IMETHOD
  GetName(nsAString& aName) override {
    aName.AssignLiteral("ReleasingTimerHolder for blobURL: ");
    aName.Append(NS_ConvertUTF8toUTF16(mURI));
    return NS_OK;
  }

  NS_IMETHOD
  BlockShutdown(nsIAsyncShutdownClient* aClient) override {
    CancelTimerAndRevokeURI();
    return NS_OK;
  }

  NS_IMETHOD
  GetState(nsIPropertyBag**) override { return NS_OK; }

 private:
  explicit ReleasingTimerHolder(const nsACString& aURI)
      : Runnable("ReleasingTimerHolder"), mURI(aURI) {}

  ~ReleasingTimerHolder() override = default;

  void RevokeURI() {
    // Remove the shutting down blocker
    nsCOMPtr<nsIAsyncShutdownClient> phase = GetShutdownPhase();
    if (phase) {
      phase->RemoveBlocker(this);
    }

    MOZ_ASSERT(NS_IsMainThread(),
               "without locking gDataTable is main-thread only");
    mozilla::dom::DataInfo* info =
        GetDataInfo(mURI, true /* We care about revoked dataInfo */);
    if (!info) {
      // Already gone!
      return;
    }

    MOZ_ASSERT(info->mRevoked);

    StaticMutexAutoLock lock(sMutex);
    gDataTable->Remove(mURI);
    if (gDataTable->Count() == 0) {
      delete gDataTable;
      gDataTable = nullptr;
    }
  }

  void CancelTimerAndRevokeURI() {
    if (mTimer) {
      mTimer->Cancel();
      mTimer = nullptr;
    }

    RevokeURI();
  }

  static nsCOMPtr<nsIAsyncShutdownClient> GetShutdownPhase() {
    nsCOMPtr<nsIAsyncShutdownService> svc = services::GetAsyncShutdownService();
    NS_ENSURE_TRUE(!!svc, nullptr);

    nsCOMPtr<nsIAsyncShutdownClient> phase;
    nsresult rv = svc->GetXpcomWillShutdown(getter_AddRefs(phase));
    NS_ENSURE_SUCCESS(rv, nullptr);

    return phase;
  }

  nsCString mURI;
  nsCOMPtr<nsITimer> mTimer;
};

NS_IMPL_ISUPPORTS_INHERITED(ReleasingTimerHolder, Runnable, nsITimerCallback,
                            nsIAsyncShutdownBlocker)

template <typename T>
static void AddDataEntryInternal(const nsACString& aURI, T aObject,
                                 nsIPrincipal* aPrincipal,
                                 const nsCString& aPartitionKey) {
  MOZ_ASSERT(NS_IsMainThread(), "changing gDataTable is main-thread only");
  StaticMutexAutoLock lock(sMutex);
  if (!gDataTable) {
    gDataTable = new nsClassHashtable<nsCStringHashKey, mozilla::dom::DataInfo>;
  }

  mozilla::UniquePtr<mozilla::dom::DataInfo> info =
      mozilla::MakeUnique<mozilla::dom::DataInfo>(aObject, aPrincipal,
                                                  aPartitionKey);
  BlobURLsReporter::GetJSStackForBlob(info.get());

  gDataTable->InsertOrUpdate(aURI, std::move(info));
}

void BlobURLProtocolHandler::Init(void) {
  static bool initialized = false;

  if (!initialized) {
    initialized = true;
    RegisterStrongMemoryReporter(new BlobURLsReporter());
  }
}

BlobURLProtocolHandler::BlobURLProtocolHandler() { Init(); }

BlobURLProtocolHandler::~BlobURLProtocolHandler() = default;

/* static */
nsresult BlobURLProtocolHandler::AddDataEntry(mozilla::dom::BlobImpl* aBlobImpl,
                                              nsIPrincipal* aPrincipal,
                                              const nsCString& aPartitionKey,
                                              nsACString& aUri) {
  MOZ_ASSERT(aBlobImpl);
  MOZ_ASSERT(aPrincipal);

  Init();

  nsresult rv = GenerateURIString(aPrincipal, aUri);
  NS_ENSURE_SUCCESS(rv, rv);

  AddDataEntryInternal(aUri, aBlobImpl, aPrincipal, aPartitionKey);

  BroadcastBlobURLRegistration(aUri, aBlobImpl, aPrincipal, aPartitionKey);
  return NS_OK;
}

/* static */
nsresult BlobURLProtocolHandler::AddDataEntry(MediaSource* aMediaSource,
                                              nsIPrincipal* aPrincipal,
                                              const nsCString& aPartitionKey,
                                              nsACString& aUri) {
  MOZ_ASSERT(aMediaSource);
  MOZ_ASSERT(aPrincipal);

  Init();

  nsresult rv = GenerateURIString(aPrincipal, aUri);
  NS_ENSURE_SUCCESS(rv, rv);

  AddDataEntryInternal(aUri, aMediaSource, aPrincipal, aPartitionKey);
  return NS_OK;
}

/* static */
void BlobURLProtocolHandler::AddDataEntry(const nsACString& aURI,
                                          nsIPrincipal* aPrincipal,
                                          const nsCString& aPartitionKey,
                                          mozilla::dom::BlobImpl* aBlobImpl) {
  MOZ_ASSERT(aPrincipal);
  MOZ_ASSERT(aBlobImpl);
  AddDataEntryInternal(aURI, aBlobImpl, aPrincipal, aPartitionKey);
}

/* static */
bool BlobURLProtocolHandler::ForEachBlobURL(
    std::function<bool(mozilla::dom::BlobImpl*, nsIPrincipal*, const nsCString&,
                       const nsACString&, bool aRevoked)>&& aCb) {
  MOZ_ASSERT(NS_IsMainThread());

  if (!gDataTable) {
    return false;
  }

  for (const auto& entry : *gDataTable) {
    mozilla::dom::DataInfo* info = entry.GetWeak();
    MOZ_ASSERT(info);

    if (info->mObjectType != mozilla::dom::DataInfo::eBlobImpl) {
      continue;
    }

    MOZ_ASSERT(info->mBlobImpl);
    if (!aCb(info->mBlobImpl, info->mPrincipal, info->mPartitionKey,
             entry.GetKey(), info->mRevoked)) {
      return false;
    }
  }

  return true;
}

/*static */
void BlobURLProtocolHandler::RemoveDataEntry(const nsACString& aUri,
                                             bool aBroadcastToOtherProcesses) {
  MOZ_ASSERT(NS_IsMainThread(), "changing gDataTable is main-thread only");
  if (!gDataTable) {
    return;
  }
  mozilla::dom::DataInfo* info = GetDataInfo(aUri);
  if (!info) {
    return;
  }

  {
    StaticMutexAutoLock lock(sMutex);
    info->mRevoked = true;
  }

  if (aBroadcastToOtherProcesses &&
      info->mObjectType == mozilla::dom::DataInfo::eBlobImpl) {
    BroadcastBlobURLUnregistration(nsCString(aUri), info->mPrincipal);
  }

  // The timer will take care of removing the entry for real after
  // RELEASING_TIMER milliseconds. In the meantime, the mozilla::dom::DataInfo,
  // marked as revoked, will not be exposed.
  ReleasingTimerHolder::Create(aUri);
}

/*static */
bool BlobURLProtocolHandler::RemoveDataEntry(const nsACString& aUri,
                                             nsIPrincipal* aPrincipal,
                                             const nsCString& aPartitionKey) {
  MOZ_ASSERT(NS_IsMainThread(), "changing gDataTable is main-thread only");
  if (!gDataTable) {
    return false;
  }

  mozilla::dom::DataInfo* info = GetDataInfo(aUri);
  if (!info) {
    return false;
  }

  if (!aPrincipal || !aPrincipal->Subsumes(info->mPrincipal)) {
    return false;
  }

  if (StaticPrefs::privacy_partition_bloburl_per_partition_key() &&
      !aPartitionKey.IsEmpty() && !info->mPartitionKey.IsEmpty() &&
      !aPartitionKey.Equals(info->mPartitionKey)) {
    return false;
  }

  RemoveDataEntry(aUri, true);
  return true;
}

/* static */
void BlobURLProtocolHandler::RemoveDataEntries() {
  MOZ_ASSERT(NS_IsMainThread(), "changing gDataTable is main-thread only");
  StaticMutexAutoLock lock(sMutex);
  if (!gDataTable) {
    return;
  }

  gDataTable->Clear();
  delete gDataTable;
  gDataTable = nullptr;
}

/* static */
bool BlobURLProtocolHandler::HasDataEntry(const nsACString& aUri) {
  MOZ_ASSERT(NS_IsMainThread(),
             "without locking gDataTable is main-thread only");
  return !!GetDataInfo(aUri);
}

/* static */
nsresult BlobURLProtocolHandler::GenerateURIString(nsIPrincipal* aPrincipal,
                                                   nsACString& aUri) {
  nsresult rv;
  nsCOMPtr<nsIUUIDGenerator> uuidgen =
      do_GetService("@mozilla.org/uuid-generator;1", &rv);
  NS_ENSURE_SUCCESS(rv, rv);

  nsID id;
  rv = uuidgen->GenerateUUIDInPlace(&id);
  NS_ENSURE_SUCCESS(rv, rv);

  aUri.AssignLiteral(BLOBURI_SCHEME);
  aUri.Append(':');

  if (aPrincipal) {
    nsAutoCString origin;
    rv = aPrincipal->GetWebExposedOriginSerialization(origin);
    if (NS_FAILED(rv)) {
      origin.AssignLiteral("null");
    }

    aUri.Append(origin);
    aUri.Append('/');
  }

  aUri += NSID_TrimBracketsASCII(id);

  return NS_OK;
}

/* static */
bool BlobURLProtocolHandler::GetDataEntry(
    const nsACString& aUri, mozilla::dom::BlobImpl** aBlobImpl,
    nsIPrincipal* aLoadingPrincipal, nsIPrincipal* aTriggeringPrincipal,
    const OriginAttributes& aOriginAttributes, uint64_t aInnerWindowId,
    const nsCString& aPartitionKey, bool aAlsoIfRevoked) {
  MOZ_ASSERT(NS_IsMainThread(),
             "without locking gDataTable is main-thread only");
  MOZ_ASSERT(aTriggeringPrincipal);

  if (!gDataTable) {
    return false;
  }

  mozilla::dom::DataInfo* info = GetDataInfo(aUri, aAlsoIfRevoked);
  if (!info) {
    return false;
  }

  // We want to be sure that we stop the creation of the channel if the blob
  // URL is copy-and-pasted on a different context (ex. private browsing or
  // containers).
  //
  // We also allow the system principal to create the channel regardless of
  // the OriginAttributes.  This is primarily for the benefit of mechanisms
  // like the Download API that explicitly create a channel with the system
  // principal and which is never mutated to have a non-zero
  // mPrivateBrowsingId or container.

  if ((NS_WARN_IF(!aLoadingPrincipal) ||
       !aLoadingPrincipal->IsSystemPrincipal()) &&
      NS_WARN_IF(!ChromeUtils::IsOriginAttributesEqualIgnoringFPD(
          aOriginAttributes,
          BasePrincipal::Cast(info->mPrincipal)->OriginAttributesRef()))) {
    return false;
  }

  if (NS_WARN_IF(!aTriggeringPrincipal->Subsumes(info->mPrincipal))) {
    return false;
  }

  if (StaticPrefs::privacy_partition_bloburl_per_partition_key() &&
      !aPartitionKey.IsEmpty() && !info->mPartitionKey.IsEmpty() &&
      !aPartitionKey.Equals(info->mPartitionKey)) {
    nsAutoString localizedMsg;
    AutoTArray<nsString, 1> param;
    CopyUTF8toUTF16(aUri, *param.AppendElement());
    nsresult rv = nsContentUtils::FormatLocalizedString(
        nsContentUtils::eDOM_PROPERTIES, "PartitionKeyDifferentError", param,
        localizedMsg);
    if (NS_WARN_IF(NS_FAILED(rv))) {
      return false;
    }

    nsContentUtils::ReportToConsoleByWindowID(
        localizedMsg, nsIScriptError::errorFlag, "DOM"_ns, aInnerWindowId);
    return false;
  }

  RefPtr<mozilla::dom::BlobImpl> blobImpl = info->mBlobImpl;
  blobImpl.forget(aBlobImpl);

  return true;
}

/* static */
void BlobURLProtocolHandler::Traverse(
    const nsACString& aUri, nsCycleCollectionTraversalCallback& aCallback) {
  MOZ_ASSERT(NS_IsMainThread(),
             "without locking gDataTable is main-thread only");
  if (!gDataTable) {
    return;
  }

  mozilla::dom::DataInfo* res;
  gDataTable->Get(aUri, &res);
  if (!res) {
    return;
  }

  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(
      aCallback, "BlobURLProtocolHandler mozilla::dom::DataInfo.mBlobImpl");
  aCallback.NoteXPCOMChild(res->mBlobImpl);

  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(
      aCallback, "BlobURLProtocolHandler mozilla::dom::DataInfo.mMediaSource");
  aCallback.NoteXPCOMChild(static_cast<EventTarget*>(res->mMediaSource));
}

NS_IMPL_ISUPPORTS(BlobURLProtocolHandler, nsIProtocolHandler,
                  nsISupportsWeakReference)

/* static */ nsresult BlobURLProtocolHandler::CreateNewURI(
    const nsACString& aSpec, const char* aCharset, nsIURI* aBaseURI,
    nsIURI** aResult) {
  *aResult = nullptr;

  // This method can be called on any thread, which is why we lock the mutex
  // for read access to gDataTable.
  bool revoked = true;
  {
    StaticMutexAutoLock lock(sMutex);
    mozilla::dom::DataInfo* info = GetDataInfo(aSpec);
    if (info && info->mObjectType == mozilla::dom::DataInfo::eBlobImpl) {
      revoked = info->mRevoked;
    }
  }

  return NS_MutateURI(new BlobURL::Mutator())
      .SetSpec(aSpec)
      .Apply(&nsIBlobURLMutator::SetRevoked, revoked)
      .Finalize(aResult);
}

NS_IMETHODIMP
BlobURLProtocolHandler::NewChannel(nsIURI* aURI, nsILoadInfo* aLoadInfo,
                                   nsIChannel** aResult) {
  auto channel = MakeRefPtr<BlobURLChannel>(aURI, aLoadInfo);
  channel.forget(aResult);
  return NS_OK;
}

NS_IMETHODIMP
BlobURLProtocolHandler::AllowPort(int32_t port, const char* scheme,
                                  bool* _retval) {
  // don't override anything.
  *_retval = false;
  return NS_OK;
}

NS_IMETHODIMP
BlobURLProtocolHandler::GetScheme(nsACString& result) {
  result.AssignLiteral(BLOBURI_SCHEME);
  return NS_OK;
}

/* static */
bool BlobURLProtocolHandler::GetBlobURLPrincipal(nsIURI* aURI,
                                                 nsIPrincipal** aPrincipal) {
  MOZ_ASSERT(aURI);
  MOZ_ASSERT(aPrincipal);

  RefPtr<BlobURL> blobURL;
  nsresult rv =
      aURI->QueryInterface(kHOSTOBJECTURICID, getter_AddRefs(blobURL));
  if (NS_FAILED(rv) || !blobURL) {
    return false;
  }

  StaticMutexAutoLock lock(sMutex);
  mozilla::dom::DataInfo* info =
      GetDataInfoFromURI(aURI, true /*aAlsoIfRevoked */);
  if (!info || info->mObjectType != mozilla::dom::DataInfo::eBlobImpl ||
      !info->mBlobImpl) {
    return false;
  }

  nsCOMPtr<nsIPrincipal> principal;

  if (blobURL->Revoked()) {
    principal = NullPrincipal::Create(
        BasePrincipal::Cast(info->mPrincipal)->OriginAttributesRef());
  } else {
    principal = info->mPrincipal;
  }

  principal.forget(aPrincipal);
  return true;
}

bool BlobURLProtocolHandler::IsBlobURLBroadcastPrincipal(
    nsIPrincipal* aPrincipal) {
  return aPrincipal->IsSystemPrincipal() ||
         aPrincipal->GetIsAddonOrExpandedAddonPrincipal();
}

}  // namespace dom
}  // namespace mozilla

nsresult NS_GetBlobForBlobURI(nsIURI* aURI, mozilla::dom::BlobImpl** aBlob) {
  *aBlob = nullptr;
  MOZ_ASSERT(NS_IsMainThread(),
             "without locking gDataTable is main-thread only");
  mozilla::dom::DataInfo* info =
      mozilla::dom::GetDataInfoFromURI(aURI, false /* aAlsoIfRevoked */);
  if (!info || info->mObjectType != mozilla::dom::DataInfo::eBlobImpl) {
    return NS_ERROR_DOM_BAD_URI;
  }

  RefPtr<mozilla::dom::BlobImpl> blob = info->mBlobImpl;
  blob.forget(aBlob);
  return NS_OK;
}

nsresult NS_GetBlobForBlobURISpec(const nsACString& aSpec,
                                  mozilla::dom::BlobImpl** aBlob,
                                  bool aAlsoIfRevoked) {
  *aBlob = nullptr;
  MOZ_ASSERT(NS_IsMainThread(),
             "without locking gDataTable is main-thread only");

  mozilla::dom::DataInfo* info =
      mozilla::dom::GetDataInfo(aSpec, aAlsoIfRevoked);
  if (!info || info->mObjectType != mozilla::dom::DataInfo::eBlobImpl ||
      !info->mBlobImpl) {
    return NS_ERROR_DOM_BAD_URI;
  }

  RefPtr<mozilla::dom::BlobImpl> blob = info->mBlobImpl;
  blob.forget(aBlob);
  return NS_OK;
}

// Blob requests may specify a range header. We parse, validate, and
// store that info here, and save it on the nsIBaseChannel, where it
// can be accessed by BlobURLInputStream::StoreBlobImplStream.
nsresult NS_SetChannelContentRangeForBlobURI(nsIChannel* aChannel, nsIURI* aURI,
                                             nsACString& aRangeHeader) {
  MOZ_ASSERT(aChannel);
  MOZ_ASSERT(aURI);
  RefPtr<mozilla::dom::BlobImpl> blobImpl;
  if (NS_FAILED(NS_GetBlobForBlobURI(aURI, getter_AddRefs(blobImpl)))) {
    return NS_BINDING_FAILED;
  }
  mozilla::IgnoredErrorResult result;
  int64_t size = static_cast<int64_t>(blobImpl->GetSize(result));
  if (result.Failed()) {
    return NS_ERROR_NO_CONTENT;
  }
  nsCOMPtr<nsIBaseChannel> baseChan = do_QueryInterface(aChannel);
  if (!baseChan || !baseChan->SetContentRangeFromHeader(aRangeHeader, size)) {
    return NS_ERROR_NET_PARTIAL_TRANSFER;
  }
  return NS_OK;
}

nsresult NS_GetSourceForMediaSourceURI(nsIURI* aURI,
                                       mozilla::dom::MediaSource** aSource) {
  *aSource = nullptr;

  MOZ_ASSERT(NS_IsMainThread(),
             "without locking gDataTable is main-thread only");
  mozilla::dom::DataInfo* info = mozilla::dom::GetDataInfoFromURI(aURI);
  if (!info || info->mObjectType != mozilla::dom::DataInfo::eMediaSource) {
    return NS_ERROR_DOM_BAD_URI;
  }

  RefPtr<mozilla::dom::MediaSource> mediaSource = info->mMediaSource;
  mediaSource.forget(aSource);
  return NS_OK;
}

namespace mozilla::dom {

bool IsType(nsIURI* aUri, mozilla::dom::DataInfo::ObjectType aType) {
  // We lock because this may be called off-main-thread
  StaticMutexAutoLock lock(sMutex);
  mozilla::dom::DataInfo* info = GetDataInfoFromURI(aUri);
  if (!info) {
    return false;
  }

  return info->mObjectType == aType;
}

bool IsBlobURI(nsIURI* aUri) {
  return IsType(aUri, mozilla::dom::DataInfo::eBlobImpl);
}

bool BlobURLSchemeIsHTTPOrHTTPS(const nsACString& aUri) {
  return (StringBeginsWith(aUri, "blob:http://"_ns) ||
          StringBeginsWith(aUri, "blob:https://"_ns));
}

bool IsMediaSourceURI(nsIURI* aUri) {
  return IsType(aUri, mozilla::dom::DataInfo::eMediaSource);
}

}  // namespace mozilla::dom

Messung V0.5
C=91 H=94 G=92

¤ Dauer der Verarbeitung: 0.20 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 und die Messung sind noch experimentell.