зеркало из https://github.com/mozilla/gecko-dev.git
244 строки
7.3 KiB
C++
244 строки
7.3 KiB
C++
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim: set ts=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 "MsaaIdGenerator.h"
|
|
|
|
#include "mozilla/a11y/AccessibleWrap.h"
|
|
#include "mozilla/ClearOnShutdown.h"
|
|
#include "mozilla/dom/ContentChild.h"
|
|
#include "mozilla/StaticPtr.h"
|
|
#include "mozilla/Unused.h"
|
|
#include "nsDataHashtable.h"
|
|
#include "nsIXULRuntime.h"
|
|
|
|
// These constants may be adjusted to modify the proportion of the Child ID
|
|
// allocated to the content ID vs proportion allocated to the unique ID. They
|
|
// must always sum to 31, ie. the width of a 32-bit integer less the sign bit.
|
|
|
|
// NB: kNumContentProcessIDBits must be large enough to successfully hold the
|
|
// maximum permitted number of e10s content processes. If the e10s maximum
|
|
// number of content processes changes, then kNumContentProcessIDBits must also
|
|
// be updated if necessary to accommodate that new value!
|
|
static const uint32_t kNumContentProcessIDBits = 7UL;
|
|
static const uint32_t kNumUniqueIDBits = (31UL - kNumContentProcessIDBits);
|
|
|
|
static_assert(kNumContentProcessIDBits + kNumUniqueIDBits == 31,
|
|
"Allocation of Content ID bits and Unique ID bits must sum to 31");
|
|
|
|
namespace mozilla {
|
|
namespace a11y {
|
|
namespace detail {
|
|
|
|
typedef nsDataHashtable<nsUint64HashKey, uint32_t> ContentParentIdMap;
|
|
|
|
#pragma pack(push, 1)
|
|
union MsaaID
|
|
{
|
|
int32_t mInt32;
|
|
uint32_t mUInt32;
|
|
struct
|
|
{
|
|
uint32_t mUniqueID:kNumUniqueIDBits;
|
|
uint32_t mContentProcessID:kNumContentProcessIDBits;
|
|
uint32_t mSignBit:1;
|
|
}
|
|
mCracked;
|
|
};
|
|
#pragma pack(pop)
|
|
|
|
static uint32_t
|
|
BuildMsaaID(const uint32_t aID, const uint32_t aContentProcessID)
|
|
{
|
|
MsaaID id;
|
|
id.mCracked.mSignBit = 0;
|
|
id.mCracked.mUniqueID = aID;
|
|
id.mCracked.mContentProcessID = aContentProcessID;
|
|
return ~id.mUInt32;
|
|
}
|
|
|
|
class MsaaIDCracker
|
|
{
|
|
public:
|
|
explicit MsaaIDCracker(const uint32_t aMsaaID)
|
|
{
|
|
mID.mUInt32 = ~aMsaaID;
|
|
}
|
|
|
|
uint32_t GetContentProcessId()
|
|
{
|
|
return mID.mCracked.mContentProcessID;
|
|
}
|
|
|
|
uint32_t GetUniqueId()
|
|
{
|
|
return mID.mCracked.mUniqueID;
|
|
}
|
|
|
|
private:
|
|
MsaaID mID;
|
|
};
|
|
|
|
} // namespace detail
|
|
|
|
constexpr MsaaIdGenerator::MsaaIdGenerator()
|
|
: mIDSet(kNumUniqueIDBits)
|
|
{}
|
|
|
|
uint32_t
|
|
MsaaIdGenerator::GetID()
|
|
{
|
|
uint32_t id = mIDSet.GetID();
|
|
MOZ_ASSERT(id <= ((1UL << kNumUniqueIDBits) - 1UL));
|
|
return detail::BuildMsaaID(id, ResolveContentProcessID());
|
|
}
|
|
|
|
void
|
|
MsaaIdGenerator::ReleaseID(AccessibleWrap* aAccWrap)
|
|
{
|
|
MOZ_ASSERT(aAccWrap);
|
|
uint32_t id = aAccWrap->GetExistingID();
|
|
MOZ_ASSERT(id != AccessibleWrap::kNoID);
|
|
detail::MsaaIDCracker cracked(id);
|
|
if (cracked.GetContentProcessId() != ResolveContentProcessID()) {
|
|
// This may happen if chrome holds a proxy whose ID was originally generated
|
|
// by a content process. Since ReleaseID only has meaning in the process
|
|
// that originally generated that ID, we ignore ReleaseID calls for any ID
|
|
// that did not come from the current process.
|
|
MOZ_ASSERT(aAccWrap->IsProxy());
|
|
return;
|
|
}
|
|
mIDSet.ReleaseID(cracked.GetUniqueId());
|
|
}
|
|
|
|
bool
|
|
MsaaIdGenerator::IsChromeID(uint32_t aID)
|
|
{
|
|
detail::MsaaIDCracker cracked(aID);
|
|
return cracked.GetContentProcessId() == 0;
|
|
}
|
|
|
|
bool
|
|
MsaaIdGenerator::IsIDForThisContentProcess(uint32_t aID)
|
|
{
|
|
MOZ_ASSERT(XRE_IsContentProcess());
|
|
detail::MsaaIDCracker cracked(aID);
|
|
return cracked.GetContentProcessId() == ResolveContentProcessID();
|
|
}
|
|
|
|
bool
|
|
MsaaIdGenerator::IsIDForContentProcess(uint32_t aID,
|
|
dom::ContentParentId aIPCContentProcessId)
|
|
{
|
|
MOZ_ASSERT(XRE_IsParentProcess());
|
|
detail::MsaaIDCracker cracked(aID);
|
|
return cracked.GetContentProcessId() ==
|
|
GetContentProcessIDFor(aIPCContentProcessId);
|
|
}
|
|
|
|
bool
|
|
MsaaIdGenerator::IsSameContentProcessFor(uint32_t aFirstID, uint32_t aSecondID)
|
|
{
|
|
detail::MsaaIDCracker firstCracked(aFirstID);
|
|
detail::MsaaIDCracker secondCracked(aSecondID);
|
|
return firstCracked.GetContentProcessId() ==
|
|
secondCracked.GetContentProcessId();
|
|
}
|
|
|
|
uint32_t
|
|
MsaaIdGenerator::ResolveContentProcessID()
|
|
{
|
|
if (XRE_IsParentProcess()) {
|
|
return 0;
|
|
}
|
|
|
|
dom::ContentChild* contentChild = dom::ContentChild::GetSingleton();
|
|
uint32_t result = contentChild->GetMsaaID();
|
|
|
|
MOZ_ASSERT(result);
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Each dom::ContentParent has a 64-bit ID. This ID is monotonically increasing
|
|
* with each new content process, so those IDs are effectively single-use. OTOH,
|
|
* MSAA requires 32-bit IDs. Since we only allocate kNumContentProcessIDBits for
|
|
* the content process ID component, the MSAA content process ID value must be
|
|
* reusable. sContentParentIdMap holds the current associations between
|
|
* dom::ContentParent IDs and the MSAA content parent IDs that have been
|
|
* allocated to them.
|
|
*/
|
|
static StaticAutoPtr<detail::ContentParentIdMap> sContentParentIdMap;
|
|
|
|
static const uint32_t kBitsPerByte = 8UL;
|
|
// Set sContentProcessIdBitmap[0] to 1 to reserve the Chrome process's id
|
|
static uint64_t sContentProcessIdBitmap[(1UL << kNumContentProcessIDBits) /
|
|
(sizeof(uint64_t) * kBitsPerByte)] = {1ULL};
|
|
static const uint32_t kBitsPerElement = sizeof(sContentProcessIdBitmap[0]) *
|
|
kBitsPerByte;
|
|
|
|
uint32_t
|
|
MsaaIdGenerator::GetContentProcessIDFor(dom::ContentParentId aIPCContentProcessID)
|
|
{
|
|
MOZ_ASSERT(XRE_IsParentProcess() && NS_IsMainThread());
|
|
if (!sContentParentIdMap) {
|
|
sContentParentIdMap = new detail::ContentParentIdMap();
|
|
ClearOnShutdown(&sContentParentIdMap);
|
|
}
|
|
|
|
uint32_t value = 0;
|
|
if (sContentParentIdMap->Get(aIPCContentProcessID, &value)) {
|
|
return value;
|
|
}
|
|
|
|
uint32_t index = 0;
|
|
for (; index < ArrayLength(sContentProcessIdBitmap); ++index) {
|
|
if (sContentProcessIdBitmap[index] == UINT64_MAX) {
|
|
continue;
|
|
}
|
|
uint32_t bitIndex = CountTrailingZeroes64(~sContentProcessIdBitmap[index]);
|
|
MOZ_ASSERT(!(sContentProcessIdBitmap[index] & (1ULL << bitIndex)));
|
|
MOZ_ASSERT(bitIndex != 0 || index != 0);
|
|
sContentProcessIdBitmap[index] |= (1ULL << bitIndex);
|
|
value = index * kBitsPerElement + bitIndex;
|
|
break;
|
|
}
|
|
|
|
// If we run out of content process IDs, we're in trouble
|
|
MOZ_RELEASE_ASSERT(index < ArrayLength(sContentProcessIdBitmap));
|
|
|
|
sContentParentIdMap->Put(aIPCContentProcessID, value);
|
|
return value;
|
|
}
|
|
|
|
void
|
|
MsaaIdGenerator::ReleaseContentProcessIDFor(dom::ContentParentId aIPCContentProcessID)
|
|
{
|
|
MOZ_ASSERT(XRE_IsParentProcess() && NS_IsMainThread());
|
|
if (!sContentParentIdMap) {
|
|
// Since Content IDs are generated lazily, ContentParent might attempt
|
|
// to release an ID that was never allocated to begin with.
|
|
return;
|
|
}
|
|
|
|
Maybe<uint32_t> mapping = sContentParentIdMap->GetAndRemove(aIPCContentProcessID);
|
|
if (!mapping) {
|
|
// Since Content IDs are generated lazily, ContentParent might attempt
|
|
// to release an ID that was never allocated to begin with.
|
|
return;
|
|
}
|
|
|
|
uint32_t index = mapping.ref() / kBitsPerElement;
|
|
MOZ_ASSERT(index < ArrayLength(sContentProcessIdBitmap));
|
|
|
|
uint64_t mask = 1ULL << (mapping.ref() % kBitsPerElement);
|
|
MOZ_ASSERT(sContentProcessIdBitmap[index] & mask);
|
|
|
|
sContentProcessIdBitmap[index] &= ~mask;
|
|
}
|
|
|
|
} // namespace a11y
|
|
} // namespace mozilla
|