LCOV - code coverage report
Current view: top level - layout/style - ErrorReporter.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 58 196 29.6 %
Date: 2017-07-14 16:53:18 Functions: 9 27 33.3 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* This Source Code Form is subject to the terms of the Mozilla Public
       3             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       4             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       5             : 
       6             : /* diagnostic reporting for CSS style sheet parser */
       7             : 
       8             : #include "mozilla/css/ErrorReporter.h"
       9             : 
      10             : #include "mozilla/StyleSheetInlines.h"
      11             : #include "mozilla/css/Loader.h"
      12             : #include "mozilla/Preferences.h"
      13             : #include "mozilla/Services.h"
      14             : #include "mozilla/SystemGroup.h"
      15             : #include "nsCSSScanner.h"
      16             : #include "nsIConsoleService.h"
      17             : #include "nsIDocument.h"
      18             : #include "nsIFactory.h"
      19             : #include "nsIScriptError.h"
      20             : #include "nsIStringBundle.h"
      21             : #include "nsServiceManagerUtils.h"
      22             : #include "nsStyleUtil.h"
      23             : #include "nsThreadUtils.h"
      24             : 
      25             : #ifdef CSS_REPORT_PARSE_ERRORS
      26             : 
      27             : using namespace mozilla;
      28             : 
      29             : namespace {
      30           0 : class ShortTermURISpecCache : public Runnable {
      31             : public:
      32           0 :   ShortTermURISpecCache()
      33           0 :    : Runnable("ShortTermURISpecCache")
      34           0 :    , mPending(false) {}
      35             : 
      36           0 :   nsString const& GetSpec(nsIURI* aURI) {
      37           0 :     if (mURI != aURI) {
      38           0 :       mURI = aURI;
      39             : 
      40           0 :       nsAutoCString cSpec;
      41           0 :       nsresult rv = mURI->GetSpec(cSpec);
      42           0 :       if (NS_FAILED(rv)) {
      43           0 :         cSpec.AssignLiteral("[nsIURI::GetSpec failed]");
      44             :       }
      45           0 :       CopyUTF8toUTF16(cSpec, mSpec);
      46             :     }
      47           0 :     return mSpec;
      48             :   }
      49             : 
      50           0 :   bool IsInUse() const { return mURI != nullptr; }
      51           0 :   bool IsPending() const { return mPending; }
      52           0 :   void SetPending() { mPending = true; }
      53             : 
      54             :   // When invoked as a runnable, zap the cache.
      55           0 :   NS_IMETHOD Run() override {
      56           0 :     mURI = nullptr;
      57           0 :     mSpec.Truncate();
      58           0 :     mPending = false;
      59           0 :     return NS_OK;
      60             :   }
      61             : 
      62             : private:
      63             :   nsCOMPtr<nsIURI> mURI;
      64             :   nsString mSpec;
      65             :   bool mPending;
      66             : };
      67             : 
      68             : } // namespace
      69             : 
      70             : static bool sReportErrors;
      71             : static nsIConsoleService *sConsoleService;
      72             : static nsIFactory *sScriptErrorFactory;
      73             : static nsIStringBundle *sStringBundle;
      74             : static ShortTermURISpecCache *sSpecCache;
      75             : 
      76             : #define CSS_ERRORS_PREF "layout.css.report_errors"
      77             : 
      78             : static bool
      79           2 : InitGlobals()
      80             : {
      81           2 :   MOZ_ASSERT(!sConsoleService && !sScriptErrorFactory && !sStringBundle,
      82             :              "should not have been called");
      83             : 
      84           2 :   if (NS_FAILED(Preferences::AddBoolVarCache(&sReportErrors, CSS_ERRORS_PREF,
      85             :                                              true))) {
      86           0 :     return false;
      87             :   }
      88             : 
      89           4 :   nsCOMPtr<nsIConsoleService> cs = do_GetService(NS_CONSOLESERVICE_CONTRACTID);
      90           2 :   if (!cs) {
      91           0 :     return false;
      92             :   }
      93             : 
      94           4 :   nsCOMPtr<nsIFactory> sf = do_GetClassObject(NS_SCRIPTERROR_CONTRACTID);
      95           2 :   if (!sf) {
      96           0 :     return false;
      97             :   }
      98             : 
      99           4 :   nsCOMPtr<nsIStringBundleService> sbs = services::GetStringBundleService();
     100           2 :   if (!sbs) {
     101           0 :     return false;
     102             :   }
     103             : 
     104           4 :   nsCOMPtr<nsIStringBundle> sb;
     105           4 :   nsresult rv = sbs->CreateBundle("chrome://global/locale/css.properties",
     106           4 :                                   getter_AddRefs(sb));
     107           2 :   if (NS_FAILED(rv) || !sb) {
     108           0 :     return false;
     109             :   }
     110             : 
     111           2 :   cs.forget(&sConsoleService);
     112           2 :   sf.forget(&sScriptErrorFactory);
     113           2 :   sb.forget(&sStringBundle);
     114             : 
     115           2 :   return true;
     116             : }
     117             : 
     118             : static inline bool
     119         198 : ShouldReportErrors()
     120             : {
     121         198 :   if (!sConsoleService) {
     122           2 :     if (!InitGlobals()) {
     123           0 :       return false;
     124             :     }
     125             :   }
     126         198 :   return sReportErrors;
     127             : }
     128             : 
     129             : namespace mozilla {
     130             : namespace css {
     131             : 
     132             : /* static */ void
     133           0 : ErrorReporter::ReleaseGlobals()
     134             : {
     135           0 :   NS_IF_RELEASE(sConsoleService);
     136           0 :   NS_IF_RELEASE(sScriptErrorFactory);
     137           0 :   NS_IF_RELEASE(sStringBundle);
     138           0 :   NS_IF_RELEASE(sSpecCache);
     139           0 : }
     140             : 
     141        2380 : ErrorReporter::ErrorReporter(const nsCSSScanner& aScanner,
     142             :                              const StyleSheet* aSheet,
     143             :                              const Loader* aLoader,
     144        2380 :                              nsIURI* aURI)
     145             :   : mScanner(&aScanner), mSheet(aSheet), mLoader(aLoader), mURI(aURI),
     146             :     mInnerWindowID(0), mErrorLineNumber(0), mPrevErrorLineNumber(0),
     147        2380 :     mErrorColNumber(0)
     148             : {
     149        2380 : }
     150             : 
     151           0 : ErrorReporter::ErrorReporter(const StyleSheet* aSheet,
     152             :                              const Loader* aLoader,
     153           0 :                              nsIURI* aURI)
     154             :   : mScanner(nullptr), mSheet(aSheet), mLoader(aLoader), mURI(aURI),
     155             :     mInnerWindowID(0), mErrorLineNumber(0), mPrevErrorLineNumber(0),
     156           0 :     mErrorColNumber(0)
     157             : {
     158           0 : }
     159             : 
     160        4760 : ErrorReporter::~ErrorReporter()
     161             : {
     162             :   // Schedule deferred cleanup for cached data. We want to strike a
     163             :   // balance between performance and memory usage, so we only allow
     164             :   // short-term caching.
     165        2380 :   if (sSpecCache && sSpecCache->IsInUse() && !sSpecCache->IsPending()) {
     166           0 :     nsCOMPtr<nsIRunnable> runnable(sSpecCache);
     167             :     nsresult rv =
     168             :       SystemGroup::Dispatch("ShortTermURISpecCache", TaskCategory::Other,
     169           0 :                             runnable.forget());
     170           0 :     if (NS_FAILED(rv)) {
     171             :       // Peform the "deferred" cleanup immediately if the dispatch fails.
     172           0 :       sSpecCache->Run();
     173             :     } else {
     174           0 :       sSpecCache->SetPending();
     175             :     }
     176             :   }
     177        2380 : }
     178             : 
     179             : void
     180         212 : ErrorReporter::OutputError()
     181             : {
     182         212 :   if (mError.IsEmpty()) {
     183         424 :     return;
     184             :   }
     185           0 :   if (!ShouldReportErrors()) {
     186           0 :     ClearError();
     187           0 :     return;
     188             :   }
     189             : 
     190           0 :   if (mInnerWindowID == 0 && (mSheet || mLoader)) {
     191           0 :     if (mSheet) {
     192           0 :       mInnerWindowID = mSheet->FindOwningWindowInnerID();
     193             :     }
     194           0 :     if (mInnerWindowID == 0 && mLoader) {
     195           0 :       nsIDocument* doc = mLoader->GetDocument();
     196           0 :       if (doc) {
     197           0 :         mInnerWindowID = doc->InnerWindowID();
     198             :       }
     199             :     }
     200             :     // don't attempt this again, even if we failed
     201           0 :     mSheet = nullptr;
     202           0 :     mLoader = nullptr;
     203             :   }
     204             : 
     205           0 :   if (mFileName.IsEmpty()) {
     206           0 :     if (mURI) {
     207           0 :       if (!sSpecCache) {
     208           0 :         sSpecCache = new ShortTermURISpecCache;
     209           0 :         NS_ADDREF(sSpecCache);
     210             :       }
     211           0 :       mFileName = sSpecCache->GetSpec(mURI);
     212           0 :       mURI = nullptr;
     213             :     } else {
     214           0 :       mFileName.AssignLiteral("from DOM");
     215             :     }
     216             :   }
     217             : 
     218             :   nsresult rv;
     219             :   nsCOMPtr<nsIScriptError> errorObject =
     220           0 :     do_CreateInstance(sScriptErrorFactory, &rv);
     221             : 
     222           0 :   if (NS_SUCCEEDED(rv)) {
     223           0 :     rv = errorObject->InitWithWindowID(mError,
     224             :                                        mFileName,
     225             :                                        mErrorLine,
     226             :                                        mErrorLineNumber,
     227             :                                        mErrorColNumber,
     228             :                                        nsIScriptError::warningFlag,
     229             :                                        "CSS Parser",
     230             :                                        mInnerWindowID);
     231           0 :     if (NS_SUCCEEDED(rv)) {
     232           0 :       sConsoleService->LogMessage(errorObject);
     233             :     }
     234             :   }
     235             : 
     236           0 :   ClearError();
     237             : }
     238             : 
     239             : void
     240           0 : ErrorReporter::OutputError(uint32_t aLineNumber, uint32_t aColNumber)
     241             : {
     242           0 :   mErrorLineNumber = aLineNumber;
     243           0 :   mErrorColNumber = aColNumber;
     244           0 :   OutputError();
     245           0 : }
     246             : 
     247             : // When Stylo's CSS parser is in use, this reporter does not have access to the CSS parser's
     248             : // state. The users of ErrorReporter need to provide:
     249             : // - the line number of the error
     250             : // - the column number of the error
     251             : // - the complete source line containing the invalid CSS
     252             : 
     253             : void
     254           0 : ErrorReporter::OutputError(uint32_t aLineNumber,
     255             :                            uint32_t aColNumber,
     256             :                            const nsACString& aSourceLine)
     257             : {
     258           0 :   mErrorLine.Truncate();
     259             :   // This could be a really long string for minified CSS; just leave it empty if we OOM.
     260           0 :   if (!AppendUTF8toUTF16(aSourceLine, mErrorLine, fallible)) {
     261           0 :     mErrorLine.Truncate();
     262             :   }
     263           0 :   mPrevErrorLineNumber = aLineNumber;
     264           0 :   OutputError(aLineNumber, aColNumber);
     265           0 : }
     266             : 
     267             : void
     268        9576 : ErrorReporter::ClearError()
     269             : {
     270        9576 :   mError.Truncate();
     271        9576 : }
     272             : 
     273             : void
     274          66 : ErrorReporter::AddToError(const nsString &aErrorText)
     275             : {
     276          66 :   if (!ShouldReportErrors()) return;
     277             : 
     278          66 :   if (mError.IsEmpty()) {
     279          64 :     mError = aErrorText;
     280          64 :     mErrorLineNumber = mScanner ? mScanner->GetLineNumber() : 0;
     281          64 :     mErrorColNumber = mScanner ? mScanner->GetColumnNumber() : 0;
     282             :     // Retrieve the error line once per line, and reuse the same nsString
     283             :     // for all errors on that line.  That causes the text of the line to
     284             :     // be shared among all the nsIScriptError objects.
     285          64 :     if (mErrorLine.IsEmpty() || mErrorLineNumber != mPrevErrorLineNumber) {
     286             :       // Be careful here: the error line might be really long and OOM
     287             :       // when we try to make a copy here.  If so, just leave it empty.
     288          64 :       if (!mScanner || !mErrorLine.Assign(mScanner->GetCurrentLine(), fallible)) {
     289           0 :         mErrorLine.Truncate();
     290             :       }
     291          64 :       mPrevErrorLineNumber = mErrorLineNumber;
     292             :     }
     293             :   } else {
     294           2 :     mError.AppendLiteral("  ");
     295           2 :     mError.Append(aErrorText);
     296             :   }
     297             : }
     298             : 
     299             : void
     300           0 : ErrorReporter::ReportUnexpected(const char *aMessage)
     301             : {
     302           0 :   if (!ShouldReportErrors()) return;
     303             : 
     304           0 :   nsAutoString str;
     305           0 :   sStringBundle->GetStringFromName(NS_ConvertASCIItoUTF16(aMessage).get(),
     306           0 :                                    getter_Copies(str));
     307           0 :   AddToError(str);
     308             : }
     309             : 
     310             : void
     311           0 : ErrorReporter::ReportUnexpected(const char *aMessage,
     312             :                                 const nsString &aParam)
     313             : {
     314           0 :   if (!ShouldReportErrors()) return;
     315             : 
     316           0 :   nsAutoString qparam;
     317           0 :   nsStyleUtil::AppendEscapedCSSIdent(aParam, qparam);
     318           0 :   const char16_t *params[1] = { qparam.get() };
     319             : 
     320           0 :   nsAutoString str;
     321           0 :   sStringBundle->FormatStringFromName(NS_ConvertASCIItoUTF16(aMessage).get(),
     322           0 :                                       params, ArrayLength(params),
     323           0 :                                       getter_Copies(str));
     324           0 :   AddToError(str);
     325             : }
     326             : 
     327             : void
     328          66 : ErrorReporter::ReportUnexpectedUnescaped(const char *aMessage,
     329             :                                          const nsAutoString& aParam)
     330             : {
     331          66 :   if (!ShouldReportErrors()) return;
     332             : 
     333          66 :   const char16_t *params[1] = { aParam.get() };
     334             : 
     335         132 :   nsAutoString str;
     336         198 :   sStringBundle->FormatStringFromName(NS_ConvertASCIItoUTF16(aMessage).get(),
     337          66 :                                       params, ArrayLength(params),
     338         198 :                                       getter_Copies(str));
     339          66 :   AddToError(str);
     340             : }
     341             : 
     342             : void
     343          66 : ErrorReporter::ReportUnexpected(const char *aMessage,
     344             :                                 const nsCSSToken &aToken)
     345             : {
     346          66 :   if (!ShouldReportErrors()) return;
     347             : 
     348         132 :   nsAutoString tokenString;
     349          66 :   aToken.AppendToString(tokenString);
     350          66 :   ReportUnexpectedUnescaped(aMessage, tokenString);
     351             : }
     352             : 
     353             : void
     354           0 : ErrorReporter::ReportUnexpected(const char *aMessage,
     355             :                                 const nsCSSToken &aToken,
     356             :                                 char16_t aChar)
     357             : {
     358           0 :   if (!ShouldReportErrors()) return;
     359             : 
     360           0 :   nsAutoString tokenString;
     361           0 :   aToken.AppendToString(tokenString);
     362           0 :   const char16_t charStr[2] = { aChar, 0 };
     363           0 :   const char16_t *params[2] = { tokenString.get(), charStr };
     364             : 
     365           0 :   nsAutoString str;
     366           0 :   sStringBundle->FormatStringFromName(NS_ConvertASCIItoUTF16(aMessage).get(),
     367           0 :                                       params, ArrayLength(params),
     368           0 :                                       getter_Copies(str));
     369           0 :   AddToError(str);
     370             : }
     371             : 
     372             : void
     373           0 : ErrorReporter::ReportUnexpected(const char *aMessage,
     374             :                                 const nsString &aParam,
     375             :                                 const nsString &aValue)
     376             : {
     377           0 :   if (!ShouldReportErrors()) return;
     378             : 
     379           0 :   nsAutoString qparam;
     380           0 :   nsStyleUtil::AppendEscapedCSSIdent(aParam, qparam);
     381           0 :   const char16_t *params[2] = { qparam.get(), aValue.get() };
     382             : 
     383           0 :   nsAutoString str;
     384           0 :   sStringBundle->FormatStringFromName(NS_ConvertASCIItoUTF16(aMessage).get(),
     385           0 :                                       params, ArrayLength(params),
     386           0 :                                       getter_Copies(str));
     387           0 :   AddToError(str);
     388             : }
     389             : 
     390             : void
     391           0 : ErrorReporter::ReportUnexpectedEOF(const char *aMessage)
     392             : {
     393           0 :   if (!ShouldReportErrors()) return;
     394             : 
     395           0 :   nsAutoString innerStr;
     396           0 :   sStringBundle->GetStringFromName(NS_ConvertASCIItoUTF16(aMessage).get(),
     397           0 :                                    getter_Copies(innerStr));
     398           0 :   const char16_t *params[1] = { innerStr.get() };
     399             : 
     400           0 :   nsAutoString str;
     401           0 :   sStringBundle->FormatStringFromName(u"PEUnexpEOF2",
     402           0 :                                       params, ArrayLength(params),
     403           0 :                                       getter_Copies(str));
     404           0 :   AddToError(str);
     405             : }
     406             : 
     407             : void
     408           0 : ErrorReporter::ReportUnexpectedEOF(char16_t aExpected)
     409             : {
     410           0 :   if (!ShouldReportErrors()) return;
     411             : 
     412             :   const char16_t expectedStr[] = {
     413             :     char16_t('\''), aExpected, char16_t('\''), char16_t(0)
     414           0 :   };
     415           0 :   const char16_t *params[1] = { expectedStr };
     416             : 
     417           0 :   nsAutoString str;
     418           0 :   sStringBundle->FormatStringFromName(u"PEUnexpEOF2",
     419           0 :                                       params, ArrayLength(params),
     420           0 :                                       getter_Copies(str));
     421           0 :   AddToError(str);
     422             : }
     423             : 
     424             : } // namespace css
     425             : } // namespace mozilla
     426             : 
     427             : #endif

Generated by: LCOV version 1.13