Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/xbl/nsXBLProtoImplProperty.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 "nsXBLProtoImplProperty.h"
12
#include "nsUnicharUtils.h"
13
#include "nsReadableUtils.h"
14
#include "nsJSUtils.h"
15
#include "nsXBLPrototypeBinding.h"
16
#include "nsXBLSerialize.h"
17
#include "xpcpublic.h"
18
19
using namespace mozilla;
20
using namespace mozilla::dom;
21
22
nsXBLProtoImplProperty::nsXBLProtoImplProperty(const char16_t* aName,
23
                                               const char16_t* aGetter,
24
                                               const char16_t* aSetter,
25
                                               const char16_t* aReadOnly,
26
                                               uint32_t aLineNumber) :
27
  nsXBLProtoImplMember(aName),
28
  mJSAttributes(JSPROP_ENUMERATE)
29
#ifdef DEBUG
30
  , mIsCompiled(false)
31
#endif
32
0
{
33
0
  MOZ_COUNT_CTOR(nsXBLProtoImplProperty);
34
0
35
0
  if (aReadOnly) {
36
0
    nsAutoString readOnly; readOnly.Assign(*aReadOnly);
37
0
    if (readOnly.LowerCaseEqualsLiteral("true"))
38
0
      mJSAttributes |= JSPROP_READONLY;
39
0
  }
40
0
41
0
  if (aGetter) {
42
0
    AppendGetterText(nsDependentString(aGetter));
43
0
    SetGetterLineNumber(aLineNumber);
44
0
  }
45
0
  if (aSetter) {
46
0
    AppendSetterText(nsDependentString(aSetter));
47
0
    SetSetterLineNumber(aLineNumber);
48
0
  }
49
0
}
50
51
nsXBLProtoImplProperty::nsXBLProtoImplProperty(const char16_t* aName,
52
                                               const bool aIsReadOnly)
53
  : nsXBLProtoImplMember(aName),
54
    mJSAttributes(JSPROP_ENUMERATE)
55
#ifdef DEBUG
56
  , mIsCompiled(false)
57
#endif
58
0
{
59
0
  MOZ_COUNT_CTOR(nsXBLProtoImplProperty);
60
0
61
0
  if (aIsReadOnly)
62
0
    mJSAttributes |= JSPROP_READONLY;
63
0
}
64
65
nsXBLProtoImplProperty::~nsXBLProtoImplProperty()
66
0
{
67
0
  MOZ_COUNT_DTOR(nsXBLProtoImplProperty);
68
0
69
0
  if (!mGetter.IsCompiled()) {
70
0
    delete mGetter.GetUncompiled();
71
0
  }
72
0
73
0
  if (!mSetter.IsCompiled()) {
74
0
    delete mSetter.GetUncompiled();
75
0
  }
76
0
}
77
78
void nsXBLProtoImplProperty::EnsureUncompiledText(PropertyOp& aPropertyOp)
79
0
{
80
0
  if (!aPropertyOp.GetUncompiled()) {
81
0
    nsXBLTextWithLineNumber* text = new nsXBLTextWithLineNumber();
82
0
    aPropertyOp.SetUncompiled(text);
83
0
  }
84
0
}
85
86
void
87
nsXBLProtoImplProperty::AppendGetterText(const nsAString& aText)
88
0
{
89
0
  MOZ_ASSERT(!mIsCompiled, "Must not be compiled when accessing getter text");
90
0
  EnsureUncompiledText(mGetter);
91
0
  mGetter.GetUncompiled()->AppendText(aText);
92
0
}
93
94
void
95
nsXBLProtoImplProperty::AppendSetterText(const nsAString& aText)
96
0
{
97
0
  MOZ_ASSERT(!mIsCompiled, "Must not be compiled when accessing setter text");
98
0
  EnsureUncompiledText(mSetter);
99
0
  mSetter.GetUncompiled()->AppendText(aText);
100
0
}
101
102
void
103
nsXBLProtoImplProperty::SetGetterLineNumber(uint32_t aLineNumber)
104
0
{
105
0
  MOZ_ASSERT(!mIsCompiled, "Must not be compiled when accessing getter text");
106
0
  EnsureUncompiledText(mGetter);
107
0
  mGetter.GetUncompiled()->SetLineNumber(aLineNumber);
108
0
}
109
110
void
111
nsXBLProtoImplProperty::SetSetterLineNumber(uint32_t aLineNumber)
112
0
{
113
0
  MOZ_ASSERT(!mIsCompiled, "Must not be compiled when accessing setter text");
114
0
  EnsureUncompiledText(mSetter);
115
0
  mSetter.GetUncompiled()->SetLineNumber(aLineNumber);
116
0
}
117
118
const char* gPropertyArgs[] = { "val" };
119
120
nsresult
121
nsXBLProtoImplProperty::InstallMember(JSContext *aCx,
122
                                      JS::Handle<JSObject*> aTargetClassObject)
123
0
{
124
0
  MOZ_ASSERT(mIsCompiled, "Should not be installing an uncompiled property");
125
0
  MOZ_ASSERT(mGetter.IsCompiled() && mSetter.IsCompiled());
126
0
  MOZ_ASSERT(js::IsObjectInContextCompartment(aTargetClassObject, aCx));
127
0
128
#ifdef DEBUG
129
  {
130
    JS::Rooted<JSObject*> globalObject(aCx, JS::GetNonCCWObjectGlobal(aTargetClassObject));
131
    MOZ_ASSERT(xpc::IsInContentXBLScope(globalObject) ||
132
               globalObject == xpc::GetXBLScope(aCx, globalObject));
133
    MOZ_ASSERT(JS::CurrentGlobalOrNull(aCx) == globalObject);
134
  }
135
#endif
136
137
0
  JS::Rooted<JSObject*> getter(aCx, mGetter.GetJSFunction());
138
0
  JS::Rooted<JSObject*> setter(aCx, mSetter.GetJSFunction());
139
0
  if (getter || setter) {
140
0
    if (getter) {
141
0
      if (!(getter = JS::CloneFunctionObject(aCx, getter)))
142
0
        return NS_ERROR_OUT_OF_MEMORY;
143
0
    }
144
0
145
0
    if (setter) {
146
0
      if (!(setter = JS::CloneFunctionObject(aCx, setter)))
147
0
        return NS_ERROR_OUT_OF_MEMORY;
148
0
    }
149
0
150
0
    nsDependentString name(mName);
151
0
    if (!::JS_DefineUCProperty(aCx, aTargetClassObject,
152
0
                               static_cast<const char16_t*>(mName),
153
0
                               name.Length(),
154
0
                               getter, setter,
155
0
                               mJSAttributes))
156
0
      return NS_ERROR_OUT_OF_MEMORY;
157
0
  }
158
0
  return NS_OK;
159
0
}
160
161
nsresult
162
nsXBLProtoImplProperty::CompileMember(AutoJSAPI& jsapi, const nsString& aClassStr,
163
                                      JS::Handle<JSObject*> aClassObject)
164
0
{
165
0
  AssertInCompilationScope();
166
0
  MOZ_ASSERT(!mIsCompiled, "Trying to compile an already-compiled property");
167
0
  MOZ_ASSERT(aClassObject, "Must have class object to compile");
168
0
  MOZ_ASSERT(!mGetter.IsCompiled() && !mSetter.IsCompiled());
169
0
  JSContext *cx = jsapi.cx();
170
0
171
0
  if (!mName)
172
0
    return NS_ERROR_FAILURE; // Without a valid name, we can't install the member.
173
0
174
0
  // We have a property.
175
0
  nsresult rv = NS_OK;
176
0
177
0
  nsAutoCString functionUri;
178
0
  if (mGetter.GetUncompiled() || mSetter.GetUncompiled()) {
179
0
    functionUri = NS_ConvertUTF16toUTF8(aClassStr);
180
0
    int32_t hash = functionUri.RFindChar('#');
181
0
    if (hash != kNotFound) {
182
0
      functionUri.Truncate(hash);
183
0
    }
184
0
  }
185
0
186
0
  bool deletedGetter = false;
187
0
  nsXBLTextWithLineNumber *getterText = mGetter.GetUncompiled();
188
0
  if (getterText && getterText->GetText()) {
189
0
    nsDependentString getter(getterText->GetText());
190
0
    if (!getter.IsEmpty()) {
191
0
      JSAutoRealm ar(cx, aClassObject);
192
0
      JS::CompileOptions options(cx);
193
0
      options.setFileAndLine(functionUri.get(), getterText->GetLineNumber());
194
0
      nsCString name = NS_LITERAL_CSTRING("get_") + NS_ConvertUTF16toUTF8(mName);
195
0
      JS::Rooted<JSObject*> getterObject(cx);
196
0
      JS::AutoObjectVector emptyVector(cx);
197
0
      rv = nsJSUtils::CompileFunction(jsapi, emptyVector, options, name, 0,
198
0
                                      nullptr, getter, getterObject.address());
199
0
200
0
      delete getterText;
201
0
      deletedGetter = true;
202
0
203
0
      mGetter.SetJSFunction(getterObject);
204
0
205
0
      if (mGetter.GetJSFunction() && NS_SUCCEEDED(rv)) {
206
0
        mJSAttributes |= JSPROP_GETTER;
207
0
      }
208
0
      if (NS_FAILED(rv)) {
209
0
        mGetter.SetJSFunction(nullptr);
210
0
        mJSAttributes &= ~JSPROP_GETTER;
211
0
        /*chaining to return failure*/
212
0
      }
213
0
    }
214
0
  } // if getter is not empty
215
0
216
0
  if (!deletedGetter) {  // Empty getter
217
0
    delete getterText;
218
0
    mGetter.SetJSFunction(nullptr);
219
0
  }
220
0
221
0
  if (NS_FAILED(rv)) {
222
0
    // We failed to compile our getter.  So either we've set it to null, or
223
0
    // it's still set to the text object.  In either case, it's safe to return
224
0
    // the error here, since then we'll be cleaned up as uncompiled and that
225
0
    // will be ok.  Going on and compiling the setter and _then_ returning an
226
0
    // error, on the other hand, will try to clean up a compiled setter as
227
0
    // uncompiled and crash.
228
0
    return rv;
229
0
  }
230
0
231
0
  bool deletedSetter = false;
232
0
  nsXBLTextWithLineNumber *setterText = mSetter.GetUncompiled();
233
0
  if (setterText && setterText->GetText()) {
234
0
    nsDependentString setter(setterText->GetText());
235
0
    if (!setter.IsEmpty()) {
236
0
      JSAutoRealm ar(cx, aClassObject);
237
0
      JS::CompileOptions options(cx);
238
0
      options.setFileAndLine(functionUri.get(), setterText->GetLineNumber());
239
0
      nsCString name = NS_LITERAL_CSTRING("set_") + NS_ConvertUTF16toUTF8(mName);
240
0
      JS::Rooted<JSObject*> setterObject(cx);
241
0
      JS::AutoObjectVector emptyVector(cx);
242
0
      rv = nsJSUtils::CompileFunction(jsapi, emptyVector, options, name, 1,
243
0
                                      gPropertyArgs, setter,
244
0
                                      setterObject.address());
245
0
246
0
      delete setterText;
247
0
      deletedSetter = true;
248
0
      mSetter.SetJSFunction(setterObject);
249
0
250
0
      if (mSetter.GetJSFunction() && NS_SUCCEEDED(rv)) {
251
0
        mJSAttributes |= JSPROP_SETTER;
252
0
      }
253
0
      if (NS_FAILED(rv)) {
254
0
        mSetter.SetJSFunction(nullptr);
255
0
        mJSAttributes &= ~JSPROP_SETTER;
256
0
        /*chaining to return failure*/
257
0
      }
258
0
    }
259
0
  } // if setter wasn't empty....
260
0
261
0
  if (!deletedSetter) {  // Empty setter
262
0
    delete setterText;
263
0
    mSetter.SetJSFunction(nullptr);
264
0
  }
265
0
266
#ifdef DEBUG
267
  mIsCompiled = NS_SUCCEEDED(rv);
268
#endif
269
270
0
  return rv;
271
0
}
272
273
void
274
nsXBLProtoImplProperty::Trace(const TraceCallbacks& aCallbacks, void *aClosure)
275
0
{
276
0
  if (mJSAttributes & JSPROP_GETTER) {
277
0
    aCallbacks.Trace(&mGetter.AsHeapObject(), "mGetter", aClosure);
278
0
  }
279
0
280
0
  if (mJSAttributes & JSPROP_SETTER) {
281
0
    aCallbacks.Trace(&mSetter.AsHeapObject(), "mSetter", aClosure);
282
0
  }
283
0
}
284
285
nsresult
286
nsXBLProtoImplProperty::Read(nsIObjectInputStream* aStream,
287
                             XBLBindingSerializeDetails aType)
288
0
{
289
0
  AssertInCompilationScope();
290
0
  MOZ_ASSERT(!mIsCompiled);
291
0
  MOZ_ASSERT(!mGetter.GetUncompiled() && !mSetter.GetUncompiled());
292
0
293
0
  AutoJSContext cx;
294
0
  JS::Rooted<JSObject*> getterObject(cx);
295
0
  if (aType == XBLBinding_Serialize_GetterProperty ||
296
0
      aType == XBLBinding_Serialize_GetterSetterProperty) {
297
0
    nsresult rv = XBL_DeserializeFunction(aStream, &getterObject);
298
0
    NS_ENSURE_SUCCESS(rv, rv);
299
0
300
0
    mJSAttributes |= JSPROP_GETTER;
301
0
  }
302
0
  mGetter.SetJSFunction(getterObject);
303
0
304
0
  JS::Rooted<JSObject*> setterObject(cx);
305
0
  if (aType == XBLBinding_Serialize_SetterProperty ||
306
0
      aType == XBLBinding_Serialize_GetterSetterProperty) {
307
0
    nsresult rv = XBL_DeserializeFunction(aStream, &setterObject);
308
0
    NS_ENSURE_SUCCESS(rv, rv);
309
0
310
0
    mJSAttributes |= JSPROP_SETTER;
311
0
  }
312
0
  mSetter.SetJSFunction(setterObject);
313
0
314
#ifdef DEBUG
315
  mIsCompiled = true;
316
#endif
317
318
0
  return NS_OK;
319
0
}
320
321
nsresult
322
nsXBLProtoImplProperty::Write(nsIObjectOutputStream* aStream)
323
0
{
324
0
  AssertInCompilationScope();
325
0
  XBLBindingSerializeDetails type;
326
0
327
0
  if (mJSAttributes & JSPROP_GETTER) {
328
0
    type = mJSAttributes & JSPROP_SETTER ?
329
0
           XBLBinding_Serialize_GetterSetterProperty :
330
0
           XBLBinding_Serialize_GetterProperty;
331
0
  }
332
0
  else {
333
0
    type = XBLBinding_Serialize_SetterProperty;
334
0
  }
335
0
336
0
  if (mJSAttributes & JSPROP_READONLY) {
337
0
    type |= XBLBinding_Serialize_ReadOnly;
338
0
  }
339
0
340
0
  nsresult rv = aStream->Write8(type);
341
0
  NS_ENSURE_SUCCESS(rv, rv);
342
0
  rv = aStream->WriteWStringZ(mName);
343
0
  NS_ENSURE_SUCCESS(rv, rv);
344
0
345
0
  MOZ_ASSERT_IF(mJSAttributes & (JSPROP_GETTER | JSPROP_SETTER), mIsCompiled);
346
0
347
0
  if (mJSAttributes & JSPROP_GETTER) {
348
0
    JS::Rooted<JSObject*> function(RootingCx(), mGetter.GetJSFunction());
349
0
    rv = XBL_SerializeFunction(aStream, function);
350
0
    NS_ENSURE_SUCCESS(rv, rv);
351
0
  }
352
0
353
0
  if (mJSAttributes & JSPROP_SETTER) {
354
0
    JS::Rooted<JSObject*> function(RootingCx(), mSetter.GetJSFunction());
355
0
    rv = XBL_SerializeFunction(aStream, function);
356
0
    NS_ENSURE_SUCCESS(rv, rv);
357
0
  }
358
0
359
0
  return NS_OK;
360
0
}