void DOMSVGPathSegList:: MaybeInsertNullInAnimValListAt(uint32_t aIndex, uint32_t aInternalIndex, uint32_t aArgCountForItem) { NS_ABORT_IF_FALSE(!IsAnimValList(), "call from baseVal to animVal"); if (AttrIsAnimating()) { // animVal not a clone of baseVal return; } // The anim val list is in sync with the base val list DOMSVGPathSegList *animVal = GetDOMWrapperIfExists(InternalAList().GetAnimValKey()); if (!animVal) { // No animVal list wrapper return; } NS_ABORT_IF_FALSE(animVal->mItems.Length() == mItems.Length(), "animVal list not in sync!"); animVal->mItems.InsertElementAt(aIndex, ItemProxy(nullptr, aInternalIndex)); animVal->UpdateListIndicesFromIndex(aIndex + 1, 1 + aArgCountForItem); }
NS_IMETHODIMP DOMSVGPathSegList::InsertItemBefore(nsIDOMSVGPathSeg *aNewItem, PRUint32 aIndex, nsIDOMSVGPathSeg **_retval) { *_retval = nsnull; if (IsAnimValList()) { return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR; } PRUint32 internalIndex; if (aIndex < Length()) { internalIndex = mItems[aIndex].mInternalDataIndex; } else { aIndex = Length(); internalIndex = InternalList().mData.Length(); } if (aIndex >= DOMSVGPathSeg::MaxListIndex()) { return NS_ERROR_DOM_INDEX_SIZE_ERR; } nsCOMPtr<DOMSVGPathSeg> domItem = do_QueryInterface(aNewItem); if (!domItem) { return NS_ERROR_DOM_SVG_WRONG_TYPE_ERR; } if (domItem->HasOwner()) { domItem = domItem->Clone(); // must do this before changing anything! } PRUint32 argCount = SVGPathSegUtils::ArgCountForType(domItem->Type()); // Ensure we have enough memory so we can avoid complex error handling below: if (!mItems.SetCapacity(mItems.Length() + 1) || !InternalList().mData.SetCapacity(InternalList().mData.Length() + 1 + argCount)) { return NS_ERROR_OUT_OF_MEMORY; } // Now that we know we're inserting, keep animVal list in sync as necessary. MaybeInsertNullInAnimValListAt(aIndex, internalIndex, argCount); float segAsRaw[1 + NS_SVG_PATH_SEG_MAX_ARGS]; domItem->ToSVGPathSegEncodedData(segAsRaw); InternalList().mData.InsertElementsAt(internalIndex, segAsRaw, 1 + argCount); mItems.InsertElementAt(aIndex, ItemProxy(domItem.get(), internalIndex)); // This MUST come after the insertion into InternalList(), or else under the // insertion into InternalList() the values read from domItem would be bad // data from InternalList() itself!: domItem->InsertingIntoList(this, aIndex, IsAnimValList()); UpdateListIndicesFromIndex(aIndex + 1, argCount + 1); Element()->DidChangePathSegList(true); if (AttrIsAnimating()) { Element()->AnimationNeedsResample(); } *_retval = domItem.forget().get(); return NS_OK; }
already_AddRefed<DOMSVGPathSeg> DOMSVGPathSegList::InsertItemBefore(DOMSVGPathSeg& aNewItem, uint32_t aIndex, ErrorResult& aError) { if (IsAnimValList()) { aError.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR); return nullptr; } uint32_t internalIndex; if (aIndex < LengthNoFlush()) { internalIndex = mItems[aIndex].mInternalDataIndex; } else { aIndex = LengthNoFlush(); internalIndex = InternalList().mData.Length(); } if (aIndex >= DOMSVGPathSeg::MaxListIndex()) { aError.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR); return nullptr; } nsRefPtr<DOMSVGPathSeg> domItem = &aNewItem; if (domItem->HasOwner()) { domItem = domItem->Clone(); // must do this before changing anything! } uint32_t argCount = SVGPathSegUtils::ArgCountForType(domItem->Type()); // Ensure we have enough memory so we can avoid complex error handling below: if (!mItems.SetCapacity(mItems.Length() + 1) || !InternalList().mData.SetCapacity(InternalList().mData.Length() + 1 + argCount)) { aError.Throw(NS_ERROR_OUT_OF_MEMORY); return nullptr; } nsAttrValue emptyOrOldValue = Element()->WillChangePathSegList(); // Now that we know we're inserting, keep animVal list in sync as necessary. MaybeInsertNullInAnimValListAt(aIndex, internalIndex, argCount); float segAsRaw[1 + NS_SVG_PATH_SEG_MAX_ARGS]; domItem->ToSVGPathSegEncodedData(segAsRaw); InternalList().mData.InsertElementsAt(internalIndex, segAsRaw, 1 + argCount); mItems.InsertElementAt(aIndex, ItemProxy(domItem.get(), internalIndex)); // This MUST come after the insertion into InternalList(), or else under the // insertion into InternalList() the values read from domItem would be bad // data from InternalList() itself!: domItem->InsertingIntoList(this, aIndex, IsAnimValList()); UpdateListIndicesFromIndex(aIndex + 1, argCount + 1); Element()->DidChangePathSegList(emptyOrOldValue); if (AttrIsAnimating()) { Element()->AnimationNeedsResample(); } return domItem.forget(); }
void DOMSVGPathSegList::InternalListWillChangeTo(const SVGPathData& aNewValue) { // When the number of items in our internal counterpart changes, we MUST stay // in sync. Everything in the scary comment in // DOMSVGLengthList::InternalBaseValListWillChangeTo applies here just as // much, but we have the additional issue that failing to stay in sync would // mean that - assuming we aren't reading bad memory - we would likely end up // decoding command types from argument floats when looking in our // SVGPathData's data array! Either way, we'll likely then go down // NS_NOTREACHED code paths, or end up reading/setting more bad memory!! // The only time that our other DOM list type implementations remove items is // if those items become surplus items due to an attribute change or SMIL // animation sample shortening the list. In general though, they try to keep // their existing DOM items, even when things change. To be consistent, we'd // really like to do the same thing. However, because different types of path // segment correspond to different DOMSVGPathSeg subclasses, the type of // items in our list are generally not the same, which makes this harder for // us. We have to remove DOM segments if their type is not the same as the // type of the new internal segment at their index. // // We also need to sync up mInternalDataIndex, but since we need to loop over // all the items in the new list checking types anyway, that's almost // insignificant in terms of overhead. // // Note that this method is called on every single SMIL animation resample // and we have no way to short circuit the overhead since we don't have a // way to tell if the call is due to a new animation, or a resample of an // existing animation (when the number and type of items would be the same). // (Note that a new animation could start overriding an existing animation at // any time, so checking IsAnimating() wouldn't work.) Because we get called // on every sample, it would not be acceptable alternative to throw away all // our items and let them be recreated lazily, since that would break what // script sees! uint32_t length = mItems.Length(); uint32_t index = 0; uint32_t dataLength = aNewValue.mData.Length(); uint32_t dataIndex = 0; // index into aNewValue's raw data array uint32_t newSegType; nsRefPtr<DOMSVGPathSegList> kungFuDeathGrip; if (length) { // RemovingFromList() might clear last reference to |this|. // Retain a temporary reference to keep from dying before returning. // // NOTE: For path-seg lists (unlike other list types), we have to do this // *whenever our list is nonempty* (even if we're growing in length). // That's because the path-seg-type of any segment could differ between old // list vs. new list, which will make us destroy & recreate that segment, // which could remove the last reference to us. // // (We explicitly *don't* want to create a kungFuDeathGrip in the length=0 // case, though, because we do hit this code inside our constructor before // any other owning references have been added, and at that point, the // deathgrip-removal would make us die before we exit our constructor.) kungFuDeathGrip = this; } while (index < length && dataIndex < dataLength) { newSegType = SVGPathSegUtils::DecodeType(aNewValue.mData[dataIndex]); if (ItemAt(index) && ItemAt(index)->Type() != newSegType) { ItemAt(index)->RemovingFromList(); ItemAt(index) = nullptr; } // Only after the RemovingFromList() can we touch mInternalDataIndex! mItems[index].mInternalDataIndex = dataIndex; ++index; dataIndex += 1 + SVGPathSegUtils::ArgCountForType(newSegType); } NS_ABORT_IF_FALSE((index == length && dataIndex <= dataLength) || (index <= length && dataIndex == dataLength), "very bad - list corruption?"); if (index < length) { // aNewValue has fewer items than our previous internal counterpart uint32_t newLength = index; // Remove excess items from the list: for (; index < length; ++index) { if (ItemAt(index)) { ItemAt(index)->RemovingFromList(); ItemAt(index) = nullptr; } } // Only now may we truncate mItems mItems.SetLength(newLength); } else if (dataIndex < dataLength) { // aNewValue has more items than our previous internal counterpart // Sync mItems: while (dataIndex < dataLength) { if (mItems.Length() && mItems.Length() - 1 > DOMSVGPathSeg::MaxListIndex()) { // It's safe to get out of sync with our internal list as long as we // have FEWER items than it does. return; } if (!mItems.AppendElement(ItemProxy(nullptr, dataIndex))) { // OOM ErrorResult rv; Clear(rv); MOZ_ASSERT(!rv.Failed()); return; } dataIndex += 1 + SVGPathSegUtils::ArgCountForType(SVGPathSegUtils::DecodeType(aNewValue.mData[dataIndex])); } } NS_ABORT_IF_FALSE(dataIndex == dataLength, "Serious processing error"); NS_ABORT_IF_FALSE(index == length, "Serious counting error"); }