forked from mirrors/gecko-dev
		
	This patch was generated automatically by the "modeline.py" script, available here: https://github.com/amccreight/moz-source-tools/blob/master/modeline.py For every file that is modified in this patch, the changes are as follows: (1) The patch changes the file to use the exact C++ mode lines from the Mozilla coding style guide, available here: https://developer.mozilla.org/en-US/docs/Mozilla/Developer_guide/Coding_Style#Mode_Line (2) The patch deletes any blank lines between the mode line & the MPL boilerplate comment. (3) If the file previously had the mode lines and MPL boilerplate in a single contiguous C++ comment, then the patch splits them into separate C++ comments, to match the boilerplate in the coding style. MozReview-Commit-ID: EuRsDue63tK --HG-- extra : rebase_source : 3356d4b80ff6213935192e87cdbc9103fec6084c
		
			
				
	
	
		
			440 lines
		
	
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			440 lines
		
	
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 | 
						|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
 | 
						|
/* This Source Code Form is subject to the terms of the Mozilla Public
 | 
						|
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 | 
						|
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | 
						|
 | 
						|
/* diagnostic reporting for CSS style sheet parser */
 | 
						|
 | 
						|
#include "mozilla/css/ErrorReporter.h"
 | 
						|
 | 
						|
#include "mozilla/StyleSheetInlines.h"
 | 
						|
#include "mozilla/css/Loader.h"
 | 
						|
#include "mozilla/Preferences.h"
 | 
						|
#include "mozilla/Services.h"
 | 
						|
#include "mozilla/SystemGroup.h"
 | 
						|
#include "nsCSSScanner.h"
 | 
						|
#include "nsIConsoleService.h"
 | 
						|
#include "nsIDocument.h"
 | 
						|
#include "nsIFactory.h"
 | 
						|
#include "nsIScriptError.h"
 | 
						|
#include "nsISensitiveInfoHiddenURI.h"
 | 
						|
#include "nsIStringBundle.h"
 | 
						|
#include "nsServiceManagerUtils.h"
 | 
						|
#include "nsStyleUtil.h"
 | 
						|
#include "nsThreadUtils.h"
 | 
						|
#include "nsNetUtil.h"
 | 
						|
 | 
						|
#ifdef CSS_REPORT_PARSE_ERRORS
 | 
						|
 | 
						|
using namespace mozilla;
 | 
						|
 | 
						|
namespace {
 | 
						|
class ShortTermURISpecCache : public Runnable {
 | 
						|
public:
 | 
						|
  ShortTermURISpecCache()
 | 
						|
   : Runnable("ShortTermURISpecCache")
 | 
						|
   , mPending(false) {}
 | 
						|
 | 
						|
  nsString const& GetSpec(nsIURI* aURI) {
 | 
						|
    if (mURI != aURI) {
 | 
						|
      mURI = aURI;
 | 
						|
 | 
						|
      if (NS_FAILED(NS_GetSanitizedURIStringFromURI(mURI, mSpec))) {
 | 
						|
        mSpec.AssignLiteral("[nsIURI::GetSpec failed]");
 | 
						|
      }
 | 
						|
    }
 | 
						|
    return mSpec;
 | 
						|
  }
 | 
						|
 | 
						|
  bool IsInUse() const { return mURI != nullptr; }
 | 
						|
  bool IsPending() const { return mPending; }
 | 
						|
  void SetPending() { mPending = true; }
 | 
						|
 | 
						|
  // When invoked as a runnable, zap the cache.
 | 
						|
  NS_IMETHOD Run() override {
 | 
						|
    mURI = nullptr;
 | 
						|
    mSpec.Truncate();
 | 
						|
    mPending = false;
 | 
						|
    return NS_OK;
 | 
						|
  }
 | 
						|
 | 
						|
private:
 | 
						|
  nsCOMPtr<nsIURI> mURI;
 | 
						|
  nsString mSpec;
 | 
						|
  bool mPending;
 | 
						|
};
 | 
						|
 | 
						|
} // namespace
 | 
						|
 | 
						|
static bool sReportErrors;
 | 
						|
static nsIConsoleService *sConsoleService;
 | 
						|
static nsIFactory *sScriptErrorFactory;
 | 
						|
static nsIStringBundle *sStringBundle;
 | 
						|
static ShortTermURISpecCache *sSpecCache;
 | 
						|
 | 
						|
#define CSS_ERRORS_PREF "layout.css.report_errors"
 | 
						|
 | 
						|
static bool
 | 
						|
InitGlobals()
 | 
						|
{
 | 
						|
  MOZ_ASSERT(!sConsoleService && !sScriptErrorFactory && !sStringBundle,
 | 
						|
             "should not have been called");
 | 
						|
 | 
						|
  if (NS_FAILED(Preferences::AddBoolVarCache(&sReportErrors, CSS_ERRORS_PREF,
 | 
						|
                                             true))) {
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  nsCOMPtr<nsIConsoleService> cs = do_GetService(NS_CONSOLESERVICE_CONTRACTID);
 | 
						|
  if (!cs) {
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  nsCOMPtr<nsIFactory> sf = do_GetClassObject(NS_SCRIPTERROR_CONTRACTID);
 | 
						|
  if (!sf) {
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  nsCOMPtr<nsIStringBundleService> sbs = services::GetStringBundleService();
 | 
						|
  if (!sbs) {
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  nsCOMPtr<nsIStringBundle> sb;
 | 
						|
  nsresult rv = sbs->CreateBundle("chrome://global/locale/css.properties",
 | 
						|
                                  getter_AddRefs(sb));
 | 
						|
  if (NS_FAILED(rv) || !sb) {
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  cs.forget(&sConsoleService);
 | 
						|
  sf.forget(&sScriptErrorFactory);
 | 
						|
  sb.forget(&sStringBundle);
 | 
						|
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
static inline bool
 | 
						|
ShouldReportErrors()
 | 
						|
{
 | 
						|
  if (!sConsoleService) {
 | 
						|
    if (!InitGlobals()) {
 | 
						|
      return false;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return sReportErrors;
 | 
						|
}
 | 
						|
 | 
						|
namespace mozilla {
 | 
						|
namespace css {
 | 
						|
 | 
						|
/* static */ void
 | 
						|
ErrorReporter::ReleaseGlobals()
 | 
						|
{
 | 
						|
  NS_IF_RELEASE(sConsoleService);
 | 
						|
  NS_IF_RELEASE(sScriptErrorFactory);
 | 
						|
  NS_IF_RELEASE(sStringBundle);
 | 
						|
  NS_IF_RELEASE(sSpecCache);
 | 
						|
}
 | 
						|
 | 
						|
ErrorReporter::ErrorReporter(const nsCSSScanner& aScanner,
 | 
						|
                             const StyleSheet* aSheet,
 | 
						|
                             const Loader* aLoader,
 | 
						|
                             nsIURI* aURI)
 | 
						|
  : mScanner(&aScanner), mSheet(aSheet), mLoader(aLoader), mURI(aURI),
 | 
						|
    mInnerWindowID(0), mErrorLineNumber(0), mPrevErrorLineNumber(0),
 | 
						|
    mErrorColNumber(0)
 | 
						|
{
 | 
						|
}
 | 
						|
 | 
						|
ErrorReporter::ErrorReporter(const ServoStyleSheet* aSheet,
 | 
						|
                             const Loader* aLoader,
 | 
						|
                             nsIURI* aURI)
 | 
						|
  : mScanner(nullptr), mSheet(aSheet), mLoader(aLoader), mURI(aURI),
 | 
						|
    mInnerWindowID(0), mErrorLineNumber(0), mPrevErrorLineNumber(0),
 | 
						|
    mErrorColNumber(0)
 | 
						|
{
 | 
						|
}
 | 
						|
 | 
						|
ErrorReporter::~ErrorReporter()
 | 
						|
{
 | 
						|
  // Schedule deferred cleanup for cached data. We want to strike a
 | 
						|
  // balance between performance and memory usage, so we only allow
 | 
						|
  // short-term caching.
 | 
						|
  if (sSpecCache && sSpecCache->IsInUse() && !sSpecCache->IsPending()) {
 | 
						|
    nsCOMPtr<nsIRunnable> runnable(sSpecCache);
 | 
						|
    nsresult rv =
 | 
						|
      SystemGroup::Dispatch(TaskCategory::Other, runnable.forget());
 | 
						|
    if (NS_FAILED(rv)) {
 | 
						|
      // Peform the "deferred" cleanup immediately if the dispatch fails.
 | 
						|
      sSpecCache->Run();
 | 
						|
    } else {
 | 
						|
      sSpecCache->SetPending();
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
ErrorReporter::OutputError()
 | 
						|
{
 | 
						|
  if (mError.IsEmpty()) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  if (!ShouldReportErrors()) {
 | 
						|
    ClearError();
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  if (mInnerWindowID == 0 && (mSheet || mLoader)) {
 | 
						|
    if (mSheet) {
 | 
						|
      mInnerWindowID = mSheet->FindOwningWindowInnerID();
 | 
						|
    }
 | 
						|
    if (mInnerWindowID == 0 && mLoader) {
 | 
						|
      nsIDocument* doc = mLoader->GetDocument();
 | 
						|
      if (doc) {
 | 
						|
        mInnerWindowID = doc->InnerWindowID();
 | 
						|
      }
 | 
						|
    }
 | 
						|
    // don't attempt this again, even if we failed
 | 
						|
    mSheet = nullptr;
 | 
						|
    mLoader = nullptr;
 | 
						|
  }
 | 
						|
 | 
						|
  if (mFileName.IsEmpty()) {
 | 
						|
    if (mURI) {
 | 
						|
      if (!sSpecCache) {
 | 
						|
        sSpecCache = new ShortTermURISpecCache;
 | 
						|
        NS_ADDREF(sSpecCache);
 | 
						|
      }
 | 
						|
      mFileName = sSpecCache->GetSpec(mURI);
 | 
						|
      mURI = nullptr;
 | 
						|
    } else {
 | 
						|
      mFileName.AssignLiteral("from DOM");
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  nsresult rv;
 | 
						|
  nsCOMPtr<nsIScriptError> errorObject =
 | 
						|
    do_CreateInstance(sScriptErrorFactory, &rv);
 | 
						|
 | 
						|
  if (NS_SUCCEEDED(rv)) {
 | 
						|
    // It is safe to used InitWithSanitizedSource because mFileName is
 | 
						|
    // an already anonymized uri spec.
 | 
						|
    rv = errorObject->InitWithSanitizedSource(mError,
 | 
						|
                                              mFileName,
 | 
						|
                                              mErrorLine,
 | 
						|
                                              mErrorLineNumber,
 | 
						|
                                              mErrorColNumber,
 | 
						|
                                              nsIScriptError::warningFlag,
 | 
						|
                                              "CSS Parser",
 | 
						|
                                              mInnerWindowID);
 | 
						|
    if (NS_SUCCEEDED(rv)) {
 | 
						|
      sConsoleService->LogMessage(errorObject);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  ClearError();
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
ErrorReporter::OutputError(uint32_t aLineNumber, uint32_t aColNumber)
 | 
						|
{
 | 
						|
  mErrorLineNumber = aLineNumber;
 | 
						|
  mErrorColNumber = aColNumber;
 | 
						|
  OutputError();
 | 
						|
}
 | 
						|
 | 
						|
// When Stylo's CSS parser is in use, this reporter does not have access to the CSS parser's
 | 
						|
// state. The users of ErrorReporter need to provide:
 | 
						|
// - the line number of the error
 | 
						|
// - the column number of the error
 | 
						|
// - the complete source line containing the invalid CSS
 | 
						|
 | 
						|
void
 | 
						|
ErrorReporter::OutputError(uint32_t aLineNumber,
 | 
						|
                           uint32_t aColNumber,
 | 
						|
                           const nsACString& aSourceLine)
 | 
						|
{
 | 
						|
  mErrorLineNumber = aLineNumber;
 | 
						|
  mErrorColNumber = aColNumber;
 | 
						|
 | 
						|
  // Retrieve the error line once per line, and reuse the same nsString
 | 
						|
  // for all errors on that line.  That causes the text of the line to
 | 
						|
  // be shared among all the nsIScriptError objects.
 | 
						|
  if (mErrorLine.IsEmpty() || mErrorLineNumber != mPrevErrorLineNumber) {
 | 
						|
    mErrorLine.Truncate();
 | 
						|
    // This could be a really long string for minified CSS; just leave it empty if we OOM.
 | 
						|
    if (!AppendUTF8toUTF16(aSourceLine, mErrorLine, fallible)) {
 | 
						|
      mErrorLine.Truncate();
 | 
						|
    }
 | 
						|
 | 
						|
    mPrevErrorLineNumber = aLineNumber;
 | 
						|
  }
 | 
						|
 | 
						|
  OutputError();
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
ErrorReporter::ClearError()
 | 
						|
{
 | 
						|
  mError.Truncate();
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
ErrorReporter::AddToError(const nsString &aErrorText)
 | 
						|
{
 | 
						|
  if (!ShouldReportErrors()) return;
 | 
						|
 | 
						|
  if (mError.IsEmpty()) {
 | 
						|
    mError = aErrorText;
 | 
						|
    // If this error reporter is being used from Stylo, the equivalent operation occurs
 | 
						|
    // in the OutputError variant that provides source information.
 | 
						|
    if (!IsServo()) {
 | 
						|
      mErrorLineNumber = mScanner->GetLineNumber();
 | 
						|
      mErrorColNumber = mScanner->GetColumnNumber();
 | 
						|
      // Retrieve the error line once per line, and reuse the same nsString
 | 
						|
      // for all errors on that line.  That causes the text of the line to
 | 
						|
      // be shared among all the nsIScriptError objects.
 | 
						|
      if (mErrorLine.IsEmpty() || mErrorLineNumber != mPrevErrorLineNumber) {
 | 
						|
        // Be careful here: the error line might be really long and OOM
 | 
						|
        // when we try to make a copy here.  If so, just leave it empty.
 | 
						|
        if (!mErrorLine.Assign(mScanner->GetCurrentLine(), fallible)) {
 | 
						|
          mErrorLine.Truncate();
 | 
						|
        }
 | 
						|
        mPrevErrorLineNumber = mErrorLineNumber;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  } else {
 | 
						|
    mError.AppendLiteral("  ");
 | 
						|
    mError.Append(aErrorText);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
ErrorReporter::ReportUnexpected(const char *aMessage)
 | 
						|
{
 | 
						|
  if (!ShouldReportErrors()) return;
 | 
						|
 | 
						|
  nsAutoString str;
 | 
						|
  sStringBundle->GetStringFromName(aMessage, str);
 | 
						|
  AddToError(str);
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
ErrorReporter::ReportUnexpected(const char *aMessage,
 | 
						|
                                const nsString &aParam)
 | 
						|
{
 | 
						|
  if (!ShouldReportErrors()) return;
 | 
						|
 | 
						|
  nsAutoString qparam;
 | 
						|
  nsStyleUtil::AppendEscapedCSSIdent(aParam, qparam);
 | 
						|
  const char16_t *params[1] = { qparam.get() };
 | 
						|
 | 
						|
  nsAutoString str;
 | 
						|
  sStringBundle->FormatStringFromName(aMessage, params, ArrayLength(params),
 | 
						|
                                      str);
 | 
						|
  AddToError(str);
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
ErrorReporter::ReportUnexpectedUnescaped(const char *aMessage,
 | 
						|
                                         const nsAutoString& aParam)
 | 
						|
{
 | 
						|
  if (!ShouldReportErrors()) return;
 | 
						|
 | 
						|
  const char16_t *params[1] = { aParam.get() };
 | 
						|
 | 
						|
  nsAutoString str;
 | 
						|
  sStringBundle->FormatStringFromName(aMessage, params, ArrayLength(params),
 | 
						|
                                      str);
 | 
						|
  AddToError(str);
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
ErrorReporter::ReportUnexpected(const char *aMessage,
 | 
						|
                                const nsCSSToken &aToken)
 | 
						|
{
 | 
						|
  if (!ShouldReportErrors()) return;
 | 
						|
 | 
						|
  nsAutoString tokenString;
 | 
						|
  aToken.AppendToString(tokenString);
 | 
						|
  ReportUnexpectedUnescaped(aMessage, tokenString);
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
ErrorReporter::ReportUnexpected(const char *aMessage,
 | 
						|
                                const nsCSSToken &aToken,
 | 
						|
                                char16_t aChar)
 | 
						|
{
 | 
						|
  if (!ShouldReportErrors()) return;
 | 
						|
 | 
						|
  nsAutoString tokenString;
 | 
						|
  aToken.AppendToString(tokenString);
 | 
						|
  const char16_t charStr[2] = { aChar, 0 };
 | 
						|
  const char16_t *params[2] = { tokenString.get(), charStr };
 | 
						|
 | 
						|
  nsAutoString str;
 | 
						|
  sStringBundle->FormatStringFromName(aMessage, params, ArrayLength(params),
 | 
						|
                                      str);
 | 
						|
  AddToError(str);
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
ErrorReporter::ReportUnexpected(const char *aMessage,
 | 
						|
                                const nsString &aParam,
 | 
						|
                                const nsString &aValue)
 | 
						|
{
 | 
						|
  if (!ShouldReportErrors()) return;
 | 
						|
 | 
						|
  nsAutoString qparam;
 | 
						|
  nsStyleUtil::AppendEscapedCSSIdent(aParam, qparam);
 | 
						|
  const char16_t *params[2] = { qparam.get(), aValue.get() };
 | 
						|
 | 
						|
  nsAutoString str;
 | 
						|
  sStringBundle->FormatStringFromName(aMessage, params, ArrayLength(params),
 | 
						|
                                      str);
 | 
						|
  AddToError(str);
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
ErrorReporter::ReportUnexpectedEOF(const char *aMessage)
 | 
						|
{
 | 
						|
  if (!ShouldReportErrors()) return;
 | 
						|
 | 
						|
  nsAutoString innerStr;
 | 
						|
  sStringBundle->GetStringFromName(aMessage, innerStr);
 | 
						|
  const char16_t *params[1] = { innerStr.get() };
 | 
						|
 | 
						|
  nsAutoString str;
 | 
						|
  sStringBundle->FormatStringFromName("PEUnexpEOF2", params,
 | 
						|
                                      ArrayLength(params), str);
 | 
						|
  AddToError(str);
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
ErrorReporter::ReportUnexpectedEOF(char16_t aExpected)
 | 
						|
{
 | 
						|
  if (!ShouldReportErrors()) return;
 | 
						|
 | 
						|
  const char16_t expectedStr[] = {
 | 
						|
    char16_t('\''), aExpected, char16_t('\''), char16_t(0)
 | 
						|
  };
 | 
						|
  const char16_t *params[1] = { expectedStr };
 | 
						|
 | 
						|
  nsAutoString str;
 | 
						|
  sStringBundle->FormatStringFromName("PEUnexpEOF2", params,
 | 
						|
                                      ArrayLength(params), str);
 | 
						|
  AddToError(str);
 | 
						|
}
 | 
						|
 | 
						|
bool
 | 
						|
ErrorReporter::IsServo() const
 | 
						|
{
 | 
						|
  return !mScanner;
 | 
						|
}
 | 
						|
 | 
						|
} // namespace css
 | 
						|
} // namespace mozilla
 | 
						|
 | 
						|
#endif
 |