Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/base/nsJSTimeoutHandler.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
#include <algorithm>
8
9
#include "mozilla/Attributes.h"
10
#include "mozilla/Likely.h"
11
#include "mozilla/Maybe.h"
12
#include "mozilla/dom/CSPEvalChecker.h"
13
#include "mozilla/dom/FunctionBinding.h"
14
#include "mozilla/dom/WorkerPrivate.h"
15
#include "nsCOMPtr.h"
16
#include "nsContentUtils.h"
17
#include "nsError.h"
18
#include "nsGlobalWindow.h"
19
#include "nsIContentSecurityPolicy.h"
20
#include "nsIDocument.h"
21
#include "nsIScriptTimeoutHandler.h"
22
#include "nsIXPConnect.h"
23
#include "nsJSUtils.h"
24
25
using namespace mozilla;
26
using namespace mozilla::dom;
27
28
// Our JS nsIScriptTimeoutHandler implementation.
29
class nsJSScriptTimeoutHandler final : public nsIScriptTimeoutHandler
30
{
31
public:
32
  // nsISupports
33
  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
34
  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsJSScriptTimeoutHandler)
35
36
  nsJSScriptTimeoutHandler();
37
  // This will call SwapElements on aArguments with an empty array.
38
  nsJSScriptTimeoutHandler(JSContext* aCx, nsGlobalWindowInner* aWindow,
39
                           Function& aFunction,
40
                           nsTArray<JS::Heap<JS::Value>>&& aArguments,
41
                           ErrorResult& aError);
42
  nsJSScriptTimeoutHandler(JSContext* aCx, nsGlobalWindowInner* aWindow,
43
                           const nsAString& aExpression, bool* aAllowEval,
44
                           ErrorResult& aError);
45
  nsJSScriptTimeoutHandler(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
46
                           Function& aFunction,
47
                           nsTArray<JS::Heap<JS::Value>>&& aArguments);
48
  nsJSScriptTimeoutHandler(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
49
                           const nsAString& aExpression, bool* aAllowEval,
50
                           ErrorResult& aRv);
51
52
  virtual const nsAString& GetHandlerText() override;
53
54
  virtual Function* GetCallback() override
55
0
  {
56
0
    return mFunction;
57
0
  }
58
59
  virtual const nsTArray<JS::Value>& GetArgs() override
60
0
  {
61
0
    return mArgs;
62
0
  }
63
64
  virtual nsresult Call() override
65
0
  {
66
0
    return NS_OK;
67
0
  }
68
69
  virtual void GetLocation(const char** aFileName, uint32_t* aLineNo,
70
                           uint32_t* aColumn) override
71
0
  {
72
0
    *aFileName = mFileName.get();
73
0
    *aLineNo = mLineNo;
74
0
    *aColumn = mColumn;
75
0
  }
76
77
  virtual void MarkForCC() override
78
0
  {
79
0
    if (mFunction) {
80
0
      mFunction->MarkForCC();
81
0
    }
82
0
  }
83
84
  void ReleaseJSObjects();
85
86
private:
87
  ~nsJSScriptTimeoutHandler();
88
89
  void Init(JSContext* aCx,
90
            nsTArray<JS::Heap<JS::Value>>&& aArguments);
91
  void Init(JSContext* aCx);
92
93
  // filename, line number and JS language version string of the
94
  // caller of setTimeout()
95
  nsCString mFileName;
96
  uint32_t mLineNo;
97
  uint32_t mColumn;
98
  nsTArray<JS::Heap<JS::Value>> mArgs;
99
100
  // The expression to evaluate or function to call. If mFunction is non-null
101
  // it should be used, else use mExpr.
102
  nsString mExpr;
103
  RefPtr<Function> mFunction;
104
};
105
106
107
// nsJSScriptTimeoutHandler
108
// QueryInterface implementation for nsJSScriptTimeoutHandler
109
NS_IMPL_CYCLE_COLLECTION_CLASS(nsJSScriptTimeoutHandler)
110
111
0
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsJSScriptTimeoutHandler)
112
0
  tmp->ReleaseJSObjects();
113
0
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
114
0
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsJSScriptTimeoutHandler)
115
0
  if (MOZ_UNLIKELY(cb.WantDebugInfo())) {
116
0
    nsAutoCString name("nsJSScriptTimeoutHandler");
117
0
    if (tmp->mFunction) {
118
0
      JSObject* obj = tmp->mFunction->CallablePreserveColor();
119
0
      JSFunction* fun = JS_GetObjectFunction(js::UncheckedUnwrapWithoutExpose(obj));
120
0
      if (fun && JS_GetFunctionId(fun)) {
121
0
        JSFlatString *funId = JS_ASSERT_STRING_IS_FLAT(JS_GetFunctionId(fun));
122
0
        size_t size = 1 + JS_PutEscapedFlatString(nullptr, 0, funId, 0);
123
0
        char *funIdName = new char[size];
124
0
        if (funIdName) {
125
0
          JS_PutEscapedFlatString(funIdName, size, funId, 0);
126
0
          name.AppendLiteral(" [");
127
0
          name.Append(funIdName);
128
0
          delete[] funIdName;
129
0
          name.Append(']');
130
0
        }
131
0
      }
132
0
    } else {
133
0
      name.AppendLiteral(" [");
134
0
      name.Append(tmp->mFileName);
135
0
      name.Append(':');
136
0
      name.AppendInt(tmp->mLineNo);
137
0
      name.Append(':');
138
0
      name.AppendInt(tmp->mColumn);
139
0
      name.Append(']');
140
0
    }
141
0
    cb.DescribeRefCountedNode(tmp->mRefCnt.get(), name.get());
142
0
  }
143
0
  else {
144
0
    NS_IMPL_CYCLE_COLLECTION_DESCRIBE(nsJSScriptTimeoutHandler,
145
0
                                      tmp->mRefCnt.get())
146
0
  }
147
0
148
0
  if (tmp->mFunction) {
149
0
    NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFunction)
150
0
  }
151
0
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
152
153
0
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsJSScriptTimeoutHandler)
154
0
  for (uint32_t i = 0; i < tmp->mArgs.Length(); ++i) {
155
0
    NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mArgs[i])
156
0
  }
157
0
NS_IMPL_CYCLE_COLLECTION_TRACE_END
158
159
0
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsJSScriptTimeoutHandler)
160
0
  NS_INTERFACE_MAP_ENTRY(nsIScriptTimeoutHandler)
161
0
  NS_INTERFACE_MAP_ENTRY(nsITimeoutHandler)
162
0
  NS_INTERFACE_MAP_ENTRY(nsISupports)
163
0
NS_INTERFACE_MAP_END
164
165
NS_IMPL_CYCLE_COLLECTING_ADDREF(nsJSScriptTimeoutHandler)
166
NS_IMPL_CYCLE_COLLECTING_RELEASE(nsJSScriptTimeoutHandler)
167
168
nsJSScriptTimeoutHandler::nsJSScriptTimeoutHandler()
169
  : mLineNo(0)
170
  , mColumn(0)
171
0
{
172
0
}
173
174
nsJSScriptTimeoutHandler::nsJSScriptTimeoutHandler(JSContext* aCx,
175
                                                   nsGlobalWindowInner *aWindow,
176
                                                   Function& aFunction,
177
                                                   nsTArray<JS::Heap<JS::Value>>&& aArguments,
178
                                                   ErrorResult& aError)
179
  : mLineNo(0)
180
  , mColumn(0)
181
  , mFunction(&aFunction)
182
0
{
183
0
  if (!aWindow->GetContextInternal() || !aWindow->FastGetGlobalJSObject()) {
184
0
    // This window was already closed, or never properly initialized,
185
0
    // don't let a timer be scheduled on such a window.
186
0
    aError.Throw(NS_ERROR_NOT_INITIALIZED);
187
0
    return;
188
0
  }
189
0
190
0
  Init(aCx, std::move(aArguments));
191
0
}
192
193
nsJSScriptTimeoutHandler::nsJSScriptTimeoutHandler(JSContext* aCx,
194
                                                   nsGlobalWindowInner *aWindow,
195
                                                   const nsAString& aExpression,
196
                                                   bool* aAllowEval,
197
                                                   ErrorResult& aError)
198
  : mLineNo(0)
199
  , mColumn(0)
200
  , mExpr(aExpression)
201
0
{
202
0
  if (!aWindow->GetContextInternal() || !aWindow->FastGetGlobalJSObject()) {
203
0
    // This window was already closed, or never properly initialized,
204
0
    // don't let a timer be scheduled on such a window.
205
0
    aError.Throw(NS_ERROR_NOT_INITIALIZED);
206
0
    return;
207
0
  }
208
0
209
0
  aError = CSPEvalChecker::CheckForWindow(aCx, aWindow, aExpression,
210
0
                                          aAllowEval);
211
0
  if (NS_WARN_IF(aError.Failed()) || !*aAllowEval) {
212
0
    return;
213
0
  }
214
0
215
0
  Init(aCx);
216
0
}
217
218
nsJSScriptTimeoutHandler::nsJSScriptTimeoutHandler(JSContext* aCx,
219
                                                   WorkerPrivate* aWorkerPrivate,
220
                                                   Function& aFunction,
221
                                                   nsTArray<JS::Heap<JS::Value>>&& aArguments)
222
  : mLineNo(0)
223
  , mColumn(0)
224
  , mFunction(&aFunction)
225
0
{
226
0
  MOZ_ASSERT(aWorkerPrivate);
227
0
  aWorkerPrivate->AssertIsOnWorkerThread();
228
0
229
0
  Init(aCx, std::move(aArguments));
230
0
}
231
232
nsJSScriptTimeoutHandler::nsJSScriptTimeoutHandler(JSContext* aCx,
233
                                                   WorkerPrivate* aWorkerPrivate,
234
                                                   const nsAString& aExpression,
235
                                                   bool* aAllowEval,
236
                                                   ErrorResult& aError)
237
  : mLineNo(0)
238
  , mColumn(0)
239
  , mExpr(aExpression)
240
0
{
241
0
  MOZ_ASSERT(aWorkerPrivate);
242
0
  aWorkerPrivate->AssertIsOnWorkerThread();
243
0
244
0
  aError = CSPEvalChecker::CheckForWorker(aCx, aWorkerPrivate, aExpression,
245
0
                                          aAllowEval);
246
0
  if (NS_WARN_IF(aError.Failed()) || !*aAllowEval) {
247
0
    return;
248
0
  }
249
0
250
0
  Init(aCx);
251
0
}
252
253
nsJSScriptTimeoutHandler::~nsJSScriptTimeoutHandler()
254
0
{
255
0
  ReleaseJSObjects();
256
0
}
257
258
void
259
nsJSScriptTimeoutHandler::Init(JSContext* aCx,
260
                               nsTArray<JS::Heap<JS::Value>>&& aArguments)
261
0
{
262
0
  mozilla::HoldJSObjects(this);
263
0
  mArgs = std::move(aArguments);
264
0
265
0
  Init(aCx);
266
0
}
267
268
void
269
nsJSScriptTimeoutHandler::Init(JSContext* aCx)
270
0
{
271
0
  // Get the calling location.
272
0
  nsJSUtils::GetCallingLocation(aCx, mFileName, &mLineNo, &mColumn);
273
0
}
274
275
void
276
nsJSScriptTimeoutHandler::ReleaseJSObjects()
277
0
{
278
0
  if (mFunction) {
279
0
    mFunction = nullptr;
280
0
    mArgs.Clear();
281
0
    mozilla::DropJSObjects(this);
282
0
  }
283
0
}
284
285
const nsAString&
286
nsJSScriptTimeoutHandler::GetHandlerText()
287
0
{
288
0
  NS_ASSERTION(!mFunction, "No expression, so no handler text!");
289
0
  return mExpr;
290
0
}
291
292
already_AddRefed<nsIScriptTimeoutHandler>
293
NS_CreateJSTimeoutHandler(JSContext *aCx, nsGlobalWindowInner *aWindow,
294
                          Function& aFunction,
295
                          const Sequence<JS::Value>& aArguments,
296
                          ErrorResult& aError)
297
0
{
298
0
  nsTArray<JS::Heap<JS::Value>> args;
299
0
  if (!args.AppendElements(aArguments, fallible)) {
300
0
    aError.Throw(NS_ERROR_OUT_OF_MEMORY);
301
0
    return nullptr;
302
0
  }
303
0
304
0
  RefPtr<nsJSScriptTimeoutHandler> handler =
305
0
    new nsJSScriptTimeoutHandler(aCx, aWindow, aFunction, std::move(args), aError);
306
0
  return aError.Failed() ? nullptr : handler.forget();
307
0
}
308
309
already_AddRefed<nsIScriptTimeoutHandler>
310
NS_CreateJSTimeoutHandler(JSContext* aCx, nsGlobalWindowInner *aWindow,
311
                          const nsAString& aExpression, ErrorResult& aError)
312
0
{
313
0
  bool allowEval = false;
314
0
  RefPtr<nsJSScriptTimeoutHandler> handler =
315
0
    new nsJSScriptTimeoutHandler(aCx, aWindow, aExpression, &allowEval, aError);
316
0
  if (aError.Failed() || !allowEval) {
317
0
    return nullptr;
318
0
  }
319
0
320
0
  return handler.forget();
321
0
}
322
323
already_AddRefed<nsIScriptTimeoutHandler>
324
NS_CreateJSTimeoutHandler(JSContext *aCx, WorkerPrivate* aWorkerPrivate,
325
                          Function& aFunction,
326
                          const Sequence<JS::Value>& aArguments,
327
                          ErrorResult& aError)
328
0
{
329
0
  nsTArray<JS::Heap<JS::Value>> args;
330
0
  if (!args.AppendElements(aArguments, fallible)) {
331
0
    aError.Throw(NS_ERROR_OUT_OF_MEMORY);
332
0
    return nullptr;
333
0
  }
334
0
335
0
  RefPtr<nsJSScriptTimeoutHandler> handler =
336
0
    new nsJSScriptTimeoutHandler(aCx, aWorkerPrivate, aFunction, std::move(args));
337
0
  return handler.forget();
338
0
}
339
340
already_AddRefed<nsIScriptTimeoutHandler>
341
NS_CreateJSTimeoutHandler(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
342
                          const nsAString& aExpression, ErrorResult& aRv)
343
0
{
344
0
  bool allowEval = false;
345
0
  RefPtr<nsJSScriptTimeoutHandler> handler =
346
0
    new nsJSScriptTimeoutHandler(aCx, aWorkerPrivate, aExpression, &allowEval,
347
0
                                 aRv);
348
0
  if (aRv.Failed() || !allowEval) {
349
0
    return nullptr;
350
0
  }
351
0
352
0
  return handler.forget();
353
0
}