Back to index

lightning-sunbird  0.9+nobinonly
nsXBLProtoImpl.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 "nsXBLProtoImpl.h"
00040 #include "nsIContent.h"
00041 #include "nsIDocument.h"
00042 #include "nsContentUtils.h"
00043 #include "nsIScriptGlobalObject.h"
00044 #include "nsIScriptGlobalObjectOwner.h"
00045 #include "nsIScriptContext.h"
00046 #include "nsIXPConnect.h"
00047 #include "nsIServiceManager.h"
00048 #include "nsIXBLDocumentInfo.h"
00049 #include "nsIDOMNode.h"
00050 
00051 nsresult NS_DOMClassInfo_PreserveNodeWrapper(nsIXPConnectWrappedNative *aWrapper);
00052 
00053 nsresult
00054 nsXBLProtoImpl::InstallImplementation(nsXBLPrototypeBinding* aBinding, nsIContent* aBoundElement)
00055 {
00056   // This function is called to install a concrete implementation on a bound element using
00057   // this prototype implementation as a guide.  The prototype implementation is compiled lazily,
00058   // so for the first bound element that needs a concrete implementation, we also build the
00059   // prototype implementation.
00060   if (!mMembers)  // Constructor and destructor also live in mMembers
00061     return NS_OK; // Nothing to do, so let's not waste time.
00062 
00063   // If the way this gets the script context changes, fix
00064   // nsXBLProtoImplAnonymousMethod::Execute
00065   nsIDocument* document = aBoundElement->GetOwnerDoc();
00066   if (!document) return NS_OK;
00067 
00068   nsIScriptGlobalObject *global = document->GetScriptGlobalObject();
00069   if (!global) return NS_OK;
00070 
00071   nsCOMPtr<nsIScriptContext> context = global->GetContext();
00072   if (!context) return NS_OK;
00073 
00074   // InitTarget objects gives us back the JS object that represents the bound element and the
00075   // class object in the bound document that represents the concrete version of this implementation.
00076   // This function also has the side effect of building up the prototype implementation if it has
00077   // not been built already.
00078   nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
00079   void * targetClassObject = nsnull;
00080   nsresult rv = InitTargetObjects(aBinding, context, aBoundElement,
00081                                   getter_AddRefs(holder), &targetClassObject);
00082   NS_ENSURE_SUCCESS(rv, rv); // kick out if we were unable to properly intialize our target objects
00083 
00084   JSObject * targetScriptObject;
00085   holder->GetJSObject(&targetScriptObject);
00086 
00087   // Walk our member list and install each one in turn.
00088   for (nsXBLProtoImplMember* curr = mMembers;
00089        curr;
00090        curr = curr->GetNext())
00091     curr->InstallMember(context, aBoundElement, targetScriptObject,
00092                         targetClassObject, mClassName);
00093   return NS_OK;
00094 }
00095 
00096 nsresult 
00097 nsXBLProtoImpl::InitTargetObjects(nsXBLPrototypeBinding* aBinding,
00098                                   nsIScriptContext* aContext, 
00099                                   nsIContent* aBoundElement, 
00100                                   nsIXPConnectJSObjectHolder** aScriptObjectHolder, 
00101                                   void** aTargetClassObject)
00102 {
00103   nsresult rv = NS_OK;
00104   *aScriptObjectHolder = nsnull;
00105   
00106   if (!mClassObject) {
00107     rv = CompilePrototypeMembers(aBinding); // This is the first time we've ever installed this binding on an element.
00108                                  // We need to go ahead and compile all methods and properties on a class
00109                                  // in our prototype binding.
00110     if (NS_FAILED(rv))
00111       return rv;
00112 
00113     if (!mClassObject)
00114       return NS_OK; // This can be ok, if all we've got are fields (and no methods/properties).
00115   }
00116 
00117   nsIDocument *ownerDoc = aBoundElement->GetOwnerDoc();
00118   nsIScriptGlobalObject *sgo;
00119 
00120   if (!ownerDoc || !(sgo = ownerDoc->GetScriptGlobalObject())) {
00121     NS_ERROR("Can't find global object for bound content!");
00122 
00123     return NS_ERROR_UNEXPECTED;
00124   }
00125 
00126   // Because our prototype implementation has a class, we need to build up a corresponding
00127   // class for the concrete implementation in the bound document.
00128   JSContext* jscontext = (JSContext*)aContext->GetNativeContext();
00129   JSObject* global = sgo->GetGlobalJSObject();
00130   nsCOMPtr<nsIXPConnectJSObjectHolder> wrapper;
00131   rv = nsContentUtils::XPConnect()->WrapNative(jscontext, global,
00132                                                aBoundElement,
00133                                                NS_GET_IID(nsISupports),
00134                                                getter_AddRefs(wrapper));
00135   NS_ENSURE_SUCCESS(rv, rv);
00136   JSObject * object = nsnull;
00137   rv = wrapper->GetJSObject(&object);
00138   NS_ENSURE_SUCCESS(rv, rv);
00139 
00140   // All of the above code was just obtaining the bound element's script object and its immediate
00141   // concrete base class.  We need to alter the object so that our concrete class is interposed
00142   // between the object and its base class.  We become the new base class of the object, and the
00143   // object's old base class becomes the new class' base class.
00144   rv = aBinding->InitClass(mClassName, jscontext, global, object,
00145                            aTargetClassObject);
00146   if (NS_FAILED(rv))
00147     return rv;
00148 
00149   // Root ourselves in the document.
00150   nsIDocument* doc = aBoundElement->GetOwnerDoc();
00151   if (doc) {
00152     nsCOMPtr<nsIXPConnectWrappedNative> nativeWrapper(do_QueryInterface(wrapper));
00153     if (nativeWrapper) {
00154       NS_DOMClassInfo_PreserveNodeWrapper(nativeWrapper);
00155     }
00156   }
00157 
00158   wrapper.swap(*aScriptObjectHolder);
00159   
00160   return rv;
00161 }
00162 
00163 nsresult
00164 nsXBLProtoImpl::CompilePrototypeMembers(nsXBLPrototypeBinding* aBinding)
00165 {
00166   // We want to pre-compile our implementation's members against a "prototype context". Then when we actually 
00167   // bind the prototype to a real xbl instance, we'll clone the pre-compiled JS into the real instance's 
00168   // context.
00169   nsCOMPtr<nsIScriptGlobalObjectOwner> globalOwner(
00170       do_QueryInterface(aBinding->XBLDocumentInfo()));
00171   nsIScriptGlobalObject* globalObject = globalOwner->GetScriptGlobalObject();
00172   NS_ENSURE_TRUE(globalObject, NS_ERROR_UNEXPECTED);
00173 
00174   nsIScriptContext *context = globalObject->GetContext();
00175   NS_ENSURE_TRUE(context, NS_ERROR_OUT_OF_MEMORY);
00176   JSObject *global = globalObject->GetGlobalJSObject();
00177 
00178   void* classObject;
00179   nsresult rv = aBinding->InitClass(mClassName,
00180                                     (JSContext *)context->GetNativeContext(),
00181                                     global, global,
00182                                     &classObject);
00183   if (NS_FAILED(rv))
00184     return rv;
00185 
00186   mClassObject = (JSObject*) classObject;
00187   if (!mClassObject)
00188     return NS_ERROR_FAILURE;
00189 
00190   // Now that we have a class object installed, we walk our member list and compile each of our
00191   // properties and methods in turn.
00192   for (nsXBLProtoImplMember* curr = mMembers;
00193        curr;
00194        curr = curr->GetNext()) {
00195     nsresult rv = curr->CompileMember(context, mClassName, mClassObject);
00196     if (NS_FAILED(rv)) {
00197       DestroyMembers(curr);
00198       return rv;
00199     }
00200   }
00201   return NS_OK;
00202 }
00203 
00204 void
00205 nsXBLProtoImpl::DestroyMembers(nsXBLProtoImplMember* aBrokenMember)
00206 {
00207   NS_ASSERTION(mClassObject, "This should never be called when there is no class object");
00208   PRBool compiled = PR_TRUE;
00209   for (nsXBLProtoImplMember* curr = mMembers; curr; curr = curr->GetNext()) {
00210     if (curr == aBrokenMember) {
00211       compiled = PR_FALSE;
00212     }
00213     curr->Destroy(compiled);
00214   }
00215 
00216   // Now clear out mMembers so we don't try to call Destroy() on them again
00217   delete mMembers;
00218   mMembers = nsnull;
00219   mConstructor = nsnull;
00220   mDestructor = nsnull;
00221 }
00222 
00223 nsresult
00224 NS_NewXBLProtoImpl(nsXBLPrototypeBinding* aBinding, 
00225                    const PRUnichar* aClassName, 
00226                    nsXBLProtoImpl** aResult)
00227 {
00228   nsXBLProtoImpl* impl = new nsXBLProtoImpl();
00229   if (!impl)
00230     return NS_ERROR_OUT_OF_MEMORY;
00231   if (aClassName)
00232     impl->mClassName.AssignWithConversion(aClassName);
00233   else
00234     aBinding->BindingURI()->GetSpec(impl->mClassName);
00235   aBinding->SetImplementation(impl);
00236   *aResult = impl;
00237 
00238   return NS_OK;
00239 }
00240