summaryrefslogtreecommitdiffstats
path: root/accessible/windows/msaa/MsaaIdGenerator.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'accessible/windows/msaa/MsaaIdGenerator.cpp')
-rw-r--r--accessible/windows/msaa/MsaaIdGenerator.cpp243
1 files changed, 243 insertions, 0 deletions
diff --git a/accessible/windows/msaa/MsaaIdGenerator.cpp b/accessible/windows/msaa/MsaaIdGenerator.cpp
new file mode 100644
index 000000000..5f4b333fa
--- /dev/null
+++ b/accessible/windows/msaa/MsaaIdGenerator.cpp
@@ -0,0 +1,243 @@
+/* -*- 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