Skip to content

Commit

Permalink
Bug 1304449: Part 1 - Modify MSAA IDs to be partitioned based on cont…
Browse files Browse the repository at this point in the history
…ent id; r=tbsaunde

MozReview-Commit-ID: AGXtMaLDFGz
  • Loading branch information
dblohm7 committed Oct 5, 2016
1 parent 72205ff commit 680b4f2
Show file tree
Hide file tree
Showing 4 changed files with 326 additions and 11 deletions.
32 changes: 21 additions & 11 deletions accessible/windows/msaa/IDSet.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,29 @@ namespace mozilla {
namespace a11y {

/**
* On windows an accessible's id must be a negative 32 bit integer. It is
* On windows an accessible's id must be a negative 32 bit integer. It is
* important to support recycling arbitrary IDs because accessibles can be
* created and destroyed at any time in the life of a page. IDSet provides 2
* operations: generate an ID in the range [ - 2^31, 0 ), and release an ID so
* operations: generate an ID in the range (0, mMaxId], and release an ID so
* it can be allocated again. Allocated ID are tracked by a sparse bitmap
* implemented with a splay tree. Nodes in the tree are keyed by the upper N
* bits of the bitwise negation of the ID, and the node contains a bitmap
* tracking the allocation of 2^(32 - N) IDs.
* bits of the ID, and the node contains a bitmap tracking the allocation of
* 2^(ceil(log2(mMaxId)) - N) IDs.
*
* Note that negation is handled by MsaaIdGenerator as it performs additional
* decoration on the ID generated by IDSet.
* @see mozilla::a11y::MsaaIdGenerator
*/
class IDSet
{
public:
constexpr IDSet() : mBitSet(), mIdx(0) {}
constexpr explicit IDSet(const uint32_t aMaxIdBits)
: mBitSet()
, mIdx(0)
, mMaxId((1UL << aMaxIdBits) - 1UL)
, mMaxIdx(mMaxId / bitsPerElt)
{
}

/**
* Return a new unique id.
Expand All @@ -46,19 +56,19 @@ class IDSet

elt->mBitvec[0] |= (1ull << i);
mIdx = idx;
return ~(elt->mIdx * bitsPerElt + i);
return (elt->mIdx * bitsPerElt + i);
}

if (elt->mBitvec[1] != UINT64_MAX) {
uint32_t i = CountTrailingZeroes64(~elt->mBitvec[1]);

elt->mBitvec[1] |= (1ull << i);
mIdx = idx;
return ~(elt->mIdx * bitsPerElt + bitsPerWord + i);
return (elt->mIdx * bitsPerElt + bitsPerWord + i);
}

idx++;
if (idx > sMaxIdx) {
if (idx > mMaxIdx) {
idx = 0;
}

Expand All @@ -73,8 +83,7 @@ class IDSet
*/
void ReleaseID(uint32_t aID)
{
aID = ~aID;
MOZ_ASSERT(aID < static_cast<uint32_t>(INT32_MAX));
MOZ_ASSERT(aID < mMaxId);

uint32_t idx = aID / bitsPerElt;
mIdx = idx;
Expand All @@ -92,7 +101,6 @@ class IDSet
static const unsigned int wordsPerElt = 2;
static const unsigned int bitsPerWord = 64;
static const unsigned int bitsPerElt = wordsPerElt * bitsPerWord;
static const uint32_t sMaxIdx = INT32_MAX / bitsPerElt;

struct BitSetElt : mozilla::SplayTreeNode<BitSetElt>
{
Expand All @@ -118,6 +126,8 @@ class IDSet

SplayTree<BitSetElt, BitSetElt> mBitSet;
uint32_t mIdx;
const uint32_t mMaxId;
const uint32_t mMaxIdx;
};

}
Expand Down
246 changes: 246 additions & 0 deletions accessible/windows/msaa/MsaaIdGenerator.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,246 @@
/* -*- 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)
, mContentProcessID(0)
{}

uint32_t
MsaaIdGenerator::GetID()
{
static const uint32_t kContentProcessId = ResolveContentProcessID();
uint32_t id = mIDSet.GetID();
MOZ_ASSERT(id <= ((1UL << kNumUniqueIDBits) - 1UL));
return detail::BuildMsaaID(id, kContentProcessId);
}

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() != mContentProcessID) {
// 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());
static const uint32_t kContentProcessId = ResolveContentProcessID();
detail::MsaaIDCracker cracked(aID);
return cracked.GetContentProcessId() == kContentProcessId;
}

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();
Unused << contentChild->SendGetA11yContentId(&mContentProcessID);

MOZ_ASSERT(mContentProcessID);
return mContentProcessID;
}

/**
* 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
57 changes: 57 additions & 0 deletions accessible/windows/msaa/MsaaIdGenerator.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/* -*- 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/. */

#ifndef mozilla_a11y_MsaaIdGenerator_h
#define mozilla_a11y_MsaaIdGenerator_h

#include "mozilla/a11y/IDSet.h"

#include "mozilla/dom/ipc/IdType.h"

namespace mozilla {
namespace a11y {

class AccessibleWrap;

/**
* This class is responsible for generating child IDs used by our MSAA
* implementation. Since e10s requires us to differentiate IDs based on the
* originating process of the accessible, a portion of the ID's bits are
* allocated to storing that information. The remaining bits represent the
* unique ID of the accessible, within that content process.
*
* The constants kNumContentProcessIDBits and kNumUniqueIDBits in the
* implementation are responsible for determining the proportion of bits that
* are allocated for each purpose.
*/
class MsaaIdGenerator
{
public:
constexpr MsaaIdGenerator();

uint32_t GetID();
void ReleaseID(AccessibleWrap* aAccWrap);
bool IsChromeID(uint32_t aID);
bool IsIDForThisContentProcess(uint32_t aID);
bool IsIDForContentProcess(uint32_t aID,
dom::ContentParentId aIPCContentProcessId);
bool IsSameContentProcessFor(uint32_t aFirstID, uint32_t aSecondID);

uint32_t GetContentProcessIDFor(dom::ContentParentId aIPCContentProcessID);
void ReleaseContentProcessIDFor(dom::ContentParentId aIPCContentProcessID);

private:
uint32_t ResolveContentProcessID();

private:
IDSet mIDSet;
uint32_t mContentProcessID;
};

} // namespace a11y
} // namespace mozilla

#endif // mozilla_a11y_MsaaIdGenerator_h
Loading

0 comments on commit 680b4f2

Please sign in to comment.