1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 /* diagnostic reporting for CSS style sheet parser */
9 #include "mozilla/css/ErrorReporter.h"
11 #include "mozilla/StaticPrefs_layout.h"
12 #include "mozilla/StyleSheetInlines.h"
13 #include "mozilla/css/Loader.h"
14 #include "mozilla/Preferences.h"
15 #include "mozilla/SchedulerGroup.h"
16 #include "mozilla/Components.h"
17 #include "nsIConsoleService.h"
18 #include "mozilla/dom/Document.h"
19 #include "nsComponentManagerUtils.h"
20 #include "nsIDocShell.h"
21 #include "nsIFactory.h"
23 #include "nsIScriptError.h"
24 #include "nsIStringBundle.h"
25 #include "nsServiceManagerUtils.h"
26 #include "nsStyleUtil.h"
27 #include "nsThreadUtils.h"
28 #include "nsNetUtil.h"
30 using namespace mozilla
;
31 using namespace mozilla::css
;
32 using namespace mozilla::dom
;
35 class ShortTermURISpecCache
: public Runnable
{
37 ShortTermURISpecCache()
38 : Runnable("ShortTermURISpecCache"), mPending(false) {}
40 nsString
const& GetSpec(nsIURI
* aURI
) {
44 if (NS_FAILED(NS_GetSanitizedURIStringFromURI(mURI
, mSpec
))) {
45 mSpec
.AssignLiteral("[nsIURI::GetSpec failed]");
51 bool IsInUse() const { return mURI
!= nullptr; }
52 bool IsPending() const { return mPending
; }
53 void SetPending() { mPending
= true; }
55 // When invoked as a runnable, zap the cache.
56 NS_IMETHOD
Run() override
{
64 nsCOMPtr
<nsIURI
> mURI
;
71 bool ErrorReporter::sInitialized
= false;
73 static nsIConsoleService
* sConsoleService
;
74 static nsIFactory
* sScriptErrorFactory
;
75 static nsIStringBundle
* sStringBundle
;
76 static ShortTermURISpecCache
* sSpecCache
;
78 void ErrorReporter::InitGlobals() {
79 MOZ_RELEASE_ASSERT(NS_IsMainThread());
80 MOZ_ASSERT(!sInitialized
, "should not have been called");
84 nsCOMPtr
<nsIConsoleService
> cs
= do_GetService(NS_CONSOLESERVICE_CONTRACTID
);
89 nsCOMPtr
<nsIFactory
> sf
= do_GetClassObject(NS_SCRIPTERROR_CONTRACTID
);
94 nsCOMPtr
<nsIStringBundleService
> sbs
= components::StringBundle::Service();
99 nsCOMPtr
<nsIStringBundle
> sb
;
100 nsresult rv
= sbs
->CreateBundle("chrome://global/locale/css.properties",
102 if (NS_FAILED(rv
) || !sb
) {
106 cs
.forget(&sConsoleService
);
107 sf
.forget(&sScriptErrorFactory
);
108 sb
.forget(&sStringBundle
);
115 void ErrorReporter::ReleaseGlobals() {
116 NS_IF_RELEASE(sConsoleService
);
117 NS_IF_RELEASE(sScriptErrorFactory
);
118 NS_IF_RELEASE(sStringBundle
);
119 NS_IF_RELEASE(sSpecCache
);
122 uint64_t ErrorReporter::FindInnerWindowId(const StyleSheet
* aSheet
,
123 const Loader
* aLoader
) {
125 if (uint64_t id
= aSheet
->FindOwningWindowInnerID()) {
130 if (Document
* doc
= aLoader
->GetDocument()) {
131 return doc
->InnerWindowID();
137 ErrorReporter::ErrorReporter(uint64_t aInnerWindowId
)
138 : mInnerWindowId(aInnerWindowId
) {
139 EnsureGlobalsInitialized();
142 ErrorReporter::~ErrorReporter() {
143 MOZ_ASSERT(NS_IsMainThread());
144 // Schedule deferred cleanup for cached data. We want to strike a
145 // balance between performance and memory usage, so we only allow
146 // short-term caching.
147 if (sSpecCache
&& sSpecCache
->IsInUse() && !sSpecCache
->IsPending()) {
148 nsCOMPtr
<nsIRunnable
> runnable(sSpecCache
);
149 nsresult rv
= SchedulerGroup::Dispatch(runnable
.forget());
151 // Peform the "deferred" cleanup immediately if the dispatch fails.
154 sSpecCache
->SetPending();
159 bool ErrorReporter::ShouldReportErrors(const Document
& aDoc
) {
160 MOZ_ASSERT(NS_IsMainThread());
161 nsIDocShell
* shell
= aDoc
.GetDocShell();
167 shell
->GetCssErrorReportingEnabled(&report
);
171 static nsINode
* SheetOwner(const StyleSheet
& aSheet
) {
172 if (nsINode
* owner
= aSheet
.GetOwnerNode()) {
176 auto* associated
= aSheet
.GetAssociatedDocumentOrShadowRoot();
177 return associated
? &associated
->AsNode() : nullptr;
180 bool ErrorReporter::ShouldReportErrors(const StyleSheet
* aSheet
,
181 const Loader
* aLoader
) {
182 MOZ_ASSERT(NS_IsMainThread());
184 if (!StaticPrefs::layout_css_report_errors()) {
189 nsINode
* owner
= SheetOwner(*aSheet
);
190 if (owner
&& ShouldReportErrors(*owner
->OwnerDoc())) {
195 if (aLoader
&& aLoader
->GetDocument() &&
196 ShouldReportErrors(*aLoader
->GetDocument())) {
203 void ErrorReporter::OutputError(const nsACString
& aSourceLine
,
204 const nsACString
& aSelectors
,
205 uint32_t aLineNumber
, uint32_t aColNumber
,
207 nsAutoString errorLine
;
208 // This could be a really long string for minified CSS; just leave it empty
210 if (!AppendUTF8toUTF16(aSourceLine
, errorLine
, fallible
)) {
211 errorLine
.Truncate();
214 nsAutoString selectors
;
215 if (!AppendUTF8toUTF16(aSelectors
, selectors
, fallible
)) {
216 selectors
.Truncate();
219 if (mError
.IsEmpty()) {
223 nsAutoString fileName
;
226 sSpecCache
= new ShortTermURISpecCache
;
227 NS_ADDREF(sSpecCache
);
229 fileName
= sSpecCache
->GetSpec(aURI
);
231 fileName
.AssignLiteral("from DOM");
235 nsCOMPtr
<nsIScriptError
> errorObject
=
236 do_CreateInstance(sScriptErrorFactory
, &rv
);
238 if (NS_SUCCEEDED(rv
)) {
239 // It is safe to used InitWithSanitizedSource because fileName is
240 // an already anonymized uri spec.
241 rv
= errorObject
->InitWithSanitizedSource(
242 mError
, fileName
, errorLine
, aLineNumber
, aColNumber
,
243 nsIScriptError::warningFlag
, "CSS Parser", mInnerWindowId
);
245 if (NS_SUCCEEDED(rv
)) {
246 errorObject
->SetCssSelectors(selectors
);
247 sConsoleService
->LogMessage(errorObject
);
254 void ErrorReporter::AddToError(const nsString
& aErrorText
) {
255 if (mError
.IsEmpty()) {
258 mError
.AppendLiteral(" ");
259 mError
.Append(aErrorText
);
263 void ErrorReporter::ReportUnexpected(const char* aMessage
) {
265 sStringBundle
->GetStringFromName(aMessage
, str
);
269 void ErrorReporter::ReportUnexpectedUnescaped(
270 const char* aMessage
, const nsTArray
<nsString
>& aParam
) {
272 sStringBundle
->FormatStringFromName(aMessage
, aParam
, str
);
277 } // namespace mozilla