Back to index

lightning-sunbird  0.9+nobinonly
nsXBLProtoImplMethod.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 "nsIScriptGlobalObject.h"
00045 #include "nsString.h"
00046 #include "nsUnicharUtils.h"
00047 #include "nsReadableUtils.h"
00048 #include "nsXBLProtoImplMethod.h"
00049 #include "nsIScriptContext.h"
00050 #include "nsContentUtils.h"
00051 #include "nsIScriptSecurityManager.h"
00052 
00053 MOZ_DECL_CTOR_COUNTER(nsXBLProtoImplMethod)
00054 
00055 nsXBLProtoImplMethod::nsXBLProtoImplMethod(const PRUnichar* aName) :
00056   nsXBLProtoImplMember(aName), 
00057   mUncompiledMethod(nsnull)
00058 #ifdef DEBUG
00059   , mIsCompiled(PR_FALSE)
00060 #endif
00061 {
00062   MOZ_COUNT_CTOR(nsXBLProtoImplMethod);
00063 }
00064 
00065 nsXBLProtoImplMethod::~nsXBLProtoImplMethod()
00066 {
00067   MOZ_COUNT_DTOR(nsXBLProtoImplMethod);
00068 }
00069 
00070 void
00071 nsXBLProtoImplMethod::Destroy(PRBool aIsCompiled)
00072 {
00073   NS_PRECONDITION(aIsCompiled == mIsCompiled,
00074                   "Incorrect aIsCompiled in nsXBLProtoImplMethod::Destroy");
00075   if (aIsCompiled) {
00076     if (mJSMethodObject)
00077       nsContentUtils::RemoveJSGCRoot(&mJSMethodObject);
00078     mJSMethodObject = nsnull;
00079   }
00080   else {
00081     delete mUncompiledMethod;
00082     mUncompiledMethod = nsnull;
00083   }
00084 }
00085 
00086 void 
00087 nsXBLProtoImplMethod::AppendBodyText(const nsAString& aText)
00088 {
00089   NS_PRECONDITION(!mIsCompiled,
00090                   "Must not be compiled when accessing uncompiled method");
00091   if (!mUncompiledMethod) {
00092     mUncompiledMethod = new nsXBLUncompiledMethod();
00093     if (!mUncompiledMethod)
00094       return;
00095   }
00096 
00097   mUncompiledMethod->AppendBodyText(aText);
00098 }
00099 
00100 void 
00101 nsXBLProtoImplMethod::AddParameter(const nsAString& aText)
00102 {
00103   NS_PRECONDITION(!mIsCompiled,
00104                   "Must not be compiled when accessing uncompiled method");
00105   if (!mUncompiledMethod) {
00106     mUncompiledMethod = new nsXBLUncompiledMethod();
00107     if (!mUncompiledMethod)
00108       return;
00109   }
00110 
00111   mUncompiledMethod->AddParameter(aText);
00112 }
00113 
00114 void
00115 nsXBLProtoImplMethod::SetLineNumber(PRUint32 aLineNumber)
00116 {
00117   NS_PRECONDITION(!mIsCompiled,
00118                   "Must not be compiled when accessing uncompiled method");
00119   if (!mUncompiledMethod) {
00120     mUncompiledMethod = new nsXBLUncompiledMethod();
00121     if (!mUncompiledMethod)
00122       return;
00123   }
00124 
00125   mUncompiledMethod->SetLineNumber(aLineNumber);
00126 }
00127 
00128 nsresult
00129 nsXBLProtoImplMethod::InstallMember(nsIScriptContext* aContext,
00130                                     nsIContent* aBoundElement, 
00131                                     void* aScriptObject,
00132                                     void* aTargetClassObject,
00133                                     const nsCString& aClassStr)
00134 {
00135   NS_PRECONDITION(mIsCompiled,
00136                   "Should not be installing an uncompiled method");
00137   JSContext* cx = (JSContext*) aContext->GetNativeContext();
00138 
00139   nsIDocument *ownerDoc = aBoundElement->GetOwnerDoc();
00140   nsIScriptGlobalObject *sgo;
00141 
00142   if (!ownerDoc || !(sgo = ownerDoc->GetScriptGlobalObject())) {
00143     NS_ERROR("Can't find global object for bound content!");
00144  
00145     return NS_ERROR_UNEXPECTED;
00146   }
00147 
00148   JSObject * scriptObject = (JSObject *) aScriptObject;
00149   NS_ASSERTION(scriptObject, "uh-oh, script Object should NOT be null or bad things will happen");
00150   if (!scriptObject)
00151     return NS_ERROR_FAILURE;
00152 
00153   JSObject * targetClassObject = (JSObject *) aTargetClassObject;
00154   JSObject * globalObject = sgo->GetGlobalJSObject();
00155 
00156   // now we want to reevaluate our property using aContext and the script object for this window...
00157   if (mJSMethodObject && targetClassObject) {
00158     nsDependentString name(mName);
00159     JSObject * method = ::JS_CloneFunctionObject(cx, mJSMethodObject, globalObject);
00160     if (!method) {
00161       return NS_ERROR_OUT_OF_MEMORY;
00162     }
00163 
00164     nsresult rv;
00165     nsAutoGCRoot root(&method, &rv);
00166     NS_ENSURE_SUCCESS(rv, rv);
00167     
00168     if (!::JS_DefineUCProperty(cx, targetClassObject,
00169                                NS_REINTERPRET_CAST(const jschar*, mName), 
00170                                name.Length(), OBJECT_TO_JSVAL(method),
00171                                NULL, NULL, JSPROP_ENUMERATE)) {
00172       return NS_ERROR_OUT_OF_MEMORY;
00173     }
00174   }
00175   return NS_OK;
00176 }
00177 
00178 nsresult 
00179 nsXBLProtoImplMethod::CompileMember(nsIScriptContext* aContext, const nsCString& aClassStr,
00180                                     void* aClassObject)
00181 {
00182   NS_PRECONDITION(!mIsCompiled,
00183                   "Trying to compile an already-compiled method");
00184   NS_PRECONDITION(aClassObject,
00185                   "Must have class object to compile");
00186 
00187 #ifdef DEBUG
00188   // We have some "ok" early returns after which we consider ourselves compiled
00189   mIsCompiled = PR_TRUE;
00190 #endif
00191   
00192   // No parameters or body was supplied, so don't install method.
00193   if (!mUncompiledMethod)
00194     return NS_OK;
00195 
00196   // Don't install method if no name or body was supplied.
00197   if (!(mName && mUncompiledMethod->mBodyText.GetText())) {
00198     delete mUncompiledMethod;
00199     mUncompiledMethod = nsnull;
00200     return NS_OK;
00201   }
00202 
00203   nsDependentString body(mUncompiledMethod->mBodyText.GetText());
00204   if (body.IsEmpty()) {
00205     delete mUncompiledMethod;
00206     mUncompiledMethod = nsnull;
00207     return NS_OK;
00208   }
00209 
00210 #ifdef DEBUG
00211   // OK, now we have some error early returns that mean we're not
00212   // really compiled...
00213   mIsCompiled = PR_FALSE;
00214 #endif
00215 
00216   // We have a method.
00217   // Allocate an array for our arguments.
00218   PRInt32 paramCount = mUncompiledMethod->GetParameterCount();
00219   char** args = nsnull;
00220   if (paramCount > 0) {
00221     args = new char*[paramCount];
00222     if (!args)
00223       return NS_ERROR_OUT_OF_MEMORY;
00224   }
00225 
00226   // Add our parameters to our args array.
00227   PRInt32 argPos = 0; 
00228   for (nsXBLParameter* curr = mUncompiledMethod->mParameters; 
00229        curr; 
00230        curr = curr->mNext) {
00231     args[argPos] = curr->mName;
00232     argPos++;
00233   }
00234 
00235   // Now that we have a body and args, compile the function
00236   // and then define it.
00237   NS_ConvertUCS2toUTF8 cname(mName);
00238   nsCAutoString functionUri(aClassStr);
00239   PRInt32 hash = functionUri.RFindChar('#');
00240   if (hash != kNotFound) {
00241     functionUri.Truncate(hash);
00242   }
00243 
00244   JSObject* methodObject = nsnull;
00245   nsresult rv = aContext->CompileFunction(aClassObject,
00246                                           cname,
00247                                           paramCount,
00248                                           (const char**)args,
00249                                           body, 
00250                                           functionUri.get(),
00251                                           mUncompiledMethod->mBodyText.GetLineNumber(),
00252                                           PR_TRUE,
00253                                           (void **) &methodObject);
00254 
00255   // Destroy our uncompiled method and delete our arg list.
00256   delete mUncompiledMethod;
00257   delete [] args;
00258   if (NS_FAILED(rv)) {
00259     mUncompiledMethod = nsnull;
00260     return rv;
00261   }
00262 
00263   mJSMethodObject = methodObject;
00264 
00265   if (methodObject) {
00266     // Root the compiled prototype script object.
00267     rv = nsContentUtils::AddJSGCRoot(&mJSMethodObject,
00268                                      "nsXBLProtoImplMethod::mJSMethodObject");
00269     if (NS_FAILED(rv)) {
00270       mJSMethodObject = nsnull;
00271     }
00272   }
00273   
00274 #ifdef DEBUG
00275   mIsCompiled = NS_SUCCEEDED(rv);
00276 #endif
00277   return rv;
00278 }
00279 
00280 nsresult
00281 nsXBLProtoImplAnonymousMethod::Execute(nsIContent* aBoundElement)
00282 {
00283   NS_PRECONDITION(mIsCompiled, "Can't execute uncompiled method");
00284   
00285   if (!mJSMethodObject) {
00286     // Nothing to do here
00287     return NS_OK;
00288   }
00289 
00290   // Get the script context the same way
00291   // nsXBLProtoImpl::InstallImplementation does.
00292   nsIDocument* document = aBoundElement->GetOwnerDoc();
00293   if (!document) {
00294     return NS_OK;
00295   }
00296 
00297   nsIScriptGlobalObject* global = document->GetScriptGlobalObject();
00298   if (!global) {
00299     return NS_OK;
00300   }
00301 
00302   nsCOMPtr<nsIScriptContext> context = global->GetContext();
00303   if (!context) {
00304     return NS_OK;
00305   }
00306   
00307   JSContext* cx = (JSContext*) context->GetNativeContext();
00308 
00309   JSObject* globalObject = global->GetGlobalJSObject();
00310 
00311   nsCOMPtr<nsIXPConnectJSObjectHolder> wrapper;
00312   nsresult rv =
00313     nsContentUtils::XPConnect()->WrapNative(cx, globalObject,
00314                                             aBoundElement,
00315                                             NS_GET_IID(nsISupports),
00316                                             getter_AddRefs(wrapper));
00317   NS_ENSURE_SUCCESS(rv, rv);
00318 
00319   JSObject* thisObject;
00320   rv = wrapper->GetJSObject(&thisObject);
00321   NS_ENSURE_SUCCESS(rv, rv);
00322 
00323   // Clone the function object, using thisObject as the parent so "this" is in
00324   // the scope chain of the resulting function (for backwards compat to the
00325   // days when this was an event handler).
00326   JSObject* method = ::JS_CloneFunctionObject(cx, mJSMethodObject,
00327                                               thisObject);
00328   if (!method) {
00329     return NS_ERROR_OUT_OF_MEMORY;
00330   }
00331 
00332   // Now call the method
00333 
00334   // Use nsCxPusher to make sure we call ScriptEvaluated when we're done.
00335   nsCxPusher pusher;
00336   NS_ENSURE_STATE(pusher.Push(aBoundElement));
00337 
00338 
00339   // Check whether it's OK to call the method.
00340   rv = nsContentUtils::GetSecurityManager()->CheckFunctionAccess(cx, method, thisObject);
00341 
00342   JSBool ok = JS_TRUE;
00343   if (NS_SUCCEEDED(rv)) {
00344     jsval retval;
00345     ok = ::JS_CallFunctionValue(cx, thisObject, OBJECT_TO_JSVAL(method),
00346                                 0 /* argc */, nsnull /* argv */, &retval);
00347   }
00348 
00349   if (!ok) {
00350     // If a constructor or destructor threw an exception, it doesn't
00351     // stop anything else.  We just report it.
00352     ::JS_ReportPendingException(cx);
00353     return NS_ERROR_FAILURE;
00354   }
00355 
00356   return NS_OK;
00357 }