Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/layout/style/ErrorReporter.cpp
Line
Count
Source (jump to first uncovered line)
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/. */
6
7
/* diagnostic reporting for CSS style sheet parser */
8
9
#include "mozilla/css/ErrorReporter.h"
10
11
#include "mozilla/StaticPrefs.h"
12
#include "mozilla/StyleSheetInlines.h"
13
#include "mozilla/css/Loader.h"
14
#include "mozilla/Preferences.h"
15
#include "mozilla/Services.h"
16
#include "mozilla/SystemGroup.h"
17
#include "nsIConsoleService.h"
18
#include "nsIDocument.h"
19
#include "nsIDocShell.h"
20
#include "nsIFactory.h"
21
#include "nsINode.h"
22
#include "nsIScriptError.h"
23
#include "nsISensitiveInfoHiddenURI.h"
24
#include "nsIStringBundle.h"
25
#include "nsServiceManagerUtils.h"
26
#include "nsStyleUtil.h"
27
#include "nsThreadUtils.h"
28
#include "nsNetUtil.h"
29
30
using namespace mozilla;
31
using namespace mozilla::css;
32
33
namespace {
34
class ShortTermURISpecCache : public Runnable {
35
public:
36
  ShortTermURISpecCache()
37
   : Runnable("ShortTermURISpecCache")
38
0
   , mPending(false) {}
39
40
0
  nsString const& GetSpec(nsIURI* aURI) {
41
0
    if (mURI != aURI) {
42
0
      mURI = aURI;
43
0
44
0
      if (NS_FAILED(NS_GetSanitizedURIStringFromURI(mURI, mSpec))) {
45
0
        mSpec.AssignLiteral("[nsIURI::GetSpec failed]");
46
0
      }
47
0
    }
48
0
    return mSpec;
49
0
  }
50
51
0
  bool IsInUse() const { return mURI != nullptr; }
52
0
  bool IsPending() const { return mPending; }
53
0
  void SetPending() { mPending = true; }
54
55
  // When invoked as a runnable, zap the cache.
56
0
  NS_IMETHOD Run() override {
57
0
    mURI = nullptr;
58
0
    mSpec.Truncate();
59
0
    mPending = false;
60
0
    return NS_OK;
61
0
  }
62
63
private:
64
  nsCOMPtr<nsIURI> mURI;
65
  nsString mSpec;
66
  bool mPending;
67
};
68
69
} // namespace
70
71
bool ErrorReporter::sInitialized = false;
72
73
static nsIConsoleService *sConsoleService;
74
static nsIFactory *sScriptErrorFactory;
75
static nsIStringBundle *sStringBundle;
76
static ShortTermURISpecCache *sSpecCache;
77
78
void
79
ErrorReporter::InitGlobals()
80
0
{
81
0
  MOZ_RELEASE_ASSERT(NS_IsMainThread());
82
0
  MOZ_ASSERT(!sInitialized, "should not have been called");
83
0
84
0
  sInitialized = true;
85
0
86
0
  nsCOMPtr<nsIConsoleService> cs = do_GetService(NS_CONSOLESERVICE_CONTRACTID);
87
0
  if (!cs) {
88
0
    return;
89
0
  }
90
0
91
0
  nsCOMPtr<nsIFactory> sf = do_GetClassObject(NS_SCRIPTERROR_CONTRACTID);
92
0
  if (!sf) {
93
0
    return;
94
0
  }
95
0
96
0
  nsCOMPtr<nsIStringBundleService> sbs = services::GetStringBundleService();
97
0
  if (!sbs) {
98
0
    return;
99
0
  }
100
0
101
0
  nsCOMPtr<nsIStringBundle> sb;
102
0
  nsresult rv = sbs->CreateBundle("chrome://global/locale/css.properties",
103
0
                                  getter_AddRefs(sb));
104
0
  if (NS_FAILED(rv) || !sb) {
105
0
    return;
106
0
  }
107
0
108
0
  cs.forget(&sConsoleService);
109
0
  sf.forget(&sScriptErrorFactory);
110
0
  sb.forget(&sStringBundle);
111
0
}
112
113
namespace mozilla {
114
namespace css {
115
116
/* static */ void
117
ErrorReporter::ReleaseGlobals()
118
0
{
119
0
  NS_IF_RELEASE(sConsoleService);
120
0
  NS_IF_RELEASE(sScriptErrorFactory);
121
0
  NS_IF_RELEASE(sStringBundle);
122
0
  NS_IF_RELEASE(sSpecCache);
123
0
}
124
125
static uint64_t
126
FindInnerWindowID(const StyleSheet* aSheet, const Loader* aLoader)
127
0
{
128
0
  uint64_t innerWindowID = 0;
129
0
  if (aSheet) {
130
0
    innerWindowID = aSheet->FindOwningWindowInnerID();
131
0
  }
132
0
  if (innerWindowID == 0 && aLoader) {
133
0
    if (nsIDocument* doc = aLoader->GetDocument()) {
134
0
      innerWindowID = doc->InnerWindowID();
135
0
    }
136
0
  }
137
0
  return innerWindowID;
138
0
}
139
140
ErrorReporter::ErrorReporter(const StyleSheet* aSheet,
141
                             const Loader* aLoader,
142
                             nsIURI* aURI)
143
  : mSheet(aSheet)
144
  , mLoader(aLoader)
145
  , mURI(aURI)
146
  , mErrorLineNumber(0)
147
  , mPrevErrorLineNumber(0)
148
  , mErrorColNumber(0)
149
0
{
150
0
  MOZ_ASSERT(ShouldReportErrors(mSheet, mLoader));
151
0
  EnsureGlobalsInitialized();
152
0
}
153
154
ErrorReporter::~ErrorReporter()
155
0
{
156
0
  MOZ_ASSERT(NS_IsMainThread());
157
0
  // Schedule deferred cleanup for cached data. We want to strike a
158
0
  // balance between performance and memory usage, so we only allow
159
0
  // short-term caching.
160
0
  if (sSpecCache && sSpecCache->IsInUse() && !sSpecCache->IsPending()) {
161
0
    nsCOMPtr<nsIRunnable> runnable(sSpecCache);
162
0
    nsresult rv =
163
0
      SystemGroup::Dispatch(TaskCategory::Other, runnable.forget());
164
0
    if (NS_FAILED(rv)) {
165
0
      // Peform the "deferred" cleanup immediately if the dispatch fails.
166
0
      sSpecCache->Run();
167
0
    } else {
168
0
      sSpecCache->SetPending();
169
0
    }
170
0
  }
171
0
}
172
173
bool
174
ErrorReporter::ShouldReportErrors(const nsIDocument& aDoc)
175
0
{
176
0
  MOZ_ASSERT(NS_IsMainThread());
177
0
  nsIDocShell* shell = aDoc.GetDocShell();
178
0
  if (!shell) {
179
0
    return false;
180
0
  }
181
0
182
0
  bool report = false;
183
0
  shell->GetCssErrorReportingEnabled(&report);
184
0
  return report;
185
0
}
186
187
static nsINode*
188
SheetOwner(const StyleSheet& aSheet)
189
0
{
190
0
  if (nsINode* owner = aSheet.GetOwnerNode()) {
191
0
    return owner;
192
0
  }
193
0
194
0
  auto* associated = aSheet.GetAssociatedDocumentOrShadowRoot();
195
0
  return associated ? &associated->AsNode() : nullptr;
196
0
}
197
198
bool
199
ErrorReporter::ShouldReportErrors(const StyleSheet* aSheet,
200
                                  const Loader* aLoader)
201
0
{
202
0
  MOZ_ASSERT(NS_IsMainThread());
203
0
204
0
  if (!StaticPrefs::layout_css_report_errors()) {
205
0
    return false;
206
0
  }
207
0
208
0
  if (aSheet) {
209
0
    nsINode* owner = SheetOwner(*aSheet);
210
0
    if (owner && ShouldReportErrors(*owner->OwnerDoc())) {
211
0
      return true;
212
0
    }
213
0
  }
214
0
215
0
  if (aLoader && aLoader->GetDocument() &&
216
0
      ShouldReportErrors(*aLoader->GetDocument())) {
217
0
    return true;
218
0
  }
219
0
220
0
  return false;
221
0
}
222
223
void
224
ErrorReporter::OutputError()
225
0
{
226
0
  MOZ_ASSERT(NS_IsMainThread());
227
0
  MOZ_ASSERT(ShouldReportErrors(mSheet, mLoader));
228
0
229
0
  if (mError.IsEmpty()) {
230
0
    return;
231
0
  }
232
0
233
0
  if (mFileName.IsEmpty()) {
234
0
    if (mURI) {
235
0
      if (!sSpecCache) {
236
0
        sSpecCache = new ShortTermURISpecCache;
237
0
        NS_ADDREF(sSpecCache);
238
0
      }
239
0
      mFileName = sSpecCache->GetSpec(mURI);
240
0
      mURI = nullptr;
241
0
    } else {
242
0
      mFileName.AssignLiteral("from DOM");
243
0
    }
244
0
  }
245
0
246
0
  nsresult rv;
247
0
  nsCOMPtr<nsIScriptError> errorObject =
248
0
    do_CreateInstance(sScriptErrorFactory, &rv);
249
0
250
0
  if (NS_SUCCEEDED(rv)) {
251
0
    // It is safe to used InitWithSanitizedSource because mFileName is
252
0
    // an already anonymized uri spec.
253
0
    rv = errorObject->InitWithSanitizedSource(mError,
254
0
                                              mFileName,
255
0
                                              mErrorLine,
256
0
                                              mErrorLineNumber,
257
0
                                              mErrorColNumber,
258
0
                                              nsIScriptError::warningFlag,
259
0
                                              "CSS Parser",
260
0
                                              FindInnerWindowID(mSheet, mLoader));
261
0
    if (NS_SUCCEEDED(rv)) {
262
0
      sConsoleService->LogMessage(errorObject);
263
0
    }
264
0
  }
265
0
266
0
  ClearError();
267
0
}
268
269
// When Stylo's CSS parser is in use, this reporter does not have access to the CSS parser's
270
// state. The users of ErrorReporter need to provide:
271
// - the line number of the error
272
// - the column number of the error
273
// - the complete source line containing the invalid CSS
274
275
void
276
ErrorReporter::OutputError(uint32_t aLineNumber,
277
                           uint32_t aColNumber,
278
                           const nsACString& aSourceLine)
279
0
{
280
0
  mErrorLineNumber = aLineNumber;
281
0
  mErrorColNumber = aColNumber;
282
0
283
0
  // Retrieve the error line once per line, and reuse the same nsString
284
0
  // for all errors on that line.  That causes the text of the line to
285
0
  // be shared among all the nsIScriptError objects.
286
0
  if (mErrorLine.IsEmpty() || mErrorLineNumber != mPrevErrorLineNumber) {
287
0
    mErrorLine.Truncate();
288
0
    // This could be a really long string for minified CSS; just leave it empty if we OOM.
289
0
    if (!AppendUTF8toUTF16(aSourceLine, mErrorLine, fallible)) {
290
0
      mErrorLine.Truncate();
291
0
    }
292
0
293
0
    mPrevErrorLineNumber = aLineNumber;
294
0
  }
295
0
296
0
  OutputError();
297
0
}
298
299
void
300
ErrorReporter::ClearError()
301
0
{
302
0
  mError.Truncate();
303
0
}
304
305
void
306
ErrorReporter::AddToError(const nsString &aErrorText)
307
0
{
308
0
  MOZ_ASSERT(ShouldReportErrors(mSheet, mLoader));
309
0
310
0
  if (mError.IsEmpty()) {
311
0
    mError = aErrorText;
312
0
  } else {
313
0
    mError.AppendLiteral("  ");
314
0
    mError.Append(aErrorText);
315
0
  }
316
0
}
317
318
void
319
ErrorReporter::ReportUnexpected(const char *aMessage)
320
0
{
321
0
  MOZ_ASSERT(ShouldReportErrors(mSheet, mLoader));
322
0
323
0
  nsAutoString str;
324
0
  sStringBundle->GetStringFromName(aMessage, str);
325
0
  AddToError(str);
326
0
}
327
328
void
329
ErrorReporter::ReportUnexpectedUnescaped(const char *aMessage,
330
                                         const nsAutoString& aParam)
331
0
{
332
0
  MOZ_ASSERT(ShouldReportErrors(mSheet, mLoader));
333
0
334
0
  const char16_t *params[1] = { aParam.get() };
335
0
336
0
  nsAutoString str;
337
0
  sStringBundle->FormatStringFromName(aMessage, params, ArrayLength(params),
338
0
                                      str);
339
0
  AddToError(str);
340
0
}
341
342
} // namespace css
343
} // namespace mozilla