summaryrefslogtreecommitdiffstats
path: root/layout/style/ErrorReporter.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'layout/style/ErrorReporter.cpp')
-rw-r--r--layout/style/ErrorReporter.cpp278
1 files changed, 278 insertions, 0 deletions
diff --git a/layout/style/ErrorReporter.cpp b/layout/style/ErrorReporter.cpp
new file mode 100644
index 0000000000..75921d0d8c
--- /dev/null
+++ b/layout/style/ErrorReporter.cpp
@@ -0,0 +1,278 @@
+/* -*- 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/SchedulerGroup.h"
+#include "mozilla/Services.h"
+#include "nsIConsoleService.h"
+#include "mozilla/dom/Document.h"
+#include "nsComponentManagerUtils.h"
+#include "nsIDocShell.h"
+#include "nsIFactory.h"
+#include "nsINode.h"
+#include "nsIScriptError.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);
+}
+
+uint64_t ErrorReporter::FindInnerWindowId(const StyleSheet* aSheet,
+ const Loader* aLoader) {
+ if (aSheet) {
+ if (uint64_t id = aSheet->FindOwningWindowInnerID()) {
+ return id;
+ }
+ }
+ if (aLoader) {
+ if (Document* doc = aLoader->GetDocument()) {
+ return doc->InnerWindowID();
+ }
+ }
+ return 0;
+}
+
+ErrorReporter::ErrorReporter(uint64_t aInnerWindowId)
+ : mInnerWindowId(aInnerWindowId) {
+ 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 =
+ SchedulerGroup::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(const nsACString& aSourceLine,
+ const nsACString& aSelectors,
+ uint32_t aLineNumber, uint32_t aColNumber,
+ nsIURI* aURI) {
+ nsAutoString errorLine;
+ // This could be a really long string for minified CSS; just leave it empty
+ // if we OOM.
+ if (!AppendUTF8toUTF16(aSourceLine, errorLine, fallible)) {
+ errorLine.Truncate();
+ }
+
+ nsAutoString selectors;
+ if (!AppendUTF8toUTF16(aSelectors, selectors, fallible)) {
+ selectors.Truncate();
+ }
+
+ if (mError.IsEmpty()) {
+ return;
+ }
+
+ nsAutoString fileName;
+ if (aURI) {
+ if (!sSpecCache) {
+ sSpecCache = new ShortTermURISpecCache;
+ NS_ADDREF(sSpecCache);
+ }
+ fileName = sSpecCache->GetSpec(aURI);
+ } else {
+ fileName.AssignLiteral("from DOM");
+ }
+
+ nsresult rv;
+ nsCOMPtr<nsIScriptError> errorObject =
+ do_CreateInstance(sScriptErrorFactory, &rv);
+
+ if (NS_SUCCEEDED(rv)) {
+ // It is safe to used InitWithSanitizedSource because fileName is
+ // an already anonymized uri spec.
+ rv = errorObject->InitWithSanitizedSource(
+ mError, fileName, errorLine, aLineNumber, aColNumber,
+ nsIScriptError::warningFlag, "CSS Parser", mInnerWindowId);
+
+ if (NS_SUCCEEDED(rv)) {
+ errorObject->SetCssSelectors(selectors);
+ sConsoleService->LogMessage(errorObject);
+ }
+ }
+
+ mError.Truncate();
+}
+
+void ErrorReporter::AddToError(const nsString& aErrorText) {
+ if (mError.IsEmpty()) {
+ mError = aErrorText;
+ } else {
+ mError.AppendLiteral(" ");
+ mError.Append(aErrorText);
+ }
+}
+
+void ErrorReporter::ReportUnexpected(const char* aMessage) {
+ nsAutoString str;
+ sStringBundle->GetStringFromName(aMessage, str);
+ AddToError(str);
+}
+
+void ErrorReporter::ReportUnexpectedUnescaped(
+ const char* aMessage, const nsTArray<nsString>& aParam) {
+ nsAutoString str;
+ sStringBundle->FormatStringFromName(aMessage, aParam, str);
+ AddToError(str);
+}
+
+} // namespace css
+} // namespace mozilla