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

Quelle  MediaParent.cpp   Sprache: C

 
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set sw=2 ts=8 et ft=cpp : */
/* 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 "MediaParent.h"

#include "mozilla/Base64.h"
#include <mozilla/StaticMutex.h>

#include "MediaUtils.h"
#include "MediaEngine.h"
#include "VideoUtils.h"
#include "nsClassHashtable.h"
#include "nsThreadUtils.h"
#include "nsNetCID.h"
#include "nsNetUtil.h"
#include "nsIInputStream.h"
#include "nsILineInputStream.h"
#include "nsIOutputStream.h"
#include "nsISafeOutputStream.h"
#include "nsAppDirectoryServiceDefs.h"
#include "nsIFile.h"
#include "nsISupportsImpl.h"
#include "mozilla/Logging.h"

#undef LOG
mozilla::LazyLogModule gMediaParentLog("MediaParent");
#define LOG(args) MOZ_LOG(gMediaParentLog, mozilla::LogLevel::Debug, args)

// A file in the profile dir is used to persist mOriginKeys used to anonymize
// deviceIds to be unique per origin, to avoid them being supercookies.

#define ORIGINKEYS_FILE u"enumerate_devices.txt"
#define ORIGINKEYS_VERSION "1"

namespace mozilla::media {

StaticMutex sOriginKeyStoreStsMutex;

class OriginKeyStore {
  NS_INLINE_DECL_REFCOUNTING(OriginKeyStore);
  class OriginKey {
   public:
    static const size_t DecodedLength = 18;
    static const size_t EncodedLength = DecodedLength * 4 / 3;

    explicit OriginKey(const nsACString& aKey,
                       int64_t aSecondsStamp = 0)  // 0 = temporal
        : mKey(aKey), mSecondsStamp(aSecondsStamp) {}

    nsCString mKey;  // Base64 encoded.
    int64_t mSecondsStamp;
  };

  class OriginKeysTable {
   public:
    OriginKeysTable() : mPersistCount(0) {}

    nsresult GetPrincipalKey(const ipc::PrincipalInfo& aPrincipalInfo,
                             nsCString& aResult, bool aPersist = false) {
      nsAutoCString principalString;
      PrincipalInfoToString(aPrincipalInfo, principalString);

      OriginKey* key;
      if (!mKeys.Get(principalString, &key)) {
        nsCString salt;  // Make a new one
        nsresult rv = GenerateRandomName(salt, OriginKey::EncodedLength);
        if (NS_WARN_IF(NS_FAILED(rv))) {
          return rv;
        }
        key = mKeys.InsertOrUpdate(principalString, MakeUnique<OriginKey>(salt))
                  .get();
      }
      if (aPersist && !key->mSecondsStamp) {
        key->mSecondsStamp = PR_Now() / PR_USEC_PER_SEC;
        mPersistCount++;
      }
      aResult = key->mKey;
      return NS_OK;
    }

    void Clear(int64_t aSinceWhen) {
      // Avoid int64_t* <-> void* casting offset
      OriginKey since(nsCString(), aSinceWhen / PR_USEC_PER_SEC);
      for (auto iter = mKeys.Iter(); !iter.Done(); iter.Next()) {
        auto originKey = iter.UserData();
        LOG((((originKey->mSecondsStamp >= since.mSecondsStamp)
                  ? "%s: REMOVE %" PRId64 " >= %" PRId64
                  : "%s: KEEP %" PRId64 " < %" PRId64),
             __FUNCTION__, originKey->mSecondsStamp, since.mSecondsStamp));

        if (originKey->mSecondsStamp >= since.mSecondsStamp) {
          iter.Remove();
        }
      }
      mPersistCount = 0;
    }

   private:
    void PrincipalInfoToString(const ipc::PrincipalInfo& aPrincipalInfo,
                               nsACString& aString) {
      switch (aPrincipalInfo.type()) {
        case ipc::PrincipalInfo::TSystemPrincipalInfo:
          aString.AssignLiteral("[System Principal]");
          return;

        case ipc::PrincipalInfo::TNullPrincipalInfo: {
          const ipc::NullPrincipalInfo& info =
              aPrincipalInfo.get_NullPrincipalInfo();
          aString.Assign(info.spec());
          return;
        }

        case ipc::PrincipalInfo::TContentPrincipalInfo: {
          const ipc::ContentPrincipalInfo& info =
              aPrincipalInfo.get_ContentPrincipalInfo();
          aString.Assign(info.originNoSuffix());

          nsAutoCString suffix;
          info.attrs().CreateSuffix(suffix);
          aString.Append(suffix);
          return;
        }

        case ipc::PrincipalInfo::TExpandedPrincipalInfo: {
          const ipc::ExpandedPrincipalInfo& info =
              aPrincipalInfo.get_ExpandedPrincipalInfo();

          aString.AssignLiteral("[Expanded Principal [");

          for (uint32_t i = 0; i < info.allowlist().Length(); i++) {
            nsAutoCString str;
            PrincipalInfoToString(info.allowlist()[i], str);

            if (i != 0) {
              aString.AppendLiteral(", ");
            }

            aString.Append(str);
          }

          aString.AppendLiteral("]]");
          return;
        }

        default:
          MOZ_CRASH("Unknown PrincipalInfo type!");
      }
    }

   protected:
    nsClassHashtable<nsCStringHashKey, OriginKey> mKeys;
    size_t mPersistCount;
  };

  class OriginKeysLoader : public OriginKeysTable {
   public:
    OriginKeysLoader() = default;

    nsresult GetPrincipalKey(const ipc::PrincipalInfo& aPrincipalInfo,
                             nsCString& aResult, bool aPersist = false) {
      auto before = mPersistCount;
      nsresult rv =
          OriginKeysTable::GetPrincipalKey(aPrincipalInfo, aResult, aPersist);
      if (NS_WARN_IF(NS_FAILED(rv))) {
        return rv;
      }

      if (mPersistCount != before) {
        Save();
      }
      return NS_OK;
    }

    already_AddRefed<nsIFile> GetFile() {
      MOZ_ASSERT(mProfileDir);
      nsCOMPtr<nsIFile> file;
      nsresult rv = mProfileDir->Clone(getter_AddRefs(file));
      if (NS_WARN_IF(NS_FAILED(rv))) {
        return nullptr;
      }
      file->Append(nsLiteralString(ORIGINKEYS_FILE));
      return file.forget();
    }

    // Format of file is key secondsstamp origin (first line is version #):
    //
    // 1
    // rOMAAbFujNwKyIpj4RJ3Wt5Q 1424733961 http://fiddle.jshell.net
    // rOMAAbFujNwKyIpj4RJ3Wt5Q 1424734841 http://mozilla.github.io
    // etc.

    nsresult Read() {
      nsCOMPtr<nsIFile> file = GetFile();
      if (NS_WARN_IF(!file)) {
        return NS_ERROR_UNEXPECTED;
      }
      bool exists;
      nsresult rv = file->Exists(&exists);
      if (NS_WARN_IF(NS_FAILED(rv))) {
        return rv;
      }
      if (!exists) {
        return NS_OK;
      }

      nsCOMPtr<nsIInputStream> stream;
      rv = NS_NewLocalFileInputStream(getter_AddRefs(stream), file);
      if (NS_WARN_IF(NS_FAILED(rv))) {
        return rv;
      }
      nsCOMPtr<nsILineInputStream> i = do_QueryInterface(stream);
      MOZ_ASSERT(i);
      MOZ_ASSERT(!mPersistCount);

      nsCString line;
      bool hasMoreLines;
      rv = i->ReadLine(line, &hasMoreLines);
      if (NS_WARN_IF(NS_FAILED(rv))) {
        return rv;
      }
      if (!line.EqualsLiteral(ORIGINKEYS_VERSION)) {
        // If version on disk is newer than we can understand then ignore it.
        return NS_OK;
      }

      while (hasMoreLines) {
        rv = i->ReadLine(line, &hasMoreLines);
        if (NS_WARN_IF(NS_FAILED(rv))) {
          return rv;
        }
        // Read key secondsstamp origin.
        // Ignore any lines that don't fit format in the comment above exactly.
        int32_t f = line.FindChar(' ');
        if (f < 0) {
          continue;
        }
        const nsACString& key = Substring(line, 0, f);
        const nsACString& s = Substring(line, f + 1);
        f = s.FindChar(' ');
        if (f < 0) {
          continue;
        }
        int64_t secondsstamp = Substring(s, 0, f).ToInteger64(&rv);
        if (NS_FAILED(rv)) {
          continue;
        }
        const nsACString& origin = Substring(s, f + 1);

        // Validate key
        if (key.Length() != OriginKey::EncodedLength) {
          continue;
        }
        nsCString dummy;
        rv = Base64Decode(key, dummy);
        if (NS_FAILED(rv)) {
          continue;
        }
        mKeys.InsertOrUpdate(origin, MakeUnique<OriginKey>(key, secondsstamp));
      }
      mPersistCount = mKeys.Count();
      return NS_OK;
    }

    nsresult Write() {
      nsCOMPtr<nsIFile> file = GetFile();
      if (NS_WARN_IF(!file)) {
        return NS_ERROR_UNEXPECTED;
      }

      nsCOMPtr<nsIOutputStream> stream;
      nsresult rv =
          NS_NewSafeLocalFileOutputStream(getter_AddRefs(stream), file);
      if (NS_WARN_IF(NS_FAILED(rv))) {
        return rv;
      }

      nsAutoCString versionBuffer;
      versionBuffer.AppendLiteral(ORIGINKEYS_VERSION);
      versionBuffer.Append('\n');

      uint32_t count;
      rv = stream->Write(versionBuffer.Data(), versionBuffer.Length(), &count);
      if (NS_WARN_IF(NS_FAILED(rv))) {
        return rv;
      }
      if (count != versionBuffer.Length()) {
        return NS_ERROR_UNEXPECTED;
      }
      for (const auto& entry : mKeys) {
        const nsACString& origin = entry.GetKey();
        OriginKey* originKey = entry.GetWeak();

        if (!originKey->mSecondsStamp) {
          continue;  // don't write temporal ones
        }

        nsCString originBuffer;
        originBuffer.Append(originKey->mKey);
        originBuffer.Append(' ');
        originBuffer.AppendInt(originKey->mSecondsStamp);
        originBuffer.Append(' ');
        originBuffer.Append(origin);
        originBuffer.Append('\n');

        rv = stream->Write(originBuffer.Data(), originBuffer.Length(), &count);
        if (NS_WARN_IF(NS_FAILED(rv)) || count != originBuffer.Length()) {
          break;
        }
      }

      nsCOMPtr<nsISafeOutputStream> safeStream = do_QueryInterface(stream);
      MOZ_ASSERT(safeStream);

      rv = safeStream->Finish();
      if (NS_WARN_IF(NS_FAILED(rv))) {
        return rv;
      }
      return NS_OK;
    }

    nsresult Load() {
      nsresult rv = Read();
      if (NS_WARN_IF(NS_FAILED(rv))) {
        Delete();
      }
      return rv;
    }

    nsresult Save() {
      nsresult rv = Write();
      if (NS_WARN_IF(NS_FAILED(rv))) {
        NS_WARNING("Failed to write data for EnumerateDevices id-persistence.");
        Delete();
      }
      return rv;
    }

    void Clear(int64_t aSinceWhen) {
      OriginKeysTable::Clear(aSinceWhen);
      Delete();
      Save();
    }

    nsresult Delete() {
      nsCOMPtr<nsIFile> file = GetFile();
      if (NS_WARN_IF(!file)) {
        return NS_ERROR_UNEXPECTED;
      }
      nsresult rv = file->Remove(false);
      if (rv == NS_ERROR_FILE_NOT_FOUND) {
        return NS_OK;
      }
      if (NS_WARN_IF(NS_FAILED(rv))) {
        return rv;
      }
      return NS_OK;
    }

    void SetProfileDir(nsIFile* aProfileDir) {
      MOZ_ASSERT(!NS_IsMainThread());
      bool first = !mProfileDir;
      mProfileDir = aProfileDir;
      // Load from disk when we first get a profileDir, but not subsequently.
      if (first) {
        Load();
      }
    }

   private:
    nsCOMPtr<nsIFile> mProfileDir;
  };

 private:
  static OriginKeyStore* sOriginKeyStore;

  virtual ~OriginKeyStore() {
    MOZ_ASSERT(NS_IsMainThread());
    sOriginKeyStore = nullptr;
    LOG(("%s", __FUNCTION__));
  }

 public:
  static RefPtr<OriginKeyStore> Get() {
    MOZ_ASSERT(NS_IsMainThread());
    if (!sOriginKeyStore) {
      sOriginKeyStore = new OriginKeyStore();
    }
    return RefPtr(sOriginKeyStore);
  }

  // Only accessed on StreamTS threads
  OriginKeysLoader mOriginKeys MOZ_GUARDED_BY(sOriginKeyStoreStsMutex);
  OriginKeysTable mPrivateBrowsingOriginKeys
      MOZ_GUARDED_BY(sOriginKeyStoreStsMutex);
};
OriginKeyStore* OriginKeyStore::sOriginKeyStore = nullptr;

template <class Super>
mozilla::ipc::IPCResult Parent<Super>::RecvGetPrincipalKey(
    const ipc::PrincipalInfo& aPrincipalInfo, const bool& aPersist,
    PMediaParent::GetPrincipalKeyResolver&& aResolve) {
  MOZ_ASSERT(NS_IsMainThread());

  // First, get profile dir.

  nsCOMPtr<nsIFile> profileDir;
  nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
                                       getter_AddRefs(profileDir));
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return IPCResult(thisfalse);
  }

  // Resolver has to be called in MainThread but the key is discovered
  // in a different thread. We wrap the resolver around a MozPromise to make
  // it more flexible and pass it to the new task. When this is done the
  // resolver is resolved in MainThread.

  // Then over to stream-transport thread (a thread pool) to do the actual
  // file io. Stash a promise to hold the answer and get an id for this request.

  nsCOMPtr<nsIEventTarget> sts =
      do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
  MOZ_ASSERT(sts);
  auto taskQueue = TaskQueue::Create(sts.forget(), "RecvGetPrincipalKey");
  RefPtr<Parent<Super>> that(this);

  InvokeAsync(
      taskQueue, __func__,
      [this, that, profileDir, aPrincipalInfo, aPersist]() {
        MOZ_ASSERT(!NS_IsMainThread());

        StaticMutexAutoLock lock(sOriginKeyStoreStsMutex);
        mOriginKeyStore->mOriginKeys.SetProfileDir(profileDir);

        nsresult rv;
        nsAutoCString result;
        if (IsPrincipalInfoPrivate(aPrincipalInfo)) {
          rv = mOriginKeyStore->mPrivateBrowsingOriginKeys.GetPrincipalKey(
              aPrincipalInfo, result);
        } else {
          rv = mOriginKeyStore->mOriginKeys.GetPrincipalKey(aPrincipalInfo,
                                                            result, aPersist);
        }

        if (NS_WARN_IF(NS_FAILED(rv))) {
          return PrincipalKeyPromise::CreateAndReject(rv, __func__);
        }
        return PrincipalKeyPromise::CreateAndResolve(result, __func__);
      })
      ->Then(
          GetCurrentSerialEventTarget(), __func__,
          [aResolve](const PrincipalKeyPromise::ResolveOrRejectValue& aValue) {
            if (aValue.IsReject()) {
              aResolve(""_ns);
            } else {
              aResolve(aValue.ResolveValue());
            }
          });

  return IPC_OK();
}

template <class Super>
mozilla::ipc::IPCResult Parent<Super>::RecvSanitizeOriginKeys(
    const uint64_t& aSinceWhen, const bool& aOnlyPrivateBrowsing) {
  MOZ_ASSERT(NS_IsMainThread());
  nsCOMPtr<nsIFile> profileDir;
  nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
                                       getter_AddRefs(profileDir));
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return IPCResult(thisfalse);
  }
  // Over to stream-transport thread (a thread pool) to do the file io.

  nsCOMPtr<nsIEventTarget> sts =
      do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
  MOZ_ASSERT(sts);
  RefPtr<Parent<Super>> that(this);

  rv = sts->Dispatch(
      NewRunnableFrom(
          [this, that, profileDir, aSinceWhen, aOnlyPrivateBrowsing]() {
            MOZ_ASSERT(!NS_IsMainThread());
            StaticMutexAutoLock lock(sOriginKeyStoreStsMutex);
            mOriginKeyStore->mPrivateBrowsingOriginKeys.Clear(aSinceWhen);
            if (!aOnlyPrivateBrowsing) {
              mOriginKeyStore->mOriginKeys.SetProfileDir(profileDir);
              mOriginKeyStore->mOriginKeys.Clear(aSinceWhen);
            }
            return NS_OK;
          }),
      NS_DISPATCH_NORMAL);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return IPCResult(thisfalse);
  }
  return IPC_OK();
}

template <class Super>
void Parent<Super>::ActorDestroy(ActorDestroyReason aWhy) {
  // No more IPC from here
  mDestroyed = true;
  LOG(("%s", __FUNCTION__));
}

template <class Super>
Parent<Super>::Parent()
    : mOriginKeyStore(OriginKeyStore::Get()), mDestroyed(false) {
  LOG(("media::Parent: %p"this));
}

template <class Super>
Parent<Super>::~Parent() {
  NS_ReleaseOnMainThread("Parent::mOriginKeyStore",
                         mOriginKeyStore.forget());
  LOG(("~media::Parent: %p"this));
}

PMediaParent* AllocPMediaParent() {
  Parent<PMediaParent>* obj = new Parent<PMediaParent>();
  obj->AddRef();
  return obj;
}

bool DeallocPMediaParent(media::PMediaParent* aActor) {
  static_cast<Parent<PMediaParent>*>(aActor)->Release();
  return true;
}

}  // namespace mozilla::media

// Instantiate templates to satisfy linker
template class mozilla::media::Parent<mozilla::media::NonE10s>;

Messung V0.5
C=93 H=96 G=94

¤ Dauer der Verarbeitung: 0.11 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.