Back to index

lightning-sunbird  0.9+nobinonly
nsJSEventListener.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; 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.org 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  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either of the GNU General Public License Version 2 or later (the "GPL"),
00026  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 #include "nsJSEventListener.h"
00038 #include "nsJSUtils.h"
00039 #include "nsString.h"
00040 #include "nsReadableUtils.h"
00041 #include "nsIServiceManager.h"
00042 #include "nsIJSContextStack.h"
00043 #include "nsIScriptSecurityManager.h"
00044 #include "nsIScriptContext.h"
00045 #include "nsIXPConnect.h"
00046 #include "nsIPrivateDOMEvent.h"
00047 #include "nsGUIEvent.h"
00048 #include "nsContentUtils.h"
00049 
00050 
00051 /*
00052  * nsJSEventListener implementation
00053  */
00054 nsJSEventListener::nsJSEventListener(nsIScriptContext *aContext,
00055                                      JSObject *aScopeObject,
00056                                      nsISupports *aObject)
00057   : nsIJSEventListener(aContext, aScopeObject, aObject),
00058     mReturnResult(nsReturnResult_eNotSet)
00059 {
00060   if (aScopeObject && aContext) {
00061     JSContext *cx = (JSContext *)aContext->GetNativeContext();
00062 
00063     ::JS_LockGCThing(cx, aScopeObject);
00064   }
00065 }
00066 
00067 nsJSEventListener::~nsJSEventListener() 
00068 {
00069   if (mScopeObject && mContext) {
00070     JSContext *cx = (JSContext *)mContext->GetNativeContext();
00071 
00072     ::JS_UnlockGCThing(cx, mScopeObject);
00073   }
00074 }
00075 
00076 NS_INTERFACE_MAP_BEGIN(nsJSEventListener)
00077   NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener)
00078   NS_INTERFACE_MAP_ENTRY(nsIJSEventListener)
00079   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMEventListener)
00080 NS_INTERFACE_MAP_END
00081 
00082 NS_IMPL_ADDREF(nsJSEventListener)
00083 
00084 NS_IMPL_RELEASE(nsJSEventListener)
00085 
00086 //static nsString onPrefix = "on";
00087 
00088 void
00089 nsJSEventListener::SetEventName(nsIAtom* aName)
00090 {
00091   mEventName = aName;
00092 }
00093 
00094 nsresult
00095 nsJSEventListener::HandleEvent(nsIDOMEvent* aEvent)
00096 {
00097   jsval funval;
00098   jsval arg;
00099   jsval *argv = &arg;
00100   PRInt32 argc = 0;
00101   void *stackPtr; // For JS_[Push|Pop]Arguments()
00102   nsAutoString eventString;
00103   // XXX This doesn't seem like the correct context on which to execute
00104   // the event handler. Might need to get one from the JS thread context
00105   // stack.
00106   JSContext* cx = (JSContext*)mContext->GetNativeContext();
00107 
00108   if (!mEventName) {
00109     if (NS_OK != aEvent->GetType(eventString)) {
00110       //JS can't handle this event yet or can't handle it at all
00111       return NS_OK;
00112     }
00113     //if (mReturnResult == nsReturnResult_eNotSet) {
00114       if (eventString.EqualsLiteral("error") ||
00115           eventString.EqualsLiteral("mouseover")) {
00116         mReturnResult = nsReturnResult_eReverseReturnResult;
00117       }
00118       else {
00119         mReturnResult = nsReturnResult_eDoNotReverseReturnResult;
00120       }
00121     //}
00122     eventString.Assign(NS_LITERAL_STRING("on") + eventString);
00123   }
00124   else {
00125     mEventName->ToString(eventString);
00126   }
00127 
00128   nsresult rv;
00129   nsIXPConnect *xpc = nsContentUtils::XPConnect();
00130 
00131   // root
00132   nsCOMPtr<nsIXPConnectJSObjectHolder> wrapper;
00133   rv = xpc->WrapNative(cx, mScopeObject, mTarget, NS_GET_IID(nsISupports),
00134                        getter_AddRefs(wrapper));
00135   NS_ENSURE_SUCCESS(rv, rv);
00136 
00137   JSObject* obj = nsnull;
00138   rv = wrapper->GetJSObject(&obj);
00139   NS_ENSURE_SUCCESS(rv, rv);
00140 
00141   if (!JS_LookupUCProperty(cx, obj,
00142                            NS_REINTERPRET_CAST(const jschar *,
00143                                                eventString.get()),
00144                            eventString.Length(), &funval)) {
00145     return NS_ERROR_FAILURE;
00146   }
00147 
00148   if (JS_TypeOfValue(cx, funval) != JSTYPE_FUNCTION) {
00149     return NS_OK;
00150   }
00151 
00152   PRBool handledScriptError = PR_FALSE;
00153   if (eventString.EqualsLiteral("onerror")) {
00154     nsCOMPtr<nsIPrivateDOMEvent> priv(do_QueryInterface(aEvent));
00155     NS_ENSURE_TRUE(priv, NS_ERROR_UNEXPECTED);
00156 
00157     nsEvent* event;
00158     priv->GetInternalNSEvent(&event);
00159     if (event->message == NS_SCRIPT_ERROR) {
00160       nsScriptErrorEvent *scriptEvent =
00161         NS_STATIC_CAST(nsScriptErrorEvent*, event);
00162 
00163       argv = ::JS_PushArguments(cx, &stackPtr, "WWi", scriptEvent->errorMsg,
00164                                 scriptEvent->fileName, scriptEvent->lineNr);
00165       NS_ENSURE_TRUE(argv, NS_ERROR_OUT_OF_MEMORY);
00166       argc = 3;
00167       handledScriptError = PR_TRUE;
00168     }
00169   }
00170 
00171   if (!handledScriptError) {
00172     rv = xpc->WrapNative(cx, obj, aEvent, NS_GET_IID(nsIDOMEvent),
00173                          getter_AddRefs(wrapper));
00174     NS_ENSURE_SUCCESS(rv, rv);
00175 
00176     JSObject *eventObj = nsnull;
00177     rv = wrapper->GetJSObject(&eventObj);
00178     NS_ENSURE_SUCCESS(rv, rv);
00179 
00180     argv[0] = OBJECT_TO_JSVAL(eventObj);
00181     argc = 1;
00182   }
00183 
00184   jsval rval;
00185   rv = mContext->CallEventHandler(obj, JSVAL_TO_OBJECT(funval), argc, argv,
00186                                   &rval);
00187 
00188   if (argv != &arg) {
00189     ::JS_PopArguments(cx, stackPtr);
00190   }
00191 
00192   if (NS_SUCCEEDED(rv)) {
00193     if (eventString.EqualsLiteral("onbeforeunload")) {
00194       nsCOMPtr<nsIPrivateDOMEvent> priv(do_QueryInterface(aEvent));
00195       NS_ENSURE_TRUE(priv, NS_ERROR_UNEXPECTED);
00196 
00197       nsEvent* event;
00198       priv->GetInternalNSEvent(&event);
00199       NS_ENSURE_TRUE(event && event->message == NS_BEFORE_PAGE_UNLOAD,
00200                      NS_ERROR_UNEXPECTED);
00201 
00202       nsBeforePageUnloadEvent *beforeUnload =
00203         NS_STATIC_CAST(nsBeforePageUnloadEvent *, event);
00204 
00205       if (!JSVAL_IS_VOID(rval)) {
00206         aEvent->PreventDefault();
00207 
00208         // Set the text in the beforeUnload event as long as it wasn't
00209         // already set (through event.returnValue, which takes
00210         // precedence over a value returned from a JS function in IE)
00211         if (JSVAL_IS_STRING(rval) && beforeUnload->text.IsEmpty()) {
00212           beforeUnload->text = nsDependentJSString(JSVAL_TO_STRING(rval));
00213         }
00214       }
00215     } else if (JSVAL_IS_BOOLEAN(rval)) {
00216       // If the handler returned false and its sense is not reversed,
00217       // or the handler returned true and its sense is reversed from
00218       // the usual (false means cancel), then prevent default.
00219 
00220       if (JSVAL_TO_BOOLEAN(rval) ==
00221           (mReturnResult == nsReturnResult_eReverseReturnResult)) {
00222         aEvent->PreventDefault();
00223       }
00224     }
00225   }
00226 
00227   return rv;
00228 }
00229 
00230 /*
00231  * Factory functions
00232  */
00233 
00234 nsresult
00235 NS_NewJSEventListener(nsIScriptContext *aContext, JSObject *aScopeObject,
00236                       nsISupports *aObject, nsIDOMEventListener ** aReturn)
00237 {
00238   nsJSEventListener* it =
00239     new nsJSEventListener(aContext, aScopeObject, aObject);
00240   if (!it) {
00241     return NS_ERROR_OUT_OF_MEMORY;
00242   }
00243 
00244   NS_ADDREF(*aReturn = it);
00245 
00246   return NS_OK;
00247 }
00248