Bug 786108 - Cache UTF-16 version of URI to prevent repeated conversions in the CSS scanner, and free the cache after a short time. r=bz

This commit is contained in:
Seth Fowler 2012-09-28 09:56:47 -07:00
parent cbbe1a35f7
commit e97e0bcb99
5 changed files with 130 additions and 15 deletions

View File

@ -0,0 +1,22 @@
<html>
<head></head>
<body></body>
<script type="text/javascript">
// Detect severe performance and memory issues when large amounts of errors
// are reported from CSS embedded in a file with a long data URI. Addressed
// by 786108; should finish quickly with that patch and run for a very long
// time otherwise.
var img = new Array;
img.push('<img src="data:image/svg+xml,');
img.push(encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="300px" height="300px">'));
for (var i = 0 ; i < 10000 ; i++)
img.push(encodeURIComponent('<circle cx="0" cy="0" r="1" style="xxx-invalid-property: 0;"/>'));
img.push(encodeURIComponent('</svg>'));
img.push('">');
document.getElementsByTagName('body')[0].innerHTML = img.join('');
</script>
</html>

View File

@ -0,0 +1,23 @@
<html>
<head></head>
<body></body>
<script type="text/javascript">
// Detect severe performance and memory issues when large amounts of errors
// are reported from CSS embedded in a file with a long data URI. Addressed
// by 786108; should finish quickly with that patch and run for a very long
// time otherwise. This version is designed for slow / memory constrained
// platforms like Android.
var img = new Array;
img.push('<img src="data:image/svg+xml,');
img.push(encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="300px" height="300px">'));
for (var i = 0 ; i < 2500 ; i++)
img.push(encodeURIComponent('<circle cx="0" cy="0" r="1" style="xxx-invalid-property: 0;"/>'));
img.push(encodeURIComponent('</svg>'));
img.push('">');
document.getElementsByTagName('body')[0].innerHTML = img.join('');
</script>
</html>

View File

@ -77,4 +77,6 @@ load 696188-1.html
load 700116.html
load 729126-1.html
load 729126-2.html
skip-if(Android) load 786108-1.html
load 786108-2.html
load 788836.html

View File

@ -249,6 +249,29 @@ nsCSSToken::AppendToString(nsString& aBuffer)
}
}
class DeferredCleanupRunnable : public nsRunnable
{
public:
DeferredCleanupRunnable(nsCSSScanner* aToClean)
: mToClean(aToClean)
{}
NS_IMETHOD Run() {
if (mToClean) {
mToClean->PerformDeferredCleanup();
}
return NS_OK;
}
void Revoke() {
mToClean = nullptr;
}
private:
nsCSSScanner* mToClean;
};
nsCSSScanner::nsCSSScanner()
: mReadPointer(nullptr)
, mSVGMode(false)
@ -271,13 +294,24 @@ nsCSSScanner::nsCSSScanner()
nsCSSScanner::~nsCSSScanner()
{
MOZ_COUNT_DTOR(nsCSSScanner);
Close();
Reset();
if (mLocalPushback != mPushback) {
delete [] mPushback;
}
}
#ifdef CSS_REPORT_PARSE_ERRORS
void
nsCSSScanner::PerformDeferredCleanup()
{
// Clean up all short term caches.
mCachedURI = nullptr;
mCachedFileName.Truncate();
// Release our DeferredCleanupRunnable.
mDeferredCleaner.Forget();
}
#define CSS_ERRORS_PREF "layout.css.report_errors"
static int
@ -331,17 +365,13 @@ nsCSSScanner::Init(const nsAString& aBuffer,
mCount = aBuffer.Length();
#ifdef CSS_REPORT_PARSE_ERRORS
// If aURI is the same as mURI, no need to reget mFileName -- it
// shouldn't have changed.
if (aURI != mURI) {
mURI = aURI;
if (aURI) {
aURI->GetSpec(mFileName);
} else {
mFileName.Adopt(NS_strdup("from DOM"));
}
// If aURI is different from mCachedURI, invalidate the filename cache.
if (aURI != mCachedURI) {
mCachedURI = aURI;
mCachedFileName.Truncate();
}
#endif // CSS_REPORT_PARSE_ERRORS
mLineNumber = aLineNumber;
// Reset variables that we use to keep track of our progress through the input
@ -406,8 +436,19 @@ nsCSSScanner::OutputError()
do_CreateInstance(gScriptErrorFactory, &rv);
if (NS_SUCCEEDED(rv)) {
// Update the cached filename if needed.
if (mCachedFileName.IsEmpty()) {
if (mCachedURI) {
nsAutoCString cFileName;
mCachedURI->GetSpec(cFileName);
CopyUTF8toUTF16(cFileName, mCachedFileName);
} else {
mCachedFileName.AssignLiteral("from DOM");
}
}
rv = errorObject->InitWithWindowID(mError,
NS_ConvertUTF8toUTF16(mFileName),
mCachedFileName,
EmptyString(),
mErrorLineNumber,
mErrorColNumber,
@ -554,19 +595,37 @@ nsCSSScanner::ReportUnexpectedTokenParams(nsCSSToken& tok,
void
nsCSSScanner::Close()
{
Reset();
// Schedule deferred cleanup for cached data. We want to strike a balance
// between performance and memory usage, so we only allow short-term caching.
#ifdef CSS_REPORT_PARSE_ERRORS
if (!mDeferredCleaner.IsPending()) {
mDeferredCleaner = new DeferredCleanupRunnable(this);
if (NS_FAILED(NS_DispatchToCurrentThread(mDeferredCleaner.get()))) {
// Peform the "deferred" cleanup immediately if the dispatch fails.
// This will also have the effect of clearing mDeferredCleaner.
nsCSSScanner::PerformDeferredCleanup();
}
}
#endif
}
void
nsCSSScanner::Reset()
{
mReadPointer = nullptr;
// Clean things up so we don't hold on to memory if our parser gets recycled.
#ifdef CSS_REPORT_PARSE_ERRORS
mFileName.Truncate();
mURI = nullptr;
mError.Truncate();
mInnerWindowID = 0;
mWindowIDCached = false;
mSheet = nullptr;
mLoader = nullptr;
#endif
if (mPushback != mLocalPushback) {
delete [] mPushback;
mPushback = mLocalPushback;

View File

@ -18,6 +18,7 @@
// for #ifdef CSS_REPORT_PARSE_ERRORS
#include "nsXPIDLString.h"
#include "nsThreadUtils.h"
class nsIURI;
// Token types
@ -91,6 +92,8 @@ struct nsCSSToken {
void AppendToString(nsString& aBuffer);
};
class DeferredCleanupRunnable;
// CSS Scanner API. Used to tokenize an input stream using the CSS
// forward compatible tokenization rules. This implementation is
// private to this package and is only used internally by the css
@ -120,6 +123,9 @@ class nsCSSScanner {
}
#ifdef CSS_REPORT_PARSE_ERRORS
// Clean up any reclaimable cached resources.
void PerformDeferredCleanup();
void AddToError(const nsSubstring& aErrorText);
void OutputError();
void ClearError();
@ -128,6 +134,8 @@ class nsCSSScanner {
void ReportUnexpected(const char* aMessage);
private:
void Reset();
void ReportUnexpectedParams(const char* aMessage,
const PRUnichar** aParams,
uint32_t aParamsLength);
@ -211,8 +219,9 @@ protected:
uint32_t mRecordStartOffset;
#ifdef CSS_REPORT_PARSE_ERRORS
nsXPIDLCString mFileName;
nsCOMPtr<nsIURI> mURI; // Cached so we know to not refetch mFileName
nsRevocableEventPtr<DeferredCleanupRunnable> mDeferredCleaner;
nsCOMPtr<nsIURI> mCachedURI; // Used to invalidate the cached filename.
nsString mCachedFileName;
uint32_t mErrorLineNumber, mColNumber, mErrorColNumber;
nsFixedString mError;
PRUnichar mErrorBuf[200];