|
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 #include "AccessCheck.h" |
|
7 #include "base/basictypes.h" |
|
8 #include "ipc/IPCMessageUtils.h" |
|
9 #include "mozilla/dom/Event.h" |
|
10 #include "mozilla/ContentEvents.h" |
|
11 #include "mozilla/DOMEventTargetHelper.h" |
|
12 #include "mozilla/EventStateManager.h" |
|
13 #include "mozilla/InternalMutationEvent.h" |
|
14 #include "mozilla/MiscEvents.h" |
|
15 #include "mozilla/MouseEvents.h" |
|
16 #include "mozilla/Preferences.h" |
|
17 #include "mozilla/TextEvents.h" |
|
18 #include "mozilla/TouchEvents.h" |
|
19 #include "nsContentUtils.h" |
|
20 #include "nsCOMPtr.h" |
|
21 #include "nsDeviceContext.h" |
|
22 #include "nsError.h" |
|
23 #include "nsGlobalWindow.h" |
|
24 #include "nsIFrame.h" |
|
25 #include "nsIContent.h" |
|
26 #include "nsIDocument.h" |
|
27 #include "nsIPresShell.h" |
|
28 #include "nsIScrollableFrame.h" |
|
29 #include "nsJSEnvironment.h" |
|
30 #include "nsLayoutUtils.h" |
|
31 #include "nsPIWindowRoot.h" |
|
32 |
|
33 namespace mozilla { |
|
34 namespace dom { |
|
35 |
|
36 namespace workers { |
|
37 extern bool IsCurrentThreadRunningChromeWorker(); |
|
38 } // namespace workers |
|
39 |
|
40 static char *sPopupAllowedEvents; |
|
41 |
|
42 Event::Event(EventTarget* aOwner, |
|
43 nsPresContext* aPresContext, |
|
44 WidgetEvent* aEvent) |
|
45 { |
|
46 ConstructorInit(aOwner, aPresContext, aEvent); |
|
47 } |
|
48 |
|
49 Event::Event(nsPIDOMWindow* aParent) |
|
50 { |
|
51 ConstructorInit(static_cast<nsGlobalWindow *>(aParent), nullptr, nullptr); |
|
52 } |
|
53 |
|
54 void |
|
55 Event::ConstructorInit(EventTarget* aOwner, |
|
56 nsPresContext* aPresContext, |
|
57 WidgetEvent* aEvent) |
|
58 { |
|
59 SetIsDOMBinding(); |
|
60 SetOwner(aOwner); |
|
61 mIsMainThreadEvent = mOwner || NS_IsMainThread(); |
|
62 if (mIsMainThreadEvent) { |
|
63 nsJSContext::LikelyShortLivingObjectCreated(); |
|
64 } |
|
65 |
|
66 mPrivateDataDuplicated = false; |
|
67 |
|
68 if (aEvent) { |
|
69 mEvent = aEvent; |
|
70 mEventIsInternal = false; |
|
71 } |
|
72 else { |
|
73 mEventIsInternal = true; |
|
74 /* |
|
75 A derived class might want to allocate its own type of aEvent |
|
76 (derived from WidgetEvent). To do this, it should take care to pass |
|
77 a non-nullptr aEvent to this ctor, e.g.: |
|
78 |
|
79 FooEvent::FooEvent(..., WidgetEvent* aEvent) |
|
80 : Event(..., aEvent ? aEvent : new WidgetEvent()) |
|
81 |
|
82 Then, to override the mEventIsInternal assignments done by the |
|
83 base ctor, it should do this in its own ctor: |
|
84 |
|
85 FooEvent::FooEvent(..., WidgetEvent* aEvent) |
|
86 ... |
|
87 { |
|
88 ... |
|
89 if (aEvent) { |
|
90 mEventIsInternal = false; |
|
91 } |
|
92 else { |
|
93 mEventIsInternal = true; |
|
94 } |
|
95 ... |
|
96 } |
|
97 */ |
|
98 mEvent = new WidgetEvent(false, 0); |
|
99 mEvent->time = PR_Now(); |
|
100 } |
|
101 |
|
102 InitPresContextData(aPresContext); |
|
103 } |
|
104 |
|
105 void |
|
106 Event::InitPresContextData(nsPresContext* aPresContext) |
|
107 { |
|
108 mPresContext = aPresContext; |
|
109 // Get the explicit original target (if it's anonymous make it null) |
|
110 { |
|
111 nsCOMPtr<nsIContent> content = GetTargetFromFrame(); |
|
112 mExplicitOriginalTarget = content; |
|
113 if (content && content->IsInAnonymousSubtree()) { |
|
114 mExplicitOriginalTarget = nullptr; |
|
115 } |
|
116 } |
|
117 } |
|
118 |
|
119 Event::~Event() |
|
120 { |
|
121 NS_ASSERT_OWNINGTHREAD(Event); |
|
122 |
|
123 if (mEventIsInternal && mEvent) { |
|
124 delete mEvent; |
|
125 } |
|
126 } |
|
127 |
|
128 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Event) |
|
129 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY |
|
130 NS_INTERFACE_MAP_ENTRY(nsISupports) |
|
131 NS_INTERFACE_MAP_ENTRY(nsIDOMEvent) |
|
132 NS_INTERFACE_MAP_END |
|
133 |
|
134 NS_IMPL_CYCLE_COLLECTING_ADDREF(Event) |
|
135 NS_IMPL_CYCLE_COLLECTING_RELEASE(Event) |
|
136 |
|
137 NS_IMPL_CYCLE_COLLECTION_CLASS(Event) |
|
138 |
|
139 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(Event) |
|
140 NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER |
|
141 NS_IMPL_CYCLE_COLLECTION_TRACE_END |
|
142 |
|
143 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Event) |
|
144 if (tmp->mEventIsInternal) { |
|
145 tmp->mEvent->target = nullptr; |
|
146 tmp->mEvent->currentTarget = nullptr; |
|
147 tmp->mEvent->originalTarget = nullptr; |
|
148 switch (tmp->mEvent->eventStructType) { |
|
149 case NS_MOUSE_EVENT: |
|
150 case NS_MOUSE_SCROLL_EVENT: |
|
151 case NS_WHEEL_EVENT: |
|
152 case NS_SIMPLE_GESTURE_EVENT: |
|
153 case NS_POINTER_EVENT: |
|
154 tmp->mEvent->AsMouseEventBase()->relatedTarget = nullptr; |
|
155 break; |
|
156 case NS_DRAG_EVENT: { |
|
157 WidgetDragEvent* dragEvent = tmp->mEvent->AsDragEvent(); |
|
158 dragEvent->dataTransfer = nullptr; |
|
159 dragEvent->relatedTarget = nullptr; |
|
160 break; |
|
161 } |
|
162 case NS_CLIPBOARD_EVENT: |
|
163 tmp->mEvent->AsClipboardEvent()->clipboardData = nullptr; |
|
164 break; |
|
165 case NS_MUTATION_EVENT: |
|
166 tmp->mEvent->AsMutationEvent()->mRelatedNode = nullptr; |
|
167 break; |
|
168 case NS_FOCUS_EVENT: |
|
169 tmp->mEvent->AsFocusEvent()->relatedTarget = nullptr; |
|
170 break; |
|
171 default: |
|
172 break; |
|
173 } |
|
174 } |
|
175 NS_IMPL_CYCLE_COLLECTION_UNLINK(mPresContext); |
|
176 NS_IMPL_CYCLE_COLLECTION_UNLINK(mExplicitOriginalTarget); |
|
177 NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwner); |
|
178 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER |
|
179 NS_IMPL_CYCLE_COLLECTION_UNLINK_END |
|
180 |
|
181 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Event) |
|
182 if (tmp->mEventIsInternal) { |
|
183 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEvent->target) |
|
184 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEvent->currentTarget) |
|
185 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEvent->originalTarget) |
|
186 switch (tmp->mEvent->eventStructType) { |
|
187 case NS_MOUSE_EVENT: |
|
188 case NS_MOUSE_SCROLL_EVENT: |
|
189 case NS_WHEEL_EVENT: |
|
190 case NS_SIMPLE_GESTURE_EVENT: |
|
191 case NS_POINTER_EVENT: |
|
192 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mEvent->relatedTarget"); |
|
193 cb.NoteXPCOMChild(tmp->mEvent->AsMouseEventBase()->relatedTarget); |
|
194 break; |
|
195 case NS_DRAG_EVENT: { |
|
196 WidgetDragEvent* dragEvent = tmp->mEvent->AsDragEvent(); |
|
197 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mEvent->dataTransfer"); |
|
198 cb.NoteXPCOMChild(dragEvent->dataTransfer); |
|
199 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mEvent->relatedTarget"); |
|
200 cb.NoteXPCOMChild(dragEvent->relatedTarget); |
|
201 break; |
|
202 } |
|
203 case NS_CLIPBOARD_EVENT: |
|
204 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mEvent->clipboardData"); |
|
205 cb.NoteXPCOMChild(tmp->mEvent->AsClipboardEvent()->clipboardData); |
|
206 break; |
|
207 case NS_MUTATION_EVENT: |
|
208 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mEvent->mRelatedNode"); |
|
209 cb.NoteXPCOMChild(tmp->mEvent->AsMutationEvent()->mRelatedNode); |
|
210 break; |
|
211 case NS_FOCUS_EVENT: |
|
212 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mEvent->relatedTarget"); |
|
213 cb.NoteXPCOMChild(tmp->mEvent->AsFocusEvent()->relatedTarget); |
|
214 break; |
|
215 default: |
|
216 break; |
|
217 } |
|
218 } |
|
219 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPresContext) |
|
220 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mExplicitOriginalTarget) |
|
221 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwner) |
|
222 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS |
|
223 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END |
|
224 |
|
225 bool |
|
226 Event::IsChrome(JSContext* aCx) const |
|
227 { |
|
228 return mIsMainThreadEvent ? |
|
229 xpc::AccessCheck::isChrome(js::GetContextCompartment(aCx)) : |
|
230 mozilla::dom::workers::IsCurrentThreadRunningChromeWorker(); |
|
231 } |
|
232 |
|
233 // nsIDOMEventInterface |
|
234 NS_METHOD |
|
235 Event::GetType(nsAString& aType) |
|
236 { |
|
237 if (!mIsMainThreadEvent || !mEvent->typeString.IsEmpty()) { |
|
238 aType = mEvent->typeString; |
|
239 return NS_OK; |
|
240 } |
|
241 const char* name = GetEventName(mEvent->message); |
|
242 |
|
243 if (name) { |
|
244 CopyASCIItoUTF16(name, aType); |
|
245 return NS_OK; |
|
246 } else if (mEvent->message == NS_USER_DEFINED_EVENT && mEvent->userType) { |
|
247 aType = Substring(nsDependentAtomString(mEvent->userType), 2); // Remove "on" |
|
248 mEvent->typeString = aType; |
|
249 return NS_OK; |
|
250 } |
|
251 |
|
252 aType.Truncate(); |
|
253 return NS_OK; |
|
254 } |
|
255 |
|
256 static EventTarget* |
|
257 GetDOMEventTarget(nsIDOMEventTarget* aTarget) |
|
258 { |
|
259 return aTarget ? aTarget->GetTargetForDOMEvent() : nullptr; |
|
260 } |
|
261 |
|
262 EventTarget* |
|
263 Event::GetTarget() const |
|
264 { |
|
265 return GetDOMEventTarget(mEvent->target); |
|
266 } |
|
267 |
|
268 NS_METHOD |
|
269 Event::GetTarget(nsIDOMEventTarget** aTarget) |
|
270 { |
|
271 NS_IF_ADDREF(*aTarget = GetTarget()); |
|
272 return NS_OK; |
|
273 } |
|
274 |
|
275 EventTarget* |
|
276 Event::GetCurrentTarget() const |
|
277 { |
|
278 return GetDOMEventTarget(mEvent->currentTarget); |
|
279 } |
|
280 |
|
281 NS_IMETHODIMP |
|
282 Event::GetCurrentTarget(nsIDOMEventTarget** aCurrentTarget) |
|
283 { |
|
284 NS_IF_ADDREF(*aCurrentTarget = GetCurrentTarget()); |
|
285 return NS_OK; |
|
286 } |
|
287 |
|
288 // |
|
289 // Get the actual event target node (may have been retargeted for mouse events) |
|
290 // |
|
291 already_AddRefed<nsIContent> |
|
292 Event::GetTargetFromFrame() |
|
293 { |
|
294 if (!mPresContext) { return nullptr; } |
|
295 |
|
296 // Get the target frame (have to get the ESM first) |
|
297 nsIFrame* targetFrame = mPresContext->EventStateManager()->GetEventTarget(); |
|
298 if (!targetFrame) { return nullptr; } |
|
299 |
|
300 // get the real content |
|
301 nsCOMPtr<nsIContent> realEventContent; |
|
302 targetFrame->GetContentForEvent(mEvent, getter_AddRefs(realEventContent)); |
|
303 return realEventContent.forget(); |
|
304 } |
|
305 |
|
306 EventTarget* |
|
307 Event::GetExplicitOriginalTarget() const |
|
308 { |
|
309 if (mExplicitOriginalTarget) { |
|
310 return mExplicitOriginalTarget; |
|
311 } |
|
312 return GetTarget(); |
|
313 } |
|
314 |
|
315 NS_IMETHODIMP |
|
316 Event::GetExplicitOriginalTarget(nsIDOMEventTarget** aRealEventTarget) |
|
317 { |
|
318 NS_IF_ADDREF(*aRealEventTarget = GetExplicitOriginalTarget()); |
|
319 return NS_OK; |
|
320 } |
|
321 |
|
322 EventTarget* |
|
323 Event::GetOriginalTarget() const |
|
324 { |
|
325 if (mEvent->originalTarget) { |
|
326 return GetDOMEventTarget(mEvent->originalTarget); |
|
327 } |
|
328 |
|
329 return GetTarget(); |
|
330 } |
|
331 |
|
332 NS_IMETHODIMP |
|
333 Event::GetOriginalTarget(nsIDOMEventTarget** aOriginalTarget) |
|
334 { |
|
335 NS_IF_ADDREF(*aOriginalTarget = GetOriginalTarget()); |
|
336 return NS_OK; |
|
337 } |
|
338 |
|
339 NS_IMETHODIMP_(void) |
|
340 Event::SetTrusted(bool aTrusted) |
|
341 { |
|
342 mEvent->mFlags.mIsTrusted = aTrusted; |
|
343 } |
|
344 |
|
345 bool |
|
346 Event::Init(mozilla::dom::EventTarget* aGlobal) |
|
347 { |
|
348 if (!mIsMainThreadEvent) { |
|
349 return nsContentUtils::ThreadsafeIsCallerChrome(); |
|
350 } |
|
351 bool trusted = false; |
|
352 nsCOMPtr<nsPIDOMWindow> w = do_QueryInterface(aGlobal); |
|
353 if (w) { |
|
354 nsCOMPtr<nsIDocument> d = w->GetExtantDoc(); |
|
355 if (d) { |
|
356 trusted = nsContentUtils::IsChromeDoc(d); |
|
357 nsIPresShell* s = d->GetShell(); |
|
358 if (s) { |
|
359 InitPresContextData(s->GetPresContext()); |
|
360 } |
|
361 } |
|
362 } |
|
363 return trusted; |
|
364 } |
|
365 |
|
366 // static |
|
367 already_AddRefed<Event> |
|
368 Event::Constructor(const GlobalObject& aGlobal, |
|
369 const nsAString& aType, |
|
370 const EventInit& aParam, |
|
371 ErrorResult& aRv) |
|
372 { |
|
373 nsCOMPtr<mozilla::dom::EventTarget> t = do_QueryInterface(aGlobal.GetAsSupports()); |
|
374 nsRefPtr<Event> e = new Event(t, nullptr, nullptr); |
|
375 bool trusted = e->Init(t); |
|
376 aRv = e->InitEvent(aType, aParam.mBubbles, aParam.mCancelable); |
|
377 e->SetTrusted(trusted); |
|
378 return e.forget(); |
|
379 } |
|
380 |
|
381 uint16_t |
|
382 Event::EventPhase() const |
|
383 { |
|
384 // Note, remember to check that this works also |
|
385 // if or when Bug 235441 is fixed. |
|
386 if ((mEvent->currentTarget && |
|
387 mEvent->currentTarget == mEvent->target) || |
|
388 mEvent->mFlags.InTargetPhase()) { |
|
389 return nsIDOMEvent::AT_TARGET; |
|
390 } |
|
391 if (mEvent->mFlags.mInCapturePhase) { |
|
392 return nsIDOMEvent::CAPTURING_PHASE; |
|
393 } |
|
394 if (mEvent->mFlags.mInBubblingPhase) { |
|
395 return nsIDOMEvent::BUBBLING_PHASE; |
|
396 } |
|
397 return nsIDOMEvent::NONE; |
|
398 } |
|
399 |
|
400 NS_IMETHODIMP |
|
401 Event::GetEventPhase(uint16_t* aEventPhase) |
|
402 { |
|
403 *aEventPhase = EventPhase(); |
|
404 return NS_OK; |
|
405 } |
|
406 |
|
407 NS_IMETHODIMP |
|
408 Event::GetBubbles(bool* aBubbles) |
|
409 { |
|
410 *aBubbles = Bubbles(); |
|
411 return NS_OK; |
|
412 } |
|
413 |
|
414 NS_IMETHODIMP |
|
415 Event::GetCancelable(bool* aCancelable) |
|
416 { |
|
417 *aCancelable = Cancelable(); |
|
418 return NS_OK; |
|
419 } |
|
420 |
|
421 NS_IMETHODIMP |
|
422 Event::GetTimeStamp(uint64_t* aTimeStamp) |
|
423 { |
|
424 *aTimeStamp = TimeStamp(); |
|
425 return NS_OK; |
|
426 } |
|
427 |
|
428 NS_IMETHODIMP |
|
429 Event::StopPropagation() |
|
430 { |
|
431 mEvent->mFlags.mPropagationStopped = true; |
|
432 return NS_OK; |
|
433 } |
|
434 |
|
435 NS_IMETHODIMP |
|
436 Event::StopImmediatePropagation() |
|
437 { |
|
438 mEvent->mFlags.mPropagationStopped = true; |
|
439 mEvent->mFlags.mImmediatePropagationStopped = true; |
|
440 return NS_OK; |
|
441 } |
|
442 |
|
443 NS_IMETHODIMP |
|
444 Event::GetIsTrusted(bool* aIsTrusted) |
|
445 { |
|
446 *aIsTrusted = IsTrusted(); |
|
447 return NS_OK; |
|
448 } |
|
449 |
|
450 NS_IMETHODIMP |
|
451 Event::PreventDefault() |
|
452 { |
|
453 // This method is called only from C++ code which must handle default action |
|
454 // of this event. So, pass true always. |
|
455 PreventDefaultInternal(true); |
|
456 return NS_OK; |
|
457 } |
|
458 |
|
459 void |
|
460 Event::PreventDefault(JSContext* aCx) |
|
461 { |
|
462 MOZ_ASSERT(aCx, "JS context must be specified"); |
|
463 |
|
464 // Note that at handling default action, another event may be dispatched. |
|
465 // Then, JS in content mey be call preventDefault() |
|
466 // even in the event is in system event group. Therefore, don't refer |
|
467 // mInSystemGroup here. |
|
468 PreventDefaultInternal(IsChrome(aCx)); |
|
469 } |
|
470 |
|
471 void |
|
472 Event::PreventDefaultInternal(bool aCalledByDefaultHandler) |
|
473 { |
|
474 if (!mEvent->mFlags.mCancelable) { |
|
475 return; |
|
476 } |
|
477 |
|
478 mEvent->mFlags.mDefaultPrevented = true; |
|
479 |
|
480 // Note that even if preventDefault() has already been called by chrome, |
|
481 // a call of preventDefault() by content needs to overwrite |
|
482 // mDefaultPreventedByContent to true because in such case, defaultPrevented |
|
483 // must be true when web apps check it after they call preventDefault(). |
|
484 if (!aCalledByDefaultHandler) { |
|
485 mEvent->mFlags.mDefaultPreventedByContent = true; |
|
486 } |
|
487 |
|
488 if (!IsTrusted()) { |
|
489 return; |
|
490 } |
|
491 |
|
492 WidgetDragEvent* dragEvent = mEvent->AsDragEvent(); |
|
493 if (!dragEvent) { |
|
494 return; |
|
495 } |
|
496 |
|
497 nsCOMPtr<nsINode> node = do_QueryInterface(mEvent->currentTarget); |
|
498 if (!node) { |
|
499 nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(mEvent->currentTarget); |
|
500 if (!win) { |
|
501 return; |
|
502 } |
|
503 node = win->GetExtantDoc(); |
|
504 } |
|
505 if (!nsContentUtils::IsChromeDoc(node->OwnerDoc())) { |
|
506 dragEvent->mDefaultPreventedOnContent = true; |
|
507 } |
|
508 } |
|
509 |
|
510 void |
|
511 Event::SetEventType(const nsAString& aEventTypeArg) |
|
512 { |
|
513 if (mIsMainThreadEvent) { |
|
514 mEvent->userType = |
|
515 nsContentUtils::GetEventIdAndAtom(aEventTypeArg, mEvent->eventStructType, |
|
516 &(mEvent->message)); |
|
517 } else { |
|
518 mEvent->userType = nullptr; |
|
519 mEvent->message = NS_USER_DEFINED_EVENT; |
|
520 mEvent->typeString = aEventTypeArg; |
|
521 } |
|
522 } |
|
523 |
|
524 NS_IMETHODIMP |
|
525 Event::InitEvent(const nsAString& aEventTypeArg, |
|
526 bool aCanBubbleArg, |
|
527 bool aCancelableArg) |
|
528 { |
|
529 // Make sure this event isn't already being dispatched. |
|
530 NS_ENSURE_TRUE(!mEvent->mFlags.mIsBeingDispatched, NS_OK); |
|
531 |
|
532 if (IsTrusted()) { |
|
533 // Ensure the caller is permitted to dispatch trusted DOM events. |
|
534 if (!nsContentUtils::ThreadsafeIsCallerChrome()) { |
|
535 SetTrusted(false); |
|
536 } |
|
537 } |
|
538 |
|
539 SetEventType(aEventTypeArg); |
|
540 |
|
541 mEvent->mFlags.mBubbles = aCanBubbleArg; |
|
542 mEvent->mFlags.mCancelable = aCancelableArg; |
|
543 |
|
544 mEvent->mFlags.mDefaultPrevented = false; |
|
545 |
|
546 // Clearing the old targets, so that the event is targeted correctly when |
|
547 // re-dispatching it. |
|
548 mEvent->target = nullptr; |
|
549 mEvent->originalTarget = nullptr; |
|
550 return NS_OK; |
|
551 } |
|
552 |
|
553 NS_IMETHODIMP |
|
554 Event::DuplicatePrivateData() |
|
555 { |
|
556 NS_ASSERTION(mEvent, "No WidgetEvent for Event duplication!"); |
|
557 if (mEventIsInternal) { |
|
558 return NS_OK; |
|
559 } |
|
560 |
|
561 mEvent = mEvent->Duplicate(); |
|
562 mPresContext = nullptr; |
|
563 mEventIsInternal = true; |
|
564 mPrivateDataDuplicated = true; |
|
565 |
|
566 return NS_OK; |
|
567 } |
|
568 |
|
569 NS_IMETHODIMP |
|
570 Event::SetTarget(nsIDOMEventTarget* aTarget) |
|
571 { |
|
572 #ifdef DEBUG |
|
573 { |
|
574 nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(aTarget); |
|
575 |
|
576 NS_ASSERTION(!win || !win->IsInnerWindow(), |
|
577 "Uh, inner window set as event target!"); |
|
578 } |
|
579 #endif |
|
580 |
|
581 mEvent->target = do_QueryInterface(aTarget); |
|
582 return NS_OK; |
|
583 } |
|
584 |
|
585 NS_IMETHODIMP_(bool) |
|
586 Event::IsDispatchStopped() |
|
587 { |
|
588 return mEvent->mFlags.mPropagationStopped; |
|
589 } |
|
590 |
|
591 NS_IMETHODIMP_(WidgetEvent*) |
|
592 Event::GetInternalNSEvent() |
|
593 { |
|
594 return mEvent; |
|
595 } |
|
596 |
|
597 NS_IMETHODIMP_(Event*) |
|
598 Event::InternalDOMEvent() |
|
599 { |
|
600 return this; |
|
601 } |
|
602 |
|
603 // return true if eventName is contained within events, delimited by |
|
604 // spaces |
|
605 static bool |
|
606 PopupAllowedForEvent(const char *eventName) |
|
607 { |
|
608 if (!sPopupAllowedEvents) { |
|
609 Event::PopupAllowedEventsChanged(); |
|
610 |
|
611 if (!sPopupAllowedEvents) { |
|
612 return false; |
|
613 } |
|
614 } |
|
615 |
|
616 nsDependentCString events(sPopupAllowedEvents); |
|
617 |
|
618 nsAFlatCString::const_iterator start, end; |
|
619 nsAFlatCString::const_iterator startiter(events.BeginReading(start)); |
|
620 events.EndReading(end); |
|
621 |
|
622 while (startiter != end) { |
|
623 nsAFlatCString::const_iterator enditer(end); |
|
624 |
|
625 if (!FindInReadable(nsDependentCString(eventName), startiter, enditer)) |
|
626 return false; |
|
627 |
|
628 // the match is surrounded by spaces, or at a string boundary |
|
629 if ((startiter == start || *--startiter == ' ') && |
|
630 (enditer == end || *enditer == ' ')) { |
|
631 return true; |
|
632 } |
|
633 |
|
634 // Move on and see if there are other matches. (The delimitation |
|
635 // requirement makes it pointless to begin the next search before |
|
636 // the end of the invalid match just found.) |
|
637 startiter = enditer; |
|
638 } |
|
639 |
|
640 return false; |
|
641 } |
|
642 |
|
643 // static |
|
644 PopupControlState |
|
645 Event::GetEventPopupControlState(WidgetEvent* aEvent) |
|
646 { |
|
647 // generally if an event handler is running, new windows are disallowed. |
|
648 // check for exceptions: |
|
649 PopupControlState abuse = openAbused; |
|
650 |
|
651 switch(aEvent->eventStructType) { |
|
652 case NS_EVENT : |
|
653 // For these following events only allow popups if they're |
|
654 // triggered while handling user input. See |
|
655 // nsPresShell::HandleEventInternal() for details. |
|
656 if (EventStateManager::IsHandlingUserInput()) { |
|
657 switch(aEvent->message) { |
|
658 case NS_FORM_SELECTED : |
|
659 if (PopupAllowedForEvent("select")) { |
|
660 abuse = openControlled; |
|
661 } |
|
662 break; |
|
663 case NS_FORM_CHANGE : |
|
664 if (PopupAllowedForEvent("change")) { |
|
665 abuse = openControlled; |
|
666 } |
|
667 break; |
|
668 } |
|
669 } |
|
670 break; |
|
671 case NS_EDITOR_INPUT_EVENT : |
|
672 // For this following event only allow popups if it's triggered |
|
673 // while handling user input. See |
|
674 // nsPresShell::HandleEventInternal() for details. |
|
675 if (EventStateManager::IsHandlingUserInput()) { |
|
676 switch(aEvent->message) { |
|
677 case NS_EDITOR_INPUT: |
|
678 if (PopupAllowedForEvent("input")) { |
|
679 abuse = openControlled; |
|
680 } |
|
681 break; |
|
682 } |
|
683 } |
|
684 break; |
|
685 case NS_INPUT_EVENT : |
|
686 // For this following event only allow popups if it's triggered |
|
687 // while handling user input. See |
|
688 // nsPresShell::HandleEventInternal() for details. |
|
689 if (EventStateManager::IsHandlingUserInput()) { |
|
690 switch(aEvent->message) { |
|
691 case NS_FORM_CHANGE : |
|
692 if (PopupAllowedForEvent("change")) { |
|
693 abuse = openControlled; |
|
694 } |
|
695 break; |
|
696 case NS_XUL_COMMAND: |
|
697 abuse = openControlled; |
|
698 break; |
|
699 } |
|
700 } |
|
701 break; |
|
702 case NS_KEY_EVENT : |
|
703 if (aEvent->mFlags.mIsTrusted) { |
|
704 uint32_t key = aEvent->AsKeyboardEvent()->keyCode; |
|
705 switch(aEvent->message) { |
|
706 case NS_KEY_PRESS : |
|
707 // return key on focused button. see note at NS_MOUSE_CLICK. |
|
708 if (key == nsIDOMKeyEvent::DOM_VK_RETURN) { |
|
709 abuse = openAllowed; |
|
710 } else if (PopupAllowedForEvent("keypress")) { |
|
711 abuse = openControlled; |
|
712 } |
|
713 break; |
|
714 case NS_KEY_UP : |
|
715 // space key on focused button. see note at NS_MOUSE_CLICK. |
|
716 if (key == nsIDOMKeyEvent::DOM_VK_SPACE) { |
|
717 abuse = openAllowed; |
|
718 } else if (PopupAllowedForEvent("keyup")) { |
|
719 abuse = openControlled; |
|
720 } |
|
721 break; |
|
722 case NS_KEY_DOWN : |
|
723 if (PopupAllowedForEvent("keydown")) { |
|
724 abuse = openControlled; |
|
725 } |
|
726 break; |
|
727 } |
|
728 } |
|
729 break; |
|
730 case NS_TOUCH_EVENT : |
|
731 if (aEvent->mFlags.mIsTrusted) { |
|
732 switch (aEvent->message) { |
|
733 case NS_TOUCH_START : |
|
734 if (PopupAllowedForEvent("touchstart")) { |
|
735 abuse = openControlled; |
|
736 } |
|
737 break; |
|
738 case NS_TOUCH_END : |
|
739 if (PopupAllowedForEvent("touchend")) { |
|
740 abuse = openControlled; |
|
741 } |
|
742 break; |
|
743 } |
|
744 } |
|
745 break; |
|
746 case NS_MOUSE_EVENT : |
|
747 if (aEvent->mFlags.mIsTrusted && |
|
748 aEvent->AsMouseEvent()->button == WidgetMouseEvent::eLeftButton) { |
|
749 switch(aEvent->message) { |
|
750 case NS_MOUSE_BUTTON_UP : |
|
751 if (PopupAllowedForEvent("mouseup")) { |
|
752 abuse = openControlled; |
|
753 } |
|
754 break; |
|
755 case NS_MOUSE_BUTTON_DOWN : |
|
756 if (PopupAllowedForEvent("mousedown")) { |
|
757 abuse = openControlled; |
|
758 } |
|
759 break; |
|
760 case NS_MOUSE_CLICK : |
|
761 /* Click events get special treatment because of their |
|
762 historical status as a more legitimate event handler. If |
|
763 click popups are enabled in the prefs, clear the popup |
|
764 status completely. */ |
|
765 if (PopupAllowedForEvent("click")) { |
|
766 abuse = openAllowed; |
|
767 } |
|
768 break; |
|
769 case NS_MOUSE_DOUBLECLICK : |
|
770 if (PopupAllowedForEvent("dblclick")) { |
|
771 abuse = openControlled; |
|
772 } |
|
773 break; |
|
774 } |
|
775 } |
|
776 break; |
|
777 case NS_FORM_EVENT : |
|
778 // For these following events only allow popups if they're |
|
779 // triggered while handling user input. See |
|
780 // nsPresShell::HandleEventInternal() for details. |
|
781 if (EventStateManager::IsHandlingUserInput()) { |
|
782 switch(aEvent->message) { |
|
783 case NS_FORM_SUBMIT : |
|
784 if (PopupAllowedForEvent("submit")) { |
|
785 abuse = openControlled; |
|
786 } |
|
787 break; |
|
788 case NS_FORM_RESET : |
|
789 if (PopupAllowedForEvent("reset")) { |
|
790 abuse = openControlled; |
|
791 } |
|
792 break; |
|
793 } |
|
794 } |
|
795 break; |
|
796 default: |
|
797 break; |
|
798 } |
|
799 |
|
800 return abuse; |
|
801 } |
|
802 |
|
803 // static |
|
804 void |
|
805 Event::PopupAllowedEventsChanged() |
|
806 { |
|
807 if (sPopupAllowedEvents) { |
|
808 nsMemory::Free(sPopupAllowedEvents); |
|
809 } |
|
810 |
|
811 nsAdoptingCString str = Preferences::GetCString("dom.popup_allowed_events"); |
|
812 |
|
813 // We'll want to do this even if str is empty to avoid looking up |
|
814 // this pref all the time if it's not set. |
|
815 sPopupAllowedEvents = ToNewCString(str); |
|
816 } |
|
817 |
|
818 // static |
|
819 void |
|
820 Event::Shutdown() |
|
821 { |
|
822 if (sPopupAllowedEvents) { |
|
823 nsMemory::Free(sPopupAllowedEvents); |
|
824 } |
|
825 } |
|
826 |
|
827 nsIntPoint |
|
828 Event::GetScreenCoords(nsPresContext* aPresContext, |
|
829 WidgetEvent* aEvent, |
|
830 LayoutDeviceIntPoint aPoint) |
|
831 { |
|
832 if (!nsContentUtils::IsCallerChrome()) { |
|
833 // For non-chrome callers, return client coordinates instead. |
|
834 // For some events, the result will be zero; specifically, for dragend |
|
835 // events (there is no widget associated with dragend events, which |
|
836 // causes GetClientX() to return zero). Since dragend is for the drag |
|
837 // originator and not for the receiver, it is probably not widely used |
|
838 // (receivers get a drop event). Therefore, returning 0 should not break |
|
839 // many web pages. Also, a few years ago Firefox returned 0. |
|
840 // See: https://bugzilla.mozilla.org/show_bug.cgi?id=466379 |
|
841 CSSIntPoint clientCoords = GetClientCoords(aPresContext, aEvent, aPoint, CSSIntPoint(0, 0)); |
|
842 return nsIntPoint(clientCoords.x, clientCoords.y); |
|
843 } |
|
844 |
|
845 if (EventStateManager::sIsPointerLocked) { |
|
846 return EventStateManager::sLastScreenPoint; |
|
847 } |
|
848 |
|
849 if (!aEvent || |
|
850 (aEvent->eventStructType != NS_MOUSE_EVENT && |
|
851 aEvent->eventStructType != NS_MOUSE_SCROLL_EVENT && |
|
852 aEvent->eventStructType != NS_WHEEL_EVENT && |
|
853 aEvent->eventStructType != NS_POINTER_EVENT && |
|
854 aEvent->eventStructType != NS_TOUCH_EVENT && |
|
855 aEvent->eventStructType != NS_DRAG_EVENT && |
|
856 aEvent->eventStructType != NS_SIMPLE_GESTURE_EVENT)) { |
|
857 return nsIntPoint(0, 0); |
|
858 } |
|
859 |
|
860 WidgetGUIEvent* guiEvent = aEvent->AsGUIEvent(); |
|
861 if (!guiEvent->widget) { |
|
862 return LayoutDeviceIntPoint::ToUntyped(aPoint); |
|
863 } |
|
864 |
|
865 LayoutDeviceIntPoint offset = aPoint + |
|
866 LayoutDeviceIntPoint::FromUntyped(guiEvent->widget->WidgetToScreenOffset()); |
|
867 nscoord factor = aPresContext->DeviceContext()->UnscaledAppUnitsPerDevPixel(); |
|
868 return nsIntPoint(nsPresContext::AppUnitsToIntCSSPixels(offset.x * factor), |
|
869 nsPresContext::AppUnitsToIntCSSPixels(offset.y * factor)); |
|
870 } |
|
871 |
|
872 // static |
|
873 CSSIntPoint |
|
874 Event::GetPageCoords(nsPresContext* aPresContext, |
|
875 WidgetEvent* aEvent, |
|
876 LayoutDeviceIntPoint aPoint, |
|
877 CSSIntPoint aDefaultPoint) |
|
878 { |
|
879 CSSIntPoint pagePoint = |
|
880 Event::GetClientCoords(aPresContext, aEvent, aPoint, aDefaultPoint); |
|
881 |
|
882 // If there is some scrolling, add scroll info to client point. |
|
883 if (aPresContext && aPresContext->GetPresShell()) { |
|
884 nsIPresShell* shell = aPresContext->GetPresShell(); |
|
885 nsIScrollableFrame* scrollframe = shell->GetRootScrollFrameAsScrollable(); |
|
886 if (scrollframe) { |
|
887 pagePoint += CSSIntPoint::FromAppUnitsRounded(scrollframe->GetScrollPosition()); |
|
888 } |
|
889 } |
|
890 |
|
891 return pagePoint; |
|
892 } |
|
893 |
|
894 // static |
|
895 CSSIntPoint |
|
896 Event::GetClientCoords(nsPresContext* aPresContext, |
|
897 WidgetEvent* aEvent, |
|
898 LayoutDeviceIntPoint aPoint, |
|
899 CSSIntPoint aDefaultPoint) |
|
900 { |
|
901 if (EventStateManager::sIsPointerLocked) { |
|
902 return EventStateManager::sLastClientPoint; |
|
903 } |
|
904 |
|
905 if (!aEvent || |
|
906 (aEvent->eventStructType != NS_MOUSE_EVENT && |
|
907 aEvent->eventStructType != NS_MOUSE_SCROLL_EVENT && |
|
908 aEvent->eventStructType != NS_WHEEL_EVENT && |
|
909 aEvent->eventStructType != NS_TOUCH_EVENT && |
|
910 aEvent->eventStructType != NS_DRAG_EVENT && |
|
911 aEvent->eventStructType != NS_POINTER_EVENT && |
|
912 aEvent->eventStructType != NS_SIMPLE_GESTURE_EVENT) || |
|
913 !aPresContext || |
|
914 !aEvent->AsGUIEvent()->widget) { |
|
915 return aDefaultPoint; |
|
916 } |
|
917 |
|
918 nsIPresShell* shell = aPresContext->GetPresShell(); |
|
919 if (!shell) { |
|
920 return CSSIntPoint(0, 0); |
|
921 } |
|
922 |
|
923 nsIFrame* rootFrame = shell->GetRootFrame(); |
|
924 if (!rootFrame) { |
|
925 return CSSIntPoint(0, 0); |
|
926 } |
|
927 nsPoint pt = |
|
928 nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, |
|
929 LayoutDeviceIntPoint::ToUntyped(aPoint), rootFrame); |
|
930 |
|
931 return CSSIntPoint::FromAppUnitsRounded(pt); |
|
932 } |
|
933 |
|
934 // To be called ONLY by Event::GetType (which has the additional |
|
935 // logic for handling user-defined events). |
|
936 // static |
|
937 const char* |
|
938 Event::GetEventName(uint32_t aEventType) |
|
939 { |
|
940 switch(aEventType) { |
|
941 #define ID_TO_EVENT(name_, _id, _type, _struct) \ |
|
942 case _id: return #name_; |
|
943 #include "mozilla/EventNameList.h" |
|
944 #undef ID_TO_EVENT |
|
945 default: |
|
946 break; |
|
947 } |
|
948 // XXXldb We can hit this case for WidgetEvent objects that we didn't |
|
949 // create and that are not user defined events since this function and |
|
950 // SetEventType are incomplete. (But fixing that requires fixing the |
|
951 // arrays in nsEventListenerManager too, since the events for which |
|
952 // this is a problem generally *are* created by Event.) |
|
953 return nullptr; |
|
954 } |
|
955 |
|
956 bool |
|
957 Event::DefaultPrevented(JSContext* aCx) const |
|
958 { |
|
959 MOZ_ASSERT(aCx, "JS context must be specified"); |
|
960 |
|
961 NS_ENSURE_TRUE(mEvent, false); |
|
962 |
|
963 // If preventDefault() has never been called, just return false. |
|
964 if (!mEvent->mFlags.mDefaultPrevented) { |
|
965 return false; |
|
966 } |
|
967 |
|
968 // If preventDefault() has been called by content, return true. Otherwise, |
|
969 // i.e., preventDefault() has been called by chrome, return true only when |
|
970 // this is called by chrome. |
|
971 return mEvent->mFlags.mDefaultPreventedByContent || IsChrome(aCx); |
|
972 } |
|
973 |
|
974 bool |
|
975 Event::GetPreventDefault() const |
|
976 { |
|
977 if (mOwner) { |
|
978 if (nsIDocument* doc = mOwner->GetExtantDoc()) { |
|
979 doc->WarnOnceAbout(nsIDocument::eGetPreventDefault); |
|
980 } |
|
981 } |
|
982 // GetPreventDefault() is legacy and Gecko specific method. Although, |
|
983 // the result should be same as defaultPrevented, we don't need to break |
|
984 // backward compatibility of legacy method. Let's behave traditionally. |
|
985 return DefaultPrevented(); |
|
986 } |
|
987 |
|
988 NS_IMETHODIMP |
|
989 Event::GetPreventDefault(bool* aReturn) |
|
990 { |
|
991 NS_ENSURE_ARG_POINTER(aReturn); |
|
992 *aReturn = GetPreventDefault(); |
|
993 return NS_OK; |
|
994 } |
|
995 |
|
996 NS_IMETHODIMP |
|
997 Event::GetDefaultPrevented(bool* aReturn) |
|
998 { |
|
999 NS_ENSURE_ARG_POINTER(aReturn); |
|
1000 // This method must be called by only event handlers implemented by C++. |
|
1001 // Then, the handlers must handle default action. So, this method don't need |
|
1002 // to check if preventDefault() has been called by content or chrome. |
|
1003 *aReturn = DefaultPrevented(); |
|
1004 return NS_OK; |
|
1005 } |
|
1006 |
|
1007 NS_IMETHODIMP_(void) |
|
1008 Event::Serialize(IPC::Message* aMsg, bool aSerializeInterfaceType) |
|
1009 { |
|
1010 if (aSerializeInterfaceType) { |
|
1011 IPC::WriteParam(aMsg, NS_LITERAL_STRING("event")); |
|
1012 } |
|
1013 |
|
1014 nsString type; |
|
1015 GetType(type); |
|
1016 IPC::WriteParam(aMsg, type); |
|
1017 |
|
1018 IPC::WriteParam(aMsg, Bubbles()); |
|
1019 IPC::WriteParam(aMsg, Cancelable()); |
|
1020 IPC::WriteParam(aMsg, IsTrusted()); |
|
1021 |
|
1022 // No timestamp serialization for now! |
|
1023 } |
|
1024 |
|
1025 NS_IMETHODIMP_(bool) |
|
1026 Event::Deserialize(const IPC::Message* aMsg, void** aIter) |
|
1027 { |
|
1028 nsString type; |
|
1029 NS_ENSURE_TRUE(IPC::ReadParam(aMsg, aIter, &type), false); |
|
1030 |
|
1031 bool bubbles = false; |
|
1032 NS_ENSURE_TRUE(IPC::ReadParam(aMsg, aIter, &bubbles), false); |
|
1033 |
|
1034 bool cancelable = false; |
|
1035 NS_ENSURE_TRUE(IPC::ReadParam(aMsg, aIter, &cancelable), false); |
|
1036 |
|
1037 bool trusted = false; |
|
1038 NS_ENSURE_TRUE(IPC::ReadParam(aMsg, aIter, &trusted), false); |
|
1039 |
|
1040 nsresult rv = InitEvent(type, bubbles, cancelable); |
|
1041 NS_ENSURE_SUCCESS(rv, false); |
|
1042 SetTrusted(trusted); |
|
1043 |
|
1044 return true; |
|
1045 } |
|
1046 |
|
1047 NS_IMETHODIMP_(void) |
|
1048 Event::SetOwner(mozilla::dom::EventTarget* aOwner) |
|
1049 { |
|
1050 mOwner = nullptr; |
|
1051 |
|
1052 if (!aOwner) { |
|
1053 return; |
|
1054 } |
|
1055 |
|
1056 nsCOMPtr<nsINode> n = do_QueryInterface(aOwner); |
|
1057 if (n) { |
|
1058 mOwner = do_QueryInterface(n->OwnerDoc()->GetScopeObject()); |
|
1059 return; |
|
1060 } |
|
1061 |
|
1062 nsCOMPtr<nsPIDOMWindow> w = do_QueryInterface(aOwner); |
|
1063 if (w) { |
|
1064 if (w->IsOuterWindow()) { |
|
1065 mOwner = w->GetCurrentInnerWindow(); |
|
1066 } else { |
|
1067 mOwner.swap(w); |
|
1068 } |
|
1069 return; |
|
1070 } |
|
1071 |
|
1072 nsCOMPtr<DOMEventTargetHelper> eth = do_QueryInterface(aOwner); |
|
1073 if (eth) { |
|
1074 mOwner = eth->GetOwner(); |
|
1075 return; |
|
1076 } |
|
1077 |
|
1078 #ifdef DEBUG |
|
1079 nsCOMPtr<nsPIWindowRoot> root = do_QueryInterface(aOwner); |
|
1080 MOZ_ASSERT(root, "Unexpected EventTarget!"); |
|
1081 #endif |
|
1082 } |
|
1083 |
|
1084 } // namespace dom |
|
1085 } // namespace mozilla |
|
1086 |
|
1087 using namespace mozilla; |
|
1088 using namespace mozilla::dom; |
|
1089 |
|
1090 nsresult |
|
1091 NS_NewDOMEvent(nsIDOMEvent** aInstancePtrResult, |
|
1092 EventTarget* aOwner, |
|
1093 nsPresContext* aPresContext, |
|
1094 WidgetEvent* aEvent) |
|
1095 { |
|
1096 Event* it = new Event(aOwner, aPresContext, aEvent); |
|
1097 NS_ADDREF(it); |
|
1098 *aInstancePtrResult = static_cast<Event*>(it); |
|
1099 return NS_OK; |
|
1100 } |