dom/xbl/nsXBLProtoImplProperty.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial