diff options
Diffstat (limited to '')
-rw-r--r-- | accessible/windows/msaa/MsaaIdGenerator.cpp | 229 |
1 files changed, 229 insertions, 0 deletions
diff --git a/accessible/windows/msaa/MsaaIdGenerator.cpp b/accessible/windows/msaa/MsaaIdGenerator.cpp new file mode 100644 index 0000000000..f69a43381c --- /dev/null +++ b/accessible/windows/msaa/MsaaIdGenerator.cpp @@ -0,0 +1,229 @@ +/* -*- 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/Assertions.h" +#include "mozilla/ClearOnShutdown.h" +#include "mozilla/DebugOnly.h" +#include "mozilla/dom/ContentChild.h" +#include "mozilla/StaticPtr.h" +#include "mozilla/Unused.h" +#include "nsDataHashtable.h" +#include "sdnAccessible.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()); +} + +bool MsaaIdGenerator::ReleaseID(uint32_t aID) { + MOZ_ASSERT(aID != AccessibleWrap::kNoID); + detail::MsaaIDCracker cracked(aID); + if (cracked.GetContentProcessId() != ResolveContentProcessID()) { + return false; + } + mIDSet.ReleaseID(cracked.GetUniqueId()); + return true; +} + +void MsaaIdGenerator::ReleaseID(NotNull<AccessibleWrap*> aAccWrap) { + if (!ReleaseID(aAccWrap->GetExistingID())) { + // 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()); + } +} + +void MsaaIdGenerator::ReleaseID(NotNull<sdnAccessible*> aSdnAcc) { + Maybe<uint32_t> id = aSdnAcc->ReleaseUniqueID(); + if (id.isSome()) { + DebugOnly<bool> released = ReleaseID(id.value()); + MOZ_ASSERT(released); + } +} + +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 |