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/. */
6 /* diagnostic reporting for CSS style sheet parser */
8 #include "mozilla/css/ErrorReporter.h"
9 #include "mozilla/css/Loader.h"
10 #include "mozilla/Preferences.h"
11 #include "mozilla/Services.h"
12 #include "nsCSSScanner.h"
13 #include "nsCSSStyleSheet.h"
14 #include "nsIConsoleService.h"
15 #include "nsIDocument.h"
16 #include "nsIFactory.h"
17 #include "nsIScriptError.h"
18 #include "nsIStringBundle.h"
19 #include "nsServiceManagerUtils.h"
20 #include "nsStyleUtil.h"
21 #include "nsThreadUtils.h"
23 #ifdef CSS_REPORT_PARSE_ERRORS
25 using mozilla::Preferences
;
26 namespace services
= mozilla::services
;
29 class ShortTermURISpecCache
: public nsRunnable
{
31 ShortTermURISpecCache() : mPending(false) {}
33 nsString
const& GetSpec(nsIURI
* aURI
) {
39 CopyUTF8toUTF16(cSpec
, mSpec
);
44 bool IsInUse() const { return mURI
!= nullptr; }
45 bool IsPending() const { return mPending
; }
46 void SetPending() { mPending
= true; }
48 // When invoked as a runnable, zap the cache.
57 nsCOMPtr
<nsIURI
> mURI
;
63 static bool sReportErrors
;
64 static nsIConsoleService
*sConsoleService
;
65 static nsIFactory
*sScriptErrorFactory
;
66 static nsIStringBundle
*sStringBundle
;
67 static ShortTermURISpecCache
*sSpecCache
;
69 #define CSS_ERRORS_PREF "layout.css.report_errors"
74 NS_ABORT_IF_FALSE(!sConsoleService
&& !sScriptErrorFactory
&& !sStringBundle
,
75 "should not have been called");
77 if (NS_FAILED(Preferences::AddBoolVarCache(&sReportErrors
, CSS_ERRORS_PREF
,
82 nsCOMPtr
<nsIConsoleService
> cs
= do_GetService(NS_CONSOLESERVICE_CONTRACTID
);
87 nsCOMPtr
<nsIFactory
> sf
= do_GetClassObject(NS_SCRIPTERROR_CONTRACTID
);
92 nsCOMPtr
<nsIStringBundleService
> sbs
= services::GetStringBundleService();
97 nsCOMPtr
<nsIStringBundle
> sb
;
98 nsresult rv
= sbs
->CreateBundle("chrome://global/locale/css.properties",
100 if (NS_FAILED(rv
) || !sb
) {
104 sConsoleService
= cs
.forget().get();
105 sScriptErrorFactory
= sf
.forget().get();
106 sStringBundle
= sb
.forget().get();
114 if (!sConsoleService
) {
115 if (!InitGlobals()) {
119 return sReportErrors
;
126 ErrorReporter::ReleaseGlobals()
128 NS_IF_RELEASE(sConsoleService
);
129 NS_IF_RELEASE(sScriptErrorFactory
);
130 NS_IF_RELEASE(sStringBundle
);
131 NS_IF_RELEASE(sSpecCache
);
134 ErrorReporter::ErrorReporter(const nsCSSScanner
& aScanner
,
135 const nsCSSStyleSheet
* aSheet
,
136 const Loader
* aLoader
,
138 : mScanner(&aScanner
), mSheet(aSheet
), mLoader(aLoader
), mURI(aURI
),
139 mInnerWindowID(0), mErrorLineNumber(0), mPrevErrorLineNumber(0),
144 ErrorReporter::~ErrorReporter()
146 // Schedule deferred cleanup for cached data. We want to strike a
147 // balance between performance and memory usage, so we only allow
148 // short-term caching.
149 if (sSpecCache
&& sSpecCache
->IsInUse() && !sSpecCache
->IsPending()) {
150 if (NS_FAILED(NS_DispatchToCurrentThread(sSpecCache
))) {
151 // Peform the "deferred" cleanup immediately if the dispatch fails.
154 sSpecCache
->SetPending();
160 ErrorReporter::OutputError()
162 if (mError
.IsEmpty()) {
165 if (!ShouldReportErrors()) {
170 if (mInnerWindowID
== 0 && (mSheet
|| mLoader
)) {
172 mInnerWindowID
= mSheet
->FindOwningWindowInnerID();
174 if (mInnerWindowID
== 0 && mLoader
) {
175 nsIDocument
* doc
= mLoader
->GetDocument();
177 mInnerWindowID
= doc
->InnerWindowID();
180 // don't attempt this again, even if we failed
185 if (mFileName
.IsEmpty()) {
188 sSpecCache
= new ShortTermURISpecCache
;
189 NS_ADDREF(sSpecCache
);
191 mFileName
= sSpecCache
->GetSpec(mURI
);
194 mFileName
.AssignLiteral("from DOM");
199 nsCOMPtr
<nsIScriptError
> errorObject
=
200 do_CreateInstance(sScriptErrorFactory
, &rv
);
202 if (NS_SUCCEEDED(rv
)) {
203 rv
= errorObject
->InitWithWindowID(mError
,
208 nsIScriptError::warningFlag
,
211 if (NS_SUCCEEDED(rv
)) {
212 sConsoleService
->LogMessage(errorObject
);
220 ErrorReporter::ClearError()
226 ErrorReporter::AddToError(const nsString
&aErrorText
)
228 if (!ShouldReportErrors()) return;
230 if (mError
.IsEmpty()) {
232 mErrorLineNumber
= mScanner
->GetLineNumber();
233 mErrorColNumber
= mScanner
->GetColumnNumber();
234 // Retrieve the error line once per line, and reuse the same nsString
235 // for all errors on that line. That causes the text of the line to
236 // be shared among all the nsIScriptError objects.
237 if (mErrorLine
.IsEmpty() || mErrorLineNumber
!= mPrevErrorLineNumber
) {
238 mErrorLine
= mScanner
->GetCurrentLine();
239 mPrevErrorLineNumber
= mErrorLineNumber
;
242 mError
.AppendLiteral(" ");
243 mError
.Append(aErrorText
);
248 ErrorReporter::ReportUnexpected(const char *aMessage
)
250 if (!ShouldReportErrors()) return;
253 sStringBundle
->GetStringFromName(NS_ConvertASCIItoUTF16(aMessage
).get(),
259 ErrorReporter::ReportUnexpected(const char *aMessage
,
260 const nsString
&aParam
)
262 if (!ShouldReportErrors()) return;
265 nsStyleUtil::AppendEscapedCSSIdent(aParam
, qparam
);
266 const PRUnichar
*params
[1] = { qparam
.get() };
269 sStringBundle
->FormatStringFromName(NS_ConvertASCIItoUTF16(aMessage
).get(),
270 params
, ArrayLength(params
),
276 ErrorReporter::ReportUnexpected(const char *aMessage
,
277 const nsCSSToken
&aToken
)
279 if (!ShouldReportErrors()) return;
281 nsAutoString tokenString
;
282 aToken
.AppendToString(tokenString
);
283 const PRUnichar
*params
[1] = { tokenString
.get() };
286 sStringBundle
->FormatStringFromName(NS_ConvertASCIItoUTF16(aMessage
).get(),
287 params
, ArrayLength(params
),
293 ErrorReporter::ReportUnexpected(const char *aMessage
,
294 const nsCSSToken
&aToken
,
297 if (!ShouldReportErrors()) return;
299 nsAutoString tokenString
;
300 aToken
.AppendToString(tokenString
);
301 const PRUnichar charStr
[2] = { aChar
, 0 };
302 const PRUnichar
*params
[2] = { tokenString
.get(), charStr
};
305 sStringBundle
->FormatStringFromName(NS_ConvertASCIItoUTF16(aMessage
).get(),
306 params
, ArrayLength(params
),
312 ErrorReporter::ReportUnexpectedEOF(const char *aMessage
)
314 if (!ShouldReportErrors()) return;
316 nsAutoString innerStr
;
317 sStringBundle
->GetStringFromName(NS_ConvertASCIItoUTF16(aMessage
).get(),
318 getter_Copies(innerStr
));
319 const PRUnichar
*params
[1] = { innerStr
.get() };
322 sStringBundle
->FormatStringFromName(NS_LITERAL_STRING("PEUnexpEOF2").get(),
323 params
, ArrayLength(params
),
329 ErrorReporter::ReportUnexpectedEOF(PRUnichar aExpected
)
331 if (!ShouldReportErrors()) return;
333 const PRUnichar expectedStr
[] = {
334 PRUnichar('\''), aExpected
, PRUnichar('\''), PRUnichar(0)
336 const PRUnichar
*params
[1] = { expectedStr
};
339 sStringBundle
->FormatStringFromName(NS_LITERAL_STRING("PEUnexpEOF2").get(),
340 params
, ArrayLength(params
),
346 } // namespace mozilla