Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/xbl/nsXBLProtoImplMethod.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 "nsAtom.h"
8
#include "nsString.h"
9
#include "jsapi.h"
10
#include "nsIContent.h"
11
#include "nsIDocument.h"
12
#include "nsIGlobalObject.h"
13
#include "nsUnicharUtils.h"
14
#include "nsReadableUtils.h"
15
#include "nsXBLProtoImplMethod.h"
16
#include "nsJSUtils.h"
17
#include "nsContentUtils.h"
18
#include "nsIScriptSecurityManager.h"
19
#include "nsIXPConnect.h"
20
#include "xpcpublic.h"
21
#include "nsXBLPrototypeBinding.h"
22
#include "mozilla/dom/Element.h"
23
#include "mozilla/dom/ScriptSettings.h"
24
25
using namespace mozilla;
26
using namespace mozilla::dom;
27
28
nsXBLProtoImplMethod::nsXBLProtoImplMethod(const char16_t* aName) :
29
  nsXBLProtoImplMember(aName),
30
  mMethod()
31
0
{
32
0
  MOZ_COUNT_CTOR(nsXBLProtoImplMethod);
33
0
}
34
35
nsXBLProtoImplMethod::~nsXBLProtoImplMethod()
36
0
{
37
0
  MOZ_COUNT_DTOR(nsXBLProtoImplMethod);
38
0
39
0
  if (!IsCompiled()) {
40
0
    delete GetUncompiledMethod();
41
0
  }
42
0
}
43
44
void
45
nsXBLProtoImplMethod::AppendBodyText(const nsAString& aText)
46
0
{
47
0
  MOZ_ASSERT(!IsCompiled(),
48
0
             "Must not be compiled when accessing uncompiled method");
49
0
50
0
  nsXBLUncompiledMethod* uncompiledMethod = GetUncompiledMethod();
51
0
  if (!uncompiledMethod) {
52
0
    uncompiledMethod = new nsXBLUncompiledMethod();
53
0
    SetUncompiledMethod(uncompiledMethod);
54
0
  }
55
0
56
0
  uncompiledMethod->AppendBodyText(aText);
57
0
}
58
59
void
60
nsXBLProtoImplMethod::AddParameter(const nsAString& aText)
61
0
{
62
0
  MOZ_ASSERT(!IsCompiled(),
63
0
             "Must not be compiled when accessing uncompiled method");
64
0
65
0
  if (aText.IsEmpty()) {
66
0
    NS_WARNING("Empty name attribute in xbl:parameter!");
67
0
    return;
68
0
  }
69
0
70
0
  nsXBLUncompiledMethod* uncompiledMethod = GetUncompiledMethod();
71
0
  if (!uncompiledMethod) {
72
0
    uncompiledMethod = new nsXBLUncompiledMethod();
73
0
    SetUncompiledMethod(uncompiledMethod);
74
0
  }
75
0
76
0
  uncompiledMethod->AddParameter(aText);
77
0
}
78
79
void
80
nsXBLProtoImplMethod::SetLineNumber(uint32_t aLineNumber)
81
0
{
82
0
  MOZ_ASSERT(!IsCompiled(),
83
0
             "Must not be compiled when accessing uncompiled method");
84
0
85
0
  nsXBLUncompiledMethod* uncompiledMethod = GetUncompiledMethod();
86
0
  if (!uncompiledMethod) {
87
0
    uncompiledMethod = new nsXBLUncompiledMethod();
88
0
    SetUncompiledMethod(uncompiledMethod);
89
0
  }
90
0
91
0
  uncompiledMethod->SetLineNumber(aLineNumber);
92
0
}
93
94
nsresult
95
nsXBLProtoImplMethod::InstallMember(JSContext* aCx,
96
                                    JS::Handle<JSObject*> aTargetClassObject)
97
0
{
98
0
  MOZ_ASSERT(IsCompiled(),
99
0
                  "Should not be installing an uncompiled method");
100
0
  MOZ_ASSERT(js::IsObjectInContextCompartment(aTargetClassObject, aCx));
101
0
102
#ifdef DEBUG
103
  {
104
    JS::Rooted<JSObject*> globalObject(aCx, JS::GetNonCCWObjectGlobal(aTargetClassObject));
105
    MOZ_ASSERT(xpc::IsInContentXBLScope(globalObject) ||
106
               globalObject == xpc::GetXBLScope(aCx, globalObject));
107
    MOZ_ASSERT(JS::CurrentGlobalOrNull(aCx) == globalObject);
108
  }
109
#endif
110
111
0
  JS::Rooted<JSObject*> jsMethodObject(aCx, GetCompiledMethod());
112
0
  if (jsMethodObject) {
113
0
    nsDependentString name(mName);
114
0
115
0
    JS::Rooted<JSObject*> method(aCx, JS::CloneFunctionObject(aCx, jsMethodObject));
116
0
    NS_ENSURE_TRUE(method, NS_ERROR_OUT_OF_MEMORY);
117
0
118
0
    if (!::JS_DefineUCProperty(aCx, aTargetClassObject,
119
0
                               static_cast<const char16_t*>(mName),
120
0
                               name.Length(), method,
121
0
                               JSPROP_ENUMERATE)) {
122
0
      return NS_ERROR_OUT_OF_MEMORY;
123
0
    }
124
0
  }
125
0
  return NS_OK;
126
0
}
127
128
nsresult
129
nsXBLProtoImplMethod::CompileMember(AutoJSAPI& jsapi, const nsString& aClassStr,
130
                                    JS::Handle<JSObject*> aClassObject)
131
0
{
132
0
  AssertInCompilationScope();
133
0
  MOZ_ASSERT(!IsCompiled(),
134
0
                  "Trying to compile an already-compiled method");
135
0
  MOZ_ASSERT(aClassObject,
136
0
                  "Must have class object to compile");
137
0
138
0
  nsXBLUncompiledMethod* uncompiledMethod = GetUncompiledMethod();
139
0
140
0
  // No parameters or body was supplied, so don't install method.
141
0
  if (!uncompiledMethod) {
142
0
    // Early return after which we consider ourselves compiled.
143
0
    SetCompiledMethod(nullptr);
144
0
145
0
    return NS_OK;
146
0
  }
147
0
148
0
  // Don't install method if no name was supplied.
149
0
  if (!mName) {
150
0
    delete uncompiledMethod;
151
0
152
0
    // Early return after which we consider ourselves compiled.
153
0
    SetCompiledMethod(nullptr);
154
0
155
0
    return NS_OK;
156
0
  }
157
0
158
0
  // We have a method.
159
0
  // Allocate an array for our arguments.
160
0
  int32_t paramCount = uncompiledMethod->GetParameterCount();
161
0
  char** args = nullptr;
162
0
  if (paramCount > 0) {
163
0
    args = new char*[paramCount];
164
0
165
0
    // Add our parameters to our args array.
166
0
    int32_t argPos = 0;
167
0
    for (nsXBLParameter* curr = uncompiledMethod->mParameters;
168
0
         curr;
169
0
         curr = curr->mNext) {
170
0
      args[argPos] = curr->mName;
171
0
      argPos++;
172
0
    }
173
0
  }
174
0
175
0
  // Get the body
176
0
  nsDependentString body;
177
0
  char16_t *bodyText = uncompiledMethod->mBodyText.GetText();
178
0
  if (bodyText)
179
0
    body.Rebind(bodyText);
180
0
181
0
  // Now that we have a body and args, compile the function
182
0
  // and then define it.
183
0
  NS_ConvertUTF16toUTF8 cname(mName);
184
0
  NS_ConvertUTF16toUTF8 functionUri(aClassStr);
185
0
  int32_t hash = functionUri.RFindChar('#');
186
0
  if (hash != kNotFound) {
187
0
    functionUri.Truncate(hash);
188
0
  }
189
0
190
0
  JSContext *cx = jsapi.cx();
191
0
  JSAutoRealm ar(cx, aClassObject);
192
0
  JS::CompileOptions options(cx);
193
0
  options.setFileAndLine(functionUri.get(),
194
0
                         uncompiledMethod->mBodyText.GetLineNumber());
195
0
  JS::Rooted<JSObject*> methodObject(cx);
196
0
  JS::AutoObjectVector emptyVector(cx);
197
0
  nsresult rv = nsJSUtils::CompileFunction(jsapi, emptyVector, options, cname,
198
0
                                           paramCount,
199
0
                                           const_cast<const char**>(args),
200
0
                                           body, methodObject.address());
201
0
202
0
  // Destroy our uncompiled method and delete our arg list.
203
0
  delete uncompiledMethod;
204
0
  delete [] args;
205
0
  if (NS_FAILED(rv)) {
206
0
    SetUncompiledMethod(nullptr);
207
0
    return rv;
208
0
  }
209
0
210
0
  SetCompiledMethod(methodObject);
211
0
212
0
  return NS_OK;
213
0
}
214
215
void
216
nsXBLProtoImplMethod::Trace(const TraceCallbacks& aCallbacks, void *aClosure)
217
0
{
218
0
  if (IsCompiled() && GetCompiledMethodPreserveColor()) {
219
0
    aCallbacks.Trace(&mMethod.AsHeapObject(), "mMethod", aClosure);
220
0
  }
221
0
}
222
223
nsresult
224
nsXBLProtoImplMethod::Read(nsIObjectInputStream* aStream)
225
0
{
226
0
  AssertInCompilationScope();
227
0
  MOZ_ASSERT(!IsCompiled() && !GetUncompiledMethod());
228
0
229
0
  AutoJSContext cx;
230
0
  JS::Rooted<JSObject*> methodObject(cx);
231
0
  nsresult rv = XBL_DeserializeFunction(aStream, &methodObject);
232
0
  if (NS_FAILED(rv)) {
233
0
    SetUncompiledMethod(nullptr);
234
0
    return rv;
235
0
  }
236
0
237
0
  SetCompiledMethod(methodObject);
238
0
239
0
  return NS_OK;
240
0
}
241
242
nsresult
243
nsXBLProtoImplMethod::Write(nsIObjectOutputStream* aStream)
244
0
{
245
0
  AssertInCompilationScope();
246
0
  MOZ_ASSERT(IsCompiled());
247
0
  if (GetCompiledMethodPreserveColor()) {
248
0
    nsresult rv = aStream->Write8(XBLBinding_Serialize_Method);
249
0
    NS_ENSURE_SUCCESS(rv, rv);
250
0
251
0
    rv = aStream->WriteWStringZ(mName);
252
0
    NS_ENSURE_SUCCESS(rv, rv);
253
0
254
0
    JS::Rooted<JSObject*> method(RootingCx(), GetCompiledMethod());
255
0
    return XBL_SerializeFunction(aStream, method);
256
0
  }
257
0
258
0
  return NS_OK;
259
0
}
260
261
nsresult
262
nsXBLProtoImplAnonymousMethod::Execute(nsIContent* aBoundElement,
263
                                       const nsXBLPrototypeBinding& aProtoBinding)
264
0
{
265
0
  MOZ_ASSERT(aBoundElement->IsElement());
266
0
  MOZ_ASSERT(IsCompiled(), "Can't execute uncompiled method");
267
0
268
0
  if (!GetCompiledMethod()) {
269
0
    // Nothing to do here
270
0
    return NS_OK;
271
0
  }
272
0
273
0
  // Get the script context the same way
274
0
  // nsXBLProtoImpl::InstallImplementation does.
275
0
  nsIDocument* document = aBoundElement->OwnerDoc();
276
0
277
0
  nsCOMPtr<nsIGlobalObject> global =
278
0
    do_QueryInterface(document->GetInnerWindow());
279
0
  if (!global) {
280
0
    return NS_OK;
281
0
  }
282
0
283
0
  nsAutoMicroTask mt;
284
0
285
0
  // We are going to run script via JS::Call, so we need a script entry point,
286
0
  // but as this is XBL related it does not appear in the HTML spec.
287
0
  // We need an actual JSContext to do GetXBLScopeOrGlobal, and it needs to
288
0
  // be in the compartment of globalObject.  But we want our XBL execution scope
289
0
  // to be our entry global.
290
0
  AutoJSAPI jsapi;
291
0
  if (!jsapi.Init(global)) {
292
0
    return NS_ERROR_UNEXPECTED;
293
0
  }
294
0
295
0
  JS::Rooted<JSObject*> scopeObject(jsapi.cx(),
296
0
    xpc::GetXBLScopeOrGlobal(jsapi.cx(), global->GetGlobalJSObject()));
297
0
  NS_ENSURE_TRUE(scopeObject, NS_ERROR_OUT_OF_MEMORY);
298
0
299
0
  dom::AutoEntryScript aes(scopeObject,
300
0
                           "XBL <constructor>/<destructor> invocation",
301
0
                           true);
302
0
  JSContext* cx = aes.cx();
303
0
  JS::AutoObjectVector scopeChain(cx);
304
0
  if (!nsJSUtils::GetScopeChainForXBL(cx, aBoundElement->AsElement(),
305
0
                                      aProtoBinding,
306
0
                                      scopeChain)) {
307
0
    return NS_ERROR_OUT_OF_MEMORY;
308
0
  }
309
0
  MOZ_ASSERT(scopeChain.length() != 0);
310
0
311
0
  // Clone the function object, using our scope chain (for backwards
312
0
  // compat to the days when this was an event handler).
313
0
  JS::Rooted<JSObject*> jsMethodObject(cx, GetCompiledMethod());
314
0
  JS::Rooted<JSObject*> method(cx, JS::CloneFunctionObject(cx, jsMethodObject,
315
0
                                                           scopeChain));
316
0
  if (!method)
317
0
    return NS_ERROR_OUT_OF_MEMORY;
318
0
319
0
  // Now call the method
320
0
321
0
  // Check whether script is enabled.
322
0
  bool scriptAllowed = xpc::Scriptability::Get(method).Allowed();
323
0
324
0
  if (scriptAllowed) {
325
0
    JS::Rooted<JS::Value> retval(cx);
326
0
    JS::Rooted<JS::Value> methodVal(cx, JS::ObjectValue(*method));
327
0
    // No need to check the return here as AutoEntryScript has taken ownership
328
0
    // of error reporting.
329
0
    ::JS::Call(cx, scopeChain[0], methodVal, JS::HandleValueArray::empty(), &retval);
330
0
  }
331
0
332
0
  return NS_OK;
333
0
}
334
335
nsresult
336
nsXBLProtoImplAnonymousMethod::Write(nsIObjectOutputStream* aStream,
337
                                     XBLBindingSerializeDetails aType)
338
0
{
339
0
  AssertInCompilationScope();
340
0
  MOZ_ASSERT(IsCompiled());
341
0
  if (GetCompiledMethodPreserveColor()) {
342
0
    nsresult rv = aStream->Write8(aType);
343
0
    NS_ENSURE_SUCCESS(rv, rv);
344
0
345
0
    rv = aStream->WriteWStringZ(mName);
346
0
    NS_ENSURE_SUCCESS(rv, rv);
347
0
348
0
    JS::Rooted<JSObject*> method(RootingCx(), GetCompiledMethod());
349
0
    rv = XBL_SerializeFunction(aStream, method);
350
0
    NS_ENSURE_SUCCESS(rv, rv);
351
0
  }
352
0
353
0
  return NS_OK;
354
0
}