Backout bug 473161

This commit is contained in:
Joe Drew 2009-01-26 17:12:05 -05:00
commit 763c2531bd
4 changed files with 43 additions and 292 deletions

View File

@ -128,8 +128,6 @@ static PRBool NewRequestAndEntry(nsIURI *uri, imgRequest **request, imgCacheEntr
NS_ADDREF(*request);
NS_ADDREF(*entry);
imgLoader::SetHasNoProxies(uri, *entry);
return PR_TRUE;
}
@ -249,26 +247,16 @@ imgCacheEntry::imgCacheEntry(imgRequest *request, PRBool mustValidateIfExpired /
mTouchedTime(SecondsFromPRTime(PR_Now())),
mExpiryTime(0),
mMustValidateIfExpired(mustValidateIfExpired),
mEvicted(PR_FALSE),
mHasNoProxies(PR_TRUE)
mEvicted(PR_FALSE)
{}
imgCacheEntry::~imgCacheEntry()
{
LOG_FUNC(gImgLog, "imgCacheEntry::~imgCacheEntry()");
}
void imgCacheEntry::TouchWithSize(PRInt32 diff)
{
LOG_SCOPE(gImgLog, "imgCacheEntry::TouchWithSize");
mTouchedTime = SecondsFromPRTime(PR_Now());
// Don't update the cache if we've been removed from it or it doesn't care
// about our size or usage.
if (!Evicted() && HasNoProxies()) {
// We can't use mKeyURI here, because we're not guaranteed to be updated if
// the request has no observers and has thus dropped its reference to us.
if (!Evicted()) {
nsCOMPtr<nsIURI> uri;
mRequest->GetKeyURI(getter_AddRefs(uri));
imgLoader::CacheEntriesChanged(uri, diff);
@ -282,34 +270,13 @@ void imgCacheEntry::Touch(PRBool updateTime /* = PR_TRUE */)
if (updateTime)
mTouchedTime = SecondsFromPRTime(PR_Now());
// Don't update the cache if we've been removed from it or it doesn't care
// about our size or usage.
if (!Evicted() && HasNoProxies()) {
// We can't use mKeyURI here, because we're not guaranteed to be updated if
// the request has no observers and has thus dropped its reference to us.
if (!Evicted()) {
nsCOMPtr<nsIURI> uri;
mRequest->GetKeyURI(getter_AddRefs(uri));
imgLoader::CacheEntriesChanged(uri);
}
}
void imgCacheEntry::SetHasNoProxies(PRBool hasNoProxies)
{
#if defined(PR_LOGGING)
nsCOMPtr<nsIURI> uri;
mRequest->GetKeyURI(getter_AddRefs(uri));
nsCAutoString spec;
if (uri)
uri->GetSpec(spec);
if (hasNoProxies)
LOG_FUNC_WITH_PARAM(gImgLog, "imgCacheEntry::SetHasNoProxies true", "uri", spec.get());
else
LOG_FUNC_WITH_PARAM(gImgLog, "imgCacheEntry::SetHasNoProxies false", "uri", spec.get());
#endif
mHasNoProxies = hasNoProxies;
}
imgCacheQueue::imgCacheQueue()
: mDirty(PR_FALSE),
mSize(0)
@ -482,17 +449,6 @@ imgCacheExpirationTracker::imgCacheExpirationTracker()
void imgCacheExpirationTracker::NotifyExpired(imgCacheEntry *entry)
{
#if defined(PR_LOGGING)
nsRefPtr<imgRequest> req(entry->GetRequest());
if (req) {
nsCOMPtr<nsIURI> uri;
req->GetKeyURI(getter_AddRefs(uri));
nsCAutoString spec;
uri->GetSpec(spec);
LOG_FUNC_WITH_PARAM(gImgLog, "imgCacheExpirationTracker::NotifyExpired", "entry", spec.get());
}
#endif
// We can be called multiple times on the same entry. Don't do work multiple
// times.
if (!entry->Evicted())
@ -534,9 +490,11 @@ void imgLoader::VerifyCacheSizes()
return;
PRUint32 queuesize = sCacheQueue.GetNumElements() + sChromeCacheQueue.GetNumElements();
PRUint32 cachesize = sCache.Count() + sChromeCache.Count();
PRUint32 trackersize = 0;
for (nsExpirationTracker<imgCacheEntry, 3>::Iterator it(gCacheTracker); it.Next(); )
trackersize++;
NS_ASSERTION(queuesize == cachesize, "Queue and cache sizes out of sync!");
NS_ASSERTION(queuesize == trackersize, "Queue and tracker sizes out of sync!");
}
@ -635,9 +593,8 @@ NS_IMETHODIMP imgLoader::FindEntryProperties(nsIURI *uri, nsIProperties **_retva
*_retval = nsnull;
if (cache.Get(spec, getter_AddRefs(entry)) && entry) {
if (gCacheTracker && entry->HasNoProxies())
if (gCacheTracker)
gCacheTracker->MarkUsed(entry);
nsRefPtr<imgRequest> request = getter_AddRefs(entry->GetRequest());
if (request) {
*_retval = request->Properties();
@ -669,13 +626,13 @@ nsresult imgLoader::ClearImageCache()
PRBool imgLoader::PutIntoCache(nsIURI *key, imgCacheEntry *entry)
{
LOG_STATIC_FUNC(gImgLog, "imgLoader::PutIntoCache");
imgCacheTable &cache = GetCache(key);
nsCAutoString spec;
key->GetSpec(spec);
LOG_STATIC_FUNC_WITH_PARAM(gImgLog, "imgLoader::PutIntoCache", "uri", spec.get());
// Check to see if this request already exists in the cache and is being
// loaded on a different thread. If so, don't allow this entry to be added to
// the cache.
@ -703,57 +660,17 @@ PRBool imgLoader::PutIntoCache(nsIURI *key, imgCacheEntry *entry)
if (!cache.Put(spec, entry))
return PR_FALSE;
return PR_TRUE;
}
PRBool imgLoader::SetHasNoProxies(nsIURI *key, imgCacheEntry *entry)
{
#if defined(PR_LOGGING)
nsCAutoString spec;
key->GetSpec(spec);
LOG_STATIC_FUNC_WITH_PARAM(gImgLog, "imgLoader::SetHasNoProxies", "uri", spec.get());
#endif
imgCacheQueue &queue = GetCacheQueue(key);
queue.Push(entry);
if (gCacheTracker)
gCacheTracker->AddObject(entry);
entry->SetHasNoProxies(PR_TRUE);
imgCacheTable &cache = GetCache(key);
CheckCacheLimits(cache, queue);
return PR_TRUE;
}
PRBool imgLoader::SetHasProxies(nsIURI *key)
{
imgCacheTable &cache = GetCache(key);
nsCAutoString spec;
key->GetSpec(spec);
LOG_STATIC_FUNC_WITH_PARAM(gImgLog, "imgLoader::SetHasProxies", "uri", spec.get());
nsRefPtr<imgCacheEntry> entry;
if (cache.Get(spec, getter_AddRefs(entry)) && entry && entry->HasNoProxies()) {
imgCacheQueue &queue = GetCacheQueue(key);
queue.Remove(entry);
if (gCacheTracker)
gCacheTracker->RemoveObject(entry);
entry->SetHasNoProxies(PR_FALSE);
return PR_TRUE;
}
return PR_FALSE;
}
void imgLoader::CacheEntriesChanged(nsIURI *uri, PRInt32 sizediff /* = 0 */)
{
imgCacheQueue &queue = GetCacheQueue(uri);
@ -774,17 +691,6 @@ void imgLoader::CheckCacheLimits(imgCacheTable &cache, imgCacheQueue &queue)
NS_ASSERTION(entry, "imgLoader::CheckCacheLimits -- NULL entry pointer");
#if defined(PR_LOGGING)
nsRefPtr<imgRequest> req(entry->GetRequest());
if (req) {
nsCOMPtr<nsIURI> uri;
req->GetKeyURI(getter_AddRefs(uri));
nsCAutoString spec;
uri->GetSpec(spec);
LOG_STATIC_FUNC_WITH_PARAM(gImgLog, "imgLoader::CheckCacheLimits", "entry", spec.get());
}
#endif
if (entry)
RemoveFromCache(entry);
}
@ -807,8 +713,6 @@ PRBool imgLoader::ValidateRequestWithNewChannel(imgRequest *request,
nsresult rv;
// If we're currently in the middle of validating this request, just hand
// back a proxy to it; the required work will be done for us.
if (request->mValidator) {
rv = CreateNewProxyForRequest(request, aLoadGroup, aObserver,
aLoadFlags, aExistingRequest,
@ -997,6 +901,7 @@ PRBool imgLoader::ValidateEntry(imgCacheEntry *aEntry,
PRBool imgLoader::RemoveFromCache(nsIURI *aKey)
{
LOG_STATIC_FUNC(gImgLog, "imgLoader::RemoveFromCache uri");
if (!aKey) return PR_FALSE;
imgCacheTable &cache = GetCache(aKey);
@ -1005,23 +910,13 @@ PRBool imgLoader::RemoveFromCache(nsIURI *aKey)
nsCAutoString spec;
aKey->GetSpec(spec);
LOG_STATIC_FUNC_WITH_PARAM(gImgLog, "imgLoader::RemoveFromCache", "uri", spec.get());
nsRefPtr<imgCacheEntry> entry;
if (cache.Get(spec, getter_AddRefs(entry)) && entry) {
if (gCacheTracker)
gCacheTracker->RemoveObject(entry);
cache.Remove(spec);
NS_ABORT_IF_FALSE(!entry->Evicted(), "Evicting an already-evicted cache entry!");
// Entries with no proxies are in the tracker.
if (entry->HasNoProxies()) {
if (gCacheTracker)
gCacheTracker->RemoveObject(entry);
queue.Remove(entry);
}
queue.Remove(entry);
entry->SetEvicted(PR_TRUE);
return PR_TRUE;
}
else
@ -1031,43 +926,15 @@ PRBool imgLoader::RemoveFromCache(nsIURI *aKey)
PRBool imgLoader::RemoveFromCache(imgCacheEntry *entry)
{
LOG_STATIC_FUNC(gImgLog, "imgLoader::RemoveFromCache entry");
PRBool ret = PR_FALSE;
nsRefPtr<imgRequest> request(getter_AddRefs(entry->GetRequest()));
if (request) {
nsCOMPtr<nsIURI> key;
if (NS_SUCCEEDED(request->GetKeyURI(getter_AddRefs(key))) && key) {
imgCacheTable &cache = GetCache(key);
imgCacheQueue &queue = GetCacheQueue(key);
nsCAutoString spec;
key->GetSpec(spec);
cache.Remove(spec);
if (entry->HasNoProxies()) {
LOG_STATIC_FUNC(gImgLog, "imgLoader::RemoveFromCache removing from tracker");
if (gCacheTracker)
gCacheTracker->RemoveObject(entry);
queue.Remove(entry);
}
entry->SetEvicted(PR_TRUE);
return PR_TRUE;
}
if (NS_SUCCEEDED(request->GetKeyURI(getter_AddRefs(key))) && key)
ret = RemoveFromCache(key);
}
return PR_FALSE;
}
static PLDHashOperator EnumEvictEntries(const nsACString&,
nsRefPtr<imgCacheEntry> &aData,
void *data)
{
nsTArray<nsRefPtr<imgCacheEntry> > *entries =
reinterpret_cast<nsTArray<nsRefPtr<imgCacheEntry> > *>(data);
entries->AppendElement(aData);
return PL_DHASH_NEXT;
return ret;
}
nsresult imgLoader::EvictEntries(imgCacheTable &aCacheToClear, imgCacheQueue &aQueueToClear)
@ -1077,8 +944,9 @@ nsresult imgLoader::EvictEntries(imgCacheTable &aCacheToClear, imgCacheQueue &aQ
// We have to make a temporary, since RemoveFromCache removes the element
// from the queue, invalidating iterators.
nsTArray<nsRefPtr<imgCacheEntry> > entries;
aCacheToClear.Enumerate(EnumEvictEntries, &entries);
for (imgCacheQueue::iterator it = aQueueToClear.begin(); it != aQueueToClear.end(); ++it)
entries.AppendElement(*it);
for (PRUint32 i = 0; i < entries.Length(); ++i)
if (!RemoveFromCache(entries[i]))
return NS_ERROR_FAILURE;
@ -1170,22 +1038,14 @@ NS_IMETHODIMP imgLoader::LoadImage(nsIURI *aURI,
aURI->GetSpec(spec);
if (cache.Get(spec, getter_AddRefs(entry)) && entry) {
if (gCacheTracker)
gCacheTracker->MarkUsed(entry);
if (ValidateEntry(entry, aURI, aInitialDocumentURI, aReferrerURI, aLoadGroup, aObserver, aCX,
requestFlags, PR_TRUE, aRequest, _retval)) {
request = getter_AddRefs(entry->GetRequest());
// If this entry has no proxies, its request has no reference to the entry.
if (entry->HasNoProxies()) {
LOG_FUNC_WITH_PARAM(gImgLog, "imgLoader::LoadImage() adding proxyless entry", "uri", spec.get());
NS_ABORT_IF_FALSE(!request->HasCacheEntry(), "Proxyless entry's request has cache entry!");
request->SetCacheEntry(entry);
if (gCacheTracker)
gCacheTracker->MarkUsed(entry);
}
entry->Touch();
#ifdef DEBUG_joe
printf("CACHEGET: %d %s %d\n", time(NULL), spec.get(), entry->GetDataSize());
#endif
@ -1313,6 +1173,9 @@ NS_IMETHODIMP imgLoader::LoadImageWithChannel(nsIChannel *channel, imgIDecoderOb
uri->GetSpec(spec);
if (cache.Get(spec, getter_AddRefs(entry)) && entry) {
if (gCacheTracker)
gCacheTracker->MarkUsed(entry);
// We don't want to kick off another network load. So we ask
// ValidateEntry to only do validation without creating a new proxy. If
// it says that the entry isn't valid any more, we'll only use the entry
@ -1338,18 +1201,6 @@ NS_IMETHODIMP imgLoader::LoadImageWithChannel(nsIChannel *channel, imgIDecoderOb
request = getter_AddRefs(entry->GetRequest());
}
}
if (request && entry) {
// If this entry has no proxies, its request has no reference to the entry.
if (entry->HasNoProxies()) {
LOG_FUNC_WITH_PARAM(gImgLog, "imgLoader::LoadImageWithChannel() adding proxyless entry", "uri", spec.get());
NS_ABORT_IF_FALSE(!request->HasCacheEntry(), "Proxyless entry's request has cache entry!");
request->SetCacheEntry(entry);
if (gCacheTracker)
gCacheTracker->MarkUsed(entry);
}
}
}
}
@ -1395,6 +1246,7 @@ NS_IMETHODIMP imgLoader::LoadImageWithChannel(nsIChannel *channel, imgIDecoderOb
return rv;
}
NS_IMETHODIMP imgLoader::SupportImageWithMimeType(const char* aMimeType, PRBool *_retval)
{
*_retval = PR_FALSE;
@ -1619,23 +1471,16 @@ NS_IMETHODIMP imgCacheValidator::OnStartRequest(nsIRequest *aRequest, nsISupport
}
}
// We can't load out of cache. We have to create a whole new request for the
// data that's coming in off the channel.
// fun stuff.
nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
nsRefPtr<imgCacheEntry> entry;
nsCOMPtr<nsIURI> uri;
mRequest->GetURI(getter_AddRefs(uri));
#if defined(PR_LOGGING)
nsCAutoString spec;
uri->GetSpec(spec);
LOG_MSG_WITH_PARAM(gImgLog, "imgCacheValidator::OnStartRequest creating new request", "uri", spec.get());
#endif
// Doom the old request's cache entry
mRequest->RemoveFromCache();
mRequest->GetURI(getter_AddRefs(uri));
mRequest->mValidator = nsnull;
mRequest = nsnull;
@ -1657,11 +1502,6 @@ NS_IMETHODIMP imgCacheValidator::OnStartRequest(nsIRequest *aRequest, nsISupport
mDestListener = static_cast<nsIStreamListener*>(pl);
// Try to add the new request into the cache. Note that the entry must be in
// the cache before the proxies' ownership changes, because adding a proxy
// changes the caching behaviour for imgRequests.
sImgLoader.PutIntoCache(uri, entry);
PRUint32 count = mProxies.Count();
for (PRInt32 i = count-1; i>=0; i--) {
imgRequestProxy *proxy = static_cast<imgRequestProxy *>(mProxies[i]);
@ -1669,6 +1509,9 @@ NS_IMETHODIMP imgCacheValidator::OnStartRequest(nsIRequest *aRequest, nsISupport
request->NotifyProxyListener(proxy);
}
// Try to add the new request into the cache.
sImgLoader.PutIntoCache(uri, entry);
NS_RELEASE(request);
if (!mDestListener)

View File

@ -61,7 +61,6 @@ class imgCacheEntry
{
public:
imgCacheEntry(imgRequest *request, PRBool mustValidateIfExpired = PR_FALSE);
~imgCacheEntry();
nsrefcnt AddRef()
{
@ -144,11 +143,6 @@ public:
return &mExpirationState;
}
PRBool HasNoProxies() const
{
return mHasNoProxies;
}
private: // methods
friend class imgLoader;
friend class imgCacheQueue;
@ -158,24 +152,18 @@ private: // methods
{
mEvicted = evict;
}
void SetHasNoProxies(PRBool hasNoProxies);
// Private, unimplemented copy constructor.
imgCacheEntry(const imgCacheEntry &);
private: // data
nsAutoRefCnt mRefCnt;
NS_DECL_OWNINGTHREAD
nsRefPtr<imgRequest> mRequest;
nsCOMPtr<nsIURI> mKeyURI;
PRUint32 mDataSize;
PRInt32 mTouchedTime;
PRInt32 mExpiryTime;
nsExpirationState mExpirationState;
PRPackedBool mMustValidateIfExpired : 1;
PRPackedBool mEvicted : 1;
PRPackedBool mHasNoProxies : 1;
PRBool mMustValidateIfExpired;
PRBool mEvicted;
};
#include <vector>
@ -262,20 +250,6 @@ public:
static void VerifyCacheSizes();
// The image loader maintains a hash table of all imgCacheEntries. However,
// only some of them will be evicted from the cache: those who have no
// imgRequestProxies watching their imgRequests.
//
// Once an imgRequest has no imgRequestProxies, it should notify us by
// calling HasNoObservers(), and null out its cache entry pointer.
//
// Upon having a proxy start observing again, it should notify us by calling
// HasObservers(). The request's cache entry will be re-set before this
// happens, by calling imgRequest::SetCacheEntry() when an entry with no
// observers is re-requested.
static PRBool SetHasNoProxies(nsIURI *key, imgCacheEntry *entry);
static PRBool SetHasProxies(nsIURI *key);
private: // methods

View File

@ -92,12 +92,7 @@ imgRequest::imgRequest() :
imgRequest::~imgRequest()
{
if (mKeyURI) {
nsCAutoString spec;
mKeyURI->GetSpec(spec);
LOG_FUNC_WITH_PARAM(gImgLog, "imgRequest::~imgRequest()", "keyuri", spec.get());
} else
LOG_FUNC(gImgLog, "imgRequest::~imgRequest()");
/* destructor code */
}
nsresult imgRequest::Init(nsIURI *aURI,
@ -110,17 +105,15 @@ nsresult imgRequest::Init(nsIURI *aURI,
{
LOG_FUNC(gImgLog, "imgRequest::Init");
NS_ABORT_IF_FALSE(!mImage, "Multiple calls to init");
NS_ABORT_IF_FALSE(aURI, "No uri");
NS_ABORT_IF_FALSE(aKeyURI, "No key uri");
NS_ABORT_IF_FALSE(aRequest, "No request");
NS_ABORT_IF_FALSE(aChannel, "No channel");
NS_ASSERTION(!mImage, "Multiple calls to init");
NS_ASSERTION(aURI, "No uri");
NS_ASSERTION(aRequest, "No request");
NS_ASSERTION(aChannel, "No channel");
mProperties = do_CreateInstance("@mozilla.org/properties;1");
if (!mProperties)
return NS_ERROR_OUT_OF_MEMORY;
mURI = aURI;
mKeyURI = aKeyURI;
mRequest = aRequest;
@ -148,28 +141,11 @@ nsresult imgRequest::Init(nsIURI *aURI,
return NS_OK;
}
void imgRequest::SetCacheEntry(imgCacheEntry *entry)
{
mCacheEntry = entry;
}
PRBool imgRequest::HasCacheEntry() const
{
return mCacheEntry != nsnull;
}
nsresult imgRequest::AddProxy(imgRequestProxy *proxy)
{
NS_PRECONDITION(proxy, "null imgRequestProxy passed in");
LOG_SCOPE_WITH_PARAM(gImgLog, "imgRequest::AddProxy", "proxy", proxy);
// If we're empty before adding, we have to tell the loader we now have
// proxies.
if (mObservers.IsEmpty()) {
NS_ABORT_IF_FALSE(mKeyURI, "Trying to SetHasProxies without key uri.");
imgLoader::SetHasProxies(mKeyURI);
}
return mObservers.AppendElementUnlessExists(proxy) ?
NS_OK : NS_ERROR_OUT_OF_MEMORY;
}
@ -206,22 +182,6 @@ nsresult imgRequest::RemoveProxy(imgRequestProxy *proxy, nsresult aStatus, PRBoo
}
if (mObservers.IsEmpty()) {
// If we have no observers, there's nothing holding us alive. If we haven't
// been cancelled and thus removed from the cache, tell the image loader so
// we can be evicted from the cache.
if (mCacheEntry) {
NS_ABORT_IF_FALSE(mKeyURI, "Removing last observer without key uri.");
imgLoader::SetHasNoProxies(mKeyURI, mCacheEntry);
}
#if defined(PR_LOGGING)
else {
nsCAutoString spec;
mKeyURI->GetSpec(spec);
LOG_MSG_WITH_PARAM(gImgLog, "imgRequest::RemoveProxy no cache entry", "uri", spec.get());
}
#endif
/* If |aStatus| is a failure code, then cancel the load if it is still in progress.
Otherwise, let the load continue, keeping 'this' in the cache with no observers.
This way, if a proxy is destroyed without calling cancel on it, it won't leak
@ -354,8 +314,6 @@ void imgRequest::Cancel(nsresult aStatus)
void imgRequest::CancelAndAbort(nsresult aStatus)
{
LOG_SCOPE(gImgLog, "imgRequest::CancelAndAbort");
Cancel(aStatus);
// It's possible for the channel to fail to open after we've set our
@ -419,12 +377,10 @@ void imgRequest::RemoveFromCache()
{
LOG_SCOPE(gImgLog, "imgRequest::RemoveFromCache");
if (mCacheEntry)
imgLoader::RemoveFromCache(mCacheEntry);
else
imgLoader::RemoveFromCache(mKeyURI);
mCacheEntry = nsnull;
if (mCacheEntry) {
imgLoader::RemoveFromCache(mURI);
mCacheEntry = nsnull;
}
}
PRBool imgRequest::HaveProxyWithObserver(imgRequestProxy* aProxyToIgnore) const
@ -1079,25 +1035,12 @@ imgRequest::OnChannelRedirect(nsIChannel *oldChannel, nsIChannel *newChannel, PR
return rv;
}
#if defined(PR_LOGGING)
nsCAutoString spec;
mKeyURI->GetSpec(spec);
LOG_MSG_WITH_PARAM(gImgLog, "imgRequest::OnChannelRedirect", "old", spec.get());
#endif
RemoveFromCache();
mChannel = newChannel;
newChannel->GetOriginalURI(getter_AddRefs(mKeyURI));
#if defined(PR_LOGGING)
mKeyURI->GetSpec(spec);
LOG_MSG_WITH_PARAM(gImgLog, "imgRequest::OnChannelRedirect", "new", spec.get());
#endif
// If we don't still have a cache entry, we don't want to refresh the cache.
if (mKeyURI && mCacheEntry)
imgLoader::PutIntoCache(mKeyURI, mCacheEntry);

View File

@ -124,7 +124,6 @@ private:
friend class imgRequestProxy;
friend class imgLoader;
friend class imgCacheValidator;
friend class imgCacheExpirationTracker;
inline void SetLoadId(void *aLoadId) {
mLoadId = aLoadId;
@ -145,14 +144,6 @@ private:
return mProperties;
}
// Reset the cache entry after we've dropped our reference to it. Used by the
// imgLoader when our cache entry is re-requested after we've dropped our
// reference to it.
void SetCacheEntry(imgCacheEntry *entry);
// Returns whether we've got a reference to the cache entry.
PRBool HasCacheEntry() const;
// Return true if at least one of our proxies, excluding
// aProxyToIgnore, has an observer. aProxyToIgnore may be null.
PRBool HaveProxyWithObserver(imgRequestProxy* aProxyToIgnore) const;