Wed, 31 Dec 2014 06:55:50 +0100
Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2
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;
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);
40 }
41 return mSpec;
42 }
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;
54 }
56 private:
57 nsCOMPtr<nsIURI> mURI;
58 nsString mSpec;
59 bool mPending;
60 };
61 }
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()
73 {
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;
80 }
82 nsCOMPtr<nsIConsoleService> cs = do_GetService(NS_CONSOLESERVICE_CONTRACTID);
83 if (!cs) {
84 return false;
85 }
87 nsCOMPtr<nsIFactory> sf = do_GetClassObject(NS_SCRIPTERROR_CONTRACTID);
88 if (!sf) {
89 return false;
90 }
92 nsCOMPtr<nsIStringBundleService> sbs = services::GetStringBundleService();
93 if (!sbs) {
94 return false;
95 }
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;
102 }
104 cs.forget(&sConsoleService);
105 sf.forget(&sScriptErrorFactory);
106 sb.forget(&sStringBundle);
108 return true;
109 }
111 static inline bool
112 ShouldReportErrors()
113 {
114 if (!sConsoleService) {
115 if (!InitGlobals()) {
116 return false;
117 }
118 }
119 return sReportErrors;
120 }
122 namespace mozilla {
123 namespace css {
125 /* static */ void
126 ErrorReporter::ReleaseGlobals()
127 {
128 NS_IF_RELEASE(sConsoleService);
129 NS_IF_RELEASE(sScriptErrorFactory);
130 NS_IF_RELEASE(sStringBundle);
131 NS_IF_RELEASE(sSpecCache);
132 }
134 ErrorReporter::ErrorReporter(const nsCSSScanner& aScanner,
135 const nsCSSStyleSheet* 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)
141 {
142 }
144 ErrorReporter::~ErrorReporter()
145 {
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();
155 }
156 }
157 }
159 void
160 ErrorReporter::OutputError()
161 {
162 if (mError.IsEmpty()) {
163 return;
164 }
165 if (!ShouldReportErrors()) {
166 ClearError();
167 return;
168 }
170 if (mInnerWindowID == 0 && (mSheet || mLoader)) {
171 if (mSheet) {
172 mInnerWindowID = mSheet->FindOwningWindowInnerID();
173 }
174 if (mInnerWindowID == 0 && mLoader) {
175 nsIDocument* doc = mLoader->GetDocument();
176 if (doc) {
177 mInnerWindowID = doc->InnerWindowID();
178 }
179 }
180 // don't attempt this again, even if we failed
181 mSheet = nullptr;
182 mLoader = nullptr;
183 }
185 if (mFileName.IsEmpty()) {
186 if (mURI) {
187 if (!sSpecCache) {
188 sSpecCache = new ShortTermURISpecCache;
189 NS_ADDREF(sSpecCache);
190 }
191 mFileName = sSpecCache->GetSpec(mURI);
192 mURI = nullptr;
193 } else {
194 mFileName.AssignLiteral("from DOM");
195 }
196 }
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);
213 }
214 }
216 ClearError();
217 }
219 void
220 ErrorReporter::OutputError(uint32_t aLineNumber, uint32_t aLineOffset)
221 {
222 mErrorLineNumber = aLineNumber;
223 mErrorColNumber = aLineOffset;
224 OutputError();
225 }
227 void
228 ErrorReporter::ClearError()
229 {
230 mError.Truncate();
231 }
233 void
234 ErrorReporter::AddToError(const nsString &aErrorText)
235 {
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;
248 }
249 } else {
250 mError.AppendLiteral(" ");
251 mError.Append(aErrorText);
252 }
253 }
255 void
256 ErrorReporter::ReportUnexpected(const char *aMessage)
257 {
258 if (!ShouldReportErrors()) return;
260 nsAutoString str;
261 sStringBundle->GetStringFromName(NS_ConvertASCIItoUTF16(aMessage).get(),
262 getter_Copies(str));
263 AddToError(str);
264 }
266 void
267 ErrorReporter::ReportUnexpected(const char *aMessage,
268 const nsString &aParam)
269 {
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);
281 }
283 void
284 ErrorReporter::ReportUnexpected(const char *aMessage,
285 const nsCSSToken &aToken)
286 {
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);
298 }
300 void
301 ErrorReporter::ReportUnexpected(const char *aMessage,
302 const nsCSSToken &aToken,
303 char16_t aChar)
304 {
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);
317 }
319 void
320 ErrorReporter::ReportUnexpectedEOF(const char *aMessage)
321 {
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);
334 }
336 void
337 ErrorReporter::ReportUnexpectedEOF(char16_t aExpected)
338 {
339 if (!ShouldReportErrors()) return;
341 const char16_t expectedStr[] = {
342 char16_t('\''), aExpected, char16_t('\''), char16_t(0)
343 };
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);
351 }
353 } // namespace css
354 } // namespace mozilla
356 #endif