Merge mozilla-central to autoland

This commit is contained in:
Carsten "Tomcat" Book 2016-10-14 15:03:01 +02:00
commit 415f86e41d
9 changed files with 555 additions and 112 deletions

View File

@ -73,10 +73,10 @@ this.ContentWebRTC = {
let devices = contentWindow.pendingGetUserMediaRequests.get(callID);
forgetGUMRequest(contentWindow, callID);
let allowedDevices = Cc["@mozilla.org/array;1"]
.createInstance(Ci.nsIMutableArray);
let allowedDevices = Cc["@mozilla.org/supports-array;1"]
.createInstance(Ci.nsISupportsArray);
for (let deviceIndex of aMessage.data.devices)
allowedDevices.appendElement(devices[deviceIndex], /*weak =*/ false);
allowedDevices.AppendElement(devices[deviceIndex]);
Services.obs.notifyObservers(allowedDevices, "getUserMedia:response:allow", callID);
break;
@ -261,8 +261,8 @@ function forgetPendingListsEventually(aContentWindow) {
}
function updateIndicators() {
let contentWindowArray = MediaManagerService.activeMediaCaptureWindows;
let count = contentWindowArray.length;
let contentWindowSupportsArray = MediaManagerService.activeMediaCaptureWindows;
let count = contentWindowSupportsArray.Count();
let state = {
showGlobalIndicator: count > 0,
@ -280,7 +280,7 @@ function updateIndicators() {
// sending duplicate notifications.
let contentWindows = new Set();
for (let i = 0; i < count; ++i) {
contentWindows.add(contentWindowArray.queryElementAt(i, Ci.nsISupports).top);
contentWindows.add(contentWindowSupportsArray.GetElementAt(i).top);
}
for (let contentWindow of contentWindows) {

View File

@ -10,7 +10,6 @@
#include "mozilla/dom/MediaStreamTrack.h"
#include "GetUserMediaRequest.h"
#include "MediaStreamListener.h"
#include "nsArray.h"
#include "nsContentUtils.h"
#include "nsHashPropertyBag.h"
#ifdef MOZ_WIDGET_GONK
@ -21,6 +20,7 @@
#include "nsIScriptGlobalObject.h"
#include "nsIPermissionManager.h"
#include "nsIPopupWindowManager.h"
#include "nsISupportsArray.h"
#include "nsIDocShell.h"
#include "nsIDocument.h"
#include "nsISupportsPrimitives.h"
@ -2406,10 +2406,14 @@ if (privileged) {
return;
}
nsCOMPtr<nsIMutableArray> devicesCopy = nsArray::Create(); // before we give up devices below
nsCOMPtr<nsISupportsArray> devicesCopy; // before we give up devices below
if (!askPermission) {
nsresult rv = NS_NewISupportsArray(getter_AddRefs(devicesCopy));
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
for (auto& device : **devices) {
nsresult rv = devicesCopy->AppendElement(device, /*weak =*/ false);
rv = devicesCopy->AppendElement(device);
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
@ -3027,15 +3031,15 @@ MediaManager::Observe(nsISupports* aSubject, const char* aTopic,
if (aSubject) {
// A particular device or devices were chosen by the user.
// NOTE: does not allow setting a device to null; assumes nullptr
nsCOMPtr<nsIArray> array(do_QueryInterface(aSubject));
nsCOMPtr<nsISupportsArray> array(do_QueryInterface(aSubject));
MOZ_ASSERT(array);
uint32_t len = 0;
array->GetLength(&len);
array->Count(&len);
bool videoFound = false, audioFound = false;
for (uint32_t i = 0; i < len; i++) {
nsCOMPtr<nsIMediaDevice> device;
array->QueryElementAt(i, NS_GET_IID(nsIMediaDevice),
getter_AddRefs(device));
nsCOMPtr<nsISupports> supports;
array->GetElementAt(i,getter_AddRefs(supports));
nsCOMPtr<nsIMediaDevice> device(do_QueryInterface(supports));
MOZ_ASSERT(device); // shouldn't be returning anything else...
if (device) {
nsString type;
@ -3129,11 +3133,14 @@ MediaManager::Observe(nsISupports* aSubject, const char* aTopic,
}
nsresult
MediaManager::GetActiveMediaCaptureWindows(nsIArray** aArray)
MediaManager::GetActiveMediaCaptureWindows(nsISupportsArray** aArray)
{
MOZ_ASSERT(aArray);
nsCOMPtr<nsIMutableArray> array = nsArray::Create();
nsISupportsArray* array;
nsresult rv = NS_NewISupportsArray(&array); // AddRefs
if (NS_FAILED(rv)) {
return rv;
}
for (auto iter = mActiveWindows.Iter(); !iter.Done(); iter.Next()) {
const uint64_t& id = iter.Key();
@ -3164,11 +3171,11 @@ MediaManager::GetActiveMediaCaptureWindows(nsIArray** aArray)
}
}
if (capturing) {
array->AppendElement(window, /*weak =*/ false);
array->AppendElement(window);
}
}
array.forget(aArray);
*aArray = array;
return NS_OK;
}
@ -3327,14 +3334,14 @@ MediaManager::IterateWindowListeners(nsPIDOMWindowInner* aWindow,
void
MediaManager::StopMediaStreams()
{
nsCOMPtr<nsIArray> array;
nsCOMPtr<nsISupportsArray> array;
GetActiveMediaCaptureWindows(getter_AddRefs(array));
uint32_t len;
array->GetLength(&len);
array->Count(&len);
for (uint32_t i = 0; i < len; i++) {
nsCOMPtr<nsPIDOMWindowInner> win;
array->QueryElementAt(i, NS_GET_IID(nsPIDOMWindowInner),
getter_AddRefs(win));
nsCOMPtr<nsISupports> window;
array->GetElementAt(i, getter_AddRefs(window));
nsCOMPtr<nsPIDOMWindowInner> win(do_QueryInterface(window));
if (win) {
OnNavigation(win->WindowID());
}
@ -3346,14 +3353,14 @@ MediaManager::IsActivelyCapturingOrHasAPermission(uint64_t aWindowId)
{
// Does page currently have a gUM stream active?
nsCOMPtr<nsIArray> array;
nsCOMPtr<nsISupportsArray> array;
GetActiveMediaCaptureWindows(getter_AddRefs(array));
uint32_t len;
array->GetLength(&len);
array->Count(&len);
for (uint32_t i = 0; i < len; i++) {
nsCOMPtr<nsPIDOMWindowInner> win;
array->QueryElementAt(i, NS_GET_IID(nsPIDOMWindowInner),
getter_AddRefs(win));
nsCOMPtr<nsISupports> window;
array->GetElementAt(i, getter_AddRefs(window));
nsCOMPtr<nsPIDOMWindowInner> win(do_QueryInterface(window));
if (win && win->WindowID() == aWindowId) {
return true;
}

View File

@ -5,12 +5,12 @@
#include "MediaManager.h"
#include "MediaPermissionGonk.h"
#include "nsArray.h"
#include "nsCOMPtr.h"
#include "nsIContentPermissionPrompt.h"
#include "nsIDocument.h"
#include "nsIDOMNavigatorUserMedia.h"
#include "nsIStringEnumerator.h"
#include "nsISupportsArray.h"
#include "nsJSUtils.h"
#include "nsQueryObject.h"
#include "nsPIDOMWindow.h"
@ -67,10 +67,12 @@ static nsresult
NotifyPermissionAllow(const nsAString &aCallID, nsTArray<nsCOMPtr<nsIMediaDevice> > &aDevices)
{
nsresult rv;
nsCOMPtr<nsIMutableArray> array = nsArray::Create();
nsCOMPtr<nsISupportsArray> array;
rv = NS_NewISupportsArray(getter_AddRefs(array));
NS_ENSURE_SUCCESS(rv, rv);
for (uint32_t i = 0; i < aDevices.Length(); ++i) {
rv = array->AppendElement(aDevices.ElementAt(i), /*weak =*/ false);
rv = array->AppendElement(aDevices.ElementAt(i));
NS_ENSURE_SUCCESS(rv, rv);
}

View File

@ -4,7 +4,7 @@
#include "nsISupports.idl"
interface nsIArray;
interface nsISupportsArray;
interface nsIDOMWindow;
%{C++
@ -16,7 +16,7 @@ interface nsIDOMWindow;
interface nsIMediaManagerService : nsISupports
{
/* return a array of inner windows that have active captures */
readonly attribute nsIArray activeMediaCaptureWindows;
readonly attribute nsISupportsArray activeMediaCaptureWindows;
/* Get the capture state for the given window and all descendant windows (iframes, etc) */
void mediaCaptureWindowState(in nsIDOMWindow aWindow, out boolean aVideo, out boolean aAudio,

View File

@ -58,7 +58,7 @@ var WebrtcUI = {
notify: function() {
let windows = MediaManagerService.activeMediaCaptureWindows;
let count = windows.length;
let count = windows.Count();
let msg = {};
if (count == 0) {
if (this._notificationId) {
@ -76,7 +76,7 @@ var WebrtcUI = {
let cameraActive = false;
let audioActive = false;
for (let i = 0; i < count; i++) {
let win = windows.queryElementAt(i, Ci.nsIDOMWindow);
let win = windows.GetElementAt(i);
let hasAudio = {};
let hasVideo = {};
MediaManagerService.mediaCaptureWindowState(win, hasVideo, hasAudio);
@ -150,19 +150,19 @@ var WebrtcUI = {
{
label: Strings.browser.GetStringFromName("getUserMedia.shareRequest.label"),
callback: function(checked /* ignored */, inputs) {
let allowedDevices = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
let allowedDevices = Cc["@mozilla.org/supports-array;1"].createInstance(Ci.nsISupportsArray);
let audioId = 0;
if (inputs && inputs.audioDevice != undefined)
audioId = inputs.audioDevice;
if (audioDevices[audioId])
allowedDevices.appendElement(audioDevices[audioId], /*weak =*/ false);
allowedDevices.AppendElement(audioDevices[audioId]);
let videoId = 0;
if (inputs && inputs.videoSource != undefined)
videoId = inputs.videoSource;
if (videoDevices[videoId]) {
allowedDevices.appendElement(videoDevices[videoId], /*weak =*/ false);
allowedDevices.AppendElement(videoDevices[videoId]);
let perms = Services.perms;
// Although the lifetime is "session" it will be removed upon
// use so it's more of a one-shot.

View File

@ -33,11 +33,17 @@ class nsISupportsArray;
[scriptable, uuid(241addc8-3608-4e73-8083-2fd6fa09eba2)]
interface nsISupportsArray : nsICollection {
[notxpcom] boolean Equals([const] in nsISupportsArray other);
[notxpcom] long IndexOf([const] in nsISupports aPossibleElement);
[notxpcom] long IndexOfStartingAt([const] in nsISupports aPossibleElement,
in unsigned long aStartIndex);
[notxpcom] long LastIndexOf([const] in nsISupports aPossibleElement);
// xpcom-compatible versions
long GetIndexOf(in nsISupports aPossibleElement);
long GetIndexOfStartingAt(in nsISupports aPossibleElement,
in unsigned long aStartIndex);
long GetLastIndexOf(in nsISupports aPossibleElement);
[notxpcom] boolean InsertElementAt(in nsISupports aElement,
@ -46,14 +52,25 @@ interface nsISupportsArray : nsICollection {
in unsigned long aIndex);
[notxpcom] boolean RemoveElementAt(in unsigned long aIndex);
[notxpcom] boolean RemoveLastElement([const] in nsISupports aElement);
// xpcom-compatible versions
void DeleteLastElement(in nsISupports aElement);
void DeleteElementAt(in unsigned long aIndex);
void Compact();
nsISupportsArray clone();
[notxpcom] boolean MoveElement(in long aFrom,
in long aTo);
[notxpcom] boolean RemoveElementsAt(in unsigned long aIndex,
in unsigned long aCount);
[notxpcom] boolean SizeTo(in long aSize);
};
%{C++

View File

@ -6,11 +6,79 @@
#include <stdint.h>
#include <string.h>
#include "nsIObjectInputStream.h"
#include "nsIObjectOutputStream.h"
#include "mozilla/CheckedInt.h"
#include "mozilla/MathAlgorithms.h"
#include "nsSupportsArray.h"
#include "nsSupportsArrayEnumerator.h"
#include "nsIObjectInputStream.h"
#include "nsIObjectOutputStream.h"
#if DEBUG_SUPPORTSARRAY
#define MAXSUPPORTS 20
class SupportsStats
{
public:
SupportsStats();
~SupportsStats();
};
static int sizesUsed; // number of the elements of the arrays used
static int sizesAlloced[MAXSUPPORTS]; // sizes of the allocations. sorted
static int NumberOfSize[MAXSUPPORTS]; // number of this allocation size (1 per array)
static int AllocedOfSize[MAXSUPPORTS]; // number of this allocation size (each size for array used)
static int GrowInPlace[MAXSUPPORTS];
// these are per-allocation
static int MaxElements[3000];
// very evil
#define ADD_TO_STATS(x,size) do {int i; for (i = 0; i < sizesUsed; i++) \
{ \
if (sizesAlloced[i] == (int)(size)) \
{ ((x)[i])++; break; } \
} \
if (i >= sizesUsed && sizesUsed < MAXSUPPORTS) \
{ sizesAlloced[sizesUsed] = (size); \
((x)[sizesUsed++])++; break; \
} \
} while (0);
#define SUB_FROM_STATS(x,size) do {int i; for (i = 0; i < sizesUsed; i++) \
{ \
if (sizesAlloced[i] == (int)(size)) \
{ ((x)[i])--; break; } \
} \
} while (0);
SupportsStats::SupportsStats()
{
sizesUsed = 1;
sizesAlloced[0] = 0;
}
SupportsStats::~SupportsStats()
{
int i;
for (i = 0; i < sizesUsed; ++i) {
printf("Size %d:\n", sizesAlloced[i]);
printf("\tNumber of SupportsArrays this size (max): %d\n", NumberOfSize[i]);
printf("\tNumber of allocations this size (total): %d\n", AllocedOfSize[i]);
printf("\tNumber of GrowsInPlace this size (total): %d\n", GrowInPlace[i]);
}
printf("Max Size of SupportsArray:\n");
for (i = 0; i < (int)(sizeof(MaxElements) / sizeof(MaxElements[0])); ++i) {
if (MaxElements[i]) {
printf("\t%d: %d\n", i, MaxElements[i]);
}
}
}
// Just so constructor/destructor get called
SupportsStats gSupportsStats;
#endif
nsresult
nsQueryElementAt::operator()(const nsIID& aIID, void** aResult) const
@ -28,11 +96,88 @@ nsQueryElementAt::operator()(const nsIID& aIID, void** aResult) const
nsSupportsArray::nsSupportsArray()
{
mArray = mAutoArray;
mArraySize = kAutoArraySize;
mCount = 0;
#if DEBUG_SUPPORTSARRAY
mMaxCount = 0;
mMaxSize = 0;
ADD_TO_STATS(NumberOfSize, kAutoArraySize * sizeof(mArray[0]));
MaxElements[0]++;
#endif
}
nsSupportsArray::~nsSupportsArray()
{
Clear();
DeleteArray();
}
bool
nsSupportsArray::GrowArrayBy(uint32_t aGrowBy)
{
const uint32_t kGrowArrayBy = 8;
const uint32_t kLinearThreshold = 16 * sizeof(nsISupports*);
// We have to grow the array. Grow by kGrowArrayBy slots if we're smaller
// than kLinearThreshold bytes, or a power of two if we're larger.
// This is much more efficient with most memory allocators, especially
// if it's very large, or of the allocator is binned.
if (aGrowBy < kGrowArrayBy) {
aGrowBy = kGrowArrayBy;
}
CheckedUint32 newCount(mArraySize);
newCount += aGrowBy; // Minimum increase
CheckedUint32 newSize(sizeof(mArray[0]));
newSize *= newCount;
if (!newSize.isValid()) {
return false;
}
if (newSize.value() >= kLinearThreshold) {
// newCount includes enough space for at least kGrowArrayBy new slots.
// Select the next power-of-two size in bytes above that if newSize is
// not a power of two.
if (newSize.value() & (newSize.value() - 1)) {
newSize = UINT64_C(1) << mozilla::CeilingLog2(newSize.value());
if (!newSize.isValid()) {
return false;
}
}
newCount = newSize / sizeof(mArray[0]);
}
// XXX This would be far more efficient in many allocators if we used
// XXX PR_Realloc(), etc
nsISupports** oldArray = mArray;
mArray = new nsISupports*[newCount.value()];
mArraySize = newCount.value();
#if DEBUG_SUPPORTSARRAY
if (oldArray == mArray) { // can't happen without use of realloc
ADD_TO_STATS(GrowInPlace, mCount);
}
ADD_TO_STATS(AllocedOfSize, mArraySize * sizeof(mArray[0]));
if (mArraySize > mMaxSize) {
ADD_TO_STATS(NumberOfSize, mArraySize * sizeof(mArray[0]));
if (oldArray != &(mAutoArray[0])) {
SUB_FROM_STATS(NumberOfSize, mCount * sizeof(mArray[0]));
}
mMaxSize = mArraySize;
}
#endif
if (oldArray) { // need to move old data
if (0 < mCount) {
::memcpy(mArray, oldArray, mCount * sizeof(nsISupports*));
}
if (oldArray != &(mAutoArray[0])) {
delete[] oldArray;
}
}
return true;
}
nsresult
@ -53,8 +198,6 @@ NS_IMPL_ISUPPORTS(nsSupportsArray, nsISupportsArray, nsICollection,
NS_IMETHODIMP
nsSupportsArray::Read(nsIObjectInputStream* aStream)
{
// TODO(ER): This used to leak when resizing the array. Not sure if that was
// intentional, I'm guessing not.
nsresult rv;
uint32_t newArraySize;
@ -63,40 +206,43 @@ nsSupportsArray::Read(nsIObjectInputStream* aStream)
return rv;
}
uint32_t count;
rv = aStream->Read32(&count);
if (newArraySize <= kAutoArraySize) {
if (mArray != mAutoArray) {
delete[] mArray;
mArray = mAutoArray;
}
newArraySize = kAutoArraySize;
} else {
if (newArraySize <= mArraySize) {
// Keep non-default-size mArray, it's more than big enough.
newArraySize = mArraySize;
} else {
nsISupports** array = new nsISupports*[newArraySize];
if (mArray != mAutoArray) {
delete[] mArray;
}
mArray = array;
}
}
mArraySize = newArraySize;
rv = aStream->Read32(&mCount);
if (NS_FAILED(rv)) {
return rv;
}
NS_ASSERTION(count <= newArraySize, "overlarge mCount!");
if (count > newArraySize) {
count = newArraySize;
NS_ASSERTION(mCount <= mArraySize, "overlarge mCount!");
if (mCount > mArraySize) {
mCount = mArraySize;
}
// Don't clear out our array until we know we have enough space for the new
// one and have successfully copied everything out of the stream.
ISupportsArray tmp;
if (!tmp.SetCapacity(newArraySize, mozilla::fallible)) {
return NS_ERROR_OUT_OF_MEMORY;
}
auto elems = tmp.AppendElements(count, mozilla::fallible);
for (uint32_t i = 0; i < count; i++) {
rv = aStream->ReadObject(true, &elems[i]);
for (uint32_t i = 0; i < mCount; i++) {
rv = aStream->ReadObject(true, &mArray[i]);
if (NS_FAILED(rv)) {
return rv;
}
}
// Now clear out existing refs and replace with the new array.
for (auto& item : mArray) {
NS_IF_RELEASE(item);
}
mArray.Clear();
mArray.SwapElements(tmp);
return NS_OK;
}
@ -105,18 +251,18 @@ nsSupportsArray::Write(nsIObjectOutputStream* aStream)
{
nsresult rv;
rv = aStream->Write32(mArray.Capacity());
rv = aStream->Write32(mArraySize);
if (NS_FAILED(rv)) {
return rv;
}
rv = aStream->Write32(mArray.Length());
rv = aStream->Write32(mCount);
if (NS_FAILED(rv)) {
return rv;
}
for (auto& item : mArray) {
rv = aStream->WriteObject(item, true);
for (uint32_t i = 0; i < mCount; i++) {
rv = aStream->WriteObject(mArray[i], true);
if (NS_FAILED(rv)) {
return rv;
}
@ -125,42 +271,141 @@ nsSupportsArray::Write(nsIObjectOutputStream* aStream)
return NS_OK;
}
void
nsSupportsArray::DeleteArray(void)
{
Clear();
if (mArray != &(mAutoArray[0])) {
delete[] mArray;
mArray = mAutoArray;
mArraySize = kAutoArraySize;
}
}
NS_IMETHODIMP_(bool)
nsSupportsArray::Equals(const nsISupportsArray* aOther)
{
if (aOther) {
uint32_t countOther;
nsISupportsArray* other = const_cast<nsISupportsArray*>(aOther);
nsresult rv = other->Count(&countOther);
if (NS_FAILED(rv)) {
return false;
}
if (mCount == countOther) {
uint32_t index = mCount;
nsCOMPtr<nsISupports> otherElem;
while (index--) {
if (NS_FAILED(other->GetElementAt(index, getter_AddRefs(otherElem)))) {
return false;
}
if (mArray[index] != otherElem) {
return false;
}
}
return true;
}
}
return false;
}
NS_IMETHODIMP
nsSupportsArray::GetElementAt(uint32_t aIndex, nsISupports** aOutPtr)
{
NS_IF_ADDREF(*aOutPtr = mArray.SafeElementAt(aIndex, nullptr));
*aOutPtr = nullptr;
if (aIndex < mCount) {
NS_IF_ADDREF(*aOutPtr = mArray[aIndex]);
}
return NS_OK;
}
NS_IMETHODIMP_(int32_t)
nsSupportsArray::IndexOf(const nsISupports* aPossibleElement)
{
return mArray.IndexOf(aPossibleElement);
return IndexOfStartingAt(aPossibleElement, 0);
}
NS_IMETHODIMP_(int32_t)
nsSupportsArray::IndexOfStartingAt(const nsISupports* aPossibleElement,
uint32_t aStartIndex)
{
if (aStartIndex < mCount) {
const nsISupports** start = (const nsISupports**)mArray; // work around goofy compiler behavior
const nsISupports** ep = (start + aStartIndex);
const nsISupports** end = (start + mCount);
while (ep < end) {
if (aPossibleElement == *ep) {
return (ep - start);
}
ep++;
}
}
return -1;
}
NS_IMETHODIMP_(int32_t)
nsSupportsArray::LastIndexOf(const nsISupports* aPossibleElement)
{
return mArray.LastIndexOf(aPossibleElement);
if (0 < mCount) {
const nsISupports** start = (const nsISupports**)mArray; // work around goofy compiler behavior
const nsISupports** ep = (start + mCount);
while (start <= --ep) {
if (aPossibleElement == *ep) {
return (ep - start);
}
}
}
return -1;
}
NS_IMETHODIMP_(bool)
nsSupportsArray::InsertElementAt(nsISupports* aElement, uint32_t aIndex)
{
if (aIndex <= mCount) {
CheckedUint32 newCount(mCount);
newCount += 1;
if (!newCount.isValid()) {
return false;
}
if (aIndex > mArray.Length() ||
!mArray.InsertElementAt(aIndex, aElement, mozilla::fallible)) {
return false;
if (mArraySize < newCount.value()) {
// need to grow the array
if (!GrowArrayBy(1)) {
return false;
}
}
// Could be slightly more efficient if GrowArrayBy knew about the
// split, but the difference is trivial.
uint32_t slide = (mCount - aIndex);
if (0 < slide) {
::memmove(mArray + aIndex + 1, mArray + aIndex,
slide * sizeof(nsISupports*));
}
mArray[aIndex] = aElement;
NS_IF_ADDREF(aElement);
mCount++;
#if DEBUG_SUPPORTSARRAY
if (mCount > mMaxCount &&
mCount < (int32_t)(sizeof(MaxElements) / sizeof(MaxElements[0]))) {
MaxElements[mCount]++;
MaxElements[mMaxCount]--;
mMaxCount = mCount;
}
#endif
return true;
}
NS_IF_ADDREF(aElement);
return true;
return false;
}
NS_IMETHODIMP_(bool)
nsSupportsArray::ReplaceElementAt(nsISupports* aElement, uint32_t aIndex)
{
if (aIndex < mArray.Length()) {
if (aIndex < mCount) {
NS_IF_ADDREF(aElement); // addref first in case it's the same object!
NS_IF_RELEASE(mArray[aIndex]);
mArray[aIndex] = aElement;
@ -170,11 +415,18 @@ nsSupportsArray::ReplaceElementAt(nsISupports* aElement, uint32_t aIndex)
}
NS_IMETHODIMP_(bool)
nsSupportsArray::RemoveElementAt(uint32_t aIndex)
nsSupportsArray::RemoveElementsAt(uint32_t aIndex, uint32_t aCount)
{
if (aIndex + 1 <= mArray.Length()) {
NS_IF_RELEASE(mArray[aIndex]);
mArray.RemoveElementAt(aIndex);
if (aIndex + aCount <= mCount) {
for (uint32_t i = 0; i < aCount; i++) {
NS_IF_RELEASE(mArray[aIndex + i]);
}
mCount -= aCount;
int32_t slide = (mCount - aIndex);
if (0 < slide) {
::memmove(mArray + aIndex, mArray + aIndex + aCount,
slide * sizeof(nsISupports*));
}
return true;
}
return false;
@ -183,7 +435,7 @@ nsSupportsArray::RemoveElementAt(uint32_t aIndex)
NS_IMETHODIMP
nsSupportsArray::RemoveElement(nsISupports* aElement)
{
int32_t theIndex = IndexOf(aElement);
int32_t theIndex = IndexOfStartingAt(aElement, 0);
if (theIndex >= 0) {
return RemoveElementAt(theIndex) ? NS_OK : NS_ERROR_FAILURE;
}
@ -191,25 +443,137 @@ nsSupportsArray::RemoveElement(nsISupports* aElement)
return NS_ERROR_FAILURE;
}
NS_IMETHODIMP_(bool)
nsSupportsArray::RemoveLastElement(const nsISupports* aElement)
{
int32_t theIndex = LastIndexOf(aElement);
if (theIndex >= 0) {
return RemoveElementAt(theIndex);
}
return false;
}
NS_IMETHODIMP_(bool)
nsSupportsArray::MoveElement(int32_t aFrom, int32_t aTo)
{
nsISupports* tempElement;
if (aTo == aFrom) {
return true;
}
if (aTo < 0 || aFrom < 0 ||
(uint32_t)aTo >= mCount || (uint32_t)aFrom >= mCount) {
// can't extend the array when moving an element. Also catches mImpl = null
return false;
}
tempElement = mArray[aFrom];
if (aTo < aFrom) {
// Moving one element closer to the head; the elements inbetween move down
::memmove(mArray + aTo + 1, mArray + aTo,
(aFrom - aTo) * sizeof(mArray[0]));
mArray[aTo] = tempElement;
} else { // already handled aFrom == aTo
// Moving one element closer to the tail; the elements inbetween move up
::memmove(mArray + aFrom, mArray + aFrom + 1,
(aTo - aFrom) * sizeof(mArray[0]));
mArray[aTo] = tempElement;
}
return true;
}
NS_IMETHODIMP
nsSupportsArray::Clear(void)
{
for (auto& item : mArray) {
NS_IF_RELEASE(item);
if (0 < mCount) {
do {
--mCount;
NS_IF_RELEASE(mArray[mCount]);
} while (0 != mCount);
}
mArray.Clear();
return NS_OK;
}
NS_IMETHODIMP
nsSupportsArray::Compact(void)
{
mArray.Compact();
#if DEBUG_SUPPORTSARRAY
uint32_t oldArraySize = mArraySize;
#endif
if ((mArraySize != mCount) && (kAutoArraySize < mArraySize)) {
nsISupports** oldArray = mArray;
if (mCount <= kAutoArraySize) {
mArray = mAutoArray;
mArraySize = kAutoArraySize;
} else {
mArray = new nsISupports*[mCount];
if (!mArray) {
mArray = oldArray;
return NS_OK;
}
mArraySize = mCount;
}
#if DEBUG_SUPPORTSARRAY
if (oldArray == mArray &&
oldArray != &(mAutoArray[0])) { // can't happen without use of realloc
ADD_TO_STATS(GrowInPlace, oldArraySize);
}
if (oldArray != &(mAutoArray[0])) {
ADD_TO_STATS(AllocedOfSize, mArraySize * sizeof(mArray[0]));
}
#endif
::memcpy(mArray, oldArray, mCount * sizeof(nsISupports*));
delete[] oldArray;
}
return NS_OK;
}
NS_IMETHODIMP_(bool)
nsSupportsArray::SizeTo(int32_t aSize)
{
#if DEBUG_SUPPORTSARRAY
uint32_t oldArraySize = mArraySize;
#endif
NS_ASSERTION(aSize >= 0, "negative aSize!");
// XXX for aSize < mCount we could resize to mCount
if (mArraySize == (uint32_t)aSize || (uint32_t)aSize < mCount) {
return true; // nothing to do
}
// switch back to autoarray if possible
nsISupports** oldArray = mArray;
if ((uint32_t)aSize <= kAutoArraySize) {
mArray = mAutoArray;
mArraySize = kAutoArraySize;
} else {
mArray = new nsISupports*[aSize];
if (!mArray) {
mArray = oldArray;
return false;
}
mArraySize = aSize;
}
#if DEBUG_SUPPORTSARRAY
if (oldArray == mArray &&
oldArray != &(mAutoArray[0])) { // can't happen without use of realloc
ADD_TO_STATS(GrowInPlace, oldArraySize);
}
if (oldArray != &(mAutoArray[0])) {
ADD_TO_STATS(AllocedOfSize, mArraySize * sizeof(mArray[0]));
}
#endif
::memcpy(mArray, oldArray, mCount * sizeof(nsISupports*));
if (oldArray != mAutoArray) {
delete[] oldArray;
}
return true;
}
NS_IMETHODIMP
nsSupportsArray::Enumerate(nsIEnumerator** aResult)
{
@ -228,10 +592,10 @@ nsSupportsArray::Clone(nsISupportsArray** aResult)
return rv;
}
for (auto& item : mArray) {
// AppendElement does an odd cast of bool to nsresult, we just cast back
// here.
if (!(bool)newArray->AppendElement(item)) {
uint32_t count = 0;
Count(&count);
for (uint32_t i = 0; i < count; i++) {
if (!newArray->InsertElementAt(mArray[i], i)) {
return NS_ERROR_OUT_OF_MEMORY;
}
}

View File

@ -7,10 +7,13 @@
#ifndef nsSupportsArray_h__
#define nsSupportsArray_h__
//#define DEBUG_SUPPORTSARRAY 1
#include "nsISupportsArray.h"
#include "nsTArray.h"
#include "mozilla/Attributes.h"
static const uint32_t kAutoArraySize = 8;
class nsSupportsArray final : public nsISupportsArray
{
~nsSupportsArray(void); // nonvirtual since we're not subclassed
@ -28,16 +31,18 @@ public:
// nsICollection methods:
NS_IMETHOD Count(uint32_t* aResult) override
{
*aResult = mArray.Length();
*aResult = mCount;
return NS_OK;
}
NS_IMETHOD GetElementAt(uint32_t aIndex, nsISupports** aResult) override;
MOZ_MUST_USE NS_IMETHOD
QueryElementAt(uint32_t aIndex, const nsIID& aIID, void** aResult) override
{
nsISupports* element = mArray.SafeElementAt(aIndex, nullptr);
if (element) {
return element->QueryInterface(aIID, aResult);
if (aIndex < mCount) {
nsISupports* element = mArray[aIndex];
if (element) {
return element->QueryInterface(aIID, aResult);
}
}
return NS_ERROR_FAILURE;
}
@ -49,15 +54,20 @@ public:
MOZ_MUST_USE NS_IMETHOD AppendElement(nsISupports* aElement) override
{
// XXX Invalid cast of bool to nsresult (bug 778110)
return (nsresult)InsertElementAt(aElement, mArray.Length())/* ? NS_OK : NS_ERROR_FAILURE*/;
return (nsresult)InsertElementAt(aElement, mCount)/* ? NS_OK : NS_ERROR_FAILURE*/;
}
// XXX this is badly named - should be RemoveFirstElement
MOZ_MUST_USE NS_IMETHOD RemoveElement(nsISupports* aElement) override;
MOZ_MUST_USE NS_IMETHOD_(bool) MoveElement(int32_t aFrom, int32_t aTo) override;
NS_IMETHOD Enumerate(nsIEnumerator** aResult) override;
NS_IMETHOD Clear(void) override;
// nsISupportsArray methods:
NS_IMETHOD_(bool) Equals(const nsISupportsArray* aOther) override;
NS_IMETHOD_(int32_t) IndexOf(const nsISupports* aPossibleElement) override;
NS_IMETHOD_(int32_t) IndexOfStartingAt(const nsISupports* aPossibleElement,
uint32_t aStartIndex = 0) override;
NS_IMETHOD_(int32_t) LastIndexOf(const nsISupports* aPossibleElement) override;
NS_IMETHOD GetIndexOf(nsISupports* aPossibleElement, int32_t* aResult) override
@ -66,6 +76,13 @@ public:
return NS_OK;
}
NS_IMETHOD GetIndexOfStartingAt(nsISupports* aPossibleElement,
uint32_t aStartIndex, int32_t* aResult) override
{
*aResult = IndexOfStartingAt(aPossibleElement, aStartIndex);
return NS_OK;
}
NS_IMETHOD GetLastIndexOf(nsISupports* aPossibleElement, int32_t* aResult) override
{
*aResult = LastIndexOf(aPossibleElement);
@ -79,7 +96,17 @@ public:
ReplaceElementAt(nsISupports* aElement, uint32_t aIndex) override;
MOZ_MUST_USE NS_IMETHOD_(bool)
RemoveElementAt(uint32_t aIndex) override;
RemoveElementAt(uint32_t aIndex) override
{
return RemoveElementsAt(aIndex, 1);
}
MOZ_MUST_USE NS_IMETHOD_(bool)
RemoveLastElement(const nsISupports* aElement) override;
MOZ_MUST_USE NS_IMETHOD DeleteLastElement(nsISupports* aElement) override
{
return (RemoveLastElement(aElement) ? NS_OK : NS_ERROR_FAILURE);
}
MOZ_MUST_USE NS_IMETHOD DeleteElementAt(uint32_t aIndex) override
{
@ -90,12 +117,28 @@ public:
MOZ_MUST_USE NS_IMETHOD Clone(nsISupportsArray** aResult) override;
MOZ_MUST_USE NS_IMETHOD_(bool)
RemoveElementsAt(uint32_t aIndex, uint32_t aCount) override;
MOZ_MUST_USE NS_IMETHOD_(bool)
SizeTo(int32_t aSize) override;
protected:
void DeleteArray(void);
bool GrowArrayBy(uint32_t aGrowBy);
nsISupports** mArray;
uint32_t mArraySize;
uint32_t mCount;
nsISupports* mAutoArray[kAutoArraySize];
#if DEBUG_SUPPORTSARRAY
uint32_t mMaxCount;
uint32_t mMaxSize;
#endif
private:
// Copy constructors are not allowed
explicit nsSupportsArray(const nsISupportsArray& aOther);
typedef AutoTArray<nsISupports*, 8> ISupportsArray;
ISupportsArray mArray;
};
#endif // nsSupportsArray_h__

View File

@ -119,19 +119,26 @@ TEST(Array, main)
// test IndexOf && LastIndexOf
int32_t expectedIndex = 0;
int32_t expectedIndex[5] = {0, 4, 6, 12, -1};
int32_t count = 0;
int32_t index = array->IndexOf(foo);
EXPECT_EQ(index, expectedIndex);
expectedIndex = 12;
EXPECT_EQ(index, expectedIndex[count]);
while (-1 != index) {
count++;
index = array->IndexOfStartingAt(foo, index + 1);
if (-1 != index)
EXPECT_EQ(index, expectedIndex[count]);
}
index = array->LastIndexOf(foo);
EXPECT_EQ(index, expectedIndex);
count--;
EXPECT_EQ(index, expectedIndex[count]);
// test ReplaceElementAt
array->ReplaceElementAt(foo, 8);
int32_t replaceResult[13] = {3, 0, 1, 2, 3, 4, 3, 5, 3, 7, 8, 9, 3};
CheckArray(array, 13, replaceResult, 9);
// test RemoveElementAt, RemoveElement
// test RemoveElementAt, RemoveElement RemoveLastElement
array->RemoveElementAt(0);
int32_t removeResult[12] = {0, 1, 2, 3, 4, 3, 5, 3, 7, 8, 9, 3};
CheckArray(array, 12, removeResult, 9);
@ -141,6 +148,9 @@ TEST(Array, main)
array->RemoveElement(foo);
int32_t removeResult3[10] = {0, 1, 2, 4, 3, 5, 7, 8, 9, 3};
CheckArray(array, 10, removeResult3, 9);
array->RemoveLastElement(foo);
int32_t removeResult4[9] = {0, 1, 2, 4, 3, 5, 7, 8, 9};
CheckArray(array, 9, removeResult4, 9);
foo = nullptr;