gecko-dev/layout/style/ErrorReporter.cpp
Nicholas Nethercote 18fae65f38 Bug 1563139 - Remove StaticPrefs.h. r=glandium
This requires replacing inclusions of it with inclusions of more specific prefs
files.

The exception is that StaticPrefsAll.h, which is equivalent to StaticPrefs.h,
and is used in `Codegen.py` because doing something smarter is tricky and
suitable for a follow-up. As a result, any change to StaticPrefList.yaml will
still trigger recompilation of all the generated DOM bindings files, but that's
still a big improvement over trigger recompilation of every file that uses
static prefs.

Most of the changes in this commit are very boring. The only changes that are
not boring are modules/libpref/*, Codegen.py, and ServoBindings.toml.

Differential Revision: https://phabricator.services.mozilla.com/D39138

--HG--
extra : moz-landing-system : lando
2019-07-26 01:10:23 +00:00

316 lines
8.4 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/StaticPrefs_layout.h"
#include "mozilla/StyleSheetInlines.h"
#include "mozilla/css/Loader.h"
#include "mozilla/Preferences.h"
#include "mozilla/Services.h"
#include "mozilla/SystemGroup.h"
#include "nsIConsoleService.h"
#include "mozilla/dom/Document.h"
#include "nsIDocShell.h"
#include "nsIFactory.h"
#include "nsINode.h"
#include "nsIScriptError.h"
#include "nsISensitiveInfoHiddenURI.h"
#include "nsIStringBundle.h"
#include "nsServiceManagerUtils.h"
#include "nsStyleUtil.h"
#include "nsThreadUtils.h"
#include "nsNetUtil.h"
using namespace mozilla;
using namespace mozilla::css;
using namespace mozilla::dom;
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
bool ErrorReporter::sInitialized = false;
static nsIConsoleService* sConsoleService;
static nsIFactory* sScriptErrorFactory;
static nsIStringBundle* sStringBundle;
static ShortTermURISpecCache* sSpecCache;
void ErrorReporter::InitGlobals() {
MOZ_RELEASE_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!sInitialized, "should not have been called");
sInitialized = true;
nsCOMPtr<nsIConsoleService> cs = do_GetService(NS_CONSOLESERVICE_CONTRACTID);
if (!cs) {
return;
}
nsCOMPtr<nsIFactory> sf = do_GetClassObject(NS_SCRIPTERROR_CONTRACTID);
if (!sf) {
return;
}
nsCOMPtr<nsIStringBundleService> sbs = services::GetStringBundleService();
if (!sbs) {
return;
}
nsCOMPtr<nsIStringBundle> sb;
nsresult rv = sbs->CreateBundle("chrome://global/locale/css.properties",
getter_AddRefs(sb));
if (NS_FAILED(rv) || !sb) {
return;
}
cs.forget(&sConsoleService);
sf.forget(&sScriptErrorFactory);
sb.forget(&sStringBundle);
}
namespace mozilla {
namespace css {
/* static */
void ErrorReporter::ReleaseGlobals() {
NS_IF_RELEASE(sConsoleService);
NS_IF_RELEASE(sScriptErrorFactory);
NS_IF_RELEASE(sStringBundle);
NS_IF_RELEASE(sSpecCache);
}
static uint64_t FindInnerWindowID(const StyleSheet* aSheet,
const Loader* aLoader) {
uint64_t innerWindowID = 0;
if (aSheet) {
innerWindowID = aSheet->FindOwningWindowInnerID();
}
if (innerWindowID == 0 && aLoader) {
if (Document* doc = aLoader->GetDocument()) {
innerWindowID = doc->InnerWindowID();
}
}
return innerWindowID;
}
ErrorReporter::ErrorReporter(const StyleSheet* aSheet, const Loader* aLoader,
nsIURI* aURI)
: mSheet(aSheet),
mLoader(aLoader),
mURI(aURI),
mErrorLineNumber(0),
mPrevErrorLineNumber(0),
mErrorColNumber(0) {
MOZ_ASSERT(ShouldReportErrors(mSheet, mLoader));
EnsureGlobalsInitialized();
}
ErrorReporter::~ErrorReporter() {
MOZ_ASSERT(NS_IsMainThread());
// 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();
}
}
}
bool ErrorReporter::ShouldReportErrors(const Document& aDoc) {
MOZ_ASSERT(NS_IsMainThread());
nsIDocShell* shell = aDoc.GetDocShell();
if (!shell) {
return false;
}
bool report = false;
shell->GetCssErrorReportingEnabled(&report);
return report;
}
static nsINode* SheetOwner(const StyleSheet& aSheet) {
if (nsINode* owner = aSheet.GetOwnerNode()) {
return owner;
}
auto* associated = aSheet.GetAssociatedDocumentOrShadowRoot();
return associated ? &associated->AsNode() : nullptr;
}
bool ErrorReporter::ShouldReportErrors(const StyleSheet* aSheet,
const Loader* aLoader) {
MOZ_ASSERT(NS_IsMainThread());
if (!StaticPrefs::layout_css_report_errors()) {
return false;
}
if (aSheet) {
nsINode* owner = SheetOwner(*aSheet);
if (owner && ShouldReportErrors(*owner->OwnerDoc())) {
return true;
}
}
if (aLoader && aLoader->GetDocument() &&
ShouldReportErrors(*aLoader->GetDocument())) {
return true;
}
return false;
}
void ErrorReporter::OutputError() {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(ShouldReportErrors(mSheet, mLoader));
if (mError.IsEmpty()) {
return;
}
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",
FindInnerWindowID(mSheet, mLoader));
if (NS_SUCCEEDED(rv)) {
errorObject->SetCssSelectors(mSelectors);
sConsoleService->LogMessage(errorObject);
}
}
ClearError();
}
// 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,
const nsACString& aSelectors) {
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;
}
if (!AppendUTF8toUTF16(aSelectors, mSelectors, fallible)) {
mSelectors.Truncate();
}
OutputError();
}
void ErrorReporter::ClearError() { mError.Truncate(); }
void ErrorReporter::AddToError(const nsString& aErrorText) {
MOZ_ASSERT(ShouldReportErrors(mSheet, mLoader));
if (mError.IsEmpty()) {
mError = aErrorText;
} else {
mError.AppendLiteral(" ");
mError.Append(aErrorText);
}
}
void ErrorReporter::ReportUnexpected(const char* aMessage) {
MOZ_ASSERT(ShouldReportErrors(mSheet, mLoader));
nsAutoString str;
sStringBundle->GetStringFromName(aMessage, str);
AddToError(str);
}
void ErrorReporter::ReportUnexpectedUnescaped(
const char* aMessage, const nsTArray<nsString>& aParam) {
MOZ_ASSERT(ShouldReportErrors(mSheet, mLoader));
nsAutoString str;
sStringBundle->FormatStringFromName(aMessage, aParam, str);
AddToError(str);
}
} // namespace css
} // namespace mozilla