1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is mozilla.org code.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Netscape Communications Corporation.
19 : * Portions created by the Initial Developer are Copyright (C) 1998
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : *
24 : * Alternatively, the contents of this file may be used under the terms of
25 : * either of the GNU General Public License Version 2 or later (the "GPL"),
26 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 : * in which case the provisions of the GPL or the LGPL are applicable instead
28 : * of those above. If you wish to allow use of your version of this file only
29 : * under the terms of either the GPL or the LGPL, and not to allow others to
30 : * use your version of this file under the terms of the MPL, indicate your
31 : * decision by deleting the provisions above and replace them with the notice
32 : * and other provisions required by the GPL or the LGPL. If you do not delete
33 : * the provisions above, a recipient may use your version of this file under
34 : * the terms of any one of the MPL, the GPL or the LGPL.
35 : *
36 : * ***** END LICENSE BLOCK ***** */
37 : #include "nsJSEventListener.h"
38 : #include "nsJSUtils.h"
39 : #include "nsString.h"
40 : #include "nsReadableUtils.h"
41 : #include "nsIServiceManager.h"
42 : #include "nsIScriptSecurityManager.h"
43 : #include "nsIScriptContext.h"
44 : #include "nsIScriptGlobalObject.h"
45 : #include "nsIScriptRuntime.h"
46 : #include "nsIXPConnect.h"
47 : #include "nsIPrivateDOMEvent.h"
48 : #include "nsGUIEvent.h"
49 : #include "nsContentUtils.h"
50 : #include "nsDOMScriptObjectHolder.h"
51 : #include "nsIMutableArray.h"
52 : #include "nsVariant.h"
53 : #include "nsIDOMBeforeUnloadEvent.h"
54 : #include "nsGkAtoms.h"
55 : #include "nsIDOMEventTarget.h"
56 : #include "nsIJSContextStack.h"
57 : #include "xpcpublic.h"
58 : #include "nsJSEnvironment.h"
59 : #include "nsDOMJSUtils.h"
60 : #ifdef NS_DEBUG
61 :
62 : #include "nspr.h" // PR_fprintf
63 :
64 : class EventListenerCounter
65 : {
66 : public:
67 1487 : ~EventListenerCounter() {
68 1487 : }
69 : };
70 :
71 1464 : static EventListenerCounter sEventListenerCounter;
72 : #endif
73 :
74 : /*
75 : * nsJSEventListener implementation
76 : */
77 0 : nsJSEventListener::nsJSEventListener(nsIScriptContext *aContext,
78 : JSObject* aScopeObject,
79 : nsISupports *aTarget,
80 : nsIAtom* aType,
81 : JSObject *aHandler)
82 : : nsIJSEventListener(aContext, aScopeObject, aTarget, aHandler),
83 0 : mEventName(aType)
84 : {
85 : // aScopeObject is the inner window's JS object, which we need to lock
86 : // until we are done with it.
87 0 : NS_ASSERTION(aScopeObject && aContext,
88 : "EventListener with no context or scope?");
89 0 : nsContentUtils::HoldScriptObject(aContext->GetScriptTypeID(), this,
90 : &NS_CYCLE_COLLECTION_NAME(nsJSEventListener),
91 0 : aScopeObject, false);
92 0 : if (aHandler) {
93 0 : nsContentUtils::HoldScriptObject(aContext->GetScriptTypeID(), this,
94 : &NS_CYCLE_COLLECTION_NAME(nsJSEventListener),
95 0 : aHandler, true);
96 : }
97 0 : }
98 :
99 0 : nsJSEventListener::~nsJSEventListener()
100 : {
101 0 : if (mContext)
102 0 : nsContentUtils::DropScriptObjects(mContext->GetScriptTypeID(), this,
103 0 : &NS_CYCLE_COLLECTION_NAME(nsJSEventListener));
104 0 : }
105 :
106 1464 : NS_IMPL_CYCLE_COLLECTION_CLASS(nsJSEventListener)
107 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsJSEventListener)
108 0 : if (tmp->mContext) {
109 0 : if (tmp->mContext->GetScriptTypeID() == nsIProgrammingLanguage::JAVASCRIPT) {
110 0 : NS_DROP_JS_OBJECTS(tmp, nsJSEventListener);
111 : }
112 : else {
113 0 : nsContentUtils::DropScriptObjects(tmp->mContext->GetScriptTypeID(), tmp,
114 0 : &NS_CYCLE_COLLECTION_NAME(nsJSEventListener));
115 : }
116 0 : tmp->mScopeObject = nsnull;
117 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mContext)
118 : }
119 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
120 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsJSEventListener)
121 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mContext)
122 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
123 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
124 :
125 0 : NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsJSEventListener)
126 0 : NS_IMPL_CYCLE_COLLECTION_TRACE_MEMBER_CALLBACK(tmp->mContext->GetScriptTypeID(),
127 : mScopeObject)
128 0 : NS_IMPL_CYCLE_COLLECTION_TRACE_MEMBER_CALLBACK(tmp->mContext->GetScriptTypeID(),
129 : mHandler)
130 0 : NS_IMPL_CYCLE_COLLECTION_TRACE_END
131 :
132 0 : NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsJSEventListener)
133 0 : return tmp->IsBlackForCC();
134 : NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
135 :
136 0 : NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsJSEventListener)
137 0 : return tmp->IsBlackForCC();
138 : NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
139 :
140 0 : NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsJSEventListener)
141 0 : return tmp->IsBlackForCC();
142 : NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
143 :
144 0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsJSEventListener)
145 0 : NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener)
146 0 : NS_INTERFACE_MAP_ENTRY(nsIJSEventListener)
147 0 : NS_INTERFACE_MAP_ENTRY(nsISupports)
148 0 : NS_INTERFACE_MAP_END
149 :
150 0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(nsJSEventListener)
151 0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(nsJSEventListener)
152 :
153 : bool
154 0 : nsJSEventListener::IsBlackForCC()
155 : {
156 0 : if ((mContext && mContext->GetScriptTypeID() ==
157 0 : nsIProgrammingLanguage::JAVASCRIPT) &&
158 0 : (!mScopeObject || !xpc_IsGrayGCThing(mScopeObject)) &&
159 0 : (!mHandler || !xpc_IsGrayGCThing(mHandler))) {
160 : nsIScriptGlobalObject* sgo =
161 0 : static_cast<nsJSContext*>(mContext.get())->GetCachedGlobalObject();
162 0 : return sgo && sgo->IsBlackForCC();
163 : }
164 0 : return false;
165 : }
166 :
167 : nsresult
168 0 : nsJSEventListener::HandleEvent(nsIDOMEvent* aEvent)
169 : {
170 0 : nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(mTarget);
171 0 : if (!target || !mContext || !mHandler)
172 0 : return NS_ERROR_FAILURE;
173 :
174 : nsresult rv;
175 0 : nsCOMPtr<nsIMutableArray> iargv;
176 :
177 0 : bool handledScriptError = false;
178 0 : if (mEventName == nsGkAtoms::onerror) {
179 0 : nsCOMPtr<nsIPrivateDOMEvent> priv(do_QueryInterface(aEvent));
180 0 : NS_ENSURE_TRUE(priv, NS_ERROR_UNEXPECTED);
181 :
182 0 : nsEvent *event = priv->GetInternalNSEvent();
183 0 : if (event->message == NS_LOAD_ERROR &&
184 : event->eventStructType == NS_SCRIPT_ERROR_EVENT) {
185 : nsScriptErrorEvent *scriptEvent =
186 0 : static_cast<nsScriptErrorEvent*>(event);
187 : // Create a temp argv for the error event.
188 0 : iargv = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
189 0 : if (NS_FAILED(rv)) return rv;
190 : // Append the event args.
191 : nsCOMPtr<nsIWritableVariant>
192 0 : var(do_CreateInstance(NS_VARIANT_CONTRACTID, &rv));
193 0 : NS_ENSURE_SUCCESS(rv, rv);
194 0 : rv = var->SetAsWString(scriptEvent->errorMsg);
195 0 : NS_ENSURE_SUCCESS(rv, rv);
196 0 : rv = iargv->AppendElement(var, false);
197 0 : NS_ENSURE_SUCCESS(rv, rv);
198 : // filename
199 0 : var = do_CreateInstance(NS_VARIANT_CONTRACTID, &rv);
200 0 : NS_ENSURE_SUCCESS(rv, rv);
201 0 : rv = var->SetAsWString(scriptEvent->fileName);
202 0 : NS_ENSURE_SUCCESS(rv, rv);
203 0 : rv = iargv->AppendElement(var, false);
204 0 : NS_ENSURE_SUCCESS(rv, rv);
205 : // line number
206 0 : var = do_CreateInstance(NS_VARIANT_CONTRACTID, &rv);
207 0 : NS_ENSURE_SUCCESS(rv, rv);
208 0 : rv = var->SetAsUint32(scriptEvent->lineNr);
209 0 : NS_ENSURE_SUCCESS(rv, rv);
210 0 : rv = iargv->AppendElement(var, false);
211 0 : NS_ENSURE_SUCCESS(rv, rv);
212 :
213 0 : handledScriptError = true;
214 : }
215 : }
216 :
217 0 : if (!handledScriptError) {
218 0 : iargv = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
219 0 : if (NS_FAILED(rv)) return rv;
220 0 : NS_ENSURE_TRUE(iargv != nsnull, NS_ERROR_OUT_OF_MEMORY);
221 0 : rv = iargv->AppendElement(aEvent, false);
222 0 : NS_ENSURE_SUCCESS(rv, rv);
223 : }
224 :
225 : // mContext is the same context which event listener manager pushes
226 : // to JS context stack.
227 : #ifdef NS_DEBUG
228 0 : JSContext* cx = nsnull;
229 : nsCOMPtr<nsIJSContextStack> stack =
230 0 : do_GetService("@mozilla.org/js/xpc/ContextStack;1");
231 0 : NS_ASSERTION(stack && NS_SUCCEEDED(stack->Peek(&cx)) && cx &&
232 : GetScriptContextFromJSContext(cx) == mContext,
233 : "JSEventListener has wrong script context?");
234 : #endif
235 0 : nsCOMPtr<nsIVariant> vrv;
236 0 : xpc_UnmarkGrayObject(mScopeObject);
237 0 : xpc_UnmarkGrayObject(mHandler);
238 0 : rv = mContext->CallEventHandler(mTarget, mScopeObject, mHandler, iargv,
239 0 : getter_AddRefs(vrv));
240 :
241 0 : if (NS_SUCCEEDED(rv)) {
242 0 : PRUint16 dataType = nsIDataType::VTYPE_VOID;
243 0 : if (vrv)
244 0 : vrv->GetDataType(&dataType);
245 :
246 0 : if (mEventName == nsGkAtoms::onbeforeunload) {
247 0 : nsCOMPtr<nsIDOMBeforeUnloadEvent> beforeUnload = do_QueryInterface(aEvent);
248 0 : NS_ENSURE_STATE(beforeUnload);
249 :
250 0 : if (dataType != nsIDataType::VTYPE_VOID) {
251 0 : aEvent->PreventDefault();
252 0 : nsAutoString text;
253 0 : beforeUnload->GetReturnValue(text);
254 :
255 : // Set the text in the beforeUnload event as long as it wasn't
256 : // already set (through event.returnValue, which takes
257 : // precedence over a value returned from a JS function in IE)
258 0 : if ((dataType == nsIDataType::VTYPE_DOMSTRING ||
259 : dataType == nsIDataType::VTYPE_CHAR_STR ||
260 : dataType == nsIDataType::VTYPE_WCHAR_STR ||
261 : dataType == nsIDataType::VTYPE_STRING_SIZE_IS ||
262 : dataType == nsIDataType::VTYPE_WSTRING_SIZE_IS ||
263 : dataType == nsIDataType::VTYPE_CSTRING ||
264 : dataType == nsIDataType::VTYPE_ASTRING)
265 0 : && text.IsEmpty()) {
266 0 : vrv->GetAsDOMString(text);
267 0 : beforeUnload->SetReturnValue(text);
268 : }
269 : }
270 0 : } else if (dataType == nsIDataType::VTYPE_BOOL) {
271 : // If the handler returned false and its sense is not reversed,
272 : // or the handler returned true and its sense is reversed from
273 : // the usual (false means cancel), then prevent default.
274 : bool brv;
275 0 : if (NS_SUCCEEDED(vrv->GetAsBool(&brv)) &&
276 0 : brv == (mEventName == nsGkAtoms::onerror ||
277 0 : mEventName == nsGkAtoms::onmouseover)) {
278 0 : aEvent->PreventDefault();
279 : }
280 : }
281 : }
282 :
283 0 : return rv;
284 : }
285 :
286 : /* virtual */ void
287 0 : nsJSEventListener::SetHandler(JSObject *aHandler)
288 : {
289 : // Technically we should drop the old mHandler and hold the new
290 : // one... except for JS this is a no-op, and we're really not
291 : // pretending very hard to support anything else. And since we
292 : // can't in fact only drop one script object (we'd have to drop
293 : // mScope too, and then re-hold it), let's just not worry about it
294 : // all.
295 0 : mHandler = aHandler;
296 0 : }
297 :
298 : /*
299 : * Factory functions
300 : */
301 :
302 : nsresult
303 0 : NS_NewJSEventListener(nsIScriptContext* aContext, JSObject* aScopeObject,
304 : nsISupports*aTarget, nsIAtom* aEventType,
305 : JSObject* aHandler, nsIJSEventListener** aReturn)
306 : {
307 0 : NS_ENSURE_ARG(aEventType);
308 : nsJSEventListener* it =
309 : new nsJSEventListener(aContext, aScopeObject, aTarget, aEventType,
310 0 : aHandler);
311 0 : NS_ADDREF(*aReturn = it);
312 :
313 0 : return NS_OK;
314 4392 : }
|