diff --git a/layout/style/crashtests/786108-1.html b/layout/style/crashtests/786108-1.html new file mode 100644 index 00000000000..2962e71177a --- /dev/null +++ b/layout/style/crashtests/786108-1.html @@ -0,0 +1,22 @@ + + + + + diff --git a/layout/style/crashtests/786108-2.html b/layout/style/crashtests/786108-2.html new file mode 100644 index 00000000000..1b2892040b7 --- /dev/null +++ b/layout/style/crashtests/786108-2.html @@ -0,0 +1,23 @@ + + + + + diff --git a/layout/style/crashtests/crashtests.list b/layout/style/crashtests/crashtests.list index f5443d8c93a..2d9ddf845fd 100644 --- a/layout/style/crashtests/crashtests.list +++ b/layout/style/crashtests/crashtests.list @@ -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 diff --git a/layout/style/nsCSSScanner.cpp b/layout/style/nsCSSScanner.cpp index 1d1744ab5f1..03aa06830d8 100644 --- a/layout/style/nsCSSScanner.cpp +++ b/layout/style/nsCSSScanner.cpp @@ -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; diff --git a/layout/style/nsCSSScanner.h b/layout/style/nsCSSScanner.h index 53646985389..74aa4d441fb 100644 --- a/layout/style/nsCSSScanner.h +++ b/layout/style/nsCSSScanner.h @@ -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 mURI; // Cached so we know to not refetch mFileName + nsRevocableEventPtr mDeferredCleaner; + nsCOMPtr mCachedURI; // Used to invalidate the cached filename. + nsString mCachedFileName; uint32_t mErrorLineNumber, mColNumber, mErrorColNumber; nsFixedString mError; PRUnichar mErrorBuf[200];