Back to index

lightning-sunbird  0.9+nobinonly
nsXBLProtoImplProperty.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is Mozilla Communicator client code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   David Hyatt <hyatt@netscape.com> (Original Author)
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either of the GNU General Public License Version 2 or later (the "GPL"),
00027  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 #include "nsIAtom.h"
00040 #include "nsString.h"
00041 #include "jsapi.h"
00042 #include "nsIContent.h"
00043 #include "nsIDocument.h"
00044 #include "nsString.h"
00045 #include "nsXBLProtoImplProperty.h"
00046 #include "nsUnicharUtils.h"
00047 #include "nsReadableUtils.h"
00048 #include "nsIScriptContext.h"
00049 #include "nsIScriptGlobalObject.h"
00050 #include "nsContentUtils.h"
00051 
00052 MOZ_DECL_CTOR_COUNTER(nsXBLProtoImplProperty)
00053 
00054 nsXBLProtoImplProperty::nsXBLProtoImplProperty(const PRUnichar* aName,
00055                                                const PRUnichar* aGetter, 
00056                                                const PRUnichar* aSetter,
00057                                                const PRUnichar* aReadOnly) :
00058   nsXBLProtoImplMember(aName), 
00059   mGetterText(nsnull),
00060   mSetterText(nsnull),
00061   mJSAttributes(JSPROP_ENUMERATE)
00062 #ifdef DEBUG
00063   , mIsCompiled(PR_FALSE)
00064 #endif
00065 {
00066   MOZ_COUNT_CTOR(nsXBLProtoImplProperty);
00067 
00068   if (aReadOnly) {
00069     nsAutoString readOnly; readOnly.Assign(*aReadOnly);
00070     if (readOnly.LowerCaseEqualsLiteral("true"))
00071       mJSAttributes |= JSPROP_READONLY;
00072   }
00073 
00074   if (aGetter)
00075     AppendGetterText(nsDependentString(aGetter));
00076   if (aSetter)
00077     AppendSetterText(nsDependentString(aSetter));
00078 }
00079 
00080 nsXBLProtoImplProperty::~nsXBLProtoImplProperty()
00081 {
00082   MOZ_COUNT_DTOR(nsXBLProtoImplProperty);
00083 }
00084 
00085 void
00086 nsXBLProtoImplProperty::Destroy(PRBool aIsCompiled)
00087 {
00088   NS_PRECONDITION(aIsCompiled == mIsCompiled,
00089                   "Incorrect aIsCompiled in nsXBLProtoImplProperty::Destroy");
00090 
00091   if ((mJSAttributes & JSPROP_GETTER) && mJSGetterObject) {
00092     nsContentUtils::RemoveJSGCRoot(&mJSGetterObject);
00093   }
00094   else {
00095     delete mGetterText;
00096   }
00097 
00098   if ((mJSAttributes & JSPROP_SETTER) && mJSSetterObject) {
00099     nsContentUtils::RemoveJSGCRoot(&mJSSetterObject);
00100   }
00101   else {
00102     delete mSetterText;
00103   }
00104 
00105   mGetterText = mSetterText = nsnull;
00106 }
00107 
00108 void 
00109 nsXBLProtoImplProperty::AppendGetterText(const nsAString& aText)
00110 {
00111   NS_PRECONDITION(!mIsCompiled,
00112                   "Must not be compiled when accessing getter text");
00113   if (!mGetterText) {
00114     mGetterText = new nsXBLTextWithLineNumber();
00115     if (!mGetterText)
00116       return;
00117   }
00118 
00119   mGetterText->AppendText(aText);
00120 }
00121 
00122 void 
00123 nsXBLProtoImplProperty::AppendSetterText(const nsAString& aText)
00124 {
00125   NS_PRECONDITION(!mIsCompiled,
00126                   "Must not be compiled when accessing setter text");
00127   if (!mSetterText) {
00128     mSetterText = new nsXBLTextWithLineNumber();
00129     if (!mSetterText)
00130       return;
00131   }
00132 
00133   mSetterText->AppendText(aText);
00134 }
00135 
00136 void
00137 nsXBLProtoImplProperty::SetGetterLineNumber(PRUint32 aLineNumber)
00138 {
00139   NS_PRECONDITION(!mIsCompiled,
00140                   "Must not be compiled when accessing getter text");
00141   if (!mGetterText) {
00142     mGetterText = new nsXBLTextWithLineNumber();
00143     if (!mGetterText)
00144       return;
00145   }
00146 
00147   mGetterText->SetLineNumber(aLineNumber);
00148 }
00149 
00150 void
00151 nsXBLProtoImplProperty::SetSetterLineNumber(PRUint32 aLineNumber)
00152 {
00153   NS_PRECONDITION(!mIsCompiled,
00154                   "Must not be compiled when accessing setter text");
00155   if (!mSetterText) {
00156     mSetterText = new nsXBLTextWithLineNumber();
00157     if (!mSetterText)
00158       return;
00159   }
00160 
00161   mSetterText->SetLineNumber(aLineNumber);
00162 }
00163 
00164 const char* gPropertyArgs[] = { "val" };
00165 
00166 nsresult
00167 nsXBLProtoImplProperty::InstallMember(nsIScriptContext* aContext,
00168                                       nsIContent* aBoundElement, 
00169                                       void* aScriptObject,
00170                                       void* aTargetClassObject,
00171                                       const nsCString& aClassStr)
00172 {
00173   NS_PRECONDITION(mIsCompiled,
00174                   "Should not be installing an uncompiled property");
00175   JSContext* cx = (JSContext*) aContext->GetNativeContext();
00176 
00177   nsIDocument *ownerDoc = aBoundElement->GetOwnerDoc();
00178   nsIScriptGlobalObject *sgo;
00179 
00180   if (!ownerDoc || !(sgo = ownerDoc->GetScriptGlobalObject())) {
00181     NS_ERROR("Can't find global object for bound content!");
00182  
00183     return NS_ERROR_UNEXPECTED;
00184   }
00185 
00186   JSObject * scriptObject = (JSObject *) aScriptObject;
00187   NS_ASSERTION(scriptObject, "uh-oh, script Object should NOT be null or bad things will happen");
00188   if (!scriptObject)
00189     return NS_ERROR_FAILURE;
00190 
00191   JSObject * targetClassObject = (JSObject *) aTargetClassObject;
00192   JSObject * globalObject = sgo->GetGlobalJSObject();
00193 
00194   // now we want to reevaluate our property using aContext and the script object for this window...
00195   if ((mJSGetterObject || mJSSetterObject) && targetClassObject) {
00196     JSObject * getter = nsnull;
00197     if (mJSGetterObject)
00198       if (!(getter = ::JS_CloneFunctionObject(cx, mJSGetterObject, globalObject)))
00199         return NS_ERROR_OUT_OF_MEMORY;
00200 
00201     nsresult rv;
00202     nsAutoGCRoot getterroot(&getter, &rv);
00203     NS_ENSURE_SUCCESS(rv, rv);
00204     
00205     JSObject * setter = nsnull;
00206     if (mJSSetterObject)
00207       if (!(setter = ::JS_CloneFunctionObject(cx, mJSSetterObject, globalObject)))
00208         return NS_ERROR_OUT_OF_MEMORY;
00209 
00210     nsAutoGCRoot setterroot(&setter, &rv);
00211     NS_ENSURE_SUCCESS(rv, rv);
00212     
00213     nsDependentString name(mName);
00214     if (!::JS_DefineUCProperty(cx, targetClassObject,
00215                                NS_REINTERPRET_CAST(const jschar*, mName), 
00216                                name.Length(), JSVAL_VOID,  (JSPropertyOp) getter, 
00217                                (JSPropertyOp) setter, mJSAttributes))
00218       return NS_ERROR_OUT_OF_MEMORY;
00219   }
00220   return NS_OK;
00221 }
00222 
00223 nsresult 
00224 nsXBLProtoImplProperty::CompileMember(nsIScriptContext* aContext, const nsCString& aClassStr,
00225                                       void* aClassObject)
00226 {
00227   NS_PRECONDITION(!mIsCompiled,
00228                   "Trying to compile an already-compiled property");
00229   NS_PRECONDITION(aClassObject,
00230                   "Must have class object to compile");
00231 
00232   if (!mName)
00233     return NS_ERROR_FAILURE; // Without a valid name, we can't install the member.
00234 
00235   // We have a property.
00236   nsresult rv = NS_OK;
00237 
00238   nsCAutoString functionUri;
00239   if (mGetterText || mSetterText) {
00240     functionUri = aClassStr;
00241     PRInt32 hash = functionUri.RFindChar('#');
00242     if (hash != kNotFound) {
00243       functionUri.Truncate(hash);
00244     }
00245   }
00246 
00247   PRBool deletedGetter = PR_FALSE;
00248   if (mGetterText && mGetterText->GetText()) {
00249     nsDependentString getter(mGetterText->GetText());
00250     if (!getter.IsEmpty()) {
00251       // Compile into a temp object so we don't wipe out mGetterText
00252       JSObject* getterObject = nsnull;
00253       rv = aContext->CompileFunction(aClassObject,
00254                                      NS_LITERAL_CSTRING("get_") +
00255                                      NS_ConvertUCS2toUTF8(mName),
00256                                      0,
00257                                      nsnull,
00258                                      getter, 
00259                                      functionUri.get(),
00260                                      mGetterText->GetLineNumber(),
00261                                      PR_TRUE,
00262                                      (void **) &getterObject);
00263 
00264       // Make sure we free mGetterText here before setting mJSGetterObject, since
00265       // that'll overwrite mGetterText
00266       delete mGetterText;
00267       deletedGetter = PR_TRUE;
00268       mJSGetterObject = getterObject;
00269     
00270       if (mJSGetterObject && NS_SUCCEEDED(rv)) {
00271         mJSAttributes |= JSPROP_GETTER | JSPROP_SHARED;
00272         // Root the compiled prototype script object.
00273         rv = nsContentUtils::AddJSGCRoot(&mJSGetterObject,
00274                                          "nsXBLProtoImplProperty::mJSGetterObject");
00275       }
00276       if (NS_FAILED(rv)) {
00277         mJSGetterObject = nsnull;
00278         mJSAttributes &= ~JSPROP_GETTER;
00279         /*chaining to return failure*/
00280       }
00281     }
00282   } // if getter is not empty
00283 
00284   if (!deletedGetter) {  // Empty getter
00285     delete mGetterText;
00286     mJSGetterObject = nsnull;
00287   }
00288   
00289   if (NS_FAILED(rv)) {
00290     // We failed to compile our getter.  So either we've set it to null, or
00291     // it's still set to the text object.  In either case, it's safe to return
00292     // the error here, since then we'll be cleaned up as uncompiled and that
00293     // will be ok.  Going on and compiling the setter and _then_ returning an
00294     // error, on the other hand, will try to clean up a compiled setter as
00295     // uncompiled and crash.
00296     return rv;
00297   }
00298 
00299   PRBool deletedSetter = PR_FALSE;
00300   if (mSetterText && mSetterText->GetText()) {
00301     nsDependentString setter(mSetterText->GetText());
00302     if (!setter.IsEmpty()) {
00303       // Compile into a temp object so we don't wipe out mSetterText
00304       JSObject* setterObject = nsnull;
00305       rv = aContext->CompileFunction(aClassObject,
00306                                      NS_LITERAL_CSTRING("set_") +
00307                                      NS_ConvertUCS2toUTF8(mName),
00308                                      1,
00309                                      gPropertyArgs,
00310                                      setter, 
00311                                      functionUri.get(),
00312                                      mSetterText->GetLineNumber(),
00313                                      PR_TRUE,
00314                                      (void **) &setterObject);
00315 
00316       // Make sure we free mSetterText here before setting mJSGetterObject, since
00317       // that'll overwrite mSetterText
00318       delete mSetterText;
00319       deletedSetter = PR_TRUE;
00320       mJSSetterObject = setterObject;
00321 
00322       if (mJSSetterObject && NS_SUCCEEDED(rv)) {
00323         mJSAttributes |= JSPROP_SETTER | JSPROP_SHARED;
00324         // Root the compiled prototype script object.
00325         rv = nsContentUtils::AddJSGCRoot(&mJSSetterObject,
00326                                          "nsXBLProtoImplProperty::mJSSetterObject");
00327       }
00328       if (NS_FAILED(rv)) {
00329         mJSSetterObject = nsnull;
00330         mJSAttributes &= ~JSPROP_SETTER;
00331         /*chaining to return failure*/
00332       }
00333     }
00334   } // if setter wasn't empty....
00335 
00336   if (!deletedSetter) {  // Empty setter
00337     delete mSetterText;
00338     mJSSetterObject = nsnull;
00339   }
00340 
00341 #ifdef DEBUG
00342   mIsCompiled = NS_SUCCEEDED(rv);
00343 #endif
00344   
00345   return rv;
00346 }