dom/xbl/nsXBLProtoImplProperty.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial