Bumping manifests a=b2g-bump
[gecko.git] / layout / style / ErrorReporter.cpp
blobcf35e939dc32b430d8d2b4bffe4c959dc76adf06
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"
10 #include "mozilla/CSSStyleSheet.h"
11 #include "mozilla/css/Loader.h"
12 #include "mozilla/Preferences.h"
13 #include "mozilla/Services.h"
14 #include "nsCSSScanner.h"
15 #include "nsIConsoleService.h"
16 #include "nsIDocument.h"
17 #include "nsIFactory.h"
18 #include "nsIScriptError.h"
19 #include "nsIStringBundle.h"
20 #include "nsServiceManagerUtils.h"
21 #include "nsStyleUtil.h"
22 #include "nsThreadUtils.h"
24 #ifdef CSS_REPORT_PARSE_ERRORS
26 using namespace mozilla;
28 namespace {
29 class ShortTermURISpecCache : public nsRunnable {
30 public:
31 ShortTermURISpecCache() : mPending(false) {}
33 nsString const& GetSpec(nsIURI* aURI) {
34 if (mURI != aURI) {
35 mURI = aURI;
37 nsAutoCString cSpec;
38 mURI->GetSpec(cSpec);
39 CopyUTF8toUTF16(cSpec, mSpec);
41 return 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.
49 NS_IMETHOD Run() {
50 mURI = nullptr;
51 mSpec.Truncate();
52 mPending = false;
53 return NS_OK;
56 private:
57 nsCOMPtr<nsIURI> mURI;
58 nsString mSpec;
59 bool mPending;
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"
71 static bool
72 InitGlobals()
74 NS_ABORT_IF_FALSE(!sConsoleService && !sScriptErrorFactory && !sStringBundle,
75 "should not have been called");
77 if (NS_FAILED(Preferences::AddBoolVarCache(&sReportErrors, CSS_ERRORS_PREF,
78 true))) {
79 return false;
82 nsCOMPtr<nsIConsoleService> cs = do_GetService(NS_CONSOLESERVICE_CONTRACTID);
83 if (!cs) {
84 return false;
87 nsCOMPtr<nsIFactory> sf = do_GetClassObject(NS_SCRIPTERROR_CONTRACTID);
88 if (!sf) {
89 return false;
92 nsCOMPtr<nsIStringBundleService> sbs = services::GetStringBundleService();
93 if (!sbs) {
94 return false;
97 nsCOMPtr<nsIStringBundle> sb;
98 nsresult rv = sbs->CreateBundle("chrome://global/locale/css.properties",
99 getter_AddRefs(sb));
100 if (NS_FAILED(rv) || !sb) {
101 return false;
104 cs.forget(&sConsoleService);
105 sf.forget(&sScriptErrorFactory);
106 sb.forget(&sStringBundle);
108 return true;
111 static inline bool
112 ShouldReportErrors()
114 if (!sConsoleService) {
115 if (!InitGlobals()) {
116 return false;
119 return sReportErrors;
122 namespace mozilla {
123 namespace css {
125 /* static */ void
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 CSSStyleSheet* aSheet,
136 const Loader* aLoader,
137 nsIURI* aURI)
138 : mScanner(&aScanner), mSheet(aSheet), mLoader(aLoader), mURI(aURI),
139 mInnerWindowID(0), mErrorLineNumber(0), mPrevErrorLineNumber(0),
140 mErrorColNumber(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.
152 sSpecCache->Run();
153 } else {
154 sSpecCache->SetPending();
159 void
160 ErrorReporter::OutputError()
162 if (mError.IsEmpty()) {
163 return;
165 if (!ShouldReportErrors()) {
166 ClearError();
167 return;
170 if (mInnerWindowID == 0 && (mSheet || mLoader)) {
171 if (mSheet) {
172 mInnerWindowID = mSheet->FindOwningWindowInnerID();
174 if (mInnerWindowID == 0 && mLoader) {
175 nsIDocument* doc = mLoader->GetDocument();
176 if (doc) {
177 mInnerWindowID = doc->InnerWindowID();
180 // don't attempt this again, even if we failed
181 mSheet = nullptr;
182 mLoader = nullptr;
185 if (mFileName.IsEmpty()) {
186 if (mURI) {
187 if (!sSpecCache) {
188 sSpecCache = new ShortTermURISpecCache;
189 NS_ADDREF(sSpecCache);
191 mFileName = sSpecCache->GetSpec(mURI);
192 mURI = nullptr;
193 } else {
194 mFileName.AssignLiteral("from DOM");
198 nsresult rv;
199 nsCOMPtr<nsIScriptError> errorObject =
200 do_CreateInstance(sScriptErrorFactory, &rv);
202 if (NS_SUCCEEDED(rv)) {
203 rv = errorObject->InitWithWindowID(mError,
204 mFileName,
205 mErrorLine,
206 mErrorLineNumber,
207 mErrorColNumber,
208 nsIScriptError::warningFlag,
209 "CSS Parser",
210 mInnerWindowID);
211 if (NS_SUCCEEDED(rv)) {
212 sConsoleService->LogMessage(errorObject);
216 ClearError();
219 void
220 ErrorReporter::OutputError(uint32_t aLineNumber, uint32_t aLineOffset)
222 mErrorLineNumber = aLineNumber;
223 mErrorColNumber = aLineOffset;
224 OutputError();
227 void
228 ErrorReporter::ClearError()
230 mError.Truncate();
233 void
234 ErrorReporter::AddToError(const nsString &aErrorText)
236 if (!ShouldReportErrors()) return;
238 if (mError.IsEmpty()) {
239 mError = aErrorText;
240 mErrorLineNumber = mScanner->GetLineNumber();
241 mErrorColNumber = mScanner->GetColumnNumber();
242 // Retrieve the error line once per line, and reuse the same nsString
243 // for all errors on that line. That causes the text of the line to
244 // be shared among all the nsIScriptError objects.
245 if (mErrorLine.IsEmpty() || mErrorLineNumber != mPrevErrorLineNumber) {
246 mErrorLine = mScanner->GetCurrentLine();
247 mPrevErrorLineNumber = mErrorLineNumber;
249 } else {
250 mError.AppendLiteral(" ");
251 mError.Append(aErrorText);
255 void
256 ErrorReporter::ReportUnexpected(const char *aMessage)
258 if (!ShouldReportErrors()) return;
260 nsAutoString str;
261 sStringBundle->GetStringFromName(NS_ConvertASCIItoUTF16(aMessage).get(),
262 getter_Copies(str));
263 AddToError(str);
266 void
267 ErrorReporter::ReportUnexpected(const char *aMessage,
268 const nsString &aParam)
270 if (!ShouldReportErrors()) return;
272 nsAutoString qparam;
273 nsStyleUtil::AppendEscapedCSSIdent(aParam, qparam);
274 const char16_t *params[1] = { qparam.get() };
276 nsAutoString str;
277 sStringBundle->FormatStringFromName(NS_ConvertASCIItoUTF16(aMessage).get(),
278 params, ArrayLength(params),
279 getter_Copies(str));
280 AddToError(str);
283 void
284 ErrorReporter::ReportUnexpected(const char *aMessage,
285 const nsCSSToken &aToken)
287 if (!ShouldReportErrors()) return;
289 nsAutoString tokenString;
290 aToken.AppendToString(tokenString);
291 const char16_t *params[1] = { tokenString.get() };
293 nsAutoString str;
294 sStringBundle->FormatStringFromName(NS_ConvertASCIItoUTF16(aMessage).get(),
295 params, ArrayLength(params),
296 getter_Copies(str));
297 AddToError(str);
300 void
301 ErrorReporter::ReportUnexpected(const char *aMessage,
302 const nsCSSToken &aToken,
303 char16_t aChar)
305 if (!ShouldReportErrors()) return;
307 nsAutoString tokenString;
308 aToken.AppendToString(tokenString);
309 const char16_t charStr[2] = { aChar, 0 };
310 const char16_t *params[2] = { tokenString.get(), charStr };
312 nsAutoString str;
313 sStringBundle->FormatStringFromName(NS_ConvertASCIItoUTF16(aMessage).get(),
314 params, ArrayLength(params),
315 getter_Copies(str));
316 AddToError(str);
319 void
320 ErrorReporter::ReportUnexpectedEOF(const char *aMessage)
322 if (!ShouldReportErrors()) return;
324 nsAutoString innerStr;
325 sStringBundle->GetStringFromName(NS_ConvertASCIItoUTF16(aMessage).get(),
326 getter_Copies(innerStr));
327 const char16_t *params[1] = { innerStr.get() };
329 nsAutoString str;
330 sStringBundle->FormatStringFromName(MOZ_UTF16("PEUnexpEOF2"),
331 params, ArrayLength(params),
332 getter_Copies(str));
333 AddToError(str);
336 void
337 ErrorReporter::ReportUnexpectedEOF(char16_t aExpected)
339 if (!ShouldReportErrors()) return;
341 const char16_t expectedStr[] = {
342 char16_t('\''), aExpected, char16_t('\''), char16_t(0)
344 const char16_t *params[1] = { expectedStr };
346 nsAutoString str;
347 sStringBundle->FormatStringFromName(MOZ_UTF16("PEUnexpEOF2"),
348 params, ArrayLength(params),
349 getter_Copies(str));
350 AddToError(str);
353 } // namespace css
354 } // namespace mozilla
356 #endif