Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include "mozilla/Attributes.h"
8 : #include "mozilla/EventDispatcher.h"
9 : #include "mozilla/EventStateManager.h"
10 : #include "mozilla/EventStates.h"
11 : #include "mozilla/IMEStateManager.h"
12 : #include "mozilla/MiscEvents.h"
13 : #include "mozilla/MathAlgorithms.h"
14 : #include "mozilla/MouseEvents.h"
15 : #include "mozilla/TextComposition.h"
16 : #include "mozilla/TextEvents.h"
17 : #include "mozilla/TouchEvents.h"
18 : #include "mozilla/dom/ContentChild.h"
19 : #include "mozilla/dom/DragEvent.h"
20 : #include "mozilla/dom/Event.h"
21 : #include "mozilla/dom/TabChild.h"
22 : #include "mozilla/dom/TabParent.h"
23 : #include "mozilla/dom/UIEvent.h"
24 :
25 : #include "ContentEventHandler.h"
26 : #include "IMEContentObserver.h"
27 : #include "WheelHandlingHelper.h"
28 :
29 : #include "nsCOMPtr.h"
30 : #include "nsFocusManager.h"
31 : #include "nsIContent.h"
32 : #include "nsIContentInlines.h"
33 : #include "nsIDocument.h"
34 : #include "nsIFrame.h"
35 : #include "nsIWidget.h"
36 : #include "nsPresContext.h"
37 : #include "nsIPresShell.h"
38 : #include "nsGkAtoms.h"
39 : #include "nsIFormControl.h"
40 : #include "nsIComboboxControlFrame.h"
41 : #include "nsIScrollableFrame.h"
42 : #include "nsIDOMHTMLElement.h"
43 : #include "nsIDOMXULControlElement.h"
44 : #include "nsNameSpaceManager.h"
45 : #include "nsIBaseWindow.h"
46 : #include "nsISelection.h"
47 : #include "nsITextControlElement.h"
48 : #include "nsFrameSelection.h"
49 : #include "nsPIDOMWindow.h"
50 : #include "nsPIWindowRoot.h"
51 : #include "nsIWebNavigation.h"
52 : #include "nsIContentViewer.h"
53 : #include "nsFrameManager.h"
54 : #include "nsITabChild.h"
55 : #include "nsPluginFrame.h"
56 : #include "nsMenuPopupFrame.h"
57 :
58 : #include "nsIDOMXULElement.h"
59 : #include "nsIDOMKeyEvent.h"
60 : #include "nsIObserverService.h"
61 : #include "nsIDocShell.h"
62 : #include "nsIDOMWheelEvent.h"
63 : #include "nsIDOMUIEvent.h"
64 : #include "nsIMozBrowserFrame.h"
65 :
66 : #include "nsSubDocumentFrame.h"
67 : #include "nsLayoutUtils.h"
68 : #include "nsIInterfaceRequestorUtils.h"
69 : #include "nsUnicharUtils.h"
70 : #include "nsContentUtils.h"
71 :
72 : #include "imgIContainer.h"
73 : #include "nsIProperties.h"
74 : #include "nsISupportsPrimitives.h"
75 :
76 : #include "nsServiceManagerUtils.h"
77 : #include "nsITimer.h"
78 : #include "nsFontMetrics.h"
79 : #include "nsIDOMXULDocument.h"
80 : #include "nsIDragService.h"
81 : #include "nsIDragSession.h"
82 : #include "mozilla/dom/DataTransfer.h"
83 : #include "nsContentAreaDragDrop.h"
84 : #ifdef MOZ_XUL
85 : #include "nsTreeBodyFrame.h"
86 : #endif
87 : #include "nsIController.h"
88 : #include "nsICommandParams.h"
89 : #include "mozilla/Services.h"
90 : #include "mozilla/dom/ContentParent.h"
91 : #include "mozilla/dom/HTMLLabelElement.h"
92 :
93 : #include "mozilla/Preferences.h"
94 : #include "mozilla/LookAndFeel.h"
95 : #include "GeckoProfiler.h"
96 : #include "Units.h"
97 : #include "mozilla/layers/APZCTreeManager.h"
98 : #include "nsIObjectLoadingContent.h"
99 :
100 : #ifdef XP_MACOSX
101 : #import <ApplicationServices/ApplicationServices.h>
102 : #endif
103 :
104 : namespace mozilla {
105 :
106 : using namespace dom;
107 :
108 : //#define DEBUG_DOCSHELL_FOCUS
109 :
110 : #define NS_USER_INTERACTION_INTERVAL 5000 // ms
111 :
112 3 : static const LayoutDeviceIntPoint kInvalidRefPoint = LayoutDeviceIntPoint(-1,-1);
113 :
114 : static uint32_t gMouseOrKeyboardEventCounter = 0;
115 : static nsITimer* gUserInteractionTimer = nullptr;
116 : static nsITimerCallback* gUserInteractionTimerCallback = nullptr;
117 :
118 : static const double kCursorLoadingTimeout = 1000; // ms
119 3 : static AutoWeakFrame gLastCursorSourceFrame;
120 : static TimeStamp gLastCursorUpdateTime;
121 :
122 : static inline int32_t
123 0 : RoundDown(double aDouble)
124 : {
125 0 : return (aDouble > 0) ? static_cast<int32_t>(floor(aDouble)) :
126 0 : static_cast<int32_t>(ceil(aDouble));
127 : }
128 :
129 : #ifdef DEBUG_DOCSHELL_FOCUS
130 : static void
131 : PrintDocTree(nsIDocShellTreeItem* aParentItem, int aLevel)
132 : {
133 : for (int32_t i=0;i<aLevel;i++) printf(" ");
134 :
135 : int32_t childWebshellCount;
136 : aParentItem->GetChildCount(&childWebshellCount);
137 : nsCOMPtr<nsIDocShell> parentAsDocShell(do_QueryInterface(aParentItem));
138 : int32_t type = aParentItem->ItemType();
139 : nsCOMPtr<nsIPresShell> presShell = parentAsDocShell->GetPresShell();
140 : RefPtr<nsPresContext> presContext;
141 : parentAsDocShell->GetPresContext(getter_AddRefs(presContext));
142 : nsCOMPtr<nsIContentViewer> cv;
143 : parentAsDocShell->GetContentViewer(getter_AddRefs(cv));
144 : nsCOMPtr<nsIDOMDocument> domDoc;
145 : if (cv)
146 : cv->GetDOMDocument(getter_AddRefs(domDoc));
147 : nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
148 : nsCOMPtr<nsIDOMWindow> domwin = doc ? doc->GetWindow() : nullptr;
149 : nsIURI* uri = doc ? doc->GetDocumentURI() : nullptr;
150 :
151 : printf("DS %p Type %s Cnt %d Doc %p DW %p EM %p%c",
152 : static_cast<void*>(parentAsDocShell.get()),
153 : type==nsIDocShellTreeItem::typeChrome?"Chrome":"Content",
154 : childWebshellCount, static_cast<void*>(doc.get()),
155 : static_cast<void*>(domwin.get()),
156 : static_cast<void*>(presContext ? presContext->EventStateManager() : nullptr),
157 : uri ? ' ' : '\n');
158 : if (uri) {
159 : nsAutoCString spec;
160 : uri->GetSpec(spec);
161 : printf("\"%s\"\n", spec.get());
162 : }
163 :
164 : if (childWebshellCount > 0) {
165 : for (int32_t i = 0; i < childWebshellCount; i++) {
166 : nsCOMPtr<nsIDocShellTreeItem> child;
167 : aParentItem->GetChildAt(i, getter_AddRefs(child));
168 : PrintDocTree(child, aLevel + 1);
169 : }
170 : }
171 : }
172 :
173 : static void
174 : PrintDocTreeAll(nsIDocShellTreeItem* aItem)
175 : {
176 : nsCOMPtr<nsIDocShellTreeItem> item = aItem;
177 : for(;;) {
178 : nsCOMPtr<nsIDocShellTreeItem> parent;
179 : item->GetParent(getter_AddRefs(parent));
180 : if (!parent)
181 : break;
182 : item = parent;
183 : }
184 :
185 : PrintDocTree(item, 0);
186 : }
187 : #endif
188 :
189 : // mask values for ui.key.chromeAccess and ui.key.contentAccess
190 : #define NS_MODIFIER_SHIFT 1
191 : #define NS_MODIFIER_CONTROL 2
192 : #define NS_MODIFIER_ALT 4
193 : #define NS_MODIFIER_META 8
194 : #define NS_MODIFIER_OS 16
195 :
196 : /******************************************************************/
197 : /* mozilla::UITimerCallback */
198 : /******************************************************************/
199 :
200 : class UITimerCallback final :
201 : public nsITimerCallback,
202 : public nsINamed
203 : {
204 : public:
205 2 : UITimerCallback() : mPreviousCount(0) {}
206 : NS_DECL_ISUPPORTS
207 : NS_DECL_NSITIMERCALLBACK
208 : NS_DECL_NSINAMED
209 : private:
210 : ~UITimerCallback() = default;
211 : uint32_t mPreviousCount;
212 : };
213 :
214 16 : NS_IMPL_ISUPPORTS(UITimerCallback, nsITimerCallback, nsINamed)
215 :
216 : // If aTimer is nullptr, this method always sends "user-interaction-inactive"
217 : // notification.
218 : NS_IMETHODIMP
219 2 : UITimerCallback::Notify(nsITimer* aTimer)
220 : {
221 : nsCOMPtr<nsIObserverService> obs =
222 4 : mozilla::services::GetObserverService();
223 2 : if (!obs)
224 0 : return NS_ERROR_FAILURE;
225 2 : if ((gMouseOrKeyboardEventCounter == mPreviousCount) || !aTimer) {
226 2 : gMouseOrKeyboardEventCounter = 0;
227 2 : obs->NotifyObservers(nullptr, "user-interaction-inactive", nullptr);
228 4 : if (gUserInteractionTimer) {
229 2 : gUserInteractionTimer->Cancel();
230 2 : NS_RELEASE(gUserInteractionTimer);
231 : }
232 : } else {
233 0 : obs->NotifyObservers(nullptr, "user-interaction-active", nullptr);
234 0 : EventStateManager::UpdateUserActivityTimer();
235 : }
236 2 : mPreviousCount = gMouseOrKeyboardEventCounter;
237 2 : return NS_OK;
238 : }
239 :
240 : NS_IMETHODIMP
241 3 : UITimerCallback::GetName(nsACString& aName)
242 : {
243 3 : aName.AssignASCII("UITimerCallback_timer");
244 3 : return NS_OK;
245 : }
246 :
247 : NS_IMETHODIMP
248 0 : UITimerCallback::SetName(const char* aName)
249 : {
250 0 : return NS_ERROR_NOT_IMPLEMENTED;
251 : }
252 :
253 : /******************************************************************/
254 : /* mozilla::OverOutElementsWrapper */
255 : /******************************************************************/
256 :
257 2 : OverOutElementsWrapper::OverOutElementsWrapper()
258 2 : : mLastOverFrame(nullptr)
259 : {
260 2 : }
261 :
262 : OverOutElementsWrapper::~OverOutElementsWrapper() = default;
263 :
264 0 : NS_IMPL_CYCLE_COLLECTION(OverOutElementsWrapper,
265 : mLastOverElement,
266 : mFirstOverEventElement,
267 : mFirstOutEventElement)
268 2 : NS_IMPL_CYCLE_COLLECTING_ADDREF(OverOutElementsWrapper)
269 0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(OverOutElementsWrapper)
270 :
271 2 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(OverOutElementsWrapper)
272 0 : NS_INTERFACE_MAP_ENTRY(nsISupports)
273 0 : NS_INTERFACE_MAP_END
274 :
275 : /******************************************************************/
276 : /* mozilla::EventStateManager */
277 : /******************************************************************/
278 :
279 : static uint32_t sESMInstanceCount = 0;
280 : static bool sPointerEventEnabled = false;
281 :
282 : uint64_t EventStateManager::sUserInputCounter = 0;
283 : int32_t EventStateManager::sUserInputEventDepth = 0;
284 : bool EventStateManager::sNormalLMouseEventInProcess = false;
285 : EventStateManager* EventStateManager::sActiveESM = nullptr;
286 : nsIDocument* EventStateManager::sMouseOverDocument = nullptr;
287 3 : AutoWeakFrame EventStateManager::sLastDragOverFrame = nullptr;
288 3 : LayoutDeviceIntPoint EventStateManager::sPreLockPoint = LayoutDeviceIntPoint(0, 0);
289 3 : LayoutDeviceIntPoint EventStateManager::sLastRefPoint = kInvalidRefPoint;
290 3 : CSSIntPoint EventStateManager::sLastScreenPoint = CSSIntPoint(0, 0);
291 3 : LayoutDeviceIntPoint EventStateManager::sSynthCenteringPoint = kInvalidRefPoint;
292 3 : CSSIntPoint EventStateManager::sLastClientPoint = CSSIntPoint(0, 0);
293 : bool EventStateManager::sIsPointerLocked = false;
294 : // Reference to the pointer locked element.
295 3 : nsWeakPtr EventStateManager::sPointerLockedElement;
296 : // Reference to the document which requested pointer lock.
297 3 : nsWeakPtr EventStateManager::sPointerLockedDoc;
298 3 : nsCOMPtr<nsIContent> EventStateManager::sDragOverContent = nullptr;
299 : TimeStamp EventStateManager::sLatestUserInputStart;
300 : TimeStamp EventStateManager::sHandlingInputStart;
301 :
302 : EventStateManager::WheelPrefs*
303 : EventStateManager::WheelPrefs::sInstance = nullptr;
304 : bool EventStateManager::WheelPrefs::sWheelEventsEnabledOnPlugins = true;
305 : EventStateManager::DeltaAccumulator*
306 : EventStateManager::DeltaAccumulator::sInstance = nullptr;
307 :
308 28 : EventStateManager::EventStateManager()
309 : : mLockCursor(0)
310 : , mLastFrameConsumedSetCursor(false)
311 : , mCurrentTarget(nullptr)
312 : // init d&d gesture state machine variables
313 : , mGestureDownPoint(0,0)
314 : , mPresContext(nullptr)
315 : , mLClickCount(0)
316 : , mMClickCount(0)
317 : , mRClickCount(0)
318 : , mInTouchDrag(false)
319 28 : , m_haveShutdown(false)
320 : {
321 28 : if (sESMInstanceCount == 0) {
322 2 : gUserInteractionTimerCallback = new UITimerCallback();
323 2 : if (gUserInteractionTimerCallback)
324 2 : NS_ADDREF(gUserInteractionTimerCallback);
325 2 : UpdateUserActivityTimer();
326 : }
327 28 : ++sESMInstanceCount;
328 :
329 : static bool sAddedPointerEventEnabled = false;
330 28 : if (!sAddedPointerEventEnabled) {
331 : Preferences::AddBoolVarCache(&sPointerEventEnabled,
332 2 : "dom.w3c_pointer_events.enabled", false);
333 2 : sAddedPointerEventEnabled = true;
334 : }
335 28 : WheelTransaction::InitializeStatics();
336 28 : }
337 :
338 : nsresult
339 3 : EventStateManager::UpdateUserActivityTimer()
340 : {
341 3 : if (!gUserInteractionTimerCallback)
342 0 : return NS_OK;
343 :
344 3 : if (!gUserInteractionTimer) {
345 3 : CallCreateInstance("@mozilla.org/timer;1", &gUserInteractionTimer);
346 3 : if (gUserInteractionTimer) {
347 3 : gUserInteractionTimer->SetTarget(
348 6 : SystemGroup::EventTargetFor(TaskCategory::Other));
349 : }
350 : }
351 :
352 3 : if (gUserInteractionTimer) {
353 3 : gUserInteractionTimer->InitWithCallback(gUserInteractionTimerCallback,
354 : NS_USER_INTERACTION_INTERVAL,
355 6 : nsITimer::TYPE_ONE_SHOT);
356 : }
357 3 : return NS_OK;
358 : }
359 :
360 : nsresult
361 28 : EventStateManager::Init()
362 : {
363 : nsCOMPtr<nsIObserverService> observerService =
364 56 : mozilla::services::GetObserverService();
365 28 : if (!observerService)
366 0 : return NS_ERROR_FAILURE;
367 :
368 28 : observerService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, true);
369 :
370 28 : if (sESMInstanceCount == 1) {
371 2 : Prefs::Init();
372 : }
373 :
374 28 : return NS_OK;
375 : }
376 :
377 0 : EventStateManager::~EventStateManager()
378 : {
379 0 : ReleaseCurrentIMEContentObserver();
380 :
381 0 : if (sActiveESM == this) {
382 0 : sActiveESM = nullptr;
383 : }
384 0 : if (Prefs::ClickHoldContextMenu())
385 0 : KillClickHoldTimer();
386 :
387 0 : if (mDocument == sMouseOverDocument)
388 0 : sMouseOverDocument = nullptr;
389 :
390 0 : --sESMInstanceCount;
391 0 : if(sESMInstanceCount == 0) {
392 0 : WheelTransaction::Shutdown();
393 0 : if (gUserInteractionTimerCallback) {
394 0 : gUserInteractionTimerCallback->Notify(nullptr);
395 0 : NS_RELEASE(gUserInteractionTimerCallback);
396 : }
397 0 : if (gUserInteractionTimer) {
398 0 : gUserInteractionTimer->Cancel();
399 0 : NS_RELEASE(gUserInteractionTimer);
400 : }
401 0 : Prefs::Shutdown();
402 0 : WheelPrefs::Shutdown();
403 0 : DeltaAccumulator::Shutdown();
404 : }
405 :
406 0 : if (sDragOverContent && sDragOverContent->OwnerDoc() == mDocument) {
407 0 : sDragOverContent = nullptr;
408 : }
409 :
410 0 : if (!m_haveShutdown) {
411 0 : Shutdown();
412 :
413 : // Don't remove from Observer service in Shutdown because Shutdown also
414 : // gets called from xpcom shutdown observer. And we don't want to remove
415 : // from the service in that case.
416 :
417 : nsCOMPtr<nsIObserverService> observerService =
418 0 : mozilla::services::GetObserverService();
419 0 : if (observerService) {
420 0 : observerService->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
421 : }
422 : }
423 :
424 0 : }
425 :
426 : nsresult
427 0 : EventStateManager::Shutdown()
428 : {
429 0 : m_haveShutdown = true;
430 0 : return NS_OK;
431 : }
432 :
433 : NS_IMETHODIMP
434 0 : EventStateManager::Observe(nsISupports* aSubject,
435 : const char* aTopic,
436 : const char16_t *someData)
437 : {
438 0 : if (!nsCRT::strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
439 0 : Shutdown();
440 : }
441 :
442 0 : return NS_OK;
443 : }
444 :
445 56 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(EventStateManager)
446 28 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIObserver)
447 28 : NS_INTERFACE_MAP_ENTRY(nsIObserver)
448 28 : NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
449 0 : NS_INTERFACE_MAP_END
450 :
451 68 : NS_IMPL_CYCLE_COLLECTING_ADDREF(EventStateManager)
452 40 : NS_IMPL_CYCLE_COLLECTING_RELEASE(EventStateManager)
453 :
454 0 : NS_IMPL_CYCLE_COLLECTION(EventStateManager,
455 : mCurrentTargetContent,
456 : mGestureDownContent,
457 : mGestureDownFrameOwner,
458 : mLastLeftMouseDownContent,
459 : mLastLeftMouseDownContentParent,
460 : mLastMiddleMouseDownContent,
461 : mLastMiddleMouseDownContentParent,
462 : mLastRightMouseDownContent,
463 : mLastRightMouseDownContentParent,
464 : mActiveContent,
465 : mHoverContent,
466 : mURLTargetContent,
467 : mMouseEnterLeaveHelper,
468 : mPointersEnterLeaveHelper,
469 : mDocument,
470 : mIMEContentObserver,
471 : mAccessKeys)
472 :
473 : void
474 0 : EventStateManager::ReleaseCurrentIMEContentObserver()
475 : {
476 0 : if (mIMEContentObserver) {
477 0 : mIMEContentObserver->DisconnectFromEventStateManager();
478 : }
479 0 : mIMEContentObserver = nullptr;
480 0 : }
481 :
482 : void
483 0 : EventStateManager::OnStartToObserveContent(
484 : IMEContentObserver* aIMEContentObserver)
485 : {
486 0 : if (mIMEContentObserver == aIMEContentObserver) {
487 0 : return;
488 : }
489 0 : ReleaseCurrentIMEContentObserver();
490 0 : mIMEContentObserver = aIMEContentObserver;
491 : }
492 :
493 : void
494 0 : EventStateManager::OnStopObservingContent(
495 : IMEContentObserver* aIMEContentObserver)
496 : {
497 0 : aIMEContentObserver->DisconnectFromEventStateManager();
498 0 : NS_ENSURE_TRUE_VOID(mIMEContentObserver == aIMEContentObserver);
499 0 : mIMEContentObserver = nullptr;
500 : }
501 :
502 : void
503 10 : EventStateManager::TryToFlushPendingNotificationsToIME()
504 : {
505 10 : if (mIMEContentObserver) {
506 0 : mIMEContentObserver->TryToFlushPendingNotifications();
507 : }
508 10 : }
509 :
510 : static bool
511 10 : IsMessageMouseUserActivity(EventMessage aMessage)
512 : {
513 6 : return aMessage == eMouseMove ||
514 6 : aMessage == eMouseUp ||
515 6 : aMessage == eMouseDown ||
516 6 : aMessage == eMouseAuxClick ||
517 6 : aMessage == eMouseDoubleClick ||
518 6 : aMessage == eMouseClick ||
519 16 : aMessage == eMouseActivate ||
520 10 : aMessage == eMouseLongTap;
521 : }
522 :
523 : static bool
524 2 : IsMessageGamepadUserActivity(EventMessage aMessage)
525 : {
526 2 : return aMessage == eGamepadButtonDown ||
527 4 : aMessage == eGamepadButtonUp ||
528 2 : aMessage == eGamepadAxisMove;
529 : }
530 :
531 : nsresult
532 10 : EventStateManager::PreHandleEvent(nsPresContext* aPresContext,
533 : WidgetEvent* aEvent,
534 : nsIFrame* aTargetFrame,
535 : nsIContent* aTargetContent,
536 : nsEventStatus* aStatus)
537 : {
538 10 : NS_ENSURE_ARG_POINTER(aStatus);
539 10 : NS_ENSURE_ARG(aPresContext);
540 10 : if (!aEvent) {
541 0 : NS_ERROR("aEvent is null. This should never happen.");
542 0 : return NS_ERROR_NULL_POINTER;
543 : }
544 :
545 10 : NS_WARNING_ASSERTION(
546 : !aTargetFrame || !aTargetFrame->GetContent() ||
547 : aTargetFrame->GetContent() == aTargetContent ||
548 : aTargetFrame->GetContent()->GetFlattenedTreeParent() == aTargetContent ||
549 : aTargetFrame->IsGeneratedContentFrame(),
550 : "aTargetFrame should be related with aTargetContent");
551 : #if DEBUG
552 10 : if (aTargetFrame && aTargetFrame->IsGeneratedContentFrame()) {
553 0 : nsCOMPtr<nsIContent> targetContent;
554 0 : aTargetFrame->GetContentForEvent(aEvent, getter_AddRefs(targetContent));
555 0 : MOZ_ASSERT(aTargetContent == targetContent,
556 : "Unexpected target for generated content frame!");
557 : }
558 : #endif
559 :
560 10 : mCurrentTarget = aTargetFrame;
561 10 : mCurrentTargetContent = nullptr;
562 :
563 : // Do not take account eMouseEnterIntoWidget/ExitFromWidget so that loading
564 : // a page when user is not active doesn't change the state to active.
565 10 : WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
566 18 : if (aEvent->IsTrusted() &&
567 20 : ((mouseEvent && mouseEvent->IsReal() &&
568 16 : IsMessageMouseUserActivity(mouseEvent->mMessage)) ||
569 12 : aEvent->mClass == eWheelEventClass ||
570 8 : aEvent->mClass == ePointerEventClass ||
571 4 : aEvent->mClass == eTouchEventClass ||
572 4 : aEvent->mClass == eKeyboardEventClass ||
573 2 : IsMessageGamepadUserActivity(aEvent->mMessage))) {
574 8 : if (gMouseOrKeyboardEventCounter == 0) {
575 : nsCOMPtr<nsIObserverService> obs =
576 2 : mozilla::services::GetObserverService();
577 1 : if (obs) {
578 1 : obs->NotifyObservers(nullptr, "user-interaction-active", nullptr);
579 1 : UpdateUserActivityTimer();
580 : }
581 : }
582 8 : ++gMouseOrKeyboardEventCounter;
583 :
584 16 : nsCOMPtr<nsINode> node = do_QueryInterface(aTargetContent);
585 16 : if (node &&
586 24 : (aEvent->mMessage == eKeyUp || aEvent->mMessage == eMouseUp ||
587 24 : aEvent->mMessage == eWheel || aEvent->mMessage == eTouchEnd ||
588 16 : aEvent->mMessage == ePointerUp)) {
589 0 : nsIDocument* doc = node->OwnerDoc();
590 0 : while (doc && !doc->UserHasInteracted()) {
591 0 : doc->SetUserHasInteracted(true);
592 0 : doc = nsContentUtils::IsChildOfSameType(doc) ?
593 : doc->GetParentDocument() : nullptr;
594 : }
595 : }
596 : }
597 :
598 10 : WheelTransaction::OnEvent(aEvent);
599 :
600 : // Focus events don't necessarily need a frame.
601 10 : if (!mCurrentTarget && !aTargetContent) {
602 0 : NS_ERROR("mCurrentTarget and aTargetContent are null");
603 0 : return NS_ERROR_NULL_POINTER;
604 : }
605 : #ifdef DEBUG
606 10 : if (aEvent->HasDragEventMessage() && sIsPointerLocked) {
607 0 : NS_ASSERTION(sIsPointerLocked,
608 : "sIsPointerLocked is true. Drag events should be suppressed when "
609 : "the pointer is locked.");
610 : }
611 : #endif
612 : // Store last known screenPoint and clientPoint so pointer lock
613 : // can use these values as constants.
614 30 : if (aEvent->IsTrusted() &&
615 10 : ((mouseEvent && mouseEvent->IsReal()) ||
616 20 : aEvent->mClass == eWheelEventClass) &&
617 10 : !sIsPointerLocked) {
618 : sLastScreenPoint =
619 10 : Event::GetScreenCoords(aPresContext, aEvent, aEvent->mRefPoint);
620 : sLastClientPoint =
621 : Event::GetClientCoords(aPresContext, aEvent, aEvent->mRefPoint,
622 10 : CSSIntPoint(0, 0));
623 : }
624 :
625 10 : *aStatus = nsEventStatus_eIgnore;
626 :
627 10 : if (aEvent->mClass == eQueryContentEventClass) {
628 0 : HandleQueryContentEvent(aEvent->AsQueryContentEvent());
629 0 : return NS_OK;
630 : }
631 :
632 10 : WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent();
633 10 : if (touchEvent && mInTouchDrag) {
634 0 : if (touchEvent->mMessage == eTouchMove) {
635 0 : GenerateDragGesture(aPresContext, touchEvent);
636 : } else {
637 0 : mInTouchDrag = false;
638 0 : StopTrackingDragGesture();
639 : }
640 : }
641 :
642 10 : switch (aEvent->mMessage) {
643 : case eContextMenu:
644 0 : if (sIsPointerLocked) {
645 0 : return NS_ERROR_DOM_INVALID_STATE_ERR;
646 : }
647 0 : break;
648 : case eMouseTouchDrag:
649 0 : mInTouchDrag = true;
650 0 : BeginTrackingDragGesture(aPresContext, mouseEvent, aTargetFrame);
651 0 : break;
652 : case eMouseDown: {
653 0 : switch (mouseEvent->button) {
654 : case WidgetMouseEvent::eLeftButton:
655 0 : BeginTrackingDragGesture(aPresContext, mouseEvent, aTargetFrame);
656 0 : mLClickCount = mouseEvent->mClickCount;
657 0 : SetClickCount(mouseEvent, aStatus);
658 0 : sNormalLMouseEventInProcess = true;
659 0 : break;
660 : case WidgetMouseEvent::eMiddleButton:
661 0 : mMClickCount = mouseEvent->mClickCount;
662 0 : SetClickCount(mouseEvent, aStatus);
663 0 : break;
664 : case WidgetMouseEvent::eRightButton:
665 0 : mRClickCount = mouseEvent->mClickCount;
666 0 : SetClickCount(mouseEvent, aStatus);
667 0 : break;
668 : }
669 0 : break;
670 : }
671 : case eMouseUp: {
672 0 : switch (mouseEvent->button) {
673 : case WidgetMouseEvent::eLeftButton:
674 0 : if (Prefs::ClickHoldContextMenu()) {
675 0 : KillClickHoldTimer();
676 : }
677 0 : StopTrackingDragGesture();
678 0 : sNormalLMouseEventInProcess = false;
679 : // then fall through...
680 : MOZ_FALLTHROUGH;
681 : case WidgetMouseEvent::eRightButton:
682 : case WidgetMouseEvent::eMiddleButton:
683 0 : SetClickCount(mouseEvent, aStatus);
684 0 : break;
685 : }
686 0 : break;
687 : }
688 : case eMouseEnterIntoWidget:
689 : // In some cases on e10s eMouseEnterIntoWidget
690 : // event was sent twice into child process of content.
691 : // (From specific widget code (sending is not permanent) and
692 : // from ESM::DispatchMouseOrPointerEvent (sending is permanent)).
693 : // IsCrossProcessForwardingStopped() helps to suppress sending accidental
694 : // event from widget code.
695 1 : aEvent->StopCrossProcessForwarding();
696 1 : break;
697 : case eMouseExitFromWidget:
698 : // If this is a remote frame, we receive eMouseExitFromWidget from the
699 : // parent the mouse exits our content. Since the parent may update the
700 : // cursor while the mouse is outside our frame, and since PuppetWidget
701 : // caches the current cursor internally, re-entering our content (say from
702 : // over a window edge) wont update the cursor if the cached value and the
703 : // current cursor match. So when the mouse exits a remote frame, clear the
704 : // cached widget cursor so a proper update will occur when the mouse
705 : // re-enters.
706 1 : if (XRE_IsContentProcess()) {
707 0 : ClearCachedWidgetCursor(mCurrentTarget);
708 : }
709 :
710 : // IsCrossProcessForwardingStopped() helps to suppress double event sending
711 : // into process of content.
712 : // For more information see comment above, at eMouseEnterIntoWidget case.
713 1 : aEvent->StopCrossProcessForwarding();
714 :
715 : // If the event is not a top-level window exit, then it's not
716 : // really an exit --- we may have traversed widget boundaries but
717 : // we're still in our toplevel window.
718 1 : if (mouseEvent->mExitFrom != WidgetMouseEvent::eTopLevel) {
719 : // Treat it as a synthetic move so we don't generate spurious
720 : // "exit" or "move" events. Any necessary "out" or "over" events
721 : // will be generated by GenerateMouseEnterExit
722 0 : mouseEvent->mMessage = eMouseMove;
723 0 : mouseEvent->mReason = WidgetMouseEvent::eSynthesized;
724 : // then fall through...
725 : } else {
726 1 : if (sPointerEventEnabled) {
727 : // We should synthetize corresponding pointer events
728 1 : GeneratePointerEnterExit(ePointerLeave, mouseEvent);
729 : }
730 1 : GenerateMouseEnterExit(mouseEvent);
731 : //This is a window level mouse exit event and should stop here
732 1 : aEvent->mMessage = eVoidEvent;
733 1 : break;
734 : }
735 : MOZ_FALLTHROUGH;
736 : case eMouseMove:
737 : case ePointerDown:
738 : case ePointerMove: {
739 : // on the Mac, GenerateDragGesture() may not return until the drag
740 : // has completed and so |aTargetFrame| may have been deleted (moving
741 : // a bookmark, for example). If this is the case, however, we know
742 : // that ClearFrameRefs() has been called and it cleared out
743 : // |mCurrentTarget|. As a result, we should pass |mCurrentTarget|
744 : // into UpdateCursor().
745 8 : GenerateDragGesture(aPresContext, mouseEvent);
746 8 : UpdateCursor(aPresContext, aEvent, mCurrentTarget, aStatus);
747 8 : GenerateMouseEnterExit(mouseEvent);
748 : // Flush pending layout changes, so that later mouse move events
749 : // will go to the right nodes.
750 8 : FlushPendingEvents(aPresContext);
751 8 : break;
752 : }
753 : case ePointerGotCapture:
754 0 : GenerateMouseEnterExit(mouseEvent);
755 0 : break;
756 : case eDragStart:
757 0 : if (Prefs::ClickHoldContextMenu()) {
758 : // an external drag gesture event came in, not generated internally
759 : // by Gecko. Make sure we get rid of the click-hold timer.
760 0 : KillClickHoldTimer();
761 : }
762 0 : break;
763 : case eDragOver:
764 : // Send the enter/exit events before eDrop.
765 0 : GenerateDragDropEnterExit(aPresContext, aEvent->AsDragEvent());
766 0 : break;
767 :
768 : case eKeyPress:
769 : {
770 0 : WidgetKeyboardEvent* keyEvent = aEvent->AsKeyboardEvent();
771 :
772 0 : int32_t modifierMask = 0;
773 0 : if (keyEvent->IsShift())
774 0 : modifierMask |= NS_MODIFIER_SHIFT;
775 0 : if (keyEvent->IsControl())
776 0 : modifierMask |= NS_MODIFIER_CONTROL;
777 0 : if (keyEvent->IsAlt())
778 0 : modifierMask |= NS_MODIFIER_ALT;
779 0 : if (keyEvent->IsMeta())
780 0 : modifierMask |= NS_MODIFIER_META;
781 0 : if (keyEvent->IsOS())
782 0 : modifierMask |= NS_MODIFIER_OS;
783 :
784 : // Prevent keyboard scrolling while an accesskey modifier is in use.
785 0 : if (modifierMask) {
786 0 : bool matchesContentAccessKey = (modifierMask == Prefs::ContentAccessModifierMask());
787 :
788 0 : if (modifierMask == Prefs::ChromeAccessModifierMask() ||
789 : matchesContentAccessKey) {
790 0 : AutoTArray<uint32_t, 10> accessCharCodes;
791 0 : keyEvent->GetAccessKeyCandidates(accessCharCodes);
792 :
793 0 : if (HandleAccessKey(keyEvent, aPresContext, accessCharCodes,
794 : modifierMask, matchesContentAccessKey)) {
795 0 : *aStatus = nsEventStatus_eConsumeNoDefault;
796 : }
797 : }
798 : }
799 : }
800 : // then fall through...
801 : MOZ_FALLTHROUGH;
802 : case eKeyDown:
803 : case eKeyUp:
804 : {
805 0 : nsIContent* content = GetFocusedContent();
806 0 : if (content)
807 0 : mCurrentTargetContent = content;
808 :
809 : // NOTE: Don't refer TextComposition::IsComposing() since UI Events
810 : // defines that KeyboardEvent.isComposing is true when it's
811 : // dispatched after compositionstart and compositionend.
812 : // TextComposition::IsComposing() is false even before
813 : // compositionend if there is no composing string.
814 : // And also don't expose other document's composition state.
815 : // A native IME context is typically shared by multiple documents.
816 : // So, don't use GetTextCompositionFor(nsIWidget*) here.
817 : RefPtr<TextComposition> composition =
818 0 : IMEStateManager::GetTextCompositionFor(aPresContext);
819 0 : aEvent->AsKeyboardEvent()->mIsComposing = !!composition;
820 : }
821 0 : break;
822 : case eWheel:
823 : case eWheelOperationStart:
824 : case eWheelOperationEnd:
825 : {
826 0 : NS_ASSERTION(aEvent->IsTrusted(),
827 : "Untrusted wheel event shouldn't be here");
828 :
829 0 : nsIContent* content = GetFocusedContent();
830 0 : if (content) {
831 0 : mCurrentTargetContent = content;
832 : }
833 :
834 0 : if (aEvent->mMessage != eWheel) {
835 0 : break;
836 : }
837 :
838 0 : WidgetWheelEvent* wheelEvent = aEvent->AsWheelEvent();
839 0 : WheelPrefs::GetInstance()->ApplyUserPrefsToDelta(wheelEvent);
840 :
841 : // If we won't dispatch a DOM event for this event, nothing to do anymore.
842 0 : if (!wheelEvent->IsAllowedToDispatchDOMEvent()) {
843 0 : break;
844 : }
845 :
846 : // Init lineOrPageDelta values for line scroll events for some devices
847 : // on some platforms which might dispatch wheel events which don't have
848 : // lineOrPageDelta values. And also, if delta values are customized by
849 : // prefs, this recomputes them.
850 : DeltaAccumulator::GetInstance()->
851 0 : InitLineOrPageDelta(aTargetFrame, this, wheelEvent);
852 : }
853 0 : break;
854 : case eSetSelection:
855 0 : IMEStateManager::HandleSelectionEvent(aPresContext, GetFocusedContent(),
856 0 : aEvent->AsSelectionEvent());
857 0 : break;
858 : case eContentCommandCut:
859 : case eContentCommandCopy:
860 : case eContentCommandPaste:
861 : case eContentCommandDelete:
862 : case eContentCommandUndo:
863 : case eContentCommandRedo:
864 : case eContentCommandPasteTransferable:
865 : case eContentCommandLookUpDictionary:
866 0 : DoContentCommandEvent(aEvent->AsContentCommandEvent());
867 0 : break;
868 : case eContentCommandScroll:
869 0 : DoContentCommandScrollEvent(aEvent->AsContentCommandEvent());
870 0 : break;
871 : case eCompositionStart:
872 0 : if (aEvent->IsTrusted()) {
873 : // If the event is trusted event, set the selected text to data of
874 : // composition event.
875 0 : WidgetCompositionEvent* compositionEvent = aEvent->AsCompositionEvent();
876 : WidgetQueryContentEvent selectedText(true, eQuerySelectedText,
877 0 : compositionEvent->mWidget);
878 0 : HandleQueryContentEvent(&selectedText);
879 0 : NS_ASSERTION(selectedText.mSucceeded, "Failed to get selected text");
880 0 : compositionEvent->mData = selectedText.mReply.mString;
881 : }
882 0 : break;
883 : default:
884 0 : break;
885 : }
886 10 : return NS_OK;
887 : }
888 :
889 : void
890 0 : EventStateManager::HandleQueryContentEvent(WidgetQueryContentEvent* aEvent)
891 : {
892 0 : switch (aEvent->mMessage) {
893 : case eQuerySelectedText:
894 : case eQueryTextContent:
895 : case eQueryCaretRect:
896 : case eQueryTextRect:
897 : case eQueryEditorRect:
898 0 : if (!IsTargetCrossProcess(aEvent)) {
899 0 : break;
900 : }
901 : // Will not be handled locally, remote the event
902 0 : GetCrossProcessTarget()->HandleQueryContentEvent(*aEvent);
903 0 : return;
904 : // Following events have not been supported in e10s mode yet.
905 : case eQueryContentState:
906 : case eQuerySelectionAsTransferable:
907 : case eQueryCharacterAtPoint:
908 : case eQueryDOMWidgetHittest:
909 : case eQueryTextRectArray:
910 0 : break;
911 : default:
912 0 : return;
913 : }
914 :
915 : // If there is an IMEContentObserver, we need to handle QueryContentEvent
916 : // with it.
917 0 : if (mIMEContentObserver) {
918 0 : RefPtr<IMEContentObserver> contentObserver = mIMEContentObserver;
919 0 : contentObserver->HandleQueryContentEvent(aEvent);
920 0 : return;
921 : }
922 :
923 0 : ContentEventHandler handler(mPresContext);
924 0 : handler.HandleQueryContentEvent(aEvent);
925 : }
926 :
927 : // static
928 : int32_t
929 0 : EventStateManager::GetAccessModifierMaskFor(nsISupports* aDocShell)
930 : {
931 0 : nsCOMPtr<nsIDocShellTreeItem> treeItem(do_QueryInterface(aDocShell));
932 0 : if (!treeItem)
933 0 : return -1; // invalid modifier
934 :
935 0 : switch (treeItem->ItemType()) {
936 : case nsIDocShellTreeItem::typeChrome:
937 0 : return Prefs::ChromeAccessModifierMask();
938 :
939 : case nsIDocShellTreeItem::typeContent:
940 0 : return Prefs::ContentAccessModifierMask();
941 :
942 : default:
943 0 : return -1; // invalid modifier
944 : }
945 : }
946 :
947 : static bool
948 0 : IsAccessKeyTarget(nsIContent* aContent, nsIFrame* aFrame, nsAString& aKey)
949 : {
950 : // Use GetAttr because we want Unicode case=insensitive matching
951 : // XXXbz shouldn't this be case-sensitive, per spec?
952 0 : nsString contentKey;
953 0 : if (!aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::accesskey, contentKey) ||
954 0 : !contentKey.Equals(aKey, nsCaseInsensitiveStringComparator()))
955 0 : return false;
956 :
957 : nsCOMPtr<nsIDOMXULDocument> xulDoc =
958 0 : do_QueryInterface(aContent->OwnerDoc());
959 0 : if (!xulDoc && !aContent->IsXULElement())
960 0 : return true;
961 :
962 : // For XUL we do visibility checks.
963 0 : if (!aFrame)
964 0 : return false;
965 :
966 0 : if (aFrame->IsFocusable())
967 0 : return true;
968 :
969 0 : if (!aFrame->IsVisibleConsideringAncestors())
970 0 : return false;
971 :
972 : // XUL controls can be activated.
973 0 : nsCOMPtr<nsIDOMXULControlElement> control(do_QueryInterface(aContent));
974 0 : if (control)
975 0 : return true;
976 :
977 : // HTML area, label and legend elements are never focusable, so
978 : // we need to check for them explicitly before giving up.
979 0 : if (aContent->IsAnyOfHTMLElements(nsGkAtoms::area,
980 : nsGkAtoms::label,
981 : nsGkAtoms::legend)) {
982 0 : return true;
983 : }
984 :
985 : // XUL label elements are never focusable, so we need to check for them
986 : // explicitly before giving up.
987 0 : if (aContent->IsXULElement(nsGkAtoms::label)) {
988 0 : return true;
989 : }
990 :
991 0 : return false;
992 : }
993 :
994 : bool
995 0 : EventStateManager::ExecuteAccessKey(nsTArray<uint32_t>& aAccessCharCodes,
996 : bool aIsTrustedEvent)
997 : {
998 0 : int32_t count, start = -1;
999 0 : nsIContent* focusedContent = GetFocusedContent();
1000 0 : if (focusedContent) {
1001 0 : start = mAccessKeys.IndexOf(focusedContent);
1002 0 : if (start == -1 && focusedContent->GetBindingParent())
1003 0 : start = mAccessKeys.IndexOf(focusedContent->GetBindingParent());
1004 : }
1005 : nsIContent *content;
1006 : nsIFrame *frame;
1007 0 : int32_t length = mAccessKeys.Count();
1008 0 : for (uint32_t i = 0; i < aAccessCharCodes.Length(); ++i) {
1009 0 : uint32_t ch = aAccessCharCodes[i];
1010 0 : nsAutoString accessKey;
1011 0 : AppendUCS4ToUTF16(ch, accessKey);
1012 0 : for (count = 1; count <= length; ++count) {
1013 0 : content = mAccessKeys[(start + count) % length];
1014 0 : frame = content->GetPrimaryFrame();
1015 0 : if (IsAccessKeyTarget(content, frame, accessKey)) {
1016 0 : bool shouldActivate = Prefs::KeyCausesActivation();
1017 0 : while (shouldActivate && ++count <= length) {
1018 0 : nsIContent *oc = mAccessKeys[(start + count) % length];
1019 0 : nsIFrame *of = oc->GetPrimaryFrame();
1020 0 : if (IsAccessKeyTarget(oc, of, accessKey))
1021 0 : shouldActivate = false;
1022 : }
1023 :
1024 0 : bool focusChanged = false;
1025 0 : if (shouldActivate) {
1026 0 : focusChanged = content->PerformAccesskey(shouldActivate, aIsTrustedEvent);
1027 : } else {
1028 0 : nsIFocusManager* fm = nsFocusManager::GetFocusManager();
1029 0 : if (fm) {
1030 0 : nsCOMPtr<nsIDOMElement> element = do_QueryInterface(content);
1031 0 : fm->SetFocus(element, nsIFocusManager::FLAG_BYKEY);
1032 0 : focusChanged = true;
1033 : }
1034 : }
1035 :
1036 0 : if (focusChanged && aIsTrustedEvent) {
1037 : // If this is a child process, inform the parent that we want the focus, but
1038 : // pass false since we don't want to change the window order.
1039 0 : nsIDocShell* docShell = mPresContext->GetDocShell();
1040 : nsCOMPtr<nsITabChild> child =
1041 0 : docShell ? docShell->GetTabChild() : nullptr;
1042 0 : if (child) {
1043 0 : child->SendRequestFocus(false);
1044 : }
1045 : }
1046 :
1047 0 : return true;
1048 : }
1049 : }
1050 : }
1051 0 : return false;
1052 : }
1053 :
1054 : // static
1055 : void
1056 0 : EventStateManager::GetAccessKeyLabelPrefix(Element* aElement, nsAString& aPrefix)
1057 : {
1058 0 : aPrefix.Truncate();
1059 0 : nsAutoString separator, modifierText;
1060 0 : nsContentUtils::GetModifierSeparatorText(separator);
1061 :
1062 0 : nsCOMPtr<nsISupports> container = aElement->OwnerDoc()->GetDocShell();
1063 0 : int32_t modifierMask = GetAccessModifierMaskFor(container);
1064 :
1065 0 : if (modifierMask == -1) {
1066 0 : return;
1067 : }
1068 :
1069 0 : if (modifierMask & NS_MODIFIER_CONTROL) {
1070 0 : nsContentUtils::GetControlText(modifierText);
1071 0 : aPrefix.Append(modifierText + separator);
1072 : }
1073 0 : if (modifierMask & NS_MODIFIER_META) {
1074 0 : nsContentUtils::GetMetaText(modifierText);
1075 0 : aPrefix.Append(modifierText + separator);
1076 : }
1077 0 : if (modifierMask & NS_MODIFIER_OS) {
1078 0 : nsContentUtils::GetOSText(modifierText);
1079 0 : aPrefix.Append(modifierText + separator);
1080 : }
1081 0 : if (modifierMask & NS_MODIFIER_ALT) {
1082 0 : nsContentUtils::GetAltText(modifierText);
1083 0 : aPrefix.Append(modifierText + separator);
1084 : }
1085 0 : if (modifierMask & NS_MODIFIER_SHIFT) {
1086 0 : nsContentUtils::GetShiftText(modifierText);
1087 0 : aPrefix.Append(modifierText + separator);
1088 : }
1089 : }
1090 :
1091 : struct MOZ_STACK_CLASS AccessKeyInfo
1092 : {
1093 : WidgetKeyboardEvent* event;
1094 : nsTArray<uint32_t>& charCodes;
1095 : int32_t modifierMask;
1096 :
1097 0 : AccessKeyInfo(WidgetKeyboardEvent* aEvent, nsTArray<uint32_t>& aCharCodes, int32_t aModifierMask)
1098 0 : : event(aEvent)
1099 : , charCodes(aCharCodes)
1100 0 : , modifierMask(aModifierMask)
1101 : {
1102 0 : }
1103 : };
1104 :
1105 : static bool
1106 0 : HandleAccessKeyInRemoteChild(TabParent* aTabParent, void* aArg)
1107 : {
1108 0 : AccessKeyInfo* accessKeyInfo = static_cast<AccessKeyInfo*>(aArg);
1109 :
1110 : // Only forward accesskeys for the active tab.
1111 : bool active;
1112 0 : aTabParent->GetDocShellIsActive(&active);
1113 0 : if (active) {
1114 0 : accessKeyInfo->event->mAccessKeyForwardedToChild = true;
1115 0 : aTabParent->HandleAccessKey(*accessKeyInfo->event,
1116 : accessKeyInfo->charCodes,
1117 0 : accessKeyInfo->modifierMask);
1118 0 : return true;
1119 : }
1120 :
1121 0 : return false;
1122 : }
1123 :
1124 : bool
1125 0 : EventStateManager::HandleAccessKey(WidgetKeyboardEvent* aEvent,
1126 : nsPresContext* aPresContext,
1127 : nsTArray<uint32_t>& aAccessCharCodes,
1128 : bool aMatchesContentAccessKey,
1129 : nsIDocShellTreeItem* aBubbledFrom,
1130 : ProcessingAccessKeyState aAccessKeyState,
1131 : int32_t aModifierMask)
1132 : {
1133 0 : EnsureDocument(mPresContext);
1134 0 : nsCOMPtr<nsIDocShell> docShell = aPresContext->GetDocShell();
1135 0 : if (NS_WARN_IF(!docShell) || NS_WARN_IF(!mDocument)) {
1136 0 : return false;
1137 : }
1138 :
1139 : // Alt or other accesskey modifier is down, we may need to do an accesskey.
1140 0 : if (mAccessKeys.Count() > 0 &&
1141 0 : aModifierMask == GetAccessModifierMaskFor(docShell)) {
1142 : // Someone registered an accesskey. Find and activate it.
1143 0 : if (ExecuteAccessKey(aAccessCharCodes, aEvent->IsTrusted())) {
1144 0 : return true;
1145 : }
1146 : }
1147 :
1148 : int32_t childCount;
1149 0 : docShell->GetChildCount(&childCount);
1150 0 : for (int32_t counter = 0; counter < childCount; counter++) {
1151 : // Not processing the child which bubbles up the handling
1152 0 : nsCOMPtr<nsIDocShellTreeItem> subShellItem;
1153 0 : docShell->GetChildAt(counter, getter_AddRefs(subShellItem));
1154 0 : if (aAccessKeyState == eAccessKeyProcessingUp &&
1155 0 : subShellItem == aBubbledFrom) {
1156 0 : continue;
1157 : }
1158 :
1159 0 : nsCOMPtr<nsIDocShell> subDS = do_QueryInterface(subShellItem);
1160 0 : if (subDS && IsShellVisible(subDS)) {
1161 0 : nsCOMPtr<nsIPresShell> subPS = subDS->GetPresShell();
1162 :
1163 : // Docshells need not have a presshell (eg. display:none
1164 : // iframes, docshells in transition between documents, etc).
1165 0 : if (!subPS) {
1166 : // Oh, well. Just move on to the next child
1167 0 : continue;
1168 : }
1169 :
1170 0 : nsPresContext *subPC = subPS->GetPresContext();
1171 :
1172 : EventStateManager* esm =
1173 0 : static_cast<EventStateManager*>(subPC->EventStateManager());
1174 :
1175 0 : if (esm &&
1176 0 : esm->HandleAccessKey(aEvent, subPC, aAccessCharCodes,
1177 : aMatchesContentAccessKey, nullptr,
1178 : eAccessKeyProcessingDown, aModifierMask)) {
1179 0 : return true;
1180 : }
1181 : }
1182 : }// if end . checking all sub docshell ends here.
1183 :
1184 : // bubble up the process to the parent docshell if necessary
1185 0 : if (eAccessKeyProcessingDown != aAccessKeyState) {
1186 0 : nsCOMPtr<nsIDocShellTreeItem> parentShellItem;
1187 0 : docShell->GetParent(getter_AddRefs(parentShellItem));
1188 0 : nsCOMPtr<nsIDocShell> parentDS = do_QueryInterface(parentShellItem);
1189 0 : if (parentDS) {
1190 0 : nsCOMPtr<nsIPresShell> parentPS = parentDS->GetPresShell();
1191 0 : NS_ASSERTION(parentPS, "Our PresShell exists but the parent's does not?");
1192 :
1193 0 : nsPresContext *parentPC = parentPS->GetPresContext();
1194 0 : NS_ASSERTION(parentPC, "PresShell without PresContext");
1195 :
1196 : EventStateManager* esm =
1197 0 : static_cast<EventStateManager*>(parentPC->EventStateManager());
1198 0 : if (esm &&
1199 0 : esm->HandleAccessKey(aEvent, parentPC, aAccessCharCodes,
1200 : aMatchesContentAccessKey, docShell,
1201 0 : eAccessKeyProcessingDown, aModifierMask)) {
1202 0 : return true;
1203 : }
1204 : }
1205 : }// if end. bubble up process
1206 :
1207 : // If the content access key modifier is pressed, try remote children
1208 0 : if (aMatchesContentAccessKey && mDocument && mDocument->GetWindow()) {
1209 : // If the focus is currently on a node with a TabParent, the key event will
1210 : // get forwarded to the child process and HandleAccessKey called from there.
1211 : // If focus is somewhere else, then we need to check the remote children.
1212 0 : nsFocusManager* fm = nsFocusManager::GetFocusManager();
1213 0 : nsIContent* focusedContent = fm ? fm->GetFocusedContent() : nullptr;
1214 0 : if (TabParent::GetFrom(focusedContent)) {
1215 : // A remote child process is focused. The key event should get sent to
1216 : // the child process.
1217 0 : aEvent->mAccessKeyForwardedToChild = true;
1218 : } else {
1219 0 : AccessKeyInfo accessKeyInfo(aEvent, aAccessCharCodes, aModifierMask);
1220 0 : nsContentUtils::CallOnAllRemoteChildren(mDocument->GetWindow(),
1221 0 : HandleAccessKeyInRemoteChild, &accessKeyInfo);
1222 : }
1223 : }
1224 :
1225 0 : return false;
1226 : }// end of HandleAccessKey
1227 :
1228 : void
1229 6 : EventStateManager::DispatchCrossProcessEvent(WidgetEvent* aEvent,
1230 : nsFrameLoader* aFrameLoader,
1231 : nsEventStatus *aStatus)
1232 : {
1233 6 : TabParent* remote = TabParent::GetFrom(aFrameLoader);
1234 6 : if (!remote) {
1235 0 : return;
1236 : }
1237 :
1238 6 : switch (aEvent->mClass) {
1239 : case eMouseEventClass: {
1240 6 : remote->SendRealMouseEvent(*aEvent->AsMouseEvent());
1241 6 : return;
1242 : }
1243 : case eKeyboardEventClass: {
1244 0 : remote->SendRealKeyEvent(*aEvent->AsKeyboardEvent());
1245 0 : return;
1246 : }
1247 : case eWheelEventClass: {
1248 0 : remote->SendMouseWheelEvent(*aEvent->AsWheelEvent());
1249 0 : return;
1250 : }
1251 : case eTouchEventClass: {
1252 : // Let the child process synthesize a mouse event if needed, and
1253 : // ensure we don't synthesize one in this process.
1254 0 : *aStatus = nsEventStatus_eConsumeNoDefault;
1255 0 : remote->SendRealTouchEvent(*aEvent->AsTouchEvent());
1256 0 : return;
1257 : }
1258 : case eDragEventClass: {
1259 0 : if (remote->Manager()->IsContentParent()) {
1260 0 : remote->Manager()->AsContentParent()->MaybeInvokeDragSession(remote);
1261 : }
1262 :
1263 0 : nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession();
1264 0 : uint32_t dropEffect = nsIDragService::DRAGDROP_ACTION_NONE;
1265 0 : uint32_t action = nsIDragService::DRAGDROP_ACTION_NONE;
1266 0 : if (dragSession) {
1267 0 : dragSession->DragEventDispatchedToChildProcess();
1268 0 : dragSession->GetDragAction(&action);
1269 0 : nsCOMPtr<nsIDOMDataTransfer> initialDataTransfer;
1270 0 : dragSession->GetDataTransfer(getter_AddRefs(initialDataTransfer));
1271 0 : if (initialDataTransfer) {
1272 0 : initialDataTransfer->GetDropEffectInt(&dropEffect);
1273 : }
1274 : }
1275 :
1276 0 : remote->SendRealDragEvent(*aEvent->AsDragEvent(), action, dropEffect);
1277 0 : return;
1278 : }
1279 : case ePluginEventClass: {
1280 0 : *aStatus = nsEventStatus_eConsumeNoDefault;
1281 0 : remote->SendPluginEvent(*aEvent->AsPluginEvent());
1282 0 : return;
1283 : }
1284 : default: {
1285 0 : MOZ_CRASH("Attempt to send non-whitelisted event?");
1286 : }
1287 : }
1288 : }
1289 :
1290 : bool
1291 18 : EventStateManager::IsRemoteTarget(nsIContent* target)
1292 : {
1293 18 : return !!TabParent::GetFrom(target);
1294 : }
1295 :
1296 : bool
1297 12 : EventStateManager::HandleCrossProcessEvent(WidgetEvent* aEvent,
1298 : nsEventStatus *aStatus) {
1299 24 : if (*aStatus == nsEventStatus_eConsumeNoDefault ||
1300 12 : !aEvent->CanBeSentToRemoteProcess()) {
1301 6 : return false;
1302 : }
1303 :
1304 6 : MOZ_ASSERT(!aEvent->HasBeenPostedToRemoteProcess(),
1305 : "Why do we need to post same event to remote processes again?");
1306 :
1307 : // Collect the remote event targets we're going to forward this
1308 : // event to.
1309 : //
1310 : // NB: the elements of |targets| must be unique, for correctness.
1311 12 : AutoTArray<nsCOMPtr<nsIContent>, 1> targets;
1312 6 : if (aEvent->mClass != eTouchEventClass || aEvent->mMessage == eTouchStart) {
1313 : // If this event only has one target, and it's remote, add it to
1314 : // the array.
1315 : nsIFrame* frame =
1316 6 : aEvent->mMessage == eDragExit ? sLastDragOverFrame.GetFrame() : GetEventTarget();
1317 6 : nsIContent* target = frame ? frame->GetContent() : nullptr;
1318 6 : if (IsRemoteTarget(target)) {
1319 6 : targets.AppendElement(target);
1320 6 : }
1321 : } else {
1322 : // This is a touch event with possibly multiple touch points.
1323 : // Each touch point may have its own target. So iterate through
1324 : // all of them and collect the unique set of targets for event
1325 : // forwarding.
1326 : //
1327 : // This loop is similar to the one used in
1328 : // PresShell::DispatchTouchEvent().
1329 : const WidgetTouchEvent::TouchArray& touches =
1330 0 : aEvent->AsTouchEvent()->mTouches;
1331 0 : for (uint32_t i = 0; i < touches.Length(); ++i) {
1332 0 : Touch* touch = touches[i];
1333 : // NB: the |mChanged| check is an optimization, subprocesses can
1334 : // compute this for themselves. If the touch hasn't changed, we
1335 : // may be able to avoid forwarding the event entirely (which is
1336 : // not free).
1337 0 : if (!touch || !touch->mChanged) {
1338 0 : continue;
1339 : }
1340 0 : nsCOMPtr<EventTarget> targetPtr = touch->mTarget;
1341 0 : if (!targetPtr) {
1342 0 : continue;
1343 : }
1344 0 : nsCOMPtr<nsIContent> target = do_QueryInterface(targetPtr);
1345 0 : if (IsRemoteTarget(target) && !targets.Contains(target)) {
1346 0 : targets.AppendElement(target);
1347 : }
1348 : }
1349 : }
1350 :
1351 6 : if (targets.Length() == 0) {
1352 0 : return false;
1353 : }
1354 :
1355 : // Look up the frame loader for all the remote targets we found, and
1356 : // then dispatch the event to the remote content they represent.
1357 12 : for (uint32_t i = 0; i < targets.Length(); ++i) {
1358 6 : nsIContent* target = targets[i];
1359 12 : nsCOMPtr<nsIFrameLoaderOwner> loaderOwner = do_QueryInterface(target);
1360 6 : if (!loaderOwner) {
1361 0 : continue;
1362 : }
1363 :
1364 12 : RefPtr<nsFrameLoader> frameLoader = loaderOwner->GetFrameLoader();
1365 6 : if (!frameLoader) {
1366 0 : continue;
1367 : }
1368 :
1369 : uint32_t eventMode;
1370 6 : frameLoader->GetEventMode(&eventMode);
1371 6 : if (eventMode == nsIFrameLoader::EVENT_MODE_DONT_FORWARD_TO_CHILD) {
1372 0 : continue;
1373 : }
1374 :
1375 6 : DispatchCrossProcessEvent(aEvent, frameLoader, aStatus);
1376 : }
1377 6 : return aEvent->HasBeenPostedToRemoteProcess();
1378 : }
1379 :
1380 : //
1381 : // CreateClickHoldTimer
1382 : //
1383 : // Fire off a timer for determining if the user wants click-hold. This timer
1384 : // is a one-shot that will be cancelled when the user moves enough to fire
1385 : // a drag.
1386 : //
1387 : void
1388 0 : EventStateManager::CreateClickHoldTimer(nsPresContext* inPresContext,
1389 : nsIFrame* inDownFrame,
1390 : WidgetGUIEvent* inMouseDownEvent)
1391 : {
1392 0 : if (!inMouseDownEvent->IsTrusted() ||
1393 0 : IsRemoteTarget(mGestureDownContent) ||
1394 : sIsPointerLocked) {
1395 0 : return;
1396 : }
1397 :
1398 : // just to be anal (er, safe)
1399 0 : if (mClickHoldTimer) {
1400 0 : mClickHoldTimer->Cancel();
1401 0 : mClickHoldTimer = nullptr;
1402 : }
1403 :
1404 : // if content clicked on has a popup, don't even start the timer
1405 : // since we'll end up conflicting and both will show.
1406 0 : if (mGestureDownContent) {
1407 : // check for the |popup| attribute
1408 0 : if (nsContentUtils::HasNonEmptyAttr(mGestureDownContent, kNameSpaceID_None,
1409 0 : nsGkAtoms::popup))
1410 0 : return;
1411 :
1412 : // check for a <menubutton> like bookmarks
1413 0 : if (mGestureDownContent->IsXULElement(nsGkAtoms::menubutton))
1414 0 : return;
1415 : }
1416 :
1417 0 : mClickHoldTimer = do_CreateInstance("@mozilla.org/timer;1");
1418 0 : if (mClickHoldTimer) {
1419 : int32_t clickHoldDelay =
1420 0 : Preferences::GetInt("ui.click_hold_context_menus.delay", 500);
1421 0 : mClickHoldTimer->SetTarget(SystemGroup::EventTargetFor(TaskCategory::Other));
1422 0 : mClickHoldTimer->InitWithNamedFuncCallback(
1423 : sClickHoldCallback,
1424 : this,
1425 : clickHoldDelay,
1426 : nsITimer::TYPE_ONE_SHOT,
1427 0 : "EventStateManager::CreateClickHoldTimer");
1428 : }
1429 : } // CreateClickHoldTimer
1430 :
1431 : //
1432 : // KillClickHoldTimer
1433 : //
1434 : // Stop the timer that would show the context menu dead in its tracks
1435 : //
1436 : void
1437 0 : EventStateManager::KillClickHoldTimer()
1438 : {
1439 0 : if (mClickHoldTimer) {
1440 0 : mClickHoldTimer->Cancel();
1441 0 : mClickHoldTimer = nullptr;
1442 : }
1443 0 : }
1444 :
1445 : //
1446 : // sClickHoldCallback
1447 : //
1448 : // This fires after the mouse has been down for a certain length of time.
1449 : //
1450 : void
1451 0 : EventStateManager::sClickHoldCallback(nsITimer* aTimer, void* aESM)
1452 : {
1453 0 : RefPtr<EventStateManager> self = static_cast<EventStateManager*>(aESM);
1454 0 : if (self) {
1455 0 : self->FireContextClick();
1456 : }
1457 :
1458 : // NOTE: |aTimer| and |self->mAutoHideTimer| are invalid after calling ClosePopup();
1459 :
1460 0 : } // sAutoHideCallback
1461 :
1462 : //
1463 : // FireContextClick
1464 : //
1465 : // If we're this far, our timer has fired, which means the mouse has been down
1466 : // for a certain period of time and has not moved enough to generate a dragGesture.
1467 : // We can be certain the user wants a context-click at this stage, so generate
1468 : // a dom event and fire it in.
1469 : //
1470 : // After the event fires, check if PreventDefault() has been set on the event which
1471 : // means that someone either ate the event or put up a context menu. This is our cue
1472 : // to stop tracking the drag gesture. If we always did this, draggable items w/out
1473 : // a context menu wouldn't be draggable after a certain length of time, which is
1474 : // _not_ what we want.
1475 : //
1476 : void
1477 0 : EventStateManager::FireContextClick()
1478 : {
1479 0 : if (!mGestureDownContent || !mPresContext || sIsPointerLocked) {
1480 0 : return;
1481 : }
1482 :
1483 : #ifdef XP_MACOSX
1484 : // Hack to ensure that we don't show a context menu when the user
1485 : // let go of the mouse after a long cpu-hogging operation prevented
1486 : // us from handling any OS events. See bug 117589.
1487 : if (!CGEventSourceButtonState(kCGEventSourceStateCombinedSessionState, kCGMouseButtonLeft))
1488 : return;
1489 : #endif
1490 :
1491 0 : nsEventStatus status = nsEventStatus_eIgnore;
1492 :
1493 : // Dispatch to the DOM. We have to fake out the ESM and tell it that the
1494 : // current target frame is actually where the mouseDown occurred, otherwise it
1495 : // will use the frame the mouse is currently over which may or may not be
1496 : // the same. (Note: saari and I have decided that we don't have to reset |mCurrentTarget|
1497 : // when we're through because no one else is doing anything more with this
1498 : // event and it will get reset on the very next event to the correct frame).
1499 0 : mCurrentTarget = mPresContext->GetPrimaryFrameFor(mGestureDownContent);
1500 : // make sure the widget sticks around
1501 0 : nsCOMPtr<nsIWidget> targetWidget;
1502 0 : if (mCurrentTarget && (targetWidget = mCurrentTarget->GetNearestWidget())) {
1503 0 : NS_ASSERTION(mPresContext == mCurrentTarget->PresContext(),
1504 : "a prescontext returned a primary frame that didn't belong to it?");
1505 :
1506 : // before dispatching, check that we're not on something that
1507 : // doesn't get a context menu
1508 0 : bool allowedToDispatch = true;
1509 :
1510 0 : if (mGestureDownContent->IsAnyOfXULElements(nsGkAtoms::scrollbar,
1511 : nsGkAtoms::scrollbarbutton,
1512 : nsGkAtoms::button)) {
1513 0 : allowedToDispatch = false;
1514 0 : } else if (mGestureDownContent->IsXULElement(nsGkAtoms::toolbarbutton)) {
1515 : // a <toolbarbutton> that has the container attribute set
1516 : // will already have its own dropdown.
1517 0 : if (nsContentUtils::HasNonEmptyAttr(mGestureDownContent,
1518 0 : kNameSpaceID_None, nsGkAtoms::container)) {
1519 0 : allowedToDispatch = false;
1520 : } else {
1521 : // If the toolbar button has an open menu, don't attempt to open
1522 : // a second menu
1523 0 : if (mGestureDownContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::open,
1524 : nsGkAtoms::_true, eCaseMatters)) {
1525 0 : allowedToDispatch = false;
1526 : }
1527 : }
1528 : }
1529 0 : else if (mGestureDownContent->IsHTMLElement()) {
1530 0 : nsCOMPtr<nsIFormControl> formCtrl(do_QueryInterface(mGestureDownContent));
1531 :
1532 0 : if (formCtrl) {
1533 0 : allowedToDispatch = formCtrl->IsTextOrNumberControl(/*aExcludePassword*/ false) ||
1534 0 : formCtrl->ControlType() == NS_FORM_INPUT_FILE;
1535 : }
1536 0 : else if (mGestureDownContent->IsAnyOfHTMLElements(nsGkAtoms::applet,
1537 : nsGkAtoms::embed,
1538 : nsGkAtoms::object,
1539 : nsGkAtoms::label)) {
1540 0 : allowedToDispatch = false;
1541 : }
1542 : }
1543 :
1544 0 : if (allowedToDispatch) {
1545 : // init the event while mCurrentTarget is still good
1546 : WidgetMouseEvent event(true, eContextMenu, targetWidget,
1547 0 : WidgetMouseEvent::eReal);
1548 0 : event.mClickCount = 1;
1549 0 : FillInEventFromGestureDown(&event);
1550 :
1551 : // stop selection tracking, we're in control now
1552 0 : if (mCurrentTarget)
1553 : {
1554 : RefPtr<nsFrameSelection> frameSel =
1555 0 : mCurrentTarget->GetFrameSelection();
1556 :
1557 0 : if (frameSel && frameSel->GetDragState()) {
1558 : // note that this can cause selection changed events to fire if we're in
1559 : // a text field, which will null out mCurrentTarget
1560 0 : frameSel->SetDragState(false);
1561 : }
1562 : }
1563 :
1564 0 : nsIDocument* doc = mGestureDownContent->GetComposedDoc();
1565 0 : AutoHandlingUserInputStatePusher userInpStatePusher(true, &event, doc);
1566 :
1567 : // dispatch to DOM
1568 0 : EventDispatcher::Dispatch(mGestureDownContent, mPresContext, &event,
1569 0 : nullptr, &status);
1570 :
1571 : // We don't need to dispatch to frame handling because no frames
1572 : // watch eContextMenu except for nsMenuFrame and that's only for
1573 : // dismissal. That's just as well since we don't really know
1574 : // which frame to send it to.
1575 : }
1576 : }
1577 :
1578 : // now check if the event has been handled. If so, stop tracking a drag
1579 0 : if (status == nsEventStatus_eConsumeNoDefault) {
1580 0 : StopTrackingDragGesture();
1581 : }
1582 :
1583 0 : KillClickHoldTimer();
1584 :
1585 : } // FireContextClick
1586 :
1587 : //
1588 : // BeginTrackingDragGesture
1589 : //
1590 : // Record that the mouse has gone down and that we should move to TRACKING state
1591 : // of d&d gesture tracker.
1592 : //
1593 : // We also use this to track click-hold context menus. When the mouse goes down,
1594 : // fire off a short timer. If the timer goes off and we have yet to fire the
1595 : // drag gesture (ie, the mouse hasn't moved a certain distance), then we can
1596 : // assume the user wants a click-hold, so fire a context-click event. We only
1597 : // want to cancel the drag gesture if the context-click event is handled.
1598 : //
1599 : void
1600 0 : EventStateManager::BeginTrackingDragGesture(nsPresContext* aPresContext,
1601 : WidgetMouseEvent* inDownEvent,
1602 : nsIFrame* inDownFrame)
1603 : {
1604 0 : if (!inDownEvent->mWidget) {
1605 0 : return;
1606 : }
1607 :
1608 : // Note that |inDownEvent| could be either a mouse down event or a
1609 : // synthesized mouse move event.
1610 : mGestureDownPoint =
1611 0 : inDownEvent->mRefPoint + inDownEvent->mWidget->WidgetToScreenOffset();
1612 :
1613 0 : if (inDownFrame) {
1614 0 : inDownFrame->GetContentForEvent(inDownEvent,
1615 0 : getter_AddRefs(mGestureDownContent));
1616 :
1617 0 : mGestureDownFrameOwner = inDownFrame->GetContent();
1618 0 : if (!mGestureDownFrameOwner) {
1619 0 : mGestureDownFrameOwner = mGestureDownContent;
1620 : }
1621 : }
1622 0 : mGestureModifiers = inDownEvent->mModifiers;
1623 0 : mGestureDownButtons = inDownEvent->buttons;
1624 :
1625 0 : if (inDownEvent->mMessage != eMouseTouchDrag && Prefs::ClickHoldContextMenu()) {
1626 : // fire off a timer to track click-hold
1627 0 : CreateClickHoldTimer(aPresContext, inDownFrame, inDownEvent);
1628 : }
1629 : }
1630 :
1631 : void
1632 0 : EventStateManager::BeginTrackingRemoteDragGesture(nsIContent* aContent)
1633 : {
1634 0 : mGestureDownContent = aContent;
1635 0 : mGestureDownFrameOwner = aContent;
1636 0 : }
1637 :
1638 : //
1639 : // StopTrackingDragGesture
1640 : //
1641 : // Record that the mouse has gone back up so that we should leave the TRACKING
1642 : // state of d&d gesture tracker and return to the START state.
1643 : //
1644 : void
1645 0 : EventStateManager::StopTrackingDragGesture()
1646 : {
1647 0 : mGestureDownContent = nullptr;
1648 0 : mGestureDownFrameOwner = nullptr;
1649 0 : }
1650 :
1651 : void
1652 0 : EventStateManager::FillInEventFromGestureDown(WidgetMouseEvent* aEvent)
1653 : {
1654 0 : NS_ASSERTION(aEvent->mWidget == mCurrentTarget->GetNearestWidget(),
1655 : "Incorrect widget in event");
1656 :
1657 : // Set the coordinates in the new event to the coordinates of
1658 : // the old event, adjusted for the fact that the widget might be
1659 : // different
1660 : aEvent->mRefPoint =
1661 0 : mGestureDownPoint - aEvent->mWidget->WidgetToScreenOffset();
1662 0 : aEvent->mModifiers = mGestureModifiers;
1663 0 : aEvent->buttons = mGestureDownButtons;
1664 0 : }
1665 :
1666 : //
1667 : // GenerateDragGesture
1668 : //
1669 : // If we're in the TRACKING state of the d&d gesture tracker, check the current position
1670 : // of the mouse in relation to the old one. If we've moved a sufficient amount from
1671 : // the mouse down, then fire off a drag gesture event.
1672 : void
1673 8 : EventStateManager::GenerateDragGesture(nsPresContext* aPresContext,
1674 : WidgetInputEvent* aEvent)
1675 : {
1676 8 : NS_ASSERTION(aPresContext, "This shouldn't happen.");
1677 8 : if (IsTrackingDragGesture()) {
1678 0 : mCurrentTarget = mGestureDownFrameOwner->GetPrimaryFrame();
1679 :
1680 0 : if (!mCurrentTarget || !mCurrentTarget->GetNearestWidget()) {
1681 0 : StopTrackingDragGesture();
1682 0 : return;
1683 : }
1684 :
1685 : // Check if selection is tracking drag gestures, if so
1686 : // don't interfere!
1687 0 : if (mCurrentTarget)
1688 : {
1689 0 : RefPtr<nsFrameSelection> frameSel = mCurrentTarget->GetFrameSelection();
1690 0 : if (frameSel && frameSel->GetDragState()) {
1691 0 : StopTrackingDragGesture();
1692 0 : return;
1693 : }
1694 : }
1695 :
1696 : // If non-native code is capturing the mouse don't start a drag.
1697 0 : if (nsIPresShell::IsMouseCapturePreventingDrag()) {
1698 0 : StopTrackingDragGesture();
1699 0 : return;
1700 : }
1701 :
1702 : static int32_t pixelThresholdX = 0;
1703 : static int32_t pixelThresholdY = 0;
1704 :
1705 0 : if (!pixelThresholdX) {
1706 0 : pixelThresholdX =
1707 0 : LookAndFeel::GetInt(LookAndFeel::eIntID_DragThresholdX, 0);
1708 0 : pixelThresholdY =
1709 0 : LookAndFeel::GetInt(LookAndFeel::eIntID_DragThresholdY, 0);
1710 0 : if (!pixelThresholdX)
1711 0 : pixelThresholdX = 5;
1712 0 : if (!pixelThresholdY)
1713 0 : pixelThresholdY = 5;
1714 : }
1715 :
1716 : // fire drag gesture if mouse has moved enough
1717 0 : LayoutDeviceIntPoint pt = aEvent->mWidget->WidgetToScreenOffset() +
1718 0 : (aEvent->AsTouchEvent() ? aEvent->AsTouchEvent()->mTouches[0]->mRefPoint
1719 0 : : aEvent->mRefPoint);
1720 0 : LayoutDeviceIntPoint distance = pt - mGestureDownPoint;
1721 0 : if (Abs(distance.x) > AssertedCast<uint32_t>(pixelThresholdX) ||
1722 0 : Abs(distance.y) > AssertedCast<uint32_t>(pixelThresholdY)) {
1723 0 : if (Prefs::ClickHoldContextMenu()) {
1724 : // stop the click-hold before we fire off the drag gesture, in case
1725 : // it takes a long time
1726 0 : KillClickHoldTimer();
1727 : }
1728 :
1729 0 : nsCOMPtr<nsIDocShell> docshell = aPresContext->GetDocShell();
1730 0 : if (!docshell) {
1731 0 : return;
1732 : }
1733 :
1734 0 : nsCOMPtr<nsPIDOMWindowOuter> window = docshell->GetWindow();
1735 0 : if (!window)
1736 0 : return;
1737 :
1738 : RefPtr<DataTransfer> dataTransfer =
1739 0 : new DataTransfer(window, eDragStart, false, -1);
1740 :
1741 0 : nsCOMPtr<nsISelection> selection;
1742 0 : nsCOMPtr<nsIContent> eventContent, targetContent;
1743 0 : mCurrentTarget->GetContentForEvent(aEvent, getter_AddRefs(eventContent));
1744 0 : if (eventContent)
1745 0 : DetermineDragTargetAndDefaultData(window, eventContent, dataTransfer,
1746 0 : getter_AddRefs(selection),
1747 0 : getter_AddRefs(targetContent));
1748 :
1749 : // Stop tracking the drag gesture now. This should stop us from
1750 : // reentering GenerateDragGesture inside DOM event processing.
1751 0 : StopTrackingDragGesture();
1752 :
1753 0 : if (!targetContent)
1754 0 : return;
1755 :
1756 : // Use our targetContent, now that we've determined it, as the
1757 : // parent object of the DataTransfer.
1758 0 : dataTransfer->SetParentObject(targetContent);
1759 :
1760 0 : sLastDragOverFrame = nullptr;
1761 0 : nsCOMPtr<nsIWidget> widget = mCurrentTarget->GetNearestWidget();
1762 :
1763 : // get the widget from the target frame
1764 0 : WidgetDragEvent startEvent(aEvent->IsTrusted(), eDragStart, widget);
1765 0 : FillInEventFromGestureDown(&startEvent);
1766 :
1767 0 : startEvent.mDataTransfer = dataTransfer;
1768 0 : if (aEvent->AsMouseEvent()) {
1769 0 : startEvent.inputSource = aEvent->AsMouseEvent()->inputSource;
1770 0 : } else if (aEvent->AsTouchEvent()) {
1771 0 : startEvent.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH;
1772 : } else {
1773 0 : MOZ_ASSERT(false);
1774 : }
1775 :
1776 : // Dispatch to the DOM. By setting mCurrentTarget we are faking
1777 : // out the ESM and telling it that the current target frame is
1778 : // actually where the mouseDown occurred, otherwise it will use
1779 : // the frame the mouse is currently over which may or may not be
1780 : // the same. (Note: saari and I have decided that we don't have
1781 : // to reset |mCurrentTarget| when we're through because no one
1782 : // else is doing anything more with this event and it will get
1783 : // reset on the very next event to the correct frame).
1784 :
1785 : // Hold onto old target content through the event and reset after.
1786 0 : nsCOMPtr<nsIContent> targetBeforeEvent = mCurrentTargetContent;
1787 :
1788 : // Set the current target to the content for the mouse down
1789 0 : mCurrentTargetContent = targetContent;
1790 :
1791 : // Dispatch the dragstart event to the DOM.
1792 0 : nsEventStatus status = nsEventStatus_eIgnore;
1793 : EventDispatcher::Dispatch(targetContent, aPresContext, &startEvent,
1794 0 : nullptr, &status);
1795 :
1796 0 : WidgetDragEvent* event = &startEvent;
1797 :
1798 : nsCOMPtr<nsIObserverService> observerService =
1799 0 : mozilla::services::GetObserverService();
1800 : // Emit observer event to allow addons to modify the DataTransfer object.
1801 0 : if (observerService) {
1802 0 : observerService->NotifyObservers(dataTransfer,
1803 : "on-datatransfer-available",
1804 0 : nullptr);
1805 : }
1806 :
1807 : // now that the dataTransfer has been updated in the dragstart and
1808 : // draggesture events, make it read only so that the data doesn't
1809 : // change during the drag.
1810 0 : dataTransfer->SetReadOnly();
1811 :
1812 0 : if (status != nsEventStatus_eConsumeNoDefault) {
1813 0 : bool dragStarted = DoDefaultDragStart(aPresContext, event, dataTransfer,
1814 0 : targetContent, selection);
1815 0 : if (dragStarted) {
1816 0 : sActiveESM = nullptr;
1817 0 : aEvent->StopPropagation();
1818 : }
1819 : }
1820 :
1821 : // Reset mCurretTargetContent to what it was
1822 0 : mCurrentTargetContent = targetBeforeEvent;
1823 : }
1824 :
1825 : // Now flush all pending notifications, for better responsiveness
1826 : // while dragging.
1827 0 : FlushPendingEvents(aPresContext);
1828 : }
1829 : } // GenerateDragGesture
1830 :
1831 : void
1832 0 : EventStateManager::DetermineDragTargetAndDefaultData(nsPIDOMWindowOuter* aWindow,
1833 : nsIContent* aSelectionTarget,
1834 : DataTransfer* aDataTransfer,
1835 : nsISelection** aSelection,
1836 : nsIContent** aTargetNode)
1837 : {
1838 0 : *aTargetNode = nullptr;
1839 :
1840 : // GetDragData determines if a selection, link or image in the content
1841 : // should be dragged, and places the data associated with the drag in the
1842 : // data transfer.
1843 : // mGestureDownContent is the node where the mousedown event for the drag
1844 : // occurred, and aSelectionTarget is the node to use when a selection is used
1845 : bool canDrag;
1846 0 : nsCOMPtr<nsIContent> dragDataNode;
1847 0 : bool wasAlt = (mGestureModifiers & MODIFIER_ALT) != 0;
1848 0 : nsresult rv = nsContentAreaDragDrop::GetDragData(aWindow, mGestureDownContent,
1849 : aSelectionTarget, wasAlt,
1850 : aDataTransfer, &canDrag, aSelection,
1851 0 : getter_AddRefs(dragDataNode));
1852 0 : if (NS_FAILED(rv) || !canDrag)
1853 0 : return;
1854 :
1855 : // if GetDragData returned a node, use that as the node being dragged.
1856 : // Otherwise, if a selection is being dragged, use the node within the
1857 : // selection that was dragged. Otherwise, just use the mousedown target.
1858 0 : nsIContent* dragContent = mGestureDownContent;
1859 0 : if (dragDataNode)
1860 0 : dragContent = dragDataNode;
1861 0 : else if (*aSelection)
1862 0 : dragContent = aSelectionTarget;
1863 :
1864 0 : nsIContent* originalDragContent = dragContent;
1865 :
1866 : // If a selection isn't being dragged, look for an ancestor with the
1867 : // draggable property set. If one is found, use that as the target of the
1868 : // drag instead of the node that was clicked on. If a draggable node wasn't
1869 : // found, just use the clicked node.
1870 0 : if (!*aSelection) {
1871 0 : while (dragContent) {
1872 0 : nsCOMPtr<nsIDOMHTMLElement> htmlElement = do_QueryInterface(dragContent);
1873 0 : if (htmlElement) {
1874 0 : bool draggable = false;
1875 0 : htmlElement->GetDraggable(&draggable);
1876 0 : if (draggable)
1877 0 : break;
1878 : }
1879 : else {
1880 0 : nsCOMPtr<nsIDOMXULElement> xulElement = do_QueryInterface(dragContent);
1881 0 : if (xulElement) {
1882 : // All XUL elements are draggable, so if a XUL element is
1883 : // encountered, stop looking for draggable nodes and just use the
1884 : // original clicked node instead.
1885 : // XXXndeakin
1886 : // In the future, we will want to improve this so that XUL has a
1887 : // better way to specify whether something is draggable than just
1888 : // on/off.
1889 0 : dragContent = mGestureDownContent;
1890 0 : break;
1891 : }
1892 : // otherwise, it's not an HTML or XUL element, so just keep looking
1893 : }
1894 0 : dragContent = dragContent->GetParent();
1895 : }
1896 : }
1897 :
1898 : // if no node in the hierarchy was found to drag, but the GetDragData method
1899 : // returned a node, use that returned node. Otherwise, nothing is draggable.
1900 0 : if (!dragContent && dragDataNode)
1901 0 : dragContent = dragDataNode;
1902 :
1903 0 : if (dragContent) {
1904 : // if an ancestor node was used instead, clear the drag data
1905 : // XXXndeakin rework this a bit. Find a way to just not call GetDragData if we don't need to.
1906 0 : if (dragContent != originalDragContent)
1907 0 : aDataTransfer->ClearAll();
1908 0 : *aTargetNode = dragContent;
1909 0 : NS_ADDREF(*aTargetNode);
1910 : }
1911 : }
1912 :
1913 : bool
1914 0 : EventStateManager::DoDefaultDragStart(nsPresContext* aPresContext,
1915 : WidgetDragEvent* aDragEvent,
1916 : DataTransfer* aDataTransfer,
1917 : nsIContent* aDragTarget,
1918 : nsISelection* aSelection)
1919 : {
1920 : nsCOMPtr<nsIDragService> dragService =
1921 0 : do_GetService("@mozilla.org/widget/dragservice;1");
1922 0 : if (!dragService)
1923 0 : return false;
1924 :
1925 : // Default handling for the dragstart event.
1926 : //
1927 : // First, check if a drag session already exists. This means that the drag
1928 : // service was called directly within a draggesture handler. In this case,
1929 : // don't do anything more, as it is assumed that the handler is managing
1930 : // drag and drop manually. Make sure to return true to indicate that a drag
1931 : // began.
1932 0 : nsCOMPtr<nsIDragSession> dragSession;
1933 0 : dragService->GetCurrentSession(getter_AddRefs(dragSession));
1934 0 : if (dragSession)
1935 0 : return true;
1936 :
1937 : // No drag session is currently active, so check if a handler added
1938 : // any items to be dragged. If not, there isn't anything to drag.
1939 0 : uint32_t count = 0;
1940 0 : if (aDataTransfer)
1941 0 : aDataTransfer->GetMozItemCount(&count);
1942 0 : if (!count)
1943 0 : return false;
1944 :
1945 : // Get the target being dragged, which may not be the same as the
1946 : // target of the mouse event. If one wasn't set in the
1947 : // aDataTransfer during the event handler, just use the original
1948 : // target instead.
1949 0 : nsCOMPtr<nsIContent> dragTarget = aDataTransfer->GetDragTarget();
1950 0 : if (!dragTarget) {
1951 0 : dragTarget = aDragTarget;
1952 0 : if (!dragTarget)
1953 0 : return false;
1954 : }
1955 :
1956 : // check which drag effect should initially be used. If the effect was not
1957 : // set, just use all actions, otherwise Windows won't allow a drop.
1958 : uint32_t action;
1959 0 : aDataTransfer->GetEffectAllowedInt(&action);
1960 0 : if (action == nsIDragService::DRAGDROP_ACTION_UNINITIALIZED)
1961 0 : action = nsIDragService::DRAGDROP_ACTION_COPY |
1962 : nsIDragService::DRAGDROP_ACTION_MOVE |
1963 : nsIDragService::DRAGDROP_ACTION_LINK;
1964 :
1965 : // get any custom drag image that was set
1966 : int32_t imageX, imageY;
1967 0 : Element* dragImage = aDataTransfer->GetDragImage(&imageX, &imageY);
1968 :
1969 : nsCOMPtr<nsIArray> transArray =
1970 0 : aDataTransfer->GetTransferables(dragTarget->AsDOMNode());
1971 0 : if (!transArray)
1972 0 : return false;
1973 :
1974 : // XXXndeakin don't really want to create a new drag DOM event
1975 : // here, but we need something to pass to the InvokeDragSession
1976 : // methods.
1977 : RefPtr<DragEvent> event =
1978 0 : NS_NewDOMDragEvent(dragTarget, aPresContext, aDragEvent);
1979 :
1980 : // Use InvokeDragSessionWithSelection if a selection is being dragged,
1981 : // such that the image can be generated from the selected text. However,
1982 : // use InvokeDragSessionWithImage if a custom image was set or something
1983 : // other than a selection is being dragged.
1984 0 : if (!dragImage && aSelection) {
1985 0 : dragService->InvokeDragSessionWithSelection(aSelection, transArray,
1986 0 : action, event, aDataTransfer);
1987 : }
1988 : else {
1989 : // if dragging within a XUL tree and no custom drag image was
1990 : // set, the region argument to InvokeDragSessionWithImage needs
1991 : // to be set to the area encompassing the selected rows of the
1992 : // tree to ensure that the drag feedback gets clipped to those
1993 : // rows. For other content, region should be null.
1994 0 : nsCOMPtr<nsIScriptableRegion> region;
1995 : #ifdef MOZ_XUL
1996 0 : if (dragTarget && !dragImage) {
1997 0 : if (dragTarget->NodeInfo()->Equals(nsGkAtoms::treechildren,
1998 : kNameSpaceID_XUL)) {
1999 : nsTreeBodyFrame* treeBody =
2000 0 : do_QueryFrame(dragTarget->GetPrimaryFrame());
2001 0 : if (treeBody) {
2002 0 : treeBody->GetSelectionRegion(getter_AddRefs(region));
2003 : }
2004 : }
2005 : }
2006 : #endif
2007 :
2008 0 : dragService->InvokeDragSessionWithImage(dragTarget->AsDOMNode(), transArray,
2009 : region, action,
2010 0 : dragImage ? dragImage->AsDOMNode() :
2011 : nullptr,
2012 : imageX, imageY, event,
2013 0 : aDataTransfer);
2014 : }
2015 :
2016 0 : return true;
2017 : }
2018 :
2019 : nsresult
2020 0 : EventStateManager::GetContentViewer(nsIContentViewer** aCv)
2021 : {
2022 0 : *aCv = nullptr;
2023 :
2024 0 : nsPIDOMWindowOuter* window = mDocument->GetWindow();
2025 0 : if (!window) return NS_ERROR_FAILURE;
2026 0 : nsCOMPtr<nsPIDOMWindowOuter> rootWindow = window->GetPrivateRoot();
2027 0 : if (!rootWindow) return NS_ERROR_FAILURE;
2028 :
2029 0 : TabChild* tabChild = TabChild::GetFrom(rootWindow);
2030 0 : if (!tabChild) {
2031 0 : nsIFocusManager* fm = nsFocusManager::GetFocusManager();
2032 0 : if (!fm) return NS_ERROR_FAILURE;
2033 :
2034 0 : nsCOMPtr<mozIDOMWindowProxy> activeWindow;
2035 0 : fm->GetActiveWindow(getter_AddRefs(activeWindow));
2036 0 : if (rootWindow != activeWindow) return NS_OK;
2037 : } else {
2038 0 : if (!tabChild->ParentIsActive()) return NS_OK;
2039 : }
2040 :
2041 0 : nsCOMPtr<nsPIDOMWindowOuter> contentWindow = nsGlobalWindow::Cast(rootWindow)->GetContent();
2042 0 : if (!contentWindow) return NS_ERROR_FAILURE;
2043 :
2044 0 : nsIDocument *doc = contentWindow->GetDoc();
2045 0 : if (!doc) return NS_ERROR_FAILURE;
2046 :
2047 0 : nsCOMPtr<nsISupports> container = doc->GetContainer();
2048 0 : if (!container) return NS_ERROR_FAILURE;
2049 :
2050 0 : nsCOMPtr<nsIDocShell> docshell = do_QueryInterface(container);
2051 0 : docshell->GetContentViewer(aCv);
2052 0 : if (!*aCv) return NS_ERROR_FAILURE;
2053 :
2054 0 : return NS_OK;
2055 : }
2056 :
2057 : nsresult
2058 0 : EventStateManager::ChangeTextSize(int32_t change)
2059 : {
2060 0 : nsCOMPtr<nsIContentViewer> cv;
2061 0 : nsresult rv = GetContentViewer(getter_AddRefs(cv));
2062 0 : NS_ENSURE_SUCCESS(rv, rv);
2063 :
2064 0 : if (cv) {
2065 : float textzoom;
2066 0 : float zoomMin = ((float)Preferences::GetInt("zoom.minPercent", 50)) / 100;
2067 0 : float zoomMax = ((float)Preferences::GetInt("zoom.maxPercent", 300)) / 100;
2068 0 : cv->GetTextZoom(&textzoom);
2069 0 : textzoom += ((float)change) / 10;
2070 0 : if (textzoom < zoomMin)
2071 0 : textzoom = zoomMin;
2072 0 : else if (textzoom > zoomMax)
2073 0 : textzoom = zoomMax;
2074 0 : cv->SetTextZoom(textzoom);
2075 : }
2076 :
2077 0 : return NS_OK;
2078 : }
2079 :
2080 : nsresult
2081 0 : EventStateManager::ChangeFullZoom(int32_t change)
2082 : {
2083 0 : nsCOMPtr<nsIContentViewer> cv;
2084 0 : nsresult rv = GetContentViewer(getter_AddRefs(cv));
2085 0 : NS_ENSURE_SUCCESS(rv, rv);
2086 :
2087 0 : if (cv) {
2088 : float fullzoom;
2089 0 : float zoomMin = ((float)Preferences::GetInt("zoom.minPercent", 50)) / 100;
2090 0 : float zoomMax = ((float)Preferences::GetInt("zoom.maxPercent", 300)) / 100;
2091 0 : cv->GetFullZoom(&fullzoom);
2092 0 : fullzoom += ((float)change) / 10;
2093 0 : if (fullzoom < zoomMin)
2094 0 : fullzoom = zoomMin;
2095 0 : else if (fullzoom > zoomMax)
2096 0 : fullzoom = zoomMax;
2097 0 : cv->SetFullZoom(fullzoom);
2098 : }
2099 :
2100 0 : return NS_OK;
2101 : }
2102 :
2103 : void
2104 0 : EventStateManager::DoScrollHistory(int32_t direction)
2105 : {
2106 0 : nsCOMPtr<nsISupports> pcContainer(mPresContext->GetContainerWeak());
2107 0 : if (pcContainer) {
2108 0 : nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(pcContainer));
2109 0 : if (webNav) {
2110 : // positive direction to go back one step, nonpositive to go forward
2111 0 : if (direction > 0)
2112 0 : webNav->GoBack();
2113 : else
2114 0 : webNav->GoForward();
2115 : }
2116 : }
2117 0 : }
2118 :
2119 : void
2120 0 : EventStateManager::DoScrollZoom(nsIFrame* aTargetFrame,
2121 : int32_t adjustment)
2122 : {
2123 : // Exclude form controls and content in chrome docshells.
2124 0 : nsIContent *content = aTargetFrame->GetContent();
2125 0 : if (content &&
2126 0 : !content->IsNodeOfType(nsINode::eHTML_FORM_CONTROL) &&
2127 0 : !nsContentUtils::IsInChromeDocshell(content->OwnerDoc()))
2128 : {
2129 : // positive adjustment to decrease zoom, negative to increase
2130 0 : int32_t change = (adjustment > 0) ? -1 : 1;
2131 :
2132 0 : EnsureDocument(mPresContext);
2133 0 : if (Preferences::GetBool("browser.zoom.full") || content->OwnerDoc()->IsSyntheticDocument()) {
2134 0 : ChangeFullZoom(change);
2135 : } else {
2136 0 : ChangeTextSize(change);
2137 : }
2138 0 : nsContentUtils::DispatchChromeEvent(mDocument, static_cast<nsIDocument*>(mDocument),
2139 0 : NS_LITERAL_STRING("ZoomChangeUsingMouseWheel"),
2140 0 : true, true);
2141 : }
2142 0 : }
2143 :
2144 : static nsIFrame*
2145 0 : GetParentFrameToScroll(nsIFrame* aFrame)
2146 : {
2147 0 : if (!aFrame)
2148 0 : return nullptr;
2149 :
2150 0 : if (aFrame->StyleDisplay()->mPosition == NS_STYLE_POSITION_FIXED &&
2151 0 : nsLayoutUtils::IsReallyFixedPos(aFrame))
2152 0 : return aFrame->PresContext()->GetPresShell()->GetRootScrollFrame();
2153 :
2154 0 : return aFrame->GetParent();
2155 : }
2156 :
2157 : /*static*/ bool
2158 3 : EventStateManager::CanVerticallyScrollFrameWithWheel(nsIFrame* aFrame)
2159 : {
2160 3 : nsIContent* c = aFrame->GetContent();
2161 3 : if (!c) {
2162 3 : return true;
2163 : }
2164 : nsCOMPtr<nsITextControlElement> ctrl =
2165 0 : do_QueryInterface(c->IsInAnonymousSubtree() ? c->GetBindingParent() : c);
2166 0 : if (ctrl && ctrl->IsSingleLineTextControl()) {
2167 0 : return false;
2168 : }
2169 0 : return true;
2170 : }
2171 :
2172 : void
2173 0 : EventStateManager::DispatchLegacyMouseScrollEvents(nsIFrame* aTargetFrame,
2174 : WidgetWheelEvent* aEvent,
2175 : nsEventStatus* aStatus)
2176 : {
2177 0 : MOZ_ASSERT(aEvent);
2178 0 : MOZ_ASSERT(aStatus);
2179 :
2180 0 : if (!aTargetFrame || *aStatus == nsEventStatus_eConsumeNoDefault) {
2181 0 : return;
2182 : }
2183 :
2184 : // Ignore mouse wheel transaction for computing legacy mouse wheel
2185 : // events' delta value.
2186 : nsIFrame* scrollFrame =
2187 : ComputeScrollTarget(aTargetFrame, aEvent,
2188 0 : COMPUTE_LEGACY_MOUSE_SCROLL_EVENT_TARGET);
2189 :
2190 0 : nsIScrollableFrame* scrollTarget = do_QueryFrame(scrollFrame);
2191 : nsPresContext* pc =
2192 0 : scrollFrame ? scrollFrame->PresContext() : aTargetFrame->PresContext();
2193 :
2194 : // DOM event's delta vales are computed from CSS pixels.
2195 0 : nsSize scrollAmount = GetScrollAmount(pc, aEvent, scrollTarget);
2196 : nsIntSize scrollAmountInCSSPixels(
2197 : nsPresContext::AppUnitsToIntCSSPixels(scrollAmount.width),
2198 0 : nsPresContext::AppUnitsToIntCSSPixels(scrollAmount.height));
2199 :
2200 : // XXX We don't deal with fractional amount in legacy event, though the
2201 : // default action handler (DoScrollText()) deals with it.
2202 : // If we implemented such strict computation, we would need additional
2203 : // accumulated delta values. It would made the code more complicated.
2204 : // And also it would computes different delta values from older version.
2205 : // It doesn't make sense to implement such code for legacy events and
2206 : // rare cases.
2207 : int32_t scrollDeltaX, scrollDeltaY, pixelDeltaX, pixelDeltaY;
2208 0 : switch (aEvent->mDeltaMode) {
2209 : case nsIDOMWheelEvent::DOM_DELTA_PAGE:
2210 0 : scrollDeltaX =
2211 0 : !aEvent->mLineOrPageDeltaX ? 0 :
2212 0 : (aEvent->mLineOrPageDeltaX > 0 ? nsIDOMUIEvent::SCROLL_PAGE_DOWN :
2213 : nsIDOMUIEvent::SCROLL_PAGE_UP);
2214 0 : scrollDeltaY =
2215 0 : !aEvent->mLineOrPageDeltaY ? 0 :
2216 0 : (aEvent->mLineOrPageDeltaY > 0 ? nsIDOMUIEvent::SCROLL_PAGE_DOWN :
2217 : nsIDOMUIEvent::SCROLL_PAGE_UP);
2218 0 : pixelDeltaX = RoundDown(aEvent->mDeltaX * scrollAmountInCSSPixels.width);
2219 0 : pixelDeltaY = RoundDown(aEvent->mDeltaY * scrollAmountInCSSPixels.height);
2220 0 : break;
2221 :
2222 : case nsIDOMWheelEvent::DOM_DELTA_LINE:
2223 0 : scrollDeltaX = aEvent->mLineOrPageDeltaX;
2224 0 : scrollDeltaY = aEvent->mLineOrPageDeltaY;
2225 0 : pixelDeltaX = RoundDown(aEvent->mDeltaX * scrollAmountInCSSPixels.width);
2226 0 : pixelDeltaY = RoundDown(aEvent->mDeltaY * scrollAmountInCSSPixels.height);
2227 0 : break;
2228 :
2229 : case nsIDOMWheelEvent::DOM_DELTA_PIXEL:
2230 0 : scrollDeltaX = aEvent->mLineOrPageDeltaX;
2231 0 : scrollDeltaY = aEvent->mLineOrPageDeltaY;
2232 0 : pixelDeltaX = RoundDown(aEvent->mDeltaX);
2233 0 : pixelDeltaY = RoundDown(aEvent->mDeltaY);
2234 0 : break;
2235 :
2236 : default:
2237 0 : MOZ_CRASH("Invalid deltaMode value comes");
2238 : }
2239 :
2240 : // Send the legacy events in following order:
2241 : // 1. Vertical scroll
2242 : // 2. Vertical pixel scroll (even if #1 isn't consumed)
2243 : // 3. Horizontal scroll (even if #1 and/or #2 are consumed)
2244 : // 4. Horizontal pixel scroll (even if #3 isn't consumed)
2245 :
2246 0 : AutoWeakFrame targetFrame(aTargetFrame);
2247 :
2248 0 : MOZ_ASSERT(*aStatus != nsEventStatus_eConsumeNoDefault &&
2249 : !aEvent->DefaultPrevented(),
2250 : "If you make legacy events dispatched for default prevented wheel "
2251 : "event, you need to initialize stateX and stateY");
2252 0 : EventState stateX, stateY;
2253 0 : if (scrollDeltaY) {
2254 : SendLineScrollEvent(aTargetFrame, aEvent, stateY,
2255 0 : scrollDeltaY, DELTA_DIRECTION_Y);
2256 0 : if (!targetFrame.IsAlive()) {
2257 0 : *aStatus = nsEventStatus_eConsumeNoDefault;
2258 0 : return;
2259 : }
2260 : }
2261 :
2262 0 : if (pixelDeltaY) {
2263 : SendPixelScrollEvent(aTargetFrame, aEvent, stateY,
2264 0 : pixelDeltaY, DELTA_DIRECTION_Y);
2265 0 : if (!targetFrame.IsAlive()) {
2266 0 : *aStatus = nsEventStatus_eConsumeNoDefault;
2267 0 : return;
2268 : }
2269 : }
2270 :
2271 0 : if (scrollDeltaX) {
2272 : SendLineScrollEvent(aTargetFrame, aEvent, stateX,
2273 0 : scrollDeltaX, DELTA_DIRECTION_X);
2274 0 : if (!targetFrame.IsAlive()) {
2275 0 : *aStatus = nsEventStatus_eConsumeNoDefault;
2276 0 : return;
2277 : }
2278 : }
2279 :
2280 0 : if (pixelDeltaX) {
2281 : SendPixelScrollEvent(aTargetFrame, aEvent, stateX,
2282 0 : pixelDeltaX, DELTA_DIRECTION_X);
2283 0 : if (!targetFrame.IsAlive()) {
2284 0 : *aStatus = nsEventStatus_eConsumeNoDefault;
2285 0 : return;
2286 : }
2287 : }
2288 :
2289 0 : if (stateY.mDefaultPrevented) {
2290 0 : *aStatus = nsEventStatus_eConsumeNoDefault;
2291 0 : aEvent->PreventDefault(!stateY.mDefaultPreventedByContent);
2292 : }
2293 :
2294 0 : if (stateX.mDefaultPrevented) {
2295 0 : *aStatus = nsEventStatus_eConsumeNoDefault;
2296 0 : aEvent->PreventDefault(!stateX.mDefaultPreventedByContent);
2297 : }
2298 : }
2299 :
2300 : void
2301 0 : EventStateManager::SendLineScrollEvent(nsIFrame* aTargetFrame,
2302 : WidgetWheelEvent* aEvent,
2303 : EventState& aState,
2304 : int32_t aDelta,
2305 : DeltaDirection aDeltaDirection)
2306 : {
2307 0 : nsCOMPtr<nsIContent> targetContent = aTargetFrame->GetContent();
2308 0 : if (!targetContent)
2309 0 : targetContent = GetFocusedContent();
2310 0 : if (!targetContent)
2311 0 : return;
2312 :
2313 0 : while (targetContent->IsNodeOfType(nsINode::eTEXT)) {
2314 0 : targetContent = targetContent->GetParent();
2315 : }
2316 :
2317 0 : WidgetMouseScrollEvent event(aEvent->IsTrusted(),
2318 0 : eLegacyMouseLineOrPageScroll, aEvent->mWidget);
2319 0 : event.mFlags.mDefaultPrevented = aState.mDefaultPrevented;
2320 0 : event.mFlags.mDefaultPreventedByContent = aState.mDefaultPreventedByContent;
2321 0 : event.mRefPoint = aEvent->mRefPoint;
2322 0 : event.mTime = aEvent->mTime;
2323 0 : event.mTimeStamp = aEvent->mTimeStamp;
2324 0 : event.mModifiers = aEvent->mModifiers;
2325 0 : event.buttons = aEvent->buttons;
2326 0 : event.mIsHorizontal = (aDeltaDirection == DELTA_DIRECTION_X);
2327 0 : event.mDelta = aDelta;
2328 0 : event.inputSource = aEvent->inputSource;
2329 :
2330 0 : nsEventStatus status = nsEventStatus_eIgnore;
2331 0 : EventDispatcher::Dispatch(targetContent, aTargetFrame->PresContext(),
2332 0 : &event, nullptr, &status);
2333 0 : aState.mDefaultPrevented =
2334 0 : event.DefaultPrevented() || status == nsEventStatus_eConsumeNoDefault;
2335 0 : aState.mDefaultPreventedByContent = event.DefaultPreventedByContent();
2336 : }
2337 :
2338 : void
2339 0 : EventStateManager::SendPixelScrollEvent(nsIFrame* aTargetFrame,
2340 : WidgetWheelEvent* aEvent,
2341 : EventState& aState,
2342 : int32_t aPixelDelta,
2343 : DeltaDirection aDeltaDirection)
2344 : {
2345 0 : nsCOMPtr<nsIContent> targetContent = aTargetFrame->GetContent();
2346 0 : if (!targetContent) {
2347 0 : targetContent = GetFocusedContent();
2348 0 : if (!targetContent)
2349 0 : return;
2350 : }
2351 :
2352 0 : while (targetContent->IsNodeOfType(nsINode::eTEXT)) {
2353 0 : targetContent = targetContent->GetParent();
2354 : }
2355 :
2356 0 : WidgetMouseScrollEvent event(aEvent->IsTrusted(),
2357 0 : eLegacyMousePixelScroll, aEvent->mWidget);
2358 0 : event.mFlags.mDefaultPrevented = aState.mDefaultPrevented;
2359 0 : event.mFlags.mDefaultPreventedByContent = aState.mDefaultPreventedByContent;
2360 0 : event.mRefPoint = aEvent->mRefPoint;
2361 0 : event.mTime = aEvent->mTime;
2362 0 : event.mTimeStamp = aEvent->mTimeStamp;
2363 0 : event.mModifiers = aEvent->mModifiers;
2364 0 : event.buttons = aEvent->buttons;
2365 0 : event.mIsHorizontal = (aDeltaDirection == DELTA_DIRECTION_X);
2366 0 : event.mDelta = aPixelDelta;
2367 0 : event.inputSource = aEvent->inputSource;
2368 :
2369 0 : nsEventStatus status = nsEventStatus_eIgnore;
2370 0 : EventDispatcher::Dispatch(targetContent, aTargetFrame->PresContext(),
2371 0 : &event, nullptr, &status);
2372 0 : aState.mDefaultPrevented =
2373 0 : event.DefaultPrevented() || status == nsEventStatus_eConsumeNoDefault;
2374 0 : aState.mDefaultPreventedByContent = event.DefaultPreventedByContent();
2375 : }
2376 :
2377 : nsIFrame*
2378 0 : EventStateManager::ComputeScrollTarget(nsIFrame* aTargetFrame,
2379 : WidgetWheelEvent* aEvent,
2380 : ComputeScrollTargetOptions aOptions)
2381 : {
2382 0 : return ComputeScrollTarget(aTargetFrame, aEvent->mDeltaX, aEvent->mDeltaY,
2383 0 : aEvent, aOptions);
2384 : }
2385 :
2386 : // Overload ComputeScrollTarget method to allow passing "test" dx and dy when looking
2387 : // for which scrollbarmediators to activate when two finger down on trackpad
2388 : // and before any actual motion
2389 : nsIFrame*
2390 0 : EventStateManager::ComputeScrollTarget(nsIFrame* aTargetFrame,
2391 : double aDirectionX,
2392 : double aDirectionY,
2393 : WidgetWheelEvent* aEvent,
2394 : ComputeScrollTargetOptions aOptions)
2395 : {
2396 0 : if ((aOptions & INCLUDE_PLUGIN_AS_TARGET) &&
2397 0 : !WheelPrefs::WheelEventsEnabledOnPlugins()) {
2398 0 : aOptions = RemovePluginFromTarget(aOptions);
2399 : }
2400 :
2401 0 : if (aOptions & PREFER_MOUSE_WHEEL_TRANSACTION) {
2402 : // If the user recently scrolled with the mousewheel, then they probably
2403 : // want to scroll the same view as before instead of the view under the
2404 : // cursor. WheelTransaction tracks the frame currently being
2405 : // scrolled with the mousewheel. We consider the transaction ended when the
2406 : // mouse moves more than "mousewheel.transaction.ignoremovedelay"
2407 : // milliseconds after the last scroll operation, or any time the mouse moves
2408 : // out of the frame, or when more than "mousewheel.transaction.timeout"
2409 : // milliseconds have passed after the last operation, even if the mouse
2410 : // hasn't moved.
2411 0 : nsIFrame* lastScrollFrame = WheelTransaction::GetTargetFrame();
2412 0 : if (lastScrollFrame) {
2413 0 : if (aOptions & INCLUDE_PLUGIN_AS_TARGET) {
2414 0 : nsPluginFrame* pluginFrame = do_QueryFrame(lastScrollFrame);
2415 0 : if (pluginFrame &&
2416 0 : pluginFrame->WantsToHandleWheelEventAsDefaultAction()) {
2417 0 : return lastScrollFrame;
2418 : }
2419 : }
2420 : nsIScrollableFrame* scrollableFrame =
2421 0 : lastScrollFrame->GetScrollTargetFrame();
2422 0 : if (scrollableFrame) {
2423 0 : nsIFrame* frameToScroll = do_QueryFrame(scrollableFrame);
2424 0 : MOZ_ASSERT(frameToScroll);
2425 0 : return frameToScroll;
2426 : }
2427 : }
2428 : }
2429 :
2430 : // If the event doesn't cause scroll actually, we cannot find scroll target
2431 : // because we check if the event can cause scroll actually on each found
2432 : // scrollable frame.
2433 0 : if (!aDirectionX && !aDirectionY) {
2434 0 : return nullptr;
2435 : }
2436 :
2437 : bool checkIfScrollableX =
2438 0 : aDirectionX && (aOptions & PREFER_ACTUAL_SCROLLABLE_TARGET_ALONG_X_AXIS);
2439 : bool checkIfScrollableY =
2440 0 : aDirectionY && (aOptions & PREFER_ACTUAL_SCROLLABLE_TARGET_ALONG_Y_AXIS);
2441 :
2442 : nsIFrame* scrollFrame =
2443 0 : !(aOptions & START_FROM_PARENT) ? aTargetFrame :
2444 0 : GetParentFrameToScroll(aTargetFrame);
2445 0 : for (; scrollFrame; scrollFrame = GetParentFrameToScroll(scrollFrame)) {
2446 : // Check whether the frame wants to provide us with a scrollable view.
2447 0 : nsIScrollableFrame* scrollableFrame = scrollFrame->GetScrollTargetFrame();
2448 0 : if (!scrollableFrame) {
2449 : // If the frame is a plugin frame, then, the plugin content may handle
2450 : // wheel events. Only when the caller computes the scroll target for
2451 : // default action handling, we should assume the plugin frame as
2452 : // scrollable if the plugin wants to handle wheel events as default
2453 : // action.
2454 0 : if (aOptions & INCLUDE_PLUGIN_AS_TARGET) {
2455 0 : nsPluginFrame* pluginFrame = do_QueryFrame(scrollFrame);
2456 0 : if (pluginFrame &&
2457 0 : pluginFrame->WantsToHandleWheelEventAsDefaultAction()) {
2458 0 : return scrollFrame;
2459 : }
2460 : }
2461 0 : nsMenuPopupFrame* menuPopupFrame = do_QueryFrame(scrollFrame);
2462 0 : if (menuPopupFrame) {
2463 0 : return nullptr;
2464 : }
2465 0 : continue;
2466 : }
2467 :
2468 0 : nsIFrame* frameToScroll = do_QueryFrame(scrollableFrame);
2469 0 : MOZ_ASSERT(frameToScroll);
2470 :
2471 : // Don't scroll vertically by mouse-wheel on a single-line text control.
2472 0 : if (checkIfScrollableY) {
2473 0 : if (!CanVerticallyScrollFrameWithWheel(scrollFrame)) {
2474 0 : continue;
2475 : }
2476 : }
2477 :
2478 0 : if (!checkIfScrollableX && !checkIfScrollableY) {
2479 0 : return frameToScroll;
2480 : }
2481 :
2482 0 : ScrollbarStyles ss = scrollableFrame->GetScrollbarStyles();
2483 0 : bool hiddenForV = (NS_STYLE_OVERFLOW_HIDDEN == ss.mVertical);
2484 0 : bool hiddenForH = (NS_STYLE_OVERFLOW_HIDDEN == ss.mHorizontal);
2485 0 : if ((hiddenForV && hiddenForH) ||
2486 0 : (checkIfScrollableY && !checkIfScrollableX && hiddenForV) ||
2487 0 : (checkIfScrollableX && !checkIfScrollableY && hiddenForH)) {
2488 0 : continue;
2489 : }
2490 :
2491 : // For default action, we should climb up the tree if cannot scroll it
2492 : // by the event actually.
2493 : bool canScroll = WheelHandlingUtils::CanScrollOn(scrollableFrame,
2494 0 : aDirectionX, aDirectionY);
2495 : // Comboboxes need special care.
2496 0 : nsIComboboxControlFrame* comboBox = do_QueryFrame(scrollFrame);
2497 0 : if (comboBox) {
2498 0 : if (comboBox->IsDroppedDown()) {
2499 : // Don't propagate to parent when drop down menu is active.
2500 0 : return canScroll ? frameToScroll : nullptr;
2501 : }
2502 : // Always propagate when not dropped down (even if focused).
2503 0 : continue;
2504 : }
2505 :
2506 0 : if (canScroll) {
2507 0 : return frameToScroll;
2508 : }
2509 : }
2510 :
2511 : nsIFrame* newFrame = nsLayoutUtils::GetCrossDocParentFrame(
2512 0 : aTargetFrame->PresContext()->FrameManager()->GetRootFrame());
2513 0 : aOptions =
2514 0 : static_cast<ComputeScrollTargetOptions>(aOptions & ~START_FROM_PARENT);
2515 0 : return newFrame ? ComputeScrollTarget(newFrame, aEvent, aOptions) : nullptr;
2516 : }
2517 :
2518 : nsSize
2519 0 : EventStateManager::GetScrollAmount(nsPresContext* aPresContext,
2520 : WidgetWheelEvent* aEvent,
2521 : nsIScrollableFrame* aScrollableFrame)
2522 : {
2523 0 : MOZ_ASSERT(aPresContext);
2524 0 : MOZ_ASSERT(aEvent);
2525 :
2526 0 : bool isPage = (aEvent->mDeltaMode == nsIDOMWheelEvent::DOM_DELTA_PAGE);
2527 0 : if (aScrollableFrame) {
2528 0 : return isPage ? aScrollableFrame->GetPageScrollAmount() :
2529 0 : aScrollableFrame->GetLineScrollAmount();
2530 : }
2531 :
2532 : // If there is no scrollable frame and page scrolling, use view port size.
2533 0 : if (isPage) {
2534 0 : return aPresContext->GetVisibleArea().Size();
2535 : }
2536 :
2537 : // If there is no scrollable frame, we should use root frame's information.
2538 0 : nsIFrame* rootFrame = aPresContext->PresShell()->GetRootFrame();
2539 0 : if (!rootFrame) {
2540 0 : return nsSize(0, 0);
2541 : }
2542 : RefPtr<nsFontMetrics> fm =
2543 0 : nsLayoutUtils::GetInflatedFontMetricsForFrame(rootFrame);
2544 0 : NS_ENSURE_TRUE(fm, nsSize(0, 0));
2545 0 : return nsSize(fm->AveCharWidth(), fm->MaxHeight());
2546 : }
2547 :
2548 : void
2549 0 : EventStateManager::DoScrollText(nsIScrollableFrame* aScrollableFrame,
2550 : WidgetWheelEvent* aEvent)
2551 : {
2552 0 : MOZ_ASSERT(aScrollableFrame);
2553 0 : MOZ_ASSERT(aEvent);
2554 :
2555 0 : nsIFrame* scrollFrame = do_QueryFrame(aScrollableFrame);
2556 0 : MOZ_ASSERT(scrollFrame);
2557 :
2558 0 : AutoWeakFrame scrollFrameWeak(scrollFrame);
2559 0 : if (!WheelTransaction::WillHandleDefaultAction(aEvent, scrollFrameWeak)) {
2560 0 : return;
2561 : }
2562 :
2563 : // Default action's actual scroll amount should be computed from device
2564 : // pixels.
2565 0 : nsPresContext* pc = scrollFrame->PresContext();
2566 0 : nsSize scrollAmount = GetScrollAmount(pc, aEvent, aScrollableFrame);
2567 : nsIntSize scrollAmountInDevPixels(
2568 : pc->AppUnitsToDevPixels(scrollAmount.width),
2569 0 : pc->AppUnitsToDevPixels(scrollAmount.height));
2570 : nsIntPoint actualDevPixelScrollAmount =
2571 : DeltaAccumulator::GetInstance()->
2572 0 : ComputeScrollAmountForDefaultAction(aEvent, scrollAmountInDevPixels);
2573 :
2574 : // Don't scroll around the axis whose overflow style is hidden.
2575 0 : ScrollbarStyles overflowStyle = aScrollableFrame->GetScrollbarStyles();
2576 0 : if (overflowStyle.mHorizontal == NS_STYLE_OVERFLOW_HIDDEN) {
2577 0 : actualDevPixelScrollAmount.x = 0;
2578 : }
2579 0 : if (overflowStyle.mVertical == NS_STYLE_OVERFLOW_HIDDEN) {
2580 0 : actualDevPixelScrollAmount.y = 0;
2581 : }
2582 :
2583 0 : nsIScrollbarMediator::ScrollSnapMode snapMode = nsIScrollbarMediator::DISABLE_SNAP;
2584 0 : nsIAtom* origin = nullptr;
2585 0 : switch (aEvent->mDeltaMode) {
2586 : case nsIDOMWheelEvent::DOM_DELTA_LINE:
2587 0 : origin = nsGkAtoms::mouseWheel;
2588 0 : snapMode = nsIScrollableFrame::ENABLE_SNAP;
2589 0 : break;
2590 : case nsIDOMWheelEvent::DOM_DELTA_PAGE:
2591 0 : origin = nsGkAtoms::pages;
2592 0 : snapMode = nsIScrollableFrame::ENABLE_SNAP;
2593 0 : break;
2594 : case nsIDOMWheelEvent::DOM_DELTA_PIXEL:
2595 0 : origin = nsGkAtoms::pixels;
2596 0 : break;
2597 : default:
2598 0 : MOZ_CRASH("Invalid deltaMode value comes");
2599 : }
2600 :
2601 : // We shouldn't scroll more one page at once except when over one page scroll
2602 : // is allowed for the event.
2603 0 : nsSize pageSize = aScrollableFrame->GetPageScrollAmount();
2604 : nsIntSize devPixelPageSize(pc->AppUnitsToDevPixels(pageSize.width),
2605 0 : pc->AppUnitsToDevPixels(pageSize.height));
2606 0 : if (!WheelPrefs::GetInstance()->IsOverOnePageScrollAllowedX(aEvent) &&
2607 0 : DeprecatedAbs(actualDevPixelScrollAmount.x) > devPixelPageSize.width) {
2608 0 : actualDevPixelScrollAmount.x =
2609 0 : (actualDevPixelScrollAmount.x >= 0) ? devPixelPageSize.width :
2610 0 : -devPixelPageSize.width;
2611 : }
2612 :
2613 0 : if (!WheelPrefs::GetInstance()->IsOverOnePageScrollAllowedY(aEvent) &&
2614 0 : DeprecatedAbs(actualDevPixelScrollAmount.y) > devPixelPageSize.height) {
2615 0 : actualDevPixelScrollAmount.y =
2616 0 : (actualDevPixelScrollAmount.y >= 0) ? devPixelPageSize.height :
2617 0 : -devPixelPageSize.height;
2618 : }
2619 :
2620 : bool isDeltaModePixel =
2621 0 : (aEvent->mDeltaMode == nsIDOMWheelEvent::DOM_DELTA_PIXEL);
2622 :
2623 : nsIScrollableFrame::ScrollMode mode;
2624 0 : switch (aEvent->mScrollType) {
2625 : case WidgetWheelEvent::SCROLL_DEFAULT:
2626 0 : if (isDeltaModePixel) {
2627 0 : mode = nsIScrollableFrame::NORMAL;
2628 0 : } else if (aEvent->mFlags.mHandledByAPZ) {
2629 0 : mode = nsIScrollableFrame::SMOOTH_MSD;
2630 : } else {
2631 0 : mode = nsIScrollableFrame::SMOOTH;
2632 : }
2633 0 : break;
2634 : case WidgetWheelEvent::SCROLL_SYNCHRONOUSLY:
2635 0 : mode = nsIScrollableFrame::INSTANT;
2636 0 : break;
2637 : case WidgetWheelEvent::SCROLL_ASYNCHRONOUSELY:
2638 0 : mode = nsIScrollableFrame::NORMAL;
2639 0 : break;
2640 : case WidgetWheelEvent::SCROLL_SMOOTHLY:
2641 0 : mode = nsIScrollableFrame::SMOOTH;
2642 0 : break;
2643 : default:
2644 0 : MOZ_CRASH("Invalid mScrollType value comes");
2645 : }
2646 :
2647 : nsIScrollableFrame::ScrollMomentum momentum =
2648 0 : aEvent->mIsMomentum ? nsIScrollableFrame::SYNTHESIZED_MOMENTUM_EVENT
2649 0 : : nsIScrollableFrame::NOT_MOMENTUM;
2650 :
2651 0 : nsIntPoint overflow;
2652 : aScrollableFrame->ScrollBy(actualDevPixelScrollAmount,
2653 : nsIScrollableFrame::DEVICE_PIXELS,
2654 0 : mode, &overflow, origin, momentum, snapMode);
2655 :
2656 0 : if (!scrollFrameWeak.IsAlive()) {
2657 : // If the scroll causes changing the layout, we can think that the event
2658 : // has been completely consumed by the content. Then, users probably don't
2659 : // want additional action.
2660 0 : aEvent->mOverflowDeltaX = aEvent->mOverflowDeltaY = 0;
2661 0 : } else if (isDeltaModePixel) {
2662 0 : aEvent->mOverflowDeltaX = overflow.x;
2663 0 : aEvent->mOverflowDeltaY = overflow.y;
2664 : } else {
2665 0 : aEvent->mOverflowDeltaX =
2666 0 : static_cast<double>(overflow.x) / scrollAmountInDevPixels.width;
2667 0 : aEvent->mOverflowDeltaY =
2668 0 : static_cast<double>(overflow.y) / scrollAmountInDevPixels.height;
2669 : }
2670 :
2671 : // If CSS overflow properties caused not to scroll, the overflowDelta* values
2672 : // should be same as delta* values since they may be used as gesture event by
2673 : // widget. However, if there is another scrollable element in the ancestor
2674 : // along the axis, probably users don't want the operation to cause
2675 : // additional action such as moving history. In such case, overflowDelta
2676 : // values should stay zero.
2677 0 : if (scrollFrameWeak.IsAlive()) {
2678 0 : if (aEvent->mDeltaX &&
2679 0 : overflowStyle.mHorizontal == NS_STYLE_OVERFLOW_HIDDEN &&
2680 0 : !ComputeScrollTarget(scrollFrame, aEvent,
2681 : COMPUTE_SCROLLABLE_ANCESTOR_ALONG_X_AXIS)) {
2682 0 : aEvent->mOverflowDeltaX = aEvent->mDeltaX;
2683 : }
2684 0 : if (aEvent->mDeltaY &&
2685 0 : overflowStyle.mVertical == NS_STYLE_OVERFLOW_HIDDEN &&
2686 0 : !ComputeScrollTarget(scrollFrame, aEvent,
2687 : COMPUTE_SCROLLABLE_ANCESTOR_ALONG_Y_AXIS)) {
2688 0 : aEvent->mOverflowDeltaY = aEvent->mDeltaY;
2689 : }
2690 : }
2691 :
2692 0 : NS_ASSERTION(aEvent->mOverflowDeltaX == 0 ||
2693 : (aEvent->mOverflowDeltaX > 0) == (aEvent->mDeltaX > 0),
2694 : "The sign of mOverflowDeltaX is different from the scroll direction");
2695 0 : NS_ASSERTION(aEvent->mOverflowDeltaY == 0 ||
2696 : (aEvent->mOverflowDeltaY > 0) == (aEvent->mDeltaY > 0),
2697 : "The sign of mOverflowDeltaY is different from the scroll direction");
2698 :
2699 0 : WheelPrefs::GetInstance()->CancelApplyingUserPrefsFromOverflowDelta(aEvent);
2700 : }
2701 :
2702 : void
2703 0 : EventStateManager::DecideGestureEvent(WidgetGestureNotifyEvent* aEvent,
2704 : nsIFrame* targetFrame)
2705 : {
2706 :
2707 0 : NS_ASSERTION(aEvent->mMessage == eGestureNotify,
2708 : "DecideGestureEvent called with a non-gesture event");
2709 :
2710 : /* Check the ancestor tree to decide if any frame is willing* to receive
2711 : * a MozPixelScroll event. If that's the case, the current touch gesture
2712 : * will be used as a pan gesture; otherwise it will be a regular
2713 : * mousedown/mousemove/click event.
2714 : *
2715 : * *willing: determine if it makes sense to pan the element using scroll events:
2716 : * - For web content: if there are any visible scrollbars on the touch point
2717 : * - For XUL: if it's an scrollable element that can currently scroll in some
2718 : * direction.
2719 : *
2720 : * Note: we'll have to one-off various cases to ensure a good usable behavior
2721 : */
2722 : WidgetGestureNotifyEvent::PanDirection panDirection =
2723 0 : WidgetGestureNotifyEvent::ePanNone;
2724 0 : bool displayPanFeedback = false;
2725 0 : for (nsIFrame* current = targetFrame; current;
2726 : current = nsLayoutUtils::GetCrossDocParentFrame(current)) {
2727 :
2728 : // e10s - mark remote content as pannable. This is a work around since
2729 : // we don't have access to remote frame scroll info here. Apz data may
2730 : // assist is solving this.
2731 0 : if (current && IsRemoteTarget(current->GetContent())) {
2732 0 : panDirection = WidgetGestureNotifyEvent::ePanBoth;
2733 : // We don't know when we reach bounds, so just disable feedback for now.
2734 0 : displayPanFeedback = false;
2735 0 : break;
2736 : }
2737 :
2738 0 : LayoutFrameType currentFrameType = current->Type();
2739 :
2740 : // Scrollbars should always be draggable
2741 0 : if (currentFrameType == LayoutFrameType::Scrollbar) {
2742 0 : panDirection = WidgetGestureNotifyEvent::ePanNone;
2743 0 : break;
2744 : }
2745 :
2746 : #ifdef MOZ_XUL
2747 : // Special check for trees
2748 0 : nsTreeBodyFrame* treeFrame = do_QueryFrame(current);
2749 0 : if (treeFrame) {
2750 0 : if (treeFrame->GetHorizontalOverflow()) {
2751 0 : panDirection = WidgetGestureNotifyEvent::ePanHorizontal;
2752 : }
2753 0 : if (treeFrame->GetVerticalOverflow()) {
2754 0 : panDirection = WidgetGestureNotifyEvent::ePanVertical;
2755 : }
2756 0 : break;
2757 : }
2758 : #endif
2759 :
2760 0 : nsIScrollableFrame* scrollableFrame = do_QueryFrame(current);
2761 0 : if (scrollableFrame) {
2762 0 : if (current->IsFrameOfType(nsIFrame::eXULBox)) {
2763 0 : displayPanFeedback = true;
2764 :
2765 0 : nsRect scrollRange = scrollableFrame->GetScrollRange();
2766 0 : bool canScrollHorizontally = scrollRange.width > 0;
2767 :
2768 0 : if (targetFrame->IsMenuFrame()) {
2769 : // menu frames report horizontal scroll when they have submenus
2770 : // and we don't want that
2771 0 : canScrollHorizontally = false;
2772 0 : displayPanFeedback = false;
2773 : }
2774 :
2775 : // Vertical panning has priority over horizontal panning, so
2776 : // when vertical movement is possible we can just finish the loop.
2777 0 : if (scrollRange.height > 0) {
2778 0 : panDirection = WidgetGestureNotifyEvent::ePanVertical;
2779 0 : break;
2780 : }
2781 :
2782 0 : if (canScrollHorizontally) {
2783 0 : panDirection = WidgetGestureNotifyEvent::ePanHorizontal;
2784 0 : displayPanFeedback = false;
2785 : }
2786 : } else { //Not a XUL box
2787 0 : uint32_t scrollbarVisibility = scrollableFrame->GetScrollbarVisibility();
2788 :
2789 : //Check if we have visible scrollbars
2790 0 : if (scrollbarVisibility & nsIScrollableFrame::VERTICAL) {
2791 0 : panDirection = WidgetGestureNotifyEvent::ePanVertical;
2792 0 : displayPanFeedback = true;
2793 0 : break;
2794 : }
2795 :
2796 0 : if (scrollbarVisibility & nsIScrollableFrame::HORIZONTAL) {
2797 0 : panDirection = WidgetGestureNotifyEvent::ePanHorizontal;
2798 0 : displayPanFeedback = true;
2799 : }
2800 : }
2801 : } //scrollableFrame
2802 : } //ancestor chain
2803 0 : aEvent->mDisplayPanFeedback = displayPanFeedback;
2804 0 : aEvent->mPanDirection = panDirection;
2805 0 : }
2806 :
2807 : #ifdef XP_MACOSX
2808 : static bool
2809 : NodeAllowsClickThrough(nsINode* aNode)
2810 : {
2811 : while (aNode) {
2812 : if (aNode->IsXULElement()) {
2813 : mozilla::dom::Element* element = aNode->AsElement();
2814 : static nsIContent::AttrValuesArray strings[] =
2815 : {&nsGkAtoms::always, &nsGkAtoms::never, nullptr};
2816 : switch (element->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::clickthrough,
2817 : strings, eCaseMatters)) {
2818 : case 0:
2819 : return true;
2820 : case 1:
2821 : return false;
2822 : }
2823 : }
2824 : aNode = nsContentUtils::GetCrossDocParentNode(aNode);
2825 : }
2826 : return true;
2827 : }
2828 : #endif
2829 :
2830 : void
2831 0 : EventStateManager::PostHandleKeyboardEvent(WidgetKeyboardEvent* aKeyboardEvent,
2832 : nsEventStatus& aStatus)
2833 : {
2834 0 : if (aStatus == nsEventStatus_eConsumeNoDefault) {
2835 0 : return;
2836 : }
2837 :
2838 0 : if (!aKeyboardEvent->HasBeenPostedToRemoteProcess()) {
2839 : // The widget expects a reply for every keyboard event. If the event wasn't
2840 : // dispatched to a content process (non-e10s or no content process
2841 : // running), we need to short-circuit here. Otherwise, we need to wait for
2842 : // the content process to handle the event.
2843 0 : aKeyboardEvent->mWidget->PostHandleKeyEvent(aKeyboardEvent);
2844 0 : if (aKeyboardEvent->DefaultPrevented()) {
2845 0 : aStatus = nsEventStatus_eConsumeNoDefault;
2846 0 : return;
2847 : }
2848 : }
2849 :
2850 : // XXX Currently, our automated tests don't support mKeyNameIndex.
2851 : // Therefore, we still need to handle this with keyCode.
2852 0 : switch(aKeyboardEvent->mKeyCode) {
2853 : case NS_VK_TAB:
2854 : case NS_VK_F6:
2855 : // This is to prevent keyboard scrolling while alt modifier in use.
2856 0 : if (!aKeyboardEvent->IsAlt()) {
2857 0 : aStatus = nsEventStatus_eConsumeNoDefault;
2858 :
2859 : // Handling the tab event after it was sent to content is bad,
2860 : // because to the FocusManager the remote-browser looks like one
2861 : // element, so we would just move the focus to the next element
2862 : // in chrome, instead of handling it in content.
2863 0 : if (aKeyboardEvent->HasBeenPostedToRemoteProcess()) {
2864 0 : break;
2865 : }
2866 :
2867 0 : EnsureDocument(mPresContext);
2868 0 : nsIFocusManager* fm = nsFocusManager::GetFocusManager();
2869 0 : if (fm && mDocument) {
2870 : // Shift focus forward or back depending on shift key
2871 : bool isDocMove =
2872 0 : aKeyboardEvent->IsControl() || aKeyboardEvent->mKeyCode == NS_VK_F6;
2873 0 : uint32_t dir = aKeyboardEvent->IsShift() ?
2874 0 : (isDocMove ? static_cast<uint32_t>(nsIFocusManager::MOVEFOCUS_BACKWARDDOC) :
2875 : static_cast<uint32_t>(nsIFocusManager::MOVEFOCUS_BACKWARD)) :
2876 0 : (isDocMove ? static_cast<uint32_t>(nsIFocusManager::MOVEFOCUS_FORWARDDOC) :
2877 0 : static_cast<uint32_t>(nsIFocusManager::MOVEFOCUS_FORWARD));
2878 0 : nsCOMPtr<nsIDOMElement> result;
2879 0 : fm->MoveFocus(mDocument->GetWindow(), nullptr, dir,
2880 : nsIFocusManager::FLAG_BYKEY,
2881 0 : getter_AddRefs(result));
2882 : }
2883 : }
2884 0 : return;
2885 : case 0:
2886 : // We handle keys with no specific keycode value below.
2887 0 : break;
2888 : default:
2889 0 : return;
2890 : }
2891 :
2892 0 : switch(aKeyboardEvent->mKeyNameIndex) {
2893 : case KEY_NAME_INDEX_ZoomIn:
2894 : case KEY_NAME_INDEX_ZoomOut:
2895 0 : ChangeFullZoom(
2896 0 : aKeyboardEvent->mKeyNameIndex == KEY_NAME_INDEX_ZoomIn ? 1 : -1);
2897 0 : aStatus = nsEventStatus_eConsumeNoDefault;
2898 0 : break;
2899 : default:
2900 0 : break;
2901 : }
2902 : }
2903 :
2904 : nsresult
2905 10 : EventStateManager::PostHandleEvent(nsPresContext* aPresContext,
2906 : WidgetEvent* aEvent,
2907 : nsIFrame* aTargetFrame,
2908 : nsEventStatus* aStatus)
2909 : {
2910 10 : NS_ENSURE_ARG(aPresContext);
2911 10 : NS_ENSURE_ARG_POINTER(aStatus);
2912 :
2913 10 : mCurrentTarget = aTargetFrame;
2914 10 : mCurrentTargetContent = nullptr;
2915 :
2916 10 : HandleCrossProcessEvent(aEvent, aStatus);
2917 : // NOTE: the above call may have destroyed aTargetFrame, please use
2918 : // mCurrentTarget henceforth. This is to avoid using it accidentally:
2919 10 : aTargetFrame = nullptr;
2920 :
2921 : // Most of the events we handle below require a frame.
2922 : // Add special cases here.
2923 10 : if (!mCurrentTarget && aEvent->mMessage != eMouseUp &&
2924 0 : aEvent->mMessage != eMouseDown) {
2925 0 : return NS_OK;
2926 : }
2927 :
2928 : //Keep the prescontext alive, we might need it after event dispatch
2929 20 : RefPtr<nsPresContext> presContext = aPresContext;
2930 10 : nsresult ret = NS_OK;
2931 :
2932 10 : switch (aEvent->mMessage) {
2933 : case eMouseDown:
2934 : {
2935 0 : WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
2936 0 : if (mouseEvent->button == WidgetMouseEvent::eLeftButton &&
2937 0 : !sNormalLMouseEventInProcess) {
2938 : // We got a mouseup event while a mousedown event was being processed.
2939 : // Make sure that the capturing content is cleared.
2940 0 : nsIPresShell::SetCapturingContent(nullptr, 0);
2941 0 : break;
2942 : }
2943 :
2944 : // For remote content, capture the event in the parent process at the
2945 : // <xul:browser remote> element. This will ensure that subsequent mousemove/mouseup
2946 : // events will continue to be dispatched to this element and therefore forwarded
2947 : // to the child.
2948 0 : if (aEvent->HasBeenPostedToRemoteProcess() &&
2949 0 : !nsIPresShell::GetCapturingContent()) {
2950 0 : nsIContent* content = mCurrentTarget ? mCurrentTarget->GetContent() : nullptr;
2951 0 : nsIPresShell::SetCapturingContent(content, 0);
2952 : }
2953 :
2954 0 : nsCOMPtr<nsIContent> activeContent;
2955 : // When content calls PreventDefault on pointerdown, we also call
2956 : // PreventDefault on the subsequent mouse events to suppress default
2957 : // behaviors. Normally, aStatus should be nsEventStatus_eConsumeNoDefault
2958 : // when the event is DefaultPrevented but it's reset to
2959 : // nsEventStatus_eIgnore in EventStateManager::PreHandleEvent. So we also
2960 : // check if the event is DefaultPrevented.
2961 0 : if (nsEventStatus_eConsumeNoDefault != *aStatus &&
2962 0 : !aEvent->DefaultPrevented()) {
2963 0 : nsCOMPtr<nsIContent> newFocus;
2964 0 : bool suppressBlur = false;
2965 0 : if (mCurrentTarget) {
2966 0 : mCurrentTarget->GetContentForEvent(aEvent, getter_AddRefs(newFocus));
2967 0 : const nsStyleUserInterface* ui = mCurrentTarget->StyleUserInterface();
2968 0 : activeContent = mCurrentTarget->GetContent();
2969 :
2970 : // In some cases, we do not want to even blur the current focused
2971 : // element. Those cases are:
2972 : // 1. -moz-user-focus CSS property is set to 'ignore';
2973 : // 2. Element with NS_EVENT_STATE_DISABLED
2974 : // (aka :disabled pseudo-class for HTML element);
2975 : // 3. XUL control element has the disabled property set to 'true'.
2976 : //
2977 : // We can't use nsIFrame::IsFocusable() because we want to blur when
2978 : // we click on a visibility: none element.
2979 : // We can't use nsIContent::IsFocusable() because we want to blur when
2980 : // we click on a non-focusable element like a <div>.
2981 : // We have to use |aEvent->mTarget| to not make sure we do not check
2982 : // an anonymous node of the targeted element.
2983 0 : suppressBlur = (ui->mUserFocus == StyleUserFocus::Ignore);
2984 :
2985 0 : if (!suppressBlur) {
2986 0 : nsCOMPtr<Element> element = do_QueryInterface(aEvent->mTarget);
2987 0 : suppressBlur = element &&
2988 0 : element->State().HasState(NS_EVENT_STATE_DISABLED);
2989 : }
2990 :
2991 0 : if (!suppressBlur) {
2992 : nsCOMPtr<nsIDOMXULControlElement> xulControl =
2993 0 : do_QueryInterface(aEvent->mTarget);
2994 0 : if (xulControl) {
2995 : bool disabled;
2996 0 : xulControl->GetDisabled(&disabled);
2997 0 : suppressBlur = disabled;
2998 : }
2999 : }
3000 : }
3001 :
3002 0 : if (!suppressBlur) {
3003 0 : suppressBlur = nsContentUtils::IsUserFocusIgnored(activeContent);
3004 : }
3005 :
3006 0 : nsIFrame* currFrame = mCurrentTarget;
3007 :
3008 : // When a root content which isn't editable but has an editable HTML
3009 : // <body> element is clicked, we should redirect the focus to the
3010 : // the <body> element. E.g., when an user click bottom of the editor
3011 : // where is outside of the <body> element, the <body> should be focused
3012 : // and the user can edit immediately after that.
3013 : //
3014 : // NOTE: The newFocus isn't editable that also means it's not in
3015 : // designMode. In designMode, all contents are not focusable.
3016 0 : if (newFocus && !newFocus->IsEditable()) {
3017 0 : nsIDocument *doc = newFocus->GetComposedDoc();
3018 0 : if (doc && newFocus == doc->GetRootElement()) {
3019 : nsIContent *bodyContent =
3020 0 : nsLayoutUtils::GetEditableRootContentByContentEditable(doc);
3021 0 : if (bodyContent) {
3022 0 : nsIFrame* bodyFrame = bodyContent->GetPrimaryFrame();
3023 0 : if (bodyFrame) {
3024 0 : currFrame = bodyFrame;
3025 0 : newFocus = bodyContent;
3026 : }
3027 : }
3028 : }
3029 : }
3030 :
3031 : // When the mouse is pressed, the default action is to focus the
3032 : // target. Look for the nearest enclosing focusable frame.
3033 0 : while (currFrame) {
3034 : // If the mousedown happened inside a popup, don't
3035 : // try to set focus on one of its containing elements
3036 0 : const nsStyleDisplay* display = currFrame->StyleDisplay();
3037 0 : if (display->mDisplay == StyleDisplay::MozPopup) {
3038 0 : newFocus = nullptr;
3039 0 : break;
3040 : }
3041 :
3042 : int32_t tabIndexUnused;
3043 0 : if (currFrame->IsFocusable(&tabIndexUnused, true)) {
3044 0 : newFocus = currFrame->GetContent();
3045 0 : nsCOMPtr<nsIDOMElement> domElement(do_QueryInterface(newFocus));
3046 0 : if (domElement)
3047 0 : break;
3048 : }
3049 0 : currFrame = currFrame->GetParent();
3050 : }
3051 :
3052 0 : nsIFocusManager* fm = nsFocusManager::GetFocusManager();
3053 0 : if (fm) {
3054 : // if something was found to focus, focus it. Otherwise, if the
3055 : // element that was clicked doesn't have -moz-user-focus: ignore,
3056 : // clear the existing focus. For -moz-user-focus: ignore, the focus
3057 : // is just left as is.
3058 : // Another effect of mouse clicking, handled in nsSelection, is that
3059 : // it should update the caret position to where the mouse was
3060 : // clicked. Because the focus is cleared when clicking on a
3061 : // non-focusable node, the next press of the tab key will cause
3062 : // focus to be shifted from the caret position instead of the root.
3063 0 : if (newFocus && currFrame) {
3064 : // use the mouse flag and the noscroll flag so that the content
3065 : // doesn't unexpectedly scroll when clicking an element that is
3066 : // only half visible
3067 : uint32_t flags = nsIFocusManager::FLAG_BYMOUSE |
3068 0 : nsIFocusManager::FLAG_NOSCROLL;
3069 : // If this was a touch-generated event, pass that information:
3070 0 : if (mouseEvent->inputSource == nsIDOMMouseEvent::MOZ_SOURCE_TOUCH) {
3071 0 : flags |= nsIFocusManager::FLAG_BYTOUCH;
3072 : }
3073 0 : nsCOMPtr<nsIDOMElement> newFocusElement = do_QueryInterface(newFocus);
3074 0 : fm->SetFocus(newFocusElement, flags);
3075 : }
3076 0 : else if (!suppressBlur) {
3077 : // clear the focus within the frame and then set it as the
3078 : // focused frame
3079 0 : EnsureDocument(mPresContext);
3080 0 : if (mDocument) {
3081 : #ifdef XP_MACOSX
3082 : if (!activeContent || !activeContent->IsXULElement())
3083 : #endif
3084 0 : fm->ClearFocus(mDocument->GetWindow());
3085 0 : fm->SetFocusedWindow(mDocument->GetWindow());
3086 : }
3087 : }
3088 : }
3089 :
3090 : // The rest is left button-specific.
3091 0 : if (mouseEvent->button != WidgetMouseEvent::eLeftButton) {
3092 0 : break;
3093 : }
3094 :
3095 0 : if (activeContent) {
3096 : // The nearest enclosing element goes into the
3097 : // :active state. If we fail the QI to DOMElement,
3098 : // then we know we're only a node, and that we need
3099 : // to obtain our parent element and put it into :active
3100 : // instead.
3101 0 : nsCOMPtr<nsIDOMElement> elt(do_QueryInterface(activeContent));
3102 0 : if (!elt) {
3103 0 : nsIContent* par = activeContent->GetParent();
3104 0 : if (par)
3105 0 : activeContent = par;
3106 : }
3107 : }
3108 : }
3109 : else {
3110 : // if we're here, the event handler returned false, so stop
3111 : // any of our own processing of a drag. Workaround for bug 43258.
3112 0 : StopTrackingDragGesture();
3113 :
3114 : // When the event was cancelled, there is currently a chrome document
3115 : // focused and a mousedown just occurred on a content document, ensure
3116 : // that the window that was clicked is focused.
3117 0 : EnsureDocument(mPresContext);
3118 0 : nsIFocusManager* fm = nsFocusManager::GetFocusManager();
3119 0 : if (mDocument && fm) {
3120 0 : nsCOMPtr<mozIDOMWindowProxy> window;
3121 0 : fm->GetFocusedWindow(getter_AddRefs(window));
3122 0 : auto* currentWindow = nsPIDOMWindowOuter::From(window);
3123 0 : if (currentWindow && mDocument->GetWindow() &&
3124 0 : currentWindow != mDocument->GetWindow() &&
3125 0 : !nsContentUtils::IsChromeDoc(mDocument)) {
3126 0 : nsCOMPtr<nsPIDOMWindowOuter> currentTop;
3127 0 : nsCOMPtr<nsPIDOMWindowOuter> newTop;
3128 0 : currentTop = currentWindow->GetTop();
3129 0 : newTop = mDocument->GetWindow()->GetTop();
3130 0 : nsCOMPtr<nsIDocument> currentDoc = currentWindow->GetExtantDoc();
3131 0 : if (nsContentUtils::IsChromeDoc(currentDoc) ||
3132 0 : (currentTop && newTop && currentTop != newTop)) {
3133 0 : fm->SetFocusedWindow(mDocument->GetWindow());
3134 : }
3135 : }
3136 : }
3137 : }
3138 0 : SetActiveManager(this, activeContent);
3139 : }
3140 0 : break;
3141 : case ePointerCancel: {
3142 0 : if(WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent()) {
3143 0 : GenerateMouseEnterExit(mouseEvent);
3144 : }
3145 : // After firing the pointercancel event, a user agent must also fire a
3146 : // pointerout event followed by a pointerleave event.
3147 : MOZ_FALLTHROUGH;
3148 : }
3149 : case ePointerUp: {
3150 0 : WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent();
3151 : // After UP/Cancel Touch pointers become invalid so we can remove relevant helper from Table
3152 : // Mouse/Pen pointers are valid all the time (not only between down/up)
3153 0 : if (pointerEvent->inputSource == nsIDOMMouseEvent::MOZ_SOURCE_TOUCH) {
3154 0 : mPointersEnterLeaveHelper.Remove(pointerEvent->pointerId);
3155 0 : GenerateMouseEnterExit(pointerEvent);
3156 : }
3157 0 : break;
3158 : }
3159 : case eMouseUp:
3160 : {
3161 0 : ClearGlobalActiveContent(this);
3162 0 : WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
3163 0 : if (mouseEvent && mouseEvent->IsReal()) {
3164 0 : if (!mCurrentTarget) {
3165 0 : GetEventTarget();
3166 : }
3167 : // Make sure to dispatch the click even if there is no frame for
3168 : // the current target element. This is required for Web compatibility.
3169 0 : ret = CheckForAndDispatchClick(mouseEvent, aStatus);
3170 : }
3171 :
3172 0 : nsIPresShell *shell = presContext->GetPresShell();
3173 0 : if (shell) {
3174 0 : RefPtr<nsFrameSelection> frameSelection = shell->FrameSelection();
3175 0 : frameSelection->SetDragState(false);
3176 : }
3177 : }
3178 0 : break;
3179 : case eWheelOperationEnd:
3180 : {
3181 0 : MOZ_ASSERT(aEvent->IsTrusted());
3182 0 : ScrollbarsForWheel::MayInactivate();
3183 0 : WidgetWheelEvent* wheelEvent = aEvent->AsWheelEvent();
3184 : nsIScrollableFrame* scrollTarget =
3185 0 : do_QueryFrame(ComputeScrollTarget(mCurrentTarget, wheelEvent,
3186 0 : COMPUTE_DEFAULT_ACTION_TARGET));
3187 0 : if (scrollTarget) {
3188 0 : scrollTarget->ScrollSnap();
3189 : }
3190 : }
3191 0 : break;
3192 : case eWheel:
3193 : case eWheelOperationStart:
3194 : {
3195 0 : MOZ_ASSERT(aEvent->IsTrusted());
3196 :
3197 0 : if (*aStatus == nsEventStatus_eConsumeNoDefault) {
3198 0 : ScrollbarsForWheel::Inactivate();
3199 0 : break;
3200 : }
3201 :
3202 0 : WidgetWheelEvent* wheelEvent = aEvent->AsWheelEvent();
3203 :
3204 : // Check if the frame to scroll before checking the default action
3205 : // because if the scroll target is a plugin, the default action should be
3206 : // chosen by the plugin rather than by our prefs.
3207 : nsIFrame* frameToScroll =
3208 0 : ComputeScrollTarget(mCurrentTarget, wheelEvent,
3209 0 : COMPUTE_DEFAULT_ACTION_TARGET);
3210 0 : nsPluginFrame* pluginFrame = do_QueryFrame(frameToScroll);
3211 :
3212 : // When APZ is enabled, the actual scroll animation might be handled by
3213 : // the compositor.
3214 : WheelPrefs::Action action;
3215 0 : if (pluginFrame) {
3216 0 : MOZ_ASSERT(pluginFrame->WantsToHandleWheelEventAsDefaultAction());
3217 0 : action = WheelPrefs::ACTION_SEND_TO_PLUGIN;
3218 0 : } else if (wheelEvent->mFlags.mHandledByAPZ) {
3219 0 : action = WheelPrefs::ACTION_NONE;
3220 : } else {
3221 0 : action = WheelPrefs::GetInstance()->ComputeActionFor(wheelEvent);
3222 : }
3223 0 : switch (action) {
3224 : case WheelPrefs::ACTION_SCROLL: {
3225 : // For scrolling of default action, we should honor the mouse wheel
3226 : // transaction.
3227 :
3228 0 : ScrollbarsForWheel::PrepareToScrollText(this, mCurrentTarget, wheelEvent);
3229 :
3230 0 : if (aEvent->mMessage != eWheel ||
3231 0 : (!wheelEvent->mDeltaX && !wheelEvent->mDeltaY)) {
3232 : break;
3233 : }
3234 :
3235 0 : nsIScrollableFrame* scrollTarget = do_QueryFrame(frameToScroll);
3236 0 : ScrollbarsForWheel::SetActiveScrollTarget(scrollTarget);
3237 :
3238 0 : nsIFrame* rootScrollFrame = !mCurrentTarget ? nullptr :
3239 0 : mCurrentTarget->PresContext()->PresShell()->GetRootScrollFrame();
3240 0 : nsIScrollableFrame* rootScrollableFrame = nullptr;
3241 0 : if (rootScrollFrame) {
3242 0 : rootScrollableFrame = do_QueryFrame(rootScrollFrame);
3243 : }
3244 0 : if (!scrollTarget || scrollTarget == rootScrollableFrame) {
3245 0 : wheelEvent->mViewPortIsOverscrolled = true;
3246 : }
3247 0 : wheelEvent->mOverflowDeltaX = wheelEvent->mDeltaX;
3248 0 : wheelEvent->mOverflowDeltaY = wheelEvent->mDeltaY;
3249 : WheelPrefs::GetInstance()->
3250 0 : CancelApplyingUserPrefsFromOverflowDelta(wheelEvent);
3251 0 : if (scrollTarget) {
3252 0 : DoScrollText(scrollTarget, wheelEvent);
3253 : } else {
3254 0 : WheelTransaction::EndTransaction();
3255 0 : ScrollbarsForWheel::Inactivate();
3256 : }
3257 0 : break;
3258 : }
3259 : case WheelPrefs::ACTION_HISTORY: {
3260 : // If this event doesn't cause eLegacyMouseLineOrPageScroll event or
3261 : // the direction is oblique, don't perform history back/forward.
3262 0 : int32_t intDelta = wheelEvent->GetPreferredIntDelta();
3263 0 : if (!intDelta) {
3264 0 : break;
3265 : }
3266 0 : DoScrollHistory(intDelta);
3267 0 : break;
3268 : }
3269 : case WheelPrefs::ACTION_ZOOM: {
3270 : // If this event doesn't cause eLegacyMouseLineOrPageScroll event or
3271 : // the direction is oblique, don't perform zoom in/out.
3272 0 : int32_t intDelta = wheelEvent->GetPreferredIntDelta();
3273 0 : if (!intDelta) {
3274 0 : break;
3275 : }
3276 0 : DoScrollZoom(mCurrentTarget, intDelta);
3277 0 : break;
3278 : }
3279 : case WheelPrefs::ACTION_SEND_TO_PLUGIN:
3280 0 : MOZ_ASSERT(pluginFrame);
3281 :
3282 0 : if (wheelEvent->mMessage != eWheel ||
3283 0 : (!wheelEvent->mDeltaX && !wheelEvent->mDeltaY)) {
3284 : break;
3285 : }
3286 :
3287 0 : MOZ_ASSERT(static_cast<void*>(frameToScroll) ==
3288 : static_cast<void*>(pluginFrame));
3289 0 : if (!WheelTransaction::WillHandleDefaultAction(wheelEvent,
3290 : frameToScroll)) {
3291 0 : break;
3292 : }
3293 :
3294 0 : pluginFrame->HandleWheelEventAsDefaultAction(wheelEvent);
3295 0 : break;
3296 : case WheelPrefs::ACTION_NONE:
3297 : default:
3298 0 : bool allDeltaOverflown = false;
3299 0 : if (wheelEvent->mFlags.mHandledByAPZ) {
3300 0 : if (wheelEvent->mCanTriggerSwipe) {
3301 : // For events that can trigger swipes, APZ needs to know whether
3302 : // scrolling is possible in the requested direction. It does this
3303 : // by looking at the scroll overflow values on mCanTriggerSwipe
3304 : // events after they have been processed.
3305 0 : allDeltaOverflown =
3306 0 : !ComputeScrollTarget(mCurrentTarget, wheelEvent,
3307 : COMPUTE_DEFAULT_ACTION_TARGET);
3308 : }
3309 : } else {
3310 : // The event was processed neither by APZ nor by us, so all of the
3311 : // delta values must be overflown delta values.
3312 0 : allDeltaOverflown = true;
3313 : }
3314 :
3315 0 : if (!allDeltaOverflown) {
3316 0 : break;
3317 : }
3318 0 : wheelEvent->mOverflowDeltaX = wheelEvent->mDeltaX;
3319 0 : wheelEvent->mOverflowDeltaY = wheelEvent->mDeltaY;
3320 : WheelPrefs::GetInstance()->
3321 0 : CancelApplyingUserPrefsFromOverflowDelta(wheelEvent);
3322 0 : wheelEvent->mViewPortIsOverscrolled = true;
3323 0 : break;
3324 : }
3325 0 : *aStatus = nsEventStatus_eConsumeNoDefault;
3326 : }
3327 0 : break;
3328 :
3329 : case eGestureNotify:
3330 : {
3331 0 : if (nsEventStatus_eConsumeNoDefault != *aStatus) {
3332 0 : DecideGestureEvent(aEvent->AsGestureNotifyEvent(), mCurrentTarget);
3333 : }
3334 : }
3335 0 : break;
3336 :
3337 : case eDragEnter:
3338 : case eDragOver:
3339 : {
3340 0 : NS_ASSERTION(aEvent->mClass == eDragEventClass, "Expected a drag event");
3341 :
3342 : // Check if the drag is occurring inside a scrollable area. If so, scroll
3343 : // the area when the mouse is near the edges.
3344 0 : if (mCurrentTarget && aEvent->mMessage == eDragOver) {
3345 0 : nsIFrame* checkFrame = mCurrentTarget;
3346 0 : while (checkFrame) {
3347 0 : nsIScrollableFrame* scrollFrame = do_QueryFrame(checkFrame);
3348 : // Break out so only the innermost scrollframe is scrolled.
3349 0 : if (scrollFrame && scrollFrame->DragScroll(aEvent)) {
3350 0 : break;
3351 : }
3352 0 : checkFrame = checkFrame->GetParent();
3353 : }
3354 : }
3355 :
3356 0 : nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession();
3357 0 : if (!dragSession)
3358 0 : break;
3359 :
3360 : // Reset the flag.
3361 0 : dragSession->SetOnlyChromeDrop(false);
3362 0 : if (mPresContext) {
3363 0 : EnsureDocument(mPresContext);
3364 : }
3365 0 : bool isChromeDoc = nsContentUtils::IsChromeDoc(mDocument);
3366 :
3367 : // the initial dataTransfer is the one from the dragstart event that
3368 : // was set on the dragSession when the drag began.
3369 0 : nsCOMPtr<nsIDOMDataTransfer> dataTransfer;
3370 0 : nsCOMPtr<nsIDOMDataTransfer> initialDataTransfer;
3371 0 : dragSession->GetDataTransfer(getter_AddRefs(initialDataTransfer));
3372 :
3373 0 : WidgetDragEvent *dragEvent = aEvent->AsDragEvent();
3374 :
3375 : // collect any changes to moz cursor settings stored in the event's
3376 : // data transfer.
3377 0 : UpdateDragDataTransfer(dragEvent);
3378 :
3379 : // cancelling a dragenter or dragover event means that a drop should be
3380 : // allowed, so update the dropEffect and the canDrop state to indicate
3381 : // that a drag is allowed. If the event isn't cancelled, a drop won't be
3382 : // allowed. Essentially, to allow a drop somewhere, specify the effects
3383 : // using the effectAllowed and dropEffect properties in a dragenter or
3384 : // dragover event and cancel the event. To not allow a drop somewhere,
3385 : // don't cancel the event or set the effectAllowed or dropEffect to
3386 : // "none". This way, if the event is just ignored, no drop will be
3387 : // allowed.
3388 0 : uint32_t dropEffect = nsIDragService::DRAGDROP_ACTION_NONE;
3389 0 : uint32_t action = nsIDragService::DRAGDROP_ACTION_NONE;
3390 0 : if (nsEventStatus_eConsumeNoDefault == *aStatus) {
3391 : // if the event has a dataTransfer set, use it.
3392 0 : if (dragEvent->mDataTransfer) {
3393 : // get the dataTransfer and the dropEffect that was set on it
3394 0 : dataTransfer = do_QueryInterface(dragEvent->mDataTransfer);
3395 0 : dataTransfer->GetDropEffectInt(&dropEffect);
3396 : }
3397 : else {
3398 : // if dragEvent->mDataTransfer is null, it means that no attempt was
3399 : // made to access the dataTransfer during the event, yet the event
3400 : // was cancelled. Instead, use the initial data transfer available
3401 : // from the drag session. The drop effect would not have been
3402 : // initialized (which is done in DragEvent::GetDataTransfer),
3403 : // so set it from the drag action. We'll still want to filter it
3404 : // based on the effectAllowed below.
3405 0 : dataTransfer = initialDataTransfer;
3406 :
3407 0 : dragSession->GetDragAction(&action);
3408 :
3409 : // filter the drop effect based on the action. Use UNINITIALIZED as
3410 : // any effect is allowed.
3411 0 : dropEffect = nsContentUtils::FilterDropEffect(action,
3412 : nsIDragService::DRAGDROP_ACTION_UNINITIALIZED);
3413 : }
3414 :
3415 : // At this point, if the dataTransfer is null, it means that the
3416 : // drag was originally started by directly calling the drag service.
3417 : // Just assume that all effects are allowed.
3418 0 : uint32_t effectAllowed = nsIDragService::DRAGDROP_ACTION_UNINITIALIZED;
3419 0 : if (dataTransfer)
3420 0 : dataTransfer->GetEffectAllowedInt(&effectAllowed);
3421 :
3422 : // set the drag action based on the drop effect and effect allowed.
3423 : // The drop effect field on the drag transfer object specifies the
3424 : // desired current drop effect. However, it cannot be used if the
3425 : // effectAllowed state doesn't include that type of action. If the
3426 : // dropEffect is "none", then the action will be 'none' so a drop will
3427 : // not be allowed.
3428 0 : if (effectAllowed == nsIDragService::DRAGDROP_ACTION_UNINITIALIZED ||
3429 0 : dropEffect & effectAllowed)
3430 0 : action = dropEffect;
3431 :
3432 0 : if (action == nsIDragService::DRAGDROP_ACTION_NONE)
3433 0 : dropEffect = nsIDragService::DRAGDROP_ACTION_NONE;
3434 :
3435 : // inform the drag session that a drop is allowed on this node.
3436 0 : dragSession->SetDragAction(action);
3437 0 : dragSession->SetCanDrop(action != nsIDragService::DRAGDROP_ACTION_NONE);
3438 :
3439 : // For now, do this only for dragover.
3440 : //XXXsmaug dragenter needs some more work.
3441 0 : if (aEvent->mMessage == eDragOver && !isChromeDoc) {
3442 : // Someone has called preventDefault(), check whether is was on
3443 : // content or chrome.
3444 0 : dragSession->SetOnlyChromeDrop(
3445 0 : !dragEvent->mDefaultPreventedOnContent);
3446 : }
3447 0 : } else if (aEvent->mMessage == eDragOver && !isChromeDoc) {
3448 : // No one called preventDefault(), so handle drop only in chrome.
3449 0 : dragSession->SetOnlyChromeDrop(true);
3450 : }
3451 0 : if (ContentChild* child = ContentChild::GetSingleton()) {
3452 0 : child->SendUpdateDropEffect(action, dropEffect);
3453 : }
3454 0 : if (aEvent->HasBeenPostedToRemoteProcess()) {
3455 0 : dragSession->SetCanDrop(true);
3456 0 : } else if (initialDataTransfer) {
3457 : // Now set the drop effect in the initial dataTransfer. This ensures
3458 : // that we can get the desired drop effect in the drop event. For events
3459 : // dispatched to content, the content process will take care of setting
3460 : // this.
3461 0 : initialDataTransfer->SetDropEffectInt(dropEffect);
3462 : }
3463 : }
3464 0 : break;
3465 :
3466 : case eDrop:
3467 : {
3468 0 : sLastDragOverFrame = nullptr;
3469 0 : ClearGlobalActiveContent(this);
3470 0 : break;
3471 : }
3472 : case eDragExit:
3473 : // make sure to fire the enter and exit_synth events after the
3474 : // eDragExit event, otherwise we'll clean up too early
3475 0 : GenerateDragDropEnterExit(presContext, aEvent->AsDragEvent());
3476 0 : if (ContentChild* child = ContentChild::GetSingleton()) {
3477 : // SendUpdateDropEffect to prevent nsIDragService from waiting for
3478 : // response of forwarded dragexit event.
3479 0 : child->SendUpdateDropEffect(nsIDragService::DRAGDROP_ACTION_NONE,
3480 0 : nsIDragService::DRAGDROP_ACTION_NONE);
3481 : }
3482 0 : break;
3483 :
3484 : case eKeyUp:
3485 0 : break;
3486 :
3487 : case eKeyPress:
3488 : {
3489 0 : WidgetKeyboardEvent* keyEvent = aEvent->AsKeyboardEvent();
3490 0 : PostHandleKeyboardEvent(keyEvent, *aStatus);
3491 : }
3492 0 : break;
3493 :
3494 : case eMouseEnterIntoWidget:
3495 1 : if (mCurrentTarget) {
3496 2 : nsCOMPtr<nsIContent> targetContent;
3497 1 : mCurrentTarget->GetContentForEvent(aEvent, getter_AddRefs(targetContent));
3498 1 : SetContentState(targetContent, NS_EVENT_STATE_HOVER);
3499 : }
3500 1 : break;
3501 :
3502 : #ifdef XP_MACOSX
3503 : case eMouseActivate:
3504 : if (mCurrentTarget) {
3505 : nsCOMPtr<nsIContent> targetContent;
3506 : mCurrentTarget->GetContentForEvent(aEvent, getter_AddRefs(targetContent));
3507 : if (!NodeAllowsClickThrough(targetContent)) {
3508 : *aStatus = nsEventStatus_eConsumeNoDefault;
3509 : }
3510 : }
3511 : break;
3512 : #endif
3513 :
3514 : default:
3515 9 : break;
3516 : }
3517 :
3518 : //Reset target frame to null to avoid mistargeting after reentrant event
3519 10 : mCurrentTarget = nullptr;
3520 10 : mCurrentTargetContent = nullptr;
3521 :
3522 10 : return ret;
3523 : }
3524 :
3525 : TabParent*
3526 0 : EventStateManager::GetCrossProcessTarget()
3527 : {
3528 0 : return IMEStateManager::GetActiveTabParent();
3529 : }
3530 :
3531 : bool
3532 0 : EventStateManager::IsTargetCrossProcess(WidgetGUIEvent* aEvent)
3533 : {
3534 : // Check to see if there is a focused, editable content in chrome,
3535 : // in that case, do not forward IME events to content
3536 0 : nsIContent *focusedContent = GetFocusedContent();
3537 0 : if (focusedContent && focusedContent->IsEditable())
3538 0 : return false;
3539 0 : return IMEStateManager::GetActiveTabParent() != nullptr;
3540 : }
3541 :
3542 : void
3543 4 : EventStateManager::NotifyDestroyPresContext(nsPresContext* aPresContext)
3544 : {
3545 4 : IMEStateManager::OnDestroyPresContext(aPresContext);
3546 4 : if (mHoverContent) {
3547 : // Bug 70855: Presentation is going away, possibly for a reframe.
3548 : // Reset the hover state so that if we're recreating the presentation,
3549 : // we won't have the old hover state still set in the new presentation,
3550 : // as if the new presentation is resized, a new element may be hovered.
3551 0 : SetContentState(nullptr, NS_EVENT_STATE_HOVER);
3552 : }
3553 4 : mPointersEnterLeaveHelper.Clear();
3554 4 : }
3555 :
3556 : void
3557 28 : EventStateManager::SetPresContext(nsPresContext* aPresContext)
3558 : {
3559 28 : mPresContext = aPresContext;
3560 28 : }
3561 :
3562 : void
3563 5 : EventStateManager::ClearFrameRefs(nsIFrame* aFrame)
3564 : {
3565 5 : if (aFrame && aFrame == mCurrentTarget) {
3566 0 : mCurrentTargetContent = aFrame->GetContent();
3567 : }
3568 5 : }
3569 :
3570 : void
3571 8 : EventStateManager::UpdateCursor(nsPresContext* aPresContext,
3572 : WidgetEvent* aEvent,
3573 : nsIFrame* aTargetFrame,
3574 : nsEventStatus* aStatus)
3575 : {
3576 8 : if (aTargetFrame && IsRemoteTarget(aTargetFrame->GetContent())) {
3577 8 : return;
3578 : }
3579 :
3580 0 : int32_t cursor = NS_STYLE_CURSOR_DEFAULT;
3581 0 : imgIContainer* container = nullptr;
3582 0 : bool haveHotspot = false;
3583 0 : float hotspotX = 0.0f, hotspotY = 0.0f;
3584 :
3585 : //If cursor is locked just use the locked one
3586 0 : if (mLockCursor) {
3587 0 : cursor = mLockCursor;
3588 : }
3589 : //If not locked, look for correct cursor
3590 0 : else if (aTargetFrame) {
3591 0 : nsIFrame::Cursor framecursor;
3592 : nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent,
3593 0 : aTargetFrame);
3594 : // Avoid setting cursor when the mouse is over a windowless pluign.
3595 0 : if (NS_FAILED(aTargetFrame->GetCursor(pt, framecursor))) {
3596 0 : if (XRE_IsContentProcess()) {
3597 0 : mLastFrameConsumedSetCursor = true;
3598 : }
3599 0 : return;
3600 : }
3601 : // Make sure cursors get reset after the mouse leaves a
3602 : // windowless plugin frame.
3603 0 : if (mLastFrameConsumedSetCursor) {
3604 0 : ClearCachedWidgetCursor(aTargetFrame);
3605 0 : mLastFrameConsumedSetCursor = false;
3606 : }
3607 : // If the current cursor is from the same frame, and it is now
3608 : // loading some new image for the cursor, we should wait for a
3609 : // while rather than taking its fallback cursor directly.
3610 0 : if (framecursor.mLoading &&
3611 0 : gLastCursorSourceFrame == aTargetFrame &&
3612 0 : TimeStamp::NowLoRes() - gLastCursorUpdateTime <
3613 0 : TimeDuration::FromMilliseconds(kCursorLoadingTimeout)) {
3614 0 : return;
3615 : }
3616 0 : cursor = framecursor.mCursor;
3617 0 : container = framecursor.mContainer;
3618 0 : haveHotspot = framecursor.mHaveHotspot;
3619 0 : hotspotX = framecursor.mHotspotX;
3620 0 : hotspotY = framecursor.mHotspotY;
3621 : }
3622 :
3623 0 : if (nsContentUtils::UseActivityCursor()) {
3624 : // Check whether or not to show the busy cursor
3625 0 : nsCOMPtr<nsIDocShell> docShell(aPresContext->GetDocShell());
3626 0 : if (!docShell) return;
3627 0 : uint32_t busyFlags = nsIDocShell::BUSY_FLAGS_NONE;
3628 0 : docShell->GetBusyFlags(&busyFlags);
3629 :
3630 : // Show busy cursor everywhere before page loads
3631 : // and just replace the arrow cursor after page starts loading
3632 0 : if (busyFlags & nsIDocShell::BUSY_FLAGS_BUSY &&
3633 0 : (cursor == NS_STYLE_CURSOR_AUTO || cursor == NS_STYLE_CURSOR_DEFAULT))
3634 : {
3635 0 : cursor = NS_STYLE_CURSOR_SPINNING;
3636 0 : container = nullptr;
3637 : }
3638 : }
3639 :
3640 0 : if (aTargetFrame) {
3641 0 : SetCursor(cursor, container, haveHotspot, hotspotX, hotspotY,
3642 0 : aTargetFrame->GetNearestWidget(), false);
3643 0 : gLastCursorSourceFrame = aTargetFrame;
3644 0 : gLastCursorUpdateTime = TimeStamp::NowLoRes();
3645 : }
3646 :
3647 0 : if (mLockCursor || NS_STYLE_CURSOR_AUTO != cursor) {
3648 0 : *aStatus = nsEventStatus_eConsumeDoDefault;
3649 : }
3650 : }
3651 :
3652 : void
3653 0 : EventStateManager::ClearCachedWidgetCursor(nsIFrame* aTargetFrame)
3654 : {
3655 0 : if (!aTargetFrame) {
3656 0 : return;
3657 : }
3658 0 : nsIWidget* aWidget = aTargetFrame->GetNearestWidget();
3659 0 : if (!aWidget) {
3660 0 : return;
3661 : }
3662 0 : aWidget->ClearCachedCursor();
3663 : }
3664 :
3665 : nsresult
3666 0 : EventStateManager::SetCursor(int32_t aCursor, imgIContainer* aContainer,
3667 : bool aHaveHotspot,
3668 : float aHotspotX, float aHotspotY,
3669 : nsIWidget* aWidget, bool aLockCursor)
3670 : {
3671 0 : EnsureDocument(mPresContext);
3672 0 : NS_ENSURE_TRUE(mDocument, NS_ERROR_FAILURE);
3673 0 : sMouseOverDocument = mDocument.get();
3674 :
3675 : nsCursor c;
3676 :
3677 0 : NS_ENSURE_TRUE(aWidget, NS_ERROR_FAILURE);
3678 0 : if (aLockCursor) {
3679 0 : if (NS_STYLE_CURSOR_AUTO != aCursor) {
3680 0 : mLockCursor = aCursor;
3681 : }
3682 : else {
3683 : //If cursor style is set to auto we unlock the cursor again.
3684 0 : mLockCursor = 0;
3685 : }
3686 : }
3687 0 : switch (aCursor) {
3688 : default:
3689 : case NS_STYLE_CURSOR_AUTO:
3690 : case NS_STYLE_CURSOR_DEFAULT:
3691 0 : c = eCursor_standard;
3692 0 : break;
3693 : case NS_STYLE_CURSOR_POINTER:
3694 0 : c = eCursor_hyperlink;
3695 0 : break;
3696 : case NS_STYLE_CURSOR_CROSSHAIR:
3697 0 : c = eCursor_crosshair;
3698 0 : break;
3699 : case NS_STYLE_CURSOR_MOVE:
3700 0 : c = eCursor_move;
3701 0 : break;
3702 : case NS_STYLE_CURSOR_TEXT:
3703 0 : c = eCursor_select;
3704 0 : break;
3705 : case NS_STYLE_CURSOR_WAIT:
3706 0 : c = eCursor_wait;
3707 0 : break;
3708 : case NS_STYLE_CURSOR_HELP:
3709 0 : c = eCursor_help;
3710 0 : break;
3711 : case NS_STYLE_CURSOR_N_RESIZE:
3712 0 : c = eCursor_n_resize;
3713 0 : break;
3714 : case NS_STYLE_CURSOR_S_RESIZE:
3715 0 : c = eCursor_s_resize;
3716 0 : break;
3717 : case NS_STYLE_CURSOR_W_RESIZE:
3718 0 : c = eCursor_w_resize;
3719 0 : break;
3720 : case NS_STYLE_CURSOR_E_RESIZE:
3721 0 : c = eCursor_e_resize;
3722 0 : break;
3723 : case NS_STYLE_CURSOR_NW_RESIZE:
3724 0 : c = eCursor_nw_resize;
3725 0 : break;
3726 : case NS_STYLE_CURSOR_SE_RESIZE:
3727 0 : c = eCursor_se_resize;
3728 0 : break;
3729 : case NS_STYLE_CURSOR_NE_RESIZE:
3730 0 : c = eCursor_ne_resize;
3731 0 : break;
3732 : case NS_STYLE_CURSOR_SW_RESIZE:
3733 0 : c = eCursor_sw_resize;
3734 0 : break;
3735 : case NS_STYLE_CURSOR_COPY: // CSS3
3736 0 : c = eCursor_copy;
3737 0 : break;
3738 : case NS_STYLE_CURSOR_ALIAS:
3739 0 : c = eCursor_alias;
3740 0 : break;
3741 : case NS_STYLE_CURSOR_CONTEXT_MENU:
3742 0 : c = eCursor_context_menu;
3743 0 : break;
3744 : case NS_STYLE_CURSOR_CELL:
3745 0 : c = eCursor_cell;
3746 0 : break;
3747 : case NS_STYLE_CURSOR_GRAB:
3748 0 : c = eCursor_grab;
3749 0 : break;
3750 : case NS_STYLE_CURSOR_GRABBING:
3751 0 : c = eCursor_grabbing;
3752 0 : break;
3753 : case NS_STYLE_CURSOR_SPINNING:
3754 0 : c = eCursor_spinning;
3755 0 : break;
3756 : case NS_STYLE_CURSOR_ZOOM_IN:
3757 0 : c = eCursor_zoom_in;
3758 0 : break;
3759 : case NS_STYLE_CURSOR_ZOOM_OUT:
3760 0 : c = eCursor_zoom_out;
3761 0 : break;
3762 : case NS_STYLE_CURSOR_NOT_ALLOWED:
3763 0 : c = eCursor_not_allowed;
3764 0 : break;
3765 : case NS_STYLE_CURSOR_COL_RESIZE:
3766 0 : c = eCursor_col_resize;
3767 0 : break;
3768 : case NS_STYLE_CURSOR_ROW_RESIZE:
3769 0 : c = eCursor_row_resize;
3770 0 : break;
3771 : case NS_STYLE_CURSOR_NO_DROP:
3772 0 : c = eCursor_no_drop;
3773 0 : break;
3774 : case NS_STYLE_CURSOR_VERTICAL_TEXT:
3775 0 : c = eCursor_vertical_text;
3776 0 : break;
3777 : case NS_STYLE_CURSOR_ALL_SCROLL:
3778 0 : c = eCursor_all_scroll;
3779 0 : break;
3780 : case NS_STYLE_CURSOR_NESW_RESIZE:
3781 0 : c = eCursor_nesw_resize;
3782 0 : break;
3783 : case NS_STYLE_CURSOR_NWSE_RESIZE:
3784 0 : c = eCursor_nwse_resize;
3785 0 : break;
3786 : case NS_STYLE_CURSOR_NS_RESIZE:
3787 0 : c = eCursor_ns_resize;
3788 0 : break;
3789 : case NS_STYLE_CURSOR_EW_RESIZE:
3790 0 : c = eCursor_ew_resize;
3791 0 : break;
3792 : case NS_STYLE_CURSOR_NONE:
3793 0 : c = eCursor_none;
3794 0 : break;
3795 : }
3796 :
3797 : // First, try the imgIContainer, if non-null
3798 0 : nsresult rv = NS_ERROR_FAILURE;
3799 0 : if (aContainer) {
3800 : uint32_t hotspotX, hotspotY;
3801 :
3802 : // css3-ui says to use the CSS-specified hotspot if present,
3803 : // otherwise use the intrinsic hotspot, otherwise use the top left
3804 : // corner.
3805 0 : if (aHaveHotspot) {
3806 : int32_t imgWidth, imgHeight;
3807 0 : aContainer->GetWidth(&imgWidth);
3808 0 : aContainer->GetHeight(&imgHeight);
3809 :
3810 : // XXX std::max(NS_lround(x), 0)?
3811 0 : hotspotX = aHotspotX > 0.0f
3812 0 : ? uint32_t(aHotspotX + 0.5f) : uint32_t(0);
3813 0 : if (hotspotX >= uint32_t(imgWidth))
3814 0 : hotspotX = imgWidth - 1;
3815 0 : hotspotY = aHotspotY > 0.0f
3816 0 : ? uint32_t(aHotspotY + 0.5f) : uint32_t(0);
3817 0 : if (hotspotY >= uint32_t(imgHeight))
3818 0 : hotspotY = imgHeight - 1;
3819 : } else {
3820 0 : hotspotX = 0;
3821 0 : hotspotY = 0;
3822 0 : nsCOMPtr<nsIProperties> props(do_QueryInterface(aContainer));
3823 0 : if (props) {
3824 0 : nsCOMPtr<nsISupportsPRUint32> hotspotXWrap, hotspotYWrap;
3825 :
3826 0 : props->Get("hotspotX", NS_GET_IID(nsISupportsPRUint32), getter_AddRefs(hotspotXWrap));
3827 0 : props->Get("hotspotY", NS_GET_IID(nsISupportsPRUint32), getter_AddRefs(hotspotYWrap));
3828 :
3829 0 : if (hotspotXWrap)
3830 0 : hotspotXWrap->GetData(&hotspotX);
3831 0 : if (hotspotYWrap)
3832 0 : hotspotYWrap->GetData(&hotspotY);
3833 : }
3834 : }
3835 :
3836 0 : rv = aWidget->SetCursor(aContainer, hotspotX, hotspotY);
3837 : }
3838 :
3839 0 : if (NS_FAILED(rv))
3840 0 : aWidget->SetCursor(c);
3841 :
3842 0 : return NS_OK;
3843 : }
3844 :
3845 4 : class MOZ_STACK_CLASS ESMEventCB : public EventDispatchingCallback
3846 : {
3847 : public:
3848 4 : explicit ESMEventCB(nsIContent* aTarget) : mTarget(aTarget) {}
3849 :
3850 2 : void HandleEvent(EventChainPostVisitor& aVisitor) override
3851 : {
3852 2 : if (aVisitor.mPresContext) {
3853 2 : nsIFrame* frame = aVisitor.mPresContext->GetPrimaryFrameFor(mTarget);
3854 2 : if (frame) {
3855 4 : frame->HandleEvent(aVisitor.mPresContext,
3856 2 : aVisitor.mEvent->AsGUIEvent(),
3857 4 : &aVisitor.mEventStatus);
3858 : }
3859 : }
3860 2 : }
3861 :
3862 : nsCOMPtr<nsIContent> mTarget;
3863 : };
3864 :
3865 : /*static*/ bool
3866 586 : EventStateManager::IsHandlingUserInput()
3867 : {
3868 586 : return sUserInputEventDepth > 0;
3869 : }
3870 :
3871 : static void
3872 6 : CreateMouseOrPointerWidgetEvent(WidgetMouseEvent* aMouseEvent,
3873 : EventMessage aMessage,
3874 : nsIContent* aRelatedContent,
3875 : nsAutoPtr<WidgetMouseEvent>& aNewEvent)
3876 : {
3877 6 : WidgetPointerEvent* sourcePointer = aMouseEvent->AsPointerEvent();
3878 6 : if (sourcePointer) {
3879 4 : AUTO_PROFILER_LABEL("CreateMouseOrPointerWidgetEvent", EVENTS);
3880 :
3881 4 : nsAutoPtr<WidgetPointerEvent> newPointerEvent;
3882 : newPointerEvent =
3883 2 : new WidgetPointerEvent(aMouseEvent->IsTrusted(), aMessage,
3884 4 : aMouseEvent->mWidget);
3885 2 : newPointerEvent->mIsPrimary = sourcePointer->mIsPrimary;
3886 2 : newPointerEvent->mWidth = sourcePointer->mWidth;
3887 2 : newPointerEvent->mHeight = sourcePointer->mHeight;
3888 2 : newPointerEvent->inputSource = sourcePointer->inputSource;
3889 2 : newPointerEvent->relatedTarget = aRelatedContent;
3890 2 : aNewEvent = newPointerEvent.forget();
3891 : } else {
3892 : aNewEvent =
3893 4 : new WidgetMouseEvent(aMouseEvent->IsTrusted(), aMessage,
3894 8 : aMouseEvent->mWidget, WidgetMouseEvent::eReal);
3895 4 : aNewEvent->relatedTarget = aRelatedContent;
3896 : }
3897 6 : aNewEvent->mRefPoint = aMouseEvent->mRefPoint;
3898 6 : aNewEvent->mModifiers = aMouseEvent->mModifiers;
3899 6 : aNewEvent->button = aMouseEvent->button;
3900 6 : aNewEvent->buttons = aMouseEvent->buttons;
3901 6 : aNewEvent->pressure = aMouseEvent->pressure;
3902 6 : aNewEvent->mPluginEvent = aMouseEvent->mPluginEvent;
3903 6 : aNewEvent->inputSource = aMouseEvent->inputSource;
3904 6 : aNewEvent->pointerId = aMouseEvent->pointerId;
3905 6 : }
3906 :
3907 : nsIFrame*
3908 4 : EventStateManager::DispatchMouseOrPointerEvent(WidgetMouseEvent* aMouseEvent,
3909 : EventMessage aMessage,
3910 : nsIContent* aTargetContent,
3911 : nsIContent* aRelatedContent)
3912 : {
3913 : // http://dvcs.w3.org/hg/webevents/raw-file/default/mouse-lock.html#methods
3914 : // "[When the mouse is locked on an element...e]vents that require the concept
3915 : // of a mouse cursor must not be dispatched (for example: mouseover, mouseout).
3916 4 : if (sIsPointerLocked &&
3917 0 : (aMessage == eMouseLeave ||
3918 0 : aMessage == eMouseEnter ||
3919 0 : aMessage == eMouseOver ||
3920 : aMessage == eMouseOut)) {
3921 0 : mCurrentTargetContent = nullptr;
3922 : nsCOMPtr<Element> pointerLockedElement =
3923 0 : do_QueryReferent(EventStateManager::sPointerLockedElement);
3924 0 : if (!pointerLockedElement) {
3925 0 : NS_WARNING("Should have pointer locked element, but didn't.");
3926 0 : return nullptr;
3927 : }
3928 0 : nsCOMPtr<nsIContent> content = do_QueryInterface(pointerLockedElement);
3929 0 : return mPresContext->GetPrimaryFrameFor(content);
3930 : }
3931 :
3932 4 : mCurrentTargetContent = nullptr;
3933 :
3934 4 : if (!aTargetContent) {
3935 0 : return nullptr;
3936 : }
3937 :
3938 8 : nsAutoPtr<WidgetMouseEvent> dispatchEvent;
3939 4 : CreateMouseOrPointerWidgetEvent(aMouseEvent, aMessage,
3940 4 : aRelatedContent, dispatchEvent);
3941 :
3942 8 : AutoWeakFrame previousTarget = mCurrentTarget;
3943 4 : mCurrentTargetContent = aTargetContent;
3944 :
3945 4 : nsIFrame* targetFrame = nullptr;
3946 :
3947 4 : nsEventStatus status = nsEventStatus_eIgnore;
3948 8 : ESMEventCB callback(aTargetContent);
3949 4 : EventDispatcher::Dispatch(aTargetContent, mPresContext, dispatchEvent, nullptr,
3950 8 : &status, &callback);
3951 :
3952 4 : if (mPresContext) {
3953 : // Although the primary frame was checked in event callback, it may not be
3954 : // the same object after event dispatch and handling, so refetch it.
3955 4 : targetFrame = mPresContext->GetPrimaryFrameFor(aTargetContent);
3956 :
3957 : // If we are entering/leaving remote content, dispatch a mouse enter/exit
3958 : // event to the remote frame.
3959 4 : if (IsRemoteTarget(aTargetContent)) {
3960 4 : if (aMessage == eMouseOut) {
3961 : // For remote content, send a "top-level" widget mouse exit event.
3962 2 : nsAutoPtr<WidgetMouseEvent> remoteEvent;
3963 : CreateMouseOrPointerWidgetEvent(aMouseEvent, eMouseExitFromWidget,
3964 1 : aRelatedContent, remoteEvent);
3965 1 : remoteEvent->mExitFrom = WidgetMouseEvent::eTopLevel;
3966 :
3967 : // mCurrentTarget is set to the new target, so we must reset it to the
3968 : // old target and then dispatch a cross-process event. (mCurrentTarget
3969 : // will be set back below.) HandleCrossProcessEvent will query for the
3970 : // proper target via GetEventTarget which will return mCurrentTarget.
3971 1 : mCurrentTarget = targetFrame;
3972 1 : HandleCrossProcessEvent(remoteEvent, &status);
3973 3 : } else if (aMessage == eMouseOver) {
3974 2 : nsAutoPtr<WidgetMouseEvent> remoteEvent;
3975 : CreateMouseOrPointerWidgetEvent(aMouseEvent, eMouseEnterIntoWidget,
3976 1 : aRelatedContent, remoteEvent);
3977 1 : HandleCrossProcessEvent(remoteEvent, &status);
3978 : }
3979 : }
3980 : }
3981 :
3982 4 : mCurrentTargetContent = nullptr;
3983 4 : mCurrentTarget = previousTarget;
3984 :
3985 4 : return targetFrame;
3986 : }
3987 :
3988 4 : class EnterLeaveDispatcher
3989 : {
3990 : public:
3991 4 : EnterLeaveDispatcher(EventStateManager* aESM,
3992 : nsIContent* aTarget, nsIContent* aRelatedTarget,
3993 : WidgetMouseEvent* aMouseEvent,
3994 : EventMessage aEventMessage)
3995 4 : : mESM(aESM)
3996 : , mMouseEvent(aMouseEvent)
3997 4 : , mEventMessage(aEventMessage)
3998 : {
3999 : nsPIDOMWindowInner* win =
4000 4 : aTarget ? aTarget->OwnerDoc()->GetInnerWindow() : nullptr;
4001 6 : if (aMouseEvent->AsPointerEvent() ? win && win->HasPointerEnterLeaveEventListeners() :
4002 2 : win && win->HasMouseEnterLeaveEventListeners()) {
4003 : mRelatedTarget = aRelatedTarget ?
4004 0 : aRelatedTarget->FindFirstNonChromeOnlyAccessContent() : nullptr;
4005 0 : nsINode* commonParent = nullptr;
4006 0 : if (aTarget && aRelatedTarget) {
4007 : commonParent =
4008 0 : nsContentUtils::GetCommonAncestor(aTarget, aRelatedTarget);
4009 : }
4010 0 : nsIContent* current = aTarget;
4011 : // Note, it is ok if commonParent is null!
4012 0 : while (current && current != commonParent) {
4013 0 : if (!current->ChromeOnlyAccess()) {
4014 0 : mTargets.AppendObject(current);
4015 : }
4016 : // mouseenter/leave is fired only on elements.
4017 0 : current = current->GetParent();
4018 : }
4019 : }
4020 4 : }
4021 :
4022 4 : void Dispatch()
4023 : {
4024 4 : if (mEventMessage == eMouseEnter || mEventMessage == ePointerEnter) {
4025 2 : for (int32_t i = mTargets.Count() - 1; i >= 0; --i) {
4026 0 : mESM->DispatchMouseOrPointerEvent(mMouseEvent, mEventMessage,
4027 0 : mTargets[i], mRelatedTarget);
4028 2 : }
4029 : } else {
4030 2 : for (int32_t i = 0; i < mTargets.Count(); ++i) {
4031 0 : mESM->DispatchMouseOrPointerEvent(mMouseEvent, mEventMessage,
4032 0 : mTargets[i], mRelatedTarget);
4033 : }
4034 : }
4035 4 : }
4036 :
4037 : EventStateManager* mESM;
4038 : nsCOMArray<nsIContent> mTargets;
4039 : nsCOMPtr<nsIContent> mRelatedTarget;
4040 : WidgetMouseEvent* mMouseEvent;
4041 : EventMessage mEventMessage;
4042 : };
4043 :
4044 : void
4045 4 : EventStateManager::NotifyMouseOut(WidgetMouseEvent* aMouseEvent,
4046 : nsIContent* aMovingInto)
4047 : {
4048 4 : OverOutElementsWrapper* wrapper = GetWrapperByEventID(aMouseEvent);
4049 :
4050 4 : if (!wrapper->mLastOverElement)
4051 4 : return;
4052 : // Before firing mouseout, check for recursion
4053 2 : if (wrapper->mLastOverElement == wrapper->mFirstOutEventElement)
4054 0 : return;
4055 :
4056 2 : if (wrapper->mLastOverFrame) {
4057 : // if the frame is associated with a subdocument,
4058 : // tell the subdocument that we're moving out of it
4059 2 : nsSubDocumentFrame* subdocFrame = do_QueryFrame(wrapper->mLastOverFrame.GetFrame());
4060 2 : if (subdocFrame) {
4061 4 : nsCOMPtr<nsIDocShell> docshell;
4062 2 : subdocFrame->GetDocShell(getter_AddRefs(docshell));
4063 2 : if (docshell) {
4064 0 : RefPtr<nsPresContext> presContext;
4065 0 : docshell->GetPresContext(getter_AddRefs(presContext));
4066 :
4067 0 : if (presContext) {
4068 0 : EventStateManager* kidESM = presContext->EventStateManager();
4069 : // Not moving into any element in this subdocument
4070 0 : kidESM->NotifyMouseOut(aMouseEvent, nullptr);
4071 : }
4072 : }
4073 : }
4074 : }
4075 : // That could have caused DOM events which could wreak havoc. Reverify
4076 : // things and be careful.
4077 2 : if (!wrapper->mLastOverElement)
4078 0 : return;
4079 :
4080 : // Store the first mouseOut event we fire and don't refire mouseOut
4081 : // to that element while the first mouseOut is still ongoing.
4082 2 : wrapper->mFirstOutEventElement = wrapper->mLastOverElement;
4083 :
4084 : // Don't touch hover state if aMovingInto is non-null. Caller will update
4085 : // hover state itself, and we have optimizations for hover switching between
4086 : // two nearby elements both deep in the DOM tree that would be defeated by
4087 : // switching the hover state to null here.
4088 2 : bool isPointer = aMouseEvent->mClass == ePointerEventClass;
4089 2 : if (!aMovingInto && !isPointer) {
4090 : // Unset :hover
4091 1 : SetContentState(nullptr, NS_EVENT_STATE_HOVER);
4092 : }
4093 :
4094 : EnterLeaveDispatcher leaveDispatcher(this, wrapper->mLastOverElement,
4095 : aMovingInto, aMouseEvent,
4096 4 : isPointer ? ePointerLeave : eMouseLeave);
4097 :
4098 : // Fire mouseout
4099 2 : DispatchMouseOrPointerEvent(aMouseEvent, isPointer ? ePointerOut : eMouseOut,
4100 2 : wrapper->mLastOverElement, aMovingInto);
4101 2 : leaveDispatcher.Dispatch();
4102 :
4103 2 : wrapper->mLastOverFrame = nullptr;
4104 2 : wrapper->mLastOverElement = nullptr;
4105 :
4106 : // Turn recursion protection back off
4107 2 : wrapper->mFirstOutEventElement = nullptr;
4108 : }
4109 :
4110 : void
4111 8 : EventStateManager::NotifyMouseOver(WidgetMouseEvent* aMouseEvent,
4112 : nsIContent* aContent)
4113 : {
4114 8 : NS_ASSERTION(aContent, "Mouse must be over something");
4115 :
4116 8 : OverOutElementsWrapper* wrapper = GetWrapperByEventID(aMouseEvent);
4117 :
4118 8 : if (wrapper->mLastOverElement == aContent)
4119 12 : return;
4120 :
4121 : // Before firing mouseover, check for recursion
4122 2 : if (aContent == wrapper->mFirstOverEventElement)
4123 0 : return;
4124 :
4125 : // Check to see if we're a subdocument and if so update the parent
4126 : // document's ESM state to indicate that the mouse is over the
4127 : // content associated with our subdocument.
4128 2 : EnsureDocument(mPresContext);
4129 2 : if (nsIDocument *parentDoc = mDocument->GetParentDocument()) {
4130 0 : if (nsIContent *docContent = parentDoc->FindContentForSubDocument(mDocument)) {
4131 0 : if (nsIPresShell *parentShell = parentDoc->GetShell()) {
4132 : EventStateManager* parentESM =
4133 0 : parentShell->GetPresContext()->EventStateManager();
4134 0 : parentESM->NotifyMouseOver(aMouseEvent, docContent);
4135 : }
4136 : }
4137 : }
4138 : // Firing the DOM event in the parent document could cause all kinds
4139 : // of havoc. Reverify and take care.
4140 2 : if (wrapper->mLastOverElement == aContent)
4141 0 : return;
4142 :
4143 : // Remember mLastOverElement as the related content for the
4144 : // DispatchMouseOrPointerEvent() call below, since NotifyMouseOut() resets it, bug 298477.
4145 4 : nsCOMPtr<nsIContent> lastOverElement = wrapper->mLastOverElement;
4146 :
4147 2 : bool isPointer = aMouseEvent->mClass == ePointerEventClass;
4148 :
4149 : EnterLeaveDispatcher enterDispatcher(this, aContent, lastOverElement,
4150 : aMouseEvent,
4151 4 : isPointer ? ePointerEnter : eMouseEnter);
4152 :
4153 2 : NotifyMouseOut(aMouseEvent, aContent);
4154 :
4155 : // Store the first mouseOver event we fire and don't refire mouseOver
4156 : // to that element while the first mouseOver is still ongoing.
4157 2 : wrapper->mFirstOverEventElement = aContent;
4158 :
4159 2 : if (!isPointer) {
4160 1 : SetContentState(aContent, NS_EVENT_STATE_HOVER);
4161 : }
4162 :
4163 : // Fire mouseover
4164 : wrapper->mLastOverFrame =
4165 : DispatchMouseOrPointerEvent(aMouseEvent,
4166 : isPointer ? ePointerOver : eMouseOver,
4167 2 : aContent, lastOverElement);
4168 2 : enterDispatcher.Dispatch();
4169 2 : wrapper->mLastOverElement = aContent;
4170 :
4171 : // Turn recursion protection back off
4172 2 : wrapper->mFirstOverEventElement = nullptr;
4173 : }
4174 :
4175 : // Returns the center point of the window's client area. This is
4176 : // in widget coordinates, i.e. relative to the widget's top-left
4177 : // corner, not in screen coordinates, the same units that UIEvent::
4178 : // refpoint is in. It may not be the exact center of the window if
4179 : // the platform requires rounding the coordinate.
4180 : static LayoutDeviceIntPoint
4181 0 : GetWindowClientRectCenter(nsIWidget* aWidget)
4182 : {
4183 0 : NS_ENSURE_TRUE(aWidget, LayoutDeviceIntPoint(0, 0));
4184 :
4185 0 : LayoutDeviceIntRect rect = aWidget->GetClientBounds();
4186 0 : LayoutDeviceIntPoint point(rect.x + rect.width / 2,
4187 0 : rect.y + rect.height / 2);
4188 0 : int32_t round = aWidget->RoundsWidgetCoordinatesTo();
4189 0 : point.x = point.x / round * round;
4190 0 : point.y = point.y / round * round;
4191 0 : return point - aWidget->WidgetToScreenOffset();
4192 : }
4193 :
4194 : void
4195 1 : EventStateManager::GeneratePointerEnterExit(EventMessage aMessage,
4196 : WidgetMouseEvent* aEvent)
4197 : {
4198 2 : WidgetPointerEvent pointerEvent(*aEvent);
4199 1 : pointerEvent.mMessage = aMessage;
4200 1 : GenerateMouseEnterExit(&pointerEvent);
4201 1 : }
4202 :
4203 : void
4204 10 : EventStateManager::GenerateMouseEnterExit(WidgetMouseEvent* aMouseEvent)
4205 : {
4206 10 : EnsureDocument(mPresContext);
4207 10 : if (!mDocument)
4208 0 : return;
4209 :
4210 : // Hold onto old target content through the event and reset after.
4211 20 : nsCOMPtr<nsIContent> targetBeforeEvent = mCurrentTargetContent;
4212 :
4213 10 : switch(aMouseEvent->mMessage) {
4214 : case eMouseMove:
4215 : {
4216 : // Mouse movement is reported on the MouseEvent.movement{X,Y} fields.
4217 : // Movement is calculated in UIEvent::GetMovementPoint() as:
4218 : // previous_mousemove_mRefPoint - current_mousemove_mRefPoint.
4219 4 : if (sIsPointerLocked && aMouseEvent->mWidget) {
4220 : // The pointer is locked. If the pointer is not located at the center of
4221 : // the window, dispatch a synthetic mousemove to return the pointer there.
4222 : // Doing this between "real" pointer moves gives the impression that the
4223 : // (locked) pointer can continue moving and won't stop at the screen
4224 : // boundary. We cancel the synthetic event so that we don't end up
4225 : // dispatching the centering move event to content.
4226 : LayoutDeviceIntPoint center =
4227 0 : GetWindowClientRectCenter(aMouseEvent->mWidget);
4228 0 : aMouseEvent->mLastRefPoint = center;
4229 0 : if (aMouseEvent->mRefPoint != center) {
4230 : // Mouse move doesn't finish at the center of the window. Dispatch a
4231 : // synthetic native mouse event to move the pointer back to the center
4232 : // of the window, to faciliate more movement. But first, record that
4233 : // we've dispatched a synthetic mouse movement, so we can cancel it
4234 : // in the other branch here.
4235 0 : sSynthCenteringPoint = center;
4236 0 : aMouseEvent->mWidget->SynthesizeNativeMouseMove(
4237 0 : center + aMouseEvent->mWidget->WidgetToScreenOffset(), nullptr);
4238 0 : } else if (aMouseEvent->mRefPoint == sSynthCenteringPoint) {
4239 : // This is the "synthetic native" event we dispatched to re-center the
4240 : // pointer. Cancel it so we don't expose the centering move to content.
4241 0 : aMouseEvent->StopPropagation();
4242 : // Clear sSynthCenteringPoint so we don't cancel other events
4243 : // targeted at the center.
4244 0 : sSynthCenteringPoint = kInvalidRefPoint;
4245 : }
4246 4 : } else if (sLastRefPoint == kInvalidRefPoint) {
4247 : // We don't have a valid previous mousemove mRefPoint. This is either
4248 : // the first move we've encountered, or the mouse has just re-entered
4249 : // the application window. We should report (0,0) movement for this
4250 : // case, so make the current and previous mRefPoints the same.
4251 1 : aMouseEvent->mLastRefPoint = aMouseEvent->mRefPoint;
4252 : } else {
4253 3 : aMouseEvent->mLastRefPoint = sLastRefPoint;
4254 : }
4255 :
4256 : // Update the last known mRefPoint with the current mRefPoint.
4257 4 : sLastRefPoint = aMouseEvent->mRefPoint;
4258 : }
4259 : MOZ_FALLTHROUGH;
4260 : case ePointerMove:
4261 : case ePointerDown:
4262 : case ePointerGotCapture:
4263 : {
4264 : // Get the target content target (mousemove target == mouseover target)
4265 16 : nsCOMPtr<nsIContent> targetElement = GetEventTargetContent(aMouseEvent);
4266 8 : if (!targetElement) {
4267 : // We're always over the document root, even if we're only
4268 : // over dead space in a page (whose frame is not associated with
4269 : // any content) or in print preview dead space
4270 0 : targetElement = mDocument->GetRootElement();
4271 : }
4272 8 : if (targetElement) {
4273 8 : NotifyMouseOver(aMouseEvent, targetElement);
4274 : }
4275 : }
4276 8 : break;
4277 : case ePointerUp:
4278 : {
4279 : // Get the target content target (mousemove target == mouseover target)
4280 0 : nsCOMPtr<nsIContent> targetElement = GetEventTargetContent(aMouseEvent);
4281 0 : if (!targetElement) {
4282 : // We're always over the document root, even if we're only
4283 : // over dead space in a page (whose frame is not associated with
4284 : // any content) or in print preview dead space
4285 0 : targetElement = mDocument->GetRootElement();
4286 : }
4287 0 : if (targetElement) {
4288 0 : OverOutElementsWrapper* helper = GetWrapperByEventID(aMouseEvent);
4289 0 : if (helper) {
4290 0 : helper->mLastOverElement = targetElement;
4291 : }
4292 0 : NotifyMouseOut(aMouseEvent, nullptr);
4293 : }
4294 : }
4295 0 : break;
4296 : case ePointerLeave:
4297 : case ePointerCancel:
4298 : case eMouseExitFromWidget:
4299 : {
4300 : // This is actually the window mouse exit or pointer leave event. We're not moving
4301 : // into any new element.
4302 :
4303 2 : OverOutElementsWrapper* helper = GetWrapperByEventID(aMouseEvent);
4304 4 : if (helper->mLastOverFrame &&
4305 2 : nsContentUtils::GetTopLevelWidget(aMouseEvent->mWidget) !=
4306 4 : nsContentUtils::GetTopLevelWidget(helper->mLastOverFrame->GetNearestWidget())) {
4307 : // the Mouse/PointerOut event widget doesn't have same top widget with
4308 : // mLastOverFrame, it's a spurious event for mLastOverFrame
4309 0 : break;
4310 : }
4311 :
4312 : // Reset sLastRefPoint, so that we'll know not to report any
4313 : // movement the next time we re-enter the window.
4314 2 : sLastRefPoint = kInvalidRefPoint;
4315 :
4316 2 : NotifyMouseOut(aMouseEvent, nullptr);
4317 : }
4318 2 : break;
4319 : default:
4320 0 : break;
4321 : }
4322 :
4323 : // reset mCurretTargetContent to what it was
4324 10 : mCurrentTargetContent = targetBeforeEvent;
4325 : }
4326 :
4327 : OverOutElementsWrapper*
4328 14 : EventStateManager::GetWrapperByEventID(WidgetMouseEvent* aEvent)
4329 : {
4330 14 : WidgetPointerEvent* pointer = aEvent->AsPointerEvent();
4331 14 : if (!pointer) {
4332 7 : MOZ_ASSERT(aEvent->AsMouseEvent() != nullptr);
4333 7 : if (!mMouseEnterLeaveHelper) {
4334 1 : mMouseEnterLeaveHelper = new OverOutElementsWrapper();
4335 : }
4336 7 : return mMouseEnterLeaveHelper;
4337 : }
4338 14 : return mPointersEnterLeaveHelper.LookupForAdd(pointer->pointerId).OrInsert(
4339 9 : [] () { return new OverOutElementsWrapper(); });
4340 : }
4341 :
4342 : /* static */ void
4343 0 : EventStateManager::SetPointerLock(nsIWidget* aWidget,
4344 : nsIContent* aElement)
4345 : {
4346 : // NOTE: aElement will be nullptr when unlocking.
4347 0 : sIsPointerLocked = !!aElement;
4348 :
4349 : // Reset mouse wheel transaction
4350 0 : WheelTransaction::EndTransaction();
4351 :
4352 : // Deal with DnD events
4353 : nsCOMPtr<nsIDragService> dragService =
4354 0 : do_GetService("@mozilla.org/widget/dragservice;1");
4355 :
4356 0 : if (sIsPointerLocked) {
4357 0 : MOZ_ASSERT(aWidget, "Locking pointer requires a widget");
4358 :
4359 : // Store the last known ref point so we can reposition the pointer after unlock.
4360 0 : sPreLockPoint = sLastRefPoint;
4361 :
4362 : // Fire a synthetic mouse move to ensure event state is updated. We first
4363 : // set the mouse to the center of the window, so that the mouse event
4364 : // doesn't report any movement.
4365 0 : sLastRefPoint = GetWindowClientRectCenter(aWidget);
4366 0 : aWidget->SynthesizeNativeMouseMove(
4367 0 : sLastRefPoint + aWidget->WidgetToScreenOffset(), nullptr);
4368 :
4369 : // Suppress DnD
4370 0 : if (dragService) {
4371 0 : dragService->Suppress();
4372 : }
4373 : } else {
4374 : // Unlocking, so return pointer to the original position by firing a
4375 : // synthetic mouse event. We first reset sLastRefPoint to its
4376 : // pre-pointerlock position, so that the synthetic mouse event reports
4377 : // no movement.
4378 0 : sLastRefPoint = sPreLockPoint;
4379 : // Reset SynthCenteringPoint to invalid so that next time we start
4380 : // locking pointer, it has its initial value.
4381 0 : sSynthCenteringPoint = kInvalidRefPoint;
4382 0 : if (aWidget) {
4383 0 : aWidget->SynthesizeNativeMouseMove(
4384 0 : sPreLockPoint + aWidget->WidgetToScreenOffset(), nullptr);
4385 : }
4386 :
4387 : // Unsuppress DnD
4388 0 : if (dragService) {
4389 0 : dragService->Unsuppress();
4390 : }
4391 : }
4392 0 : }
4393 :
4394 : void
4395 0 : EventStateManager::GenerateDragDropEnterExit(nsPresContext* aPresContext,
4396 : WidgetDragEvent* aDragEvent)
4397 : {
4398 : //Hold onto old target content through the event and reset after.
4399 0 : nsCOMPtr<nsIContent> targetBeforeEvent = mCurrentTargetContent;
4400 :
4401 0 : switch(aDragEvent->mMessage) {
4402 : case eDragOver:
4403 : {
4404 : // when dragging from one frame to another, events are fired in the
4405 : // order: dragexit, dragenter, dragleave
4406 0 : if (sLastDragOverFrame != mCurrentTarget) {
4407 : //We'll need the content, too, to check if it changed separately from the frames.
4408 0 : nsCOMPtr<nsIContent> lastContent;
4409 0 : nsCOMPtr<nsIContent> targetContent;
4410 0 : mCurrentTarget->GetContentForEvent(aDragEvent,
4411 0 : getter_AddRefs(targetContent));
4412 :
4413 0 : if (sLastDragOverFrame) {
4414 : //The frame has changed but the content may not have. Check before dispatching to content
4415 0 : sLastDragOverFrame->GetContentForEvent(aDragEvent,
4416 0 : getter_AddRefs(lastContent));
4417 :
4418 0 : FireDragEnterOrExit(sLastDragOverFrame->PresContext(),
4419 : aDragEvent, eDragExit,
4420 0 : targetContent, lastContent, sLastDragOverFrame);
4421 0 : nsIContent* target = sLastDragOverFrame ? sLastDragOverFrame.GetFrame()->GetContent() : nullptr;
4422 0 : if (IsRemoteTarget(target)) {
4423 : // Dragging something and moving from web content to chrome only
4424 : // fires dragexit and dragleave to xul:browser. We have to forward
4425 : // dragexit to sLastDragOverFrame when its content is a remote
4426 : // target. We don't forward dragleave since it's generated from
4427 : // dragexit.
4428 0 : WidgetDragEvent remoteEvent(aDragEvent->IsTrusted(), eDragExit,
4429 0 : aDragEvent->mWidget);
4430 0 : remoteEvent.AssignDragEventData(*aDragEvent, true);
4431 0 : nsEventStatus remoteStatus = nsEventStatus_eIgnore;
4432 0 : HandleCrossProcessEvent(&remoteEvent, &remoteStatus);
4433 : }
4434 : }
4435 :
4436 0 : AutoWeakFrame currentTraget = mCurrentTarget;
4437 0 : FireDragEnterOrExit(aPresContext, aDragEvent, eDragEnter,
4438 0 : lastContent, targetContent, currentTraget);
4439 :
4440 0 : if (sLastDragOverFrame) {
4441 0 : FireDragEnterOrExit(sLastDragOverFrame->PresContext(),
4442 : aDragEvent, eDragLeave,
4443 0 : targetContent, lastContent, sLastDragOverFrame);
4444 : }
4445 :
4446 0 : sLastDragOverFrame = mCurrentTarget;
4447 : }
4448 : }
4449 0 : break;
4450 :
4451 : case eDragExit:
4452 : {
4453 : //This is actually the window mouse exit event.
4454 0 : if (sLastDragOverFrame) {
4455 0 : nsCOMPtr<nsIContent> lastContent;
4456 0 : sLastDragOverFrame->GetContentForEvent(aDragEvent,
4457 0 : getter_AddRefs(lastContent));
4458 :
4459 0 : RefPtr<nsPresContext> lastDragOverFramePresContext = sLastDragOverFrame->PresContext();
4460 0 : FireDragEnterOrExit(lastDragOverFramePresContext,
4461 : aDragEvent, eDragExit,
4462 0 : nullptr, lastContent, sLastDragOverFrame);
4463 0 : FireDragEnterOrExit(lastDragOverFramePresContext,
4464 : aDragEvent, eDragLeave,
4465 0 : nullptr, lastContent, sLastDragOverFrame);
4466 :
4467 0 : sLastDragOverFrame = nullptr;
4468 : }
4469 : }
4470 0 : break;
4471 :
4472 : default:
4473 0 : break;
4474 : }
4475 :
4476 : //reset mCurretTargetContent to what it was
4477 0 : mCurrentTargetContent = targetBeforeEvent;
4478 :
4479 : // Now flush all pending notifications, for better responsiveness.
4480 0 : FlushPendingEvents(aPresContext);
4481 0 : }
4482 :
4483 : void
4484 0 : EventStateManager::FireDragEnterOrExit(nsPresContext* aPresContext,
4485 : WidgetDragEvent* aDragEvent,
4486 : EventMessage aMessage,
4487 : nsIContent* aRelatedTarget,
4488 : nsIContent* aTargetContent,
4489 : AutoWeakFrame& aTargetFrame)
4490 : {
4491 0 : MOZ_ASSERT(aMessage == eDragLeave || aMessage == eDragExit ||
4492 : aMessage == eDragEnter);
4493 0 : nsEventStatus status = nsEventStatus_eIgnore;
4494 0 : WidgetDragEvent event(aDragEvent->IsTrusted(), aMessage, aDragEvent->mWidget);
4495 0 : event.AssignDragEventData(*aDragEvent, true);
4496 0 : mCurrentTargetContent = aTargetContent;
4497 :
4498 0 : if (aTargetContent != aRelatedTarget) {
4499 : //XXX This event should still go somewhere!!
4500 0 : if (aTargetContent) {
4501 : EventDispatcher::Dispatch(aTargetContent, aPresContext, &event,
4502 0 : nullptr, &status);
4503 : }
4504 :
4505 : // adjust the drag hover if the dragenter event was cancelled or this is a drag exit
4506 0 : if (status == nsEventStatus_eConsumeNoDefault || aMessage == eDragExit) {
4507 0 : SetContentState((aMessage == eDragEnter) ? aTargetContent : nullptr,
4508 0 : NS_EVENT_STATE_DRAGOVER);
4509 : }
4510 :
4511 : // collect any changes to moz cursor settings stored in the event's
4512 : // data transfer.
4513 0 : UpdateDragDataTransfer(&event);
4514 : }
4515 :
4516 : // Finally dispatch the event to the frame
4517 0 : if (aTargetFrame)
4518 0 : aTargetFrame->HandleEvent(aPresContext, &event, &status);
4519 0 : }
4520 :
4521 : void
4522 0 : EventStateManager::UpdateDragDataTransfer(WidgetDragEvent* dragEvent)
4523 : {
4524 0 : NS_ASSERTION(dragEvent, "drag event is null in UpdateDragDataTransfer!");
4525 0 : if (!dragEvent->mDataTransfer) {
4526 0 : return;
4527 : }
4528 :
4529 0 : nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession();
4530 :
4531 0 : if (dragSession) {
4532 : // the initial dataTransfer is the one from the dragstart event that
4533 : // was set on the dragSession when the drag began.
4534 0 : nsCOMPtr<nsIDOMDataTransfer> initialDataTransfer;
4535 0 : dragSession->GetDataTransfer(getter_AddRefs(initialDataTransfer));
4536 0 : if (initialDataTransfer) {
4537 : // retrieve the current moz cursor setting and save it.
4538 0 : nsAutoString mozCursor;
4539 0 : dragEvent->mDataTransfer->GetMozCursor(mozCursor);
4540 0 : initialDataTransfer->SetMozCursor(mozCursor);
4541 : }
4542 : }
4543 : }
4544 :
4545 : nsresult
4546 0 : EventStateManager::SetClickCount(WidgetMouseEvent* aEvent,
4547 : nsEventStatus* aStatus)
4548 : {
4549 0 : nsCOMPtr<nsIContent> mouseContent;
4550 0 : nsIContent* mouseContentParent = nullptr;
4551 0 : if (mCurrentTarget) {
4552 0 : mCurrentTarget->GetContentForEvent(aEvent, getter_AddRefs(mouseContent));
4553 : }
4554 0 : if (mouseContent) {
4555 0 : if (mouseContent->IsNodeOfType(nsINode::eTEXT)) {
4556 0 : mouseContent = mouseContent->GetParent();
4557 : }
4558 0 : if (mouseContent && mouseContent->IsRootOfNativeAnonymousSubtree()) {
4559 0 : mouseContentParent = mouseContent->GetParent();
4560 : }
4561 : }
4562 :
4563 0 : switch (aEvent->button) {
4564 : case WidgetMouseEvent::eLeftButton:
4565 0 : if (aEvent->mMessage == eMouseDown) {
4566 0 : mLastLeftMouseDownContent = mouseContent;
4567 0 : mLastLeftMouseDownContentParent = mouseContentParent;
4568 0 : } else if (aEvent->mMessage == eMouseUp) {
4569 0 : if (mLastLeftMouseDownContent == mouseContent ||
4570 0 : mLastLeftMouseDownContentParent == mouseContent ||
4571 0 : mLastLeftMouseDownContent == mouseContentParent) {
4572 0 : aEvent->mClickCount = mLClickCount;
4573 0 : mLClickCount = 0;
4574 : } else {
4575 0 : aEvent->mClickCount = 0;
4576 : }
4577 0 : mLastLeftMouseDownContent = nullptr;
4578 0 : mLastLeftMouseDownContentParent = nullptr;
4579 : }
4580 0 : break;
4581 :
4582 : case WidgetMouseEvent::eMiddleButton:
4583 0 : if (aEvent->mMessage == eMouseDown) {
4584 0 : mLastMiddleMouseDownContent = mouseContent;
4585 0 : mLastMiddleMouseDownContentParent = mouseContentParent;
4586 0 : } else if (aEvent->mMessage == eMouseUp) {
4587 0 : if (mLastMiddleMouseDownContent == mouseContent ||
4588 0 : mLastMiddleMouseDownContentParent == mouseContent ||
4589 0 : mLastMiddleMouseDownContent == mouseContentParent) {
4590 0 : aEvent->mClickCount = mMClickCount;
4591 0 : mMClickCount = 0;
4592 : } else {
4593 0 : aEvent->mClickCount = 0;
4594 : }
4595 0 : mLastMiddleMouseDownContent = nullptr;
4596 0 : mLastMiddleMouseDownContentParent = nullptr;
4597 : }
4598 0 : break;
4599 :
4600 : case WidgetMouseEvent::eRightButton:
4601 0 : if (aEvent->mMessage == eMouseDown) {
4602 0 : mLastRightMouseDownContent = mouseContent;
4603 0 : mLastRightMouseDownContentParent = mouseContentParent;
4604 0 : } else if (aEvent->mMessage == eMouseUp) {
4605 0 : if (mLastRightMouseDownContent == mouseContent ||
4606 0 : mLastRightMouseDownContentParent == mouseContent ||
4607 0 : mLastRightMouseDownContent == mouseContentParent) {
4608 0 : aEvent->mClickCount = mRClickCount;
4609 0 : mRClickCount = 0;
4610 : } else {
4611 0 : aEvent->mClickCount = 0;
4612 : }
4613 0 : mLastRightMouseDownContent = nullptr;
4614 0 : mLastRightMouseDownContentParent = nullptr;
4615 : }
4616 0 : break;
4617 : }
4618 :
4619 0 : return NS_OK;
4620 : }
4621 :
4622 : nsresult
4623 0 : EventStateManager::InitAndDispatchClickEvent(WidgetMouseEvent* aEvent,
4624 : nsEventStatus* aStatus,
4625 : EventMessage aMessage,
4626 : nsIPresShell* aPresShell,
4627 : nsIContent* aMouseTarget,
4628 : AutoWeakFrame aCurrentTarget,
4629 : bool aNoContentDispatch)
4630 : {
4631 0 : WidgetMouseEvent event(aEvent->IsTrusted(), aMessage,
4632 0 : aEvent->mWidget, WidgetMouseEvent::eReal);
4633 :
4634 0 : event.mRefPoint = aEvent->mRefPoint;
4635 0 : event.mClickCount = aEvent->mClickCount;
4636 0 : event.mModifiers = aEvent->mModifiers;
4637 0 : event.buttons = aEvent->buttons;
4638 0 : event.mTime = aEvent->mTime;
4639 0 : event.mTimeStamp = aEvent->mTimeStamp;
4640 0 : event.mFlags.mNoContentDispatch = aNoContentDispatch;
4641 0 : event.button = aEvent->button;
4642 0 : event.pointerId = aEvent->pointerId;
4643 0 : event.inputSource = aEvent->inputSource;
4644 :
4645 0 : return aPresShell->HandleEventWithTarget(&event, aCurrentTarget,
4646 0 : aMouseTarget, aStatus);
4647 : }
4648 :
4649 : nsresult
4650 0 : EventStateManager::CheckForAndDispatchClick(WidgetMouseEvent* aEvent,
4651 : nsEventStatus* aStatus)
4652 : {
4653 0 : nsresult ret = NS_OK;
4654 :
4655 : //If mouse is still over same element, clickcount will be > 1.
4656 : //If it has moved it will be zero, so no click.
4657 0 : if (aEvent->mClickCount) {
4658 : //Check that the window isn't disabled before firing a click
4659 : //(see bug 366544).
4660 0 : if (aEvent->mWidget && !aEvent->mWidget->IsEnabled()) {
4661 0 : return ret;
4662 : }
4663 : //fire click
4664 : bool notDispatchToContents =
4665 0 : (aEvent->button == WidgetMouseEvent::eMiddleButton ||
4666 0 : aEvent->button == WidgetMouseEvent::eRightButton);
4667 :
4668 0 : bool fireAuxClick = notDispatchToContents;
4669 :
4670 0 : nsCOMPtr<nsIPresShell> presShell = mPresContext->GetPresShell();
4671 0 : if (presShell) {
4672 0 : nsCOMPtr<nsIContent> mouseContent = GetEventTargetContent(aEvent);
4673 : // Click events apply to *elements* not nodes. At this point the target
4674 : // content may have been reset to some non-element content, and so we need
4675 : // to walk up the closest ancestor element, just like we do in
4676 : // nsPresShell::HandlePositionedEvent.
4677 0 : while (mouseContent && !mouseContent->IsElement()) {
4678 0 : mouseContent = mouseContent->GetParent();
4679 : }
4680 :
4681 0 : if (!mouseContent && !mCurrentTarget) {
4682 0 : return NS_OK;
4683 : }
4684 :
4685 : // HandleEvent clears out mCurrentTarget which we might need again
4686 0 : AutoWeakFrame currentTarget = mCurrentTarget;
4687 0 : ret = InitAndDispatchClickEvent(aEvent, aStatus, eMouseClick,
4688 : presShell, mouseContent, currentTarget,
4689 0 : notDispatchToContents);
4690 :
4691 0 : if (NS_SUCCEEDED(ret) && aEvent->mClickCount == 2 &&
4692 0 : mouseContent && mouseContent->IsInComposedDoc()) {
4693 : //fire double click
4694 0 : ret = InitAndDispatchClickEvent(aEvent, aStatus, eMouseDoubleClick,
4695 : presShell, mouseContent, currentTarget,
4696 0 : notDispatchToContents);
4697 : }
4698 0 : if (NS_SUCCEEDED(ret) && mouseContent && fireAuxClick &&
4699 0 : mouseContent->IsInComposedDoc()) {
4700 0 : ret = InitAndDispatchClickEvent(aEvent, aStatus, eMouseAuxClick,
4701 : presShell, mouseContent, currentTarget,
4702 0 : false);
4703 : }
4704 : }
4705 : }
4706 0 : return ret;
4707 : }
4708 :
4709 : nsIFrame*
4710 193 : EventStateManager::GetEventTarget()
4711 : {
4712 : nsIPresShell *shell;
4713 371 : if (mCurrentTarget ||
4714 371 : !mPresContext ||
4715 178 : !(shell = mPresContext->GetPresShell())) {
4716 15 : return mCurrentTarget;
4717 : }
4718 :
4719 178 : if (mCurrentTargetContent) {
4720 0 : mCurrentTarget = mPresContext->GetPrimaryFrameFor(mCurrentTargetContent);
4721 0 : if (mCurrentTarget) {
4722 0 : return mCurrentTarget;
4723 : }
4724 : }
4725 :
4726 178 : nsIFrame* frame = shell->GetEventTargetFrame();
4727 178 : return (mCurrentTarget = frame);
4728 : }
4729 :
4730 : already_AddRefed<nsIContent>
4731 8 : EventStateManager::GetEventTargetContent(WidgetEvent* aEvent)
4732 : {
4733 16 : if (aEvent &&
4734 16 : (aEvent->mMessage == eFocus || aEvent->mMessage == eBlur)) {
4735 0 : nsCOMPtr<nsIContent> content = GetFocusedContent();
4736 0 : return content.forget();
4737 : }
4738 :
4739 8 : if (mCurrentTargetContent) {
4740 0 : nsCOMPtr<nsIContent> content = mCurrentTargetContent;
4741 0 : return content.forget();
4742 : }
4743 :
4744 16 : nsCOMPtr<nsIContent> content;
4745 :
4746 8 : nsIPresShell *presShell = mPresContext->GetPresShell();
4747 8 : if (presShell) {
4748 8 : content = presShell->GetEventTargetContent(aEvent);
4749 : }
4750 :
4751 : // Some events here may set mCurrentTarget but not set the corresponding
4752 : // event target in the PresShell.
4753 8 : if (!content && mCurrentTarget) {
4754 0 : mCurrentTarget->GetContentForEvent(aEvent, getter_AddRefs(content));
4755 : }
4756 :
4757 8 : return content.forget();
4758 : }
4759 :
4760 : static Element*
4761 28 : GetLabelTarget(nsIContent* aPossibleLabel)
4762 : {
4763 : mozilla::dom::HTMLLabelElement* label =
4764 28 : mozilla::dom::HTMLLabelElement::FromContent(aPossibleLabel);
4765 28 : if (!label)
4766 28 : return nullptr;
4767 :
4768 0 : return label->GetLabeledElement();
4769 : }
4770 :
4771 : static nsIContent*
4772 2 : FindCommonAncestor(nsIContent* aNode1, nsIContent* aNode2)
4773 : {
4774 2 : if (!aNode1 || !aNode2) {
4775 2 : return nullptr;
4776 : }
4777 0 : return nsContentUtils::GetCommonFlattenedTreeAncestor(aNode1, aNode2);
4778 : }
4779 :
4780 : /* static */
4781 : void
4782 0 : EventStateManager::SetFullScreenState(Element* aElement, bool aIsFullScreen)
4783 : {
4784 0 : DoStateChange(aElement, NS_EVENT_STATE_FULL_SCREEN, aIsFullScreen);
4785 0 : }
4786 :
4787 : /* static */
4788 : inline void
4789 30 : EventStateManager::DoStateChange(Element* aElement, EventStates aState,
4790 : bool aAddState)
4791 : {
4792 30 : if (aAddState) {
4793 16 : aElement->AddStates(aState);
4794 : } else {
4795 14 : aElement->RemoveStates(aState);
4796 : }
4797 30 : }
4798 :
4799 : /* static */
4800 : inline void
4801 2 : EventStateManager::DoStateChange(nsIContent* aContent, EventStates aState,
4802 : bool aStateAdded)
4803 : {
4804 2 : if (aContent->IsElement()) {
4805 2 : DoStateChange(aContent->AsElement(), aState, aStateAdded);
4806 : }
4807 2 : }
4808 :
4809 : /* static */
4810 : void
4811 30 : EventStateManager::UpdateAncestorState(nsIContent* aStartNode,
4812 : nsIContent* aStopBefore,
4813 : EventStates aState,
4814 : bool aAddState)
4815 : {
4816 58 : for (; aStartNode && aStartNode != aStopBefore;
4817 : aStartNode = aStartNode->GetFlattenedTreeParent()) {
4818 : // We might be starting with a non-element (e.g. a text node) and
4819 : // if someone is doing something weird might be ending with a
4820 : // non-element too (e.g. a document fragment)
4821 28 : if (!aStartNode->IsElement()) {
4822 0 : continue;
4823 : }
4824 28 : Element* element = aStartNode->AsElement();
4825 28 : DoStateChange(element, aState, aAddState);
4826 28 : Element* labelTarget = GetLabelTarget(element);
4827 28 : if (labelTarget) {
4828 0 : DoStateChange(labelTarget, aState, aAddState);
4829 : }
4830 : }
4831 :
4832 2 : if (aAddState) {
4833 : // We might be in a situation where a node was in hover both
4834 : // because it was hovered and because the label for it was
4835 : // hovered, and while we stopped hovering the node the label is
4836 : // still hovered. Or we might have had two nested labels for the
4837 : // same node, and while one is no longer hovered the other still
4838 : // is. In that situation, the label that's still hovered will be
4839 : // aStopBefore or some ancestor of it, and the call we just made
4840 : // to UpdateAncestorState with aAddState = false would have
4841 : // removed the hover state from the node. But the node should
4842 : // still be in hover state. To handle this situation we need to
4843 : // keep walking up the tree and any time we find a label mark its
4844 : // corresponding node as still in our state.
4845 1 : for ( ; aStartNode; aStartNode = aStartNode->GetFlattenedTreeParent()) {
4846 0 : if (!aStartNode->IsElement()) {
4847 0 : continue;
4848 : }
4849 :
4850 0 : Element* labelTarget = GetLabelTarget(aStartNode->AsElement());
4851 0 : if (labelTarget && !labelTarget->State().HasState(aState)) {
4852 0 : DoStateChange(labelTarget, aState, true);
4853 : }
4854 : }
4855 : }
4856 2 : }
4857 :
4858 : bool
4859 5 : EventStateManager::SetContentState(nsIContent* aContent, EventStates aState)
4860 : {
4861 : // We manage 4 states here: ACTIVE, HOVER, DRAGOVER, URLTARGET
4862 : // The input must be exactly one of them.
4863 5 : NS_PRECONDITION(aState == NS_EVENT_STATE_ACTIVE ||
4864 : aState == NS_EVENT_STATE_HOVER ||
4865 : aState == NS_EVENT_STATE_DRAGOVER ||
4866 : aState == NS_EVENT_STATE_URLTARGET,
4867 : "Unexpected state");
4868 :
4869 10 : nsCOMPtr<nsIContent> notifyContent1;
4870 10 : nsCOMPtr<nsIContent> notifyContent2;
4871 : bool updateAncestors;
4872 :
4873 5 : if (aState == NS_EVENT_STATE_HOVER || aState == NS_EVENT_STATE_ACTIVE) {
4874 : // Hover and active are hierarchical
4875 3 : updateAncestors = true;
4876 :
4877 : // check to see that this state is allowed by style. Check dragover too?
4878 : // XXX Is this even what we want?
4879 3 : if (mCurrentTarget)
4880 : {
4881 3 : const nsStyleUserInterface* ui = mCurrentTarget->StyleUserInterface();
4882 3 : if (ui->mUserInput == StyleUserInput::None) {
4883 0 : return false;
4884 : }
4885 : }
4886 :
4887 3 : if (aState == NS_EVENT_STATE_ACTIVE) {
4888 : // Editable content can never become active since their default actions
4889 : // are disabled. Watch out for editable content in native anonymous
4890 : // subtrees though, as they belong to text controls.
4891 0 : if (aContent && aContent->IsEditable() &&
4892 0 : !aContent->IsInNativeAnonymousSubtree()) {
4893 0 : aContent = nullptr;
4894 : }
4895 0 : if (aContent != mActiveContent) {
4896 0 : notifyContent1 = aContent;
4897 0 : notifyContent2 = mActiveContent;
4898 0 : mActiveContent = aContent;
4899 : }
4900 : } else {
4901 3 : NS_ASSERTION(aState == NS_EVENT_STATE_HOVER, "How did that happen?");
4902 : nsIContent* newHover;
4903 :
4904 3 : if (mPresContext->IsDynamic()) {
4905 3 : newHover = aContent;
4906 : } else {
4907 0 : NS_ASSERTION(!aContent ||
4908 : aContent->GetComposedDoc() ==
4909 : mPresContext->PresShell()->GetDocument(),
4910 : "Unexpected document");
4911 0 : nsIFrame *frame = aContent ? aContent->GetPrimaryFrame() : nullptr;
4912 0 : if (frame && nsLayoutUtils::IsViewportScrollbarFrame(frame)) {
4913 : // The scrollbars of viewport should not ignore the hover state.
4914 : // Because they are *not* the content of the web page.
4915 0 : newHover = aContent;
4916 : } else {
4917 : // All contents of the web page should ignore the hover state.
4918 0 : newHover = nullptr;
4919 : }
4920 : }
4921 :
4922 3 : if (newHover != mHoverContent) {
4923 2 : notifyContent1 = newHover;
4924 2 : notifyContent2 = mHoverContent;
4925 2 : mHoverContent = newHover;
4926 : }
4927 : }
4928 : } else {
4929 2 : updateAncestors = false;
4930 2 : if (aState == NS_EVENT_STATE_DRAGOVER) {
4931 0 : if (aContent != sDragOverContent) {
4932 0 : notifyContent1 = aContent;
4933 0 : notifyContent2 = sDragOverContent;
4934 0 : sDragOverContent = aContent;
4935 : }
4936 2 : } else if (aState == NS_EVENT_STATE_URLTARGET) {
4937 2 : if (aContent != mURLTargetContent) {
4938 2 : notifyContent1 = aContent;
4939 2 : notifyContent2 = mURLTargetContent;
4940 2 : mURLTargetContent = aContent;
4941 : }
4942 : }
4943 : }
4944 :
4945 : // We need to keep track of which of notifyContent1 and notifyContent2 is
4946 : // getting the state set and which is getting it unset. If both are
4947 : // non-null, then notifyContent1 is having the state set and notifyContent2
4948 : // is having it unset. But if one of them is null, we need to keep track of
4949 : // the right thing for notifyContent1 explicitly.
4950 5 : bool content1StateSet = true;
4951 5 : if (!notifyContent1) {
4952 : // This is ok because FindCommonAncestor wouldn't find anything
4953 : // anyway if notifyContent1 is null.
4954 2 : notifyContent1 = notifyContent2;
4955 2 : notifyContent2 = nullptr;
4956 2 : content1StateSet = false;
4957 : }
4958 :
4959 5 : if (notifyContent1 && mPresContext) {
4960 4 : EnsureDocument(mPresContext);
4961 4 : if (mDocument) {
4962 8 : nsAutoScriptBlocker scriptBlocker;
4963 :
4964 4 : if (updateAncestors) {
4965 : nsCOMPtr<nsIContent> commonAncestor =
4966 4 : FindCommonAncestor(notifyContent1, notifyContent2);
4967 2 : if (notifyContent2) {
4968 : // It's very important to first notify the state removal and
4969 : // then the state addition, because due to labels it's
4970 : // possible that we're removing state from some element but
4971 : // then adding it again (say because mHoverContent changed
4972 : // from a control to its label).
4973 0 : UpdateAncestorState(notifyContent2, commonAncestor, aState, false);
4974 : }
4975 2 : UpdateAncestorState(notifyContent1, commonAncestor, aState,
4976 2 : content1StateSet);
4977 : } else {
4978 2 : if (notifyContent2) {
4979 0 : DoStateChange(notifyContent2, aState, false);
4980 : }
4981 2 : DoStateChange(notifyContent1, aState, content1StateSet);
4982 : }
4983 : }
4984 : }
4985 :
4986 5 : return true;
4987 : }
4988 :
4989 : void
4990 13 : EventStateManager::ResetLastOverForContent(
4991 : const uint32_t& aIdx,
4992 : RefPtr<OverOutElementsWrapper>& aElemWrapper,
4993 : nsIContent* aContent)
4994 : {
4995 13 : if (aElemWrapper && aElemWrapper->mLastOverElement &&
4996 0 : nsContentUtils::ContentIsDescendantOf(aElemWrapper->mLastOverElement,
4997 0 : aContent)) {
4998 0 : aElemWrapper->mLastOverElement = nullptr;
4999 : }
5000 13 : }
5001 :
5002 : void
5003 11 : EventStateManager::ContentRemoved(nsIDocument* aDocument, nsIContent* aContent)
5004 : {
5005 : /*
5006 : * Anchor and area elements when focused or hovered might make the UI to show
5007 : * the current link. We want to make sure that the UI gets informed when they
5008 : * are actually removed from the DOM.
5009 : */
5010 33 : if (aContent->IsAnyOfHTMLElements(nsGkAtoms::a, nsGkAtoms::area) &&
5011 11 : (aContent->AsElement()->State().HasAtLeastOneOfStates(NS_EVENT_STATE_FOCUS |
5012 11 : NS_EVENT_STATE_HOVER))) {
5013 0 : nsGenericHTMLElement* element = static_cast<nsGenericHTMLElement*>(aContent);
5014 0 : element->LeaveLink(
5015 0 : element->GetPresContext(nsGenericHTMLElement::eForComposedDoc));
5016 : }
5017 :
5018 11 : IMEStateManager::OnRemoveContent(mPresContext, aContent);
5019 :
5020 : // inform the focus manager that the content is being removed. If this
5021 : // content is focused, the focus will be removed without firing events.
5022 11 : nsFocusManager* fm = nsFocusManager::GetFocusManager();
5023 11 : if (fm)
5024 11 : fm->ContentRemoved(aDocument, aContent);
5025 :
5026 11 : if (mHoverContent &&
5027 11 : nsContentUtils::ContentIsDescendantOf(mHoverContent, aContent)) {
5028 : // Since hover is hierarchical, set the current hover to the
5029 : // content's parent node.
5030 0 : SetContentState(aContent->GetParent(), NS_EVENT_STATE_HOVER);
5031 : }
5032 :
5033 11 : if (mActiveContent &&
5034 11 : nsContentUtils::ContentIsDescendantOf(mActiveContent, aContent)) {
5035 : // Active is hierarchical, so set the current active to the
5036 : // content's parent node.
5037 0 : SetContentState(aContent->GetParent(), NS_EVENT_STATE_ACTIVE);
5038 : }
5039 :
5040 11 : if (sDragOverContent &&
5041 11 : sDragOverContent->OwnerDoc() == aContent->OwnerDoc() &&
5042 0 : nsContentUtils::ContentIsDescendantOf(sDragOverContent, aContent)) {
5043 0 : sDragOverContent = nullptr;
5044 : }
5045 :
5046 : // See bug 292146 for why we want to null this out
5047 11 : ResetLastOverForContent(0, mMouseEnterLeaveHelper, aContent);
5048 26 : for (auto iter = mPointersEnterLeaveHelper.Iter();
5049 13 : !iter.Done();
5050 2 : iter.Next()) {
5051 2 : ResetLastOverForContent(iter.Key(), iter.Data(), aContent);
5052 : }
5053 11 : }
5054 :
5055 : bool
5056 0 : EventStateManager::EventStatusOK(WidgetGUIEvent* aEvent)
5057 : {
5058 0 : return !(aEvent->mMessage == eMouseDown &&
5059 0 : aEvent->AsMouseEvent()->button == WidgetMouseEvent::eLeftButton &&
5060 0 : !sNormalLMouseEventInProcess);
5061 : }
5062 :
5063 : //-------------------------------------------
5064 : // Access Key Registration
5065 : //-------------------------------------------
5066 : void
5067 0 : EventStateManager::RegisterAccessKey(nsIContent* aContent, uint32_t aKey)
5068 : {
5069 0 : if (aContent && mAccessKeys.IndexOf(aContent) == -1)
5070 0 : mAccessKeys.AppendObject(aContent);
5071 0 : }
5072 :
5073 : void
5074 15 : EventStateManager::UnregisterAccessKey(nsIContent* aContent, uint32_t aKey)
5075 : {
5076 15 : if (aContent)
5077 15 : mAccessKeys.RemoveObject(aContent);
5078 15 : }
5079 :
5080 : uint32_t
5081 0 : EventStateManager::GetRegisteredAccessKey(nsIContent* aContent)
5082 : {
5083 0 : MOZ_ASSERT(aContent);
5084 :
5085 0 : if (mAccessKeys.IndexOf(aContent) == -1)
5086 0 : return 0;
5087 :
5088 0 : nsAutoString accessKey;
5089 0 : aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::accesskey, accessKey);
5090 0 : return accessKey.First();
5091 : }
5092 :
5093 : void
5094 16 : EventStateManager::EnsureDocument(nsPresContext* aPresContext)
5095 : {
5096 16 : if (!mDocument)
5097 3 : mDocument = aPresContext->Document();
5098 16 : }
5099 :
5100 : void
5101 8 : EventStateManager::FlushPendingEvents(nsPresContext* aPresContext)
5102 : {
5103 8 : NS_PRECONDITION(nullptr != aPresContext, "nullptr ptr");
5104 8 : nsIPresShell *shell = aPresContext->GetPresShell();
5105 8 : if (shell) {
5106 8 : shell->FlushPendingNotifications(FlushType::InterruptibleLayout);
5107 : }
5108 8 : }
5109 :
5110 : nsIContent*
5111 0 : EventStateManager::GetFocusedContent()
5112 : {
5113 0 : nsIFocusManager* fm = nsFocusManager::GetFocusManager();
5114 0 : EnsureDocument(mPresContext);
5115 0 : if (!fm || !mDocument)
5116 0 : return nullptr;
5117 :
5118 0 : nsCOMPtr<nsPIDOMWindowOuter> focusedWindow;
5119 0 : return nsFocusManager::GetFocusedDescendant(mDocument->GetWindow(), false,
5120 0 : getter_AddRefs(focusedWindow));
5121 : }
5122 :
5123 : //-------------------------------------------------------
5124 : // Return true if the docshell is visible
5125 :
5126 : bool
5127 0 : EventStateManager::IsShellVisible(nsIDocShell* aShell)
5128 : {
5129 0 : NS_ASSERTION(aShell, "docshell is null");
5130 :
5131 0 : nsCOMPtr<nsIBaseWindow> basewin = do_QueryInterface(aShell);
5132 0 : if (!basewin)
5133 0 : return true;
5134 :
5135 0 : bool isVisible = true;
5136 0 : basewin->GetVisibility(&isVisible);
5137 :
5138 : // We should be doing some additional checks here so that
5139 : // we don't tab into hidden tabs of tabbrowser. -bryner
5140 :
5141 0 : return isVisible;
5142 : }
5143 :
5144 : nsresult
5145 0 : EventStateManager::DoContentCommandEvent(WidgetContentCommandEvent* aEvent)
5146 : {
5147 0 : EnsureDocument(mPresContext);
5148 0 : NS_ENSURE_TRUE(mDocument, NS_ERROR_FAILURE);
5149 0 : nsCOMPtr<nsPIDOMWindowOuter> window(mDocument->GetWindow());
5150 0 : NS_ENSURE_TRUE(window, NS_ERROR_FAILURE);
5151 :
5152 0 : nsCOMPtr<nsPIWindowRoot> root = window->GetTopWindowRoot();
5153 0 : NS_ENSURE_TRUE(root, NS_ERROR_FAILURE);
5154 : const char* cmd;
5155 0 : switch (aEvent->mMessage) {
5156 : case eContentCommandCut:
5157 0 : cmd = "cmd_cut";
5158 0 : break;
5159 : case eContentCommandCopy:
5160 0 : cmd = "cmd_copy";
5161 0 : break;
5162 : case eContentCommandPaste:
5163 0 : cmd = "cmd_paste";
5164 0 : break;
5165 : case eContentCommandDelete:
5166 0 : cmd = "cmd_delete";
5167 0 : break;
5168 : case eContentCommandUndo:
5169 0 : cmd = "cmd_undo";
5170 0 : break;
5171 : case eContentCommandRedo:
5172 0 : cmd = "cmd_redo";
5173 0 : break;
5174 : case eContentCommandPasteTransferable:
5175 0 : cmd = "cmd_pasteTransferable";
5176 0 : break;
5177 : case eContentCommandLookUpDictionary:
5178 0 : cmd = "cmd_lookUpDictionary";
5179 0 : break;
5180 : default:
5181 0 : return NS_ERROR_NOT_IMPLEMENTED;
5182 : }
5183 0 : nsCOMPtr<nsIController> controller;
5184 0 : nsresult rv = root->GetControllerForCommand(cmd, getter_AddRefs(controller));
5185 0 : NS_ENSURE_SUCCESS(rv, rv);
5186 0 : if (!controller) {
5187 : // When GetControllerForCommand succeeded but there is no controller, the
5188 : // command isn't supported.
5189 0 : aEvent->mIsEnabled = false;
5190 : } else {
5191 : bool canDoIt;
5192 0 : rv = controller->IsCommandEnabled(cmd, &canDoIt);
5193 0 : NS_ENSURE_SUCCESS(rv, rv);
5194 0 : aEvent->mIsEnabled = canDoIt;
5195 0 : if (canDoIt && !aEvent->mOnlyEnabledCheck) {
5196 0 : switch (aEvent->mMessage) {
5197 : case eContentCommandPasteTransferable: {
5198 0 : nsFocusManager* fm = nsFocusManager::GetFocusManager();
5199 0 : nsIContent* focusedContent = fm ? fm->GetFocusedContent() : nullptr;
5200 0 : RefPtr<TabParent> remote = TabParent::GetFrom(focusedContent);
5201 0 : if (remote) {
5202 0 : NS_ENSURE_TRUE(remote->Manager()->IsContentParent(), NS_ERROR_FAILURE);
5203 :
5204 0 : nsCOMPtr<nsITransferable> transferable = aEvent->mTransferable;
5205 0 : IPCDataTransfer ipcDataTransfer;
5206 0 : ContentParent* cp = remote->Manager()->AsContentParent();
5207 0 : nsContentUtils::TransferableToIPCTransferable(transferable,
5208 : &ipcDataTransfer,
5209 : false, nullptr,
5210 0 : cp);
5211 0 : bool isPrivateData = false;
5212 0 : transferable->GetIsPrivateData(&isPrivateData);
5213 0 : nsCOMPtr<nsIPrincipal> requestingPrincipal;
5214 0 : transferable->GetRequestingPrincipal(getter_AddRefs(requestingPrincipal));
5215 0 : remote->SendPasteTransferable(ipcDataTransfer, isPrivateData,
5216 0 : IPC::Principal(requestingPrincipal));
5217 0 : rv = NS_OK;
5218 : } else {
5219 0 : nsCOMPtr<nsICommandController> commandController = do_QueryInterface(controller);
5220 0 : NS_ENSURE_STATE(commandController);
5221 :
5222 0 : nsCOMPtr<nsICommandParams> params = do_CreateInstance("@mozilla.org/embedcomp/command-params;1", &rv);
5223 0 : NS_ENSURE_SUCCESS(rv, rv);
5224 :
5225 0 : rv = params->SetISupportsValue("transferable", aEvent->mTransferable);
5226 0 : NS_ENSURE_SUCCESS(rv, rv);
5227 :
5228 0 : rv = commandController->DoCommandWithParams(cmd, params);
5229 : }
5230 0 : break;
5231 : }
5232 :
5233 : case eContentCommandLookUpDictionary: {
5234 : nsCOMPtr<nsICommandController> commandController =
5235 0 : do_QueryInterface(controller);
5236 0 : if (NS_WARN_IF(!commandController)) {
5237 0 : return NS_ERROR_FAILURE;
5238 : }
5239 :
5240 : nsCOMPtr<nsICommandParams> params =
5241 0 : do_CreateInstance("@mozilla.org/embedcomp/command-params;1", &rv);
5242 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
5243 0 : return rv;
5244 : }
5245 :
5246 0 : rv = params->SetLongValue("x", aEvent->mRefPoint.x);
5247 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
5248 0 : return rv;
5249 : }
5250 :
5251 0 : rv = params->SetLongValue("y", aEvent->mRefPoint.y);
5252 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
5253 0 : return rv;
5254 : }
5255 :
5256 0 : rv = commandController->DoCommandWithParams(cmd, params);
5257 0 : break;
5258 : }
5259 :
5260 : default:
5261 0 : rv = controller->DoCommand(cmd);
5262 0 : break;
5263 : }
5264 0 : NS_ENSURE_SUCCESS(rv, rv);
5265 : }
5266 : }
5267 0 : aEvent->mSucceeded = true;
5268 0 : return NS_OK;
5269 : }
5270 :
5271 : nsresult
5272 0 : EventStateManager::DoContentCommandScrollEvent(
5273 : WidgetContentCommandEvent* aEvent)
5274 : {
5275 0 : NS_ENSURE_TRUE(mPresContext, NS_ERROR_NOT_AVAILABLE);
5276 0 : nsIPresShell* ps = mPresContext->GetPresShell();
5277 0 : NS_ENSURE_TRUE(ps, NS_ERROR_NOT_AVAILABLE);
5278 0 : NS_ENSURE_TRUE(aEvent->mScroll.mAmount != 0, NS_ERROR_INVALID_ARG);
5279 :
5280 : nsIScrollableFrame::ScrollUnit scrollUnit;
5281 0 : switch (aEvent->mScroll.mUnit) {
5282 : case WidgetContentCommandEvent::eCmdScrollUnit_Line:
5283 0 : scrollUnit = nsIScrollableFrame::LINES;
5284 0 : break;
5285 : case WidgetContentCommandEvent::eCmdScrollUnit_Page:
5286 0 : scrollUnit = nsIScrollableFrame::PAGES;
5287 0 : break;
5288 : case WidgetContentCommandEvent::eCmdScrollUnit_Whole:
5289 0 : scrollUnit = nsIScrollableFrame::WHOLE;
5290 0 : break;
5291 : default:
5292 0 : return NS_ERROR_INVALID_ARG;
5293 : }
5294 :
5295 0 : aEvent->mSucceeded = true;
5296 :
5297 : nsIScrollableFrame* sf =
5298 0 : ps->GetScrollableFrameToScroll(nsIPresShell::eEither);
5299 0 : aEvent->mIsEnabled = sf ?
5300 0 : (aEvent->mScroll.mIsHorizontal ?
5301 0 : WheelHandlingUtils::CanScrollOn(sf, aEvent->mScroll.mAmount, 0) :
5302 0 : WheelHandlingUtils::CanScrollOn(sf, 0, aEvent->mScroll.mAmount)) : false;
5303 :
5304 0 : if (!aEvent->mIsEnabled || aEvent->mOnlyEnabledCheck) {
5305 0 : return NS_OK;
5306 : }
5307 :
5308 0 : nsIntPoint pt(0, 0);
5309 0 : if (aEvent->mScroll.mIsHorizontal) {
5310 0 : pt.x = aEvent->mScroll.mAmount;
5311 : } else {
5312 0 : pt.y = aEvent->mScroll.mAmount;
5313 : }
5314 :
5315 : // The caller may want synchronous scrolling.
5316 0 : sf->ScrollBy(pt, scrollUnit, nsIScrollableFrame::INSTANT);
5317 0 : return NS_OK;
5318 : }
5319 :
5320 : void
5321 0 : EventStateManager::SetActiveManager(EventStateManager* aNewESM,
5322 : nsIContent* aContent)
5323 : {
5324 0 : if (sActiveESM && aNewESM != sActiveESM) {
5325 0 : sActiveESM->SetContentState(nullptr, NS_EVENT_STATE_ACTIVE);
5326 : }
5327 0 : sActiveESM = aNewESM;
5328 0 : if (sActiveESM && aContent) {
5329 0 : sActiveESM->SetContentState(aContent, NS_EVENT_STATE_ACTIVE);
5330 : }
5331 0 : }
5332 :
5333 : void
5334 0 : EventStateManager::ClearGlobalActiveContent(EventStateManager* aClearer)
5335 : {
5336 0 : if (aClearer) {
5337 0 : aClearer->SetContentState(nullptr, NS_EVENT_STATE_ACTIVE);
5338 0 : if (sDragOverContent) {
5339 0 : aClearer->SetContentState(nullptr, NS_EVENT_STATE_DRAGOVER);
5340 : }
5341 : }
5342 0 : if (sActiveESM && aClearer != sActiveESM) {
5343 0 : sActiveESM->SetContentState(nullptr, NS_EVENT_STATE_ACTIVE);
5344 : }
5345 0 : sActiveESM = nullptr;
5346 0 : }
5347 :
5348 : /******************************************************************/
5349 : /* mozilla::EventStateManager::DeltaAccumulator */
5350 : /******************************************************************/
5351 :
5352 : void
5353 0 : EventStateManager::DeltaAccumulator::InitLineOrPageDelta(
5354 : nsIFrame* aTargetFrame,
5355 : EventStateManager* aESM,
5356 : WidgetWheelEvent* aEvent)
5357 : {
5358 0 : MOZ_ASSERT(aESM);
5359 0 : MOZ_ASSERT(aEvent);
5360 :
5361 : // Reset if the previous wheel event is too old.
5362 0 : if (!mLastTime.IsNull()) {
5363 0 : TimeDuration duration = TimeStamp::Now() - mLastTime;
5364 0 : if (duration.ToMilliseconds() > WheelTransaction::GetTimeoutTime()) {
5365 0 : Reset();
5366 : }
5367 : }
5368 : // If we have accumulated delta, we may need to reset it.
5369 0 : if (IsInTransaction()) {
5370 : // If wheel event type is changed, reset the values.
5371 0 : if (mHandlingDeltaMode != aEvent->mDeltaMode ||
5372 0 : mIsNoLineOrPageDeltaDevice != aEvent->mIsNoLineOrPageDelta) {
5373 0 : Reset();
5374 : } else {
5375 : // If the delta direction is changed, we should reset only the
5376 : // accumulated values.
5377 0 : if (mX && aEvent->mDeltaX && ((aEvent->mDeltaX > 0.0) != (mX > 0.0))) {
5378 0 : mX = mPendingScrollAmountX = 0.0;
5379 : }
5380 0 : if (mY && aEvent->mDeltaY && ((aEvent->mDeltaY > 0.0) != (mY > 0.0))) {
5381 0 : mY = mPendingScrollAmountY = 0.0;
5382 : }
5383 : }
5384 : }
5385 :
5386 0 : mHandlingDeltaMode = aEvent->mDeltaMode;
5387 0 : mIsNoLineOrPageDeltaDevice = aEvent->mIsNoLineOrPageDelta;
5388 :
5389 : // If it's handling neither a device that does not provide line or page deltas
5390 : // nor delta values multiplied by prefs, we must not modify lineOrPageDelta
5391 : // values.
5392 0 : if (!mIsNoLineOrPageDeltaDevice &&
5393 0 : !EventStateManager::WheelPrefs::GetInstance()->
5394 0 : NeedToComputeLineOrPageDelta(aEvent)) {
5395 : // Set the delta values to mX and mY. They would be used when above block
5396 : // resets mX/mY/mPendingScrollAmountX/mPendingScrollAmountY if the direction
5397 : // is changed.
5398 : // NOTE: We shouldn't accumulate the delta values, it might could cause
5399 : // overflow even though it's not a realistic situation.
5400 0 : if (aEvent->mDeltaX) {
5401 0 : mX = aEvent->mDeltaX;
5402 : }
5403 0 : if (aEvent->mDeltaY) {
5404 0 : mY = aEvent->mDeltaY;
5405 : }
5406 0 : mLastTime = TimeStamp::Now();
5407 0 : return;
5408 : }
5409 :
5410 0 : mX += aEvent->mDeltaX;
5411 0 : mY += aEvent->mDeltaY;
5412 :
5413 0 : if (mHandlingDeltaMode == nsIDOMWheelEvent::DOM_DELTA_PIXEL) {
5414 : // Records pixel delta values and init mLineOrPageDeltaX and
5415 : // mLineOrPageDeltaY for wheel events which are caused by pixel only
5416 : // devices. Ignore mouse wheel transaction for computing this. The
5417 : // lineOrPageDelta values will be used by dispatching legacy
5418 : // eMouseScrollEventClass (DOMMouseScroll) but not be used for scrolling
5419 : // of default action. The transaction should be used only for the default
5420 : // action.
5421 : nsIFrame* frame =
5422 : aESM->ComputeScrollTarget(aTargetFrame, aEvent,
5423 0 : COMPUTE_LEGACY_MOUSE_SCROLL_EVENT_TARGET);
5424 : nsPresContext* pc =
5425 0 : frame ? frame->PresContext() : aTargetFrame->PresContext();
5426 0 : nsIScrollableFrame* scrollTarget = do_QueryFrame(frame);
5427 0 : nsSize scrollAmount = aESM->GetScrollAmount(pc, aEvent, scrollTarget);
5428 : nsIntSize scrollAmountInCSSPixels(
5429 : nsPresContext::AppUnitsToIntCSSPixels(scrollAmount.width),
5430 0 : nsPresContext::AppUnitsToIntCSSPixels(scrollAmount.height));
5431 :
5432 0 : aEvent->mLineOrPageDeltaX = RoundDown(mX) / scrollAmountInCSSPixels.width;
5433 0 : aEvent->mLineOrPageDeltaY = RoundDown(mY) / scrollAmountInCSSPixels.height;
5434 :
5435 0 : mX -= aEvent->mLineOrPageDeltaX * scrollAmountInCSSPixels.width;
5436 0 : mY -= aEvent->mLineOrPageDeltaY * scrollAmountInCSSPixels.height;
5437 : } else {
5438 0 : aEvent->mLineOrPageDeltaX = RoundDown(mX);
5439 0 : aEvent->mLineOrPageDeltaY = RoundDown(mY);
5440 0 : mX -= aEvent->mLineOrPageDeltaX;
5441 0 : mY -= aEvent->mLineOrPageDeltaY;
5442 : }
5443 :
5444 0 : mLastTime = TimeStamp::Now();
5445 : }
5446 :
5447 : void
5448 0 : EventStateManager::DeltaAccumulator::Reset()
5449 : {
5450 0 : mX = mY = 0.0;
5451 0 : mPendingScrollAmountX = mPendingScrollAmountY = 0.0;
5452 0 : mHandlingDeltaMode = UINT32_MAX;
5453 0 : mIsNoLineOrPageDeltaDevice = false;
5454 0 : }
5455 :
5456 : nsIntPoint
5457 0 : EventStateManager::DeltaAccumulator::ComputeScrollAmountForDefaultAction(
5458 : WidgetWheelEvent* aEvent,
5459 : const nsIntSize& aScrollAmountInDevPixels)
5460 : {
5461 0 : MOZ_ASSERT(aEvent);
5462 :
5463 : // If the wheel event is line scroll and the delta value is computed from
5464 : // system settings, allow to override the system speed.
5465 : bool allowScrollSpeedOverride =
5466 0 : (!aEvent->mCustomizedByUserPrefs &&
5467 0 : aEvent->mDeltaMode == nsIDOMWheelEvent::DOM_DELTA_LINE);
5468 : DeltaValues acceleratedDelta =
5469 0 : WheelTransaction::AccelerateWheelDelta(aEvent, allowScrollSpeedOverride);
5470 :
5471 0 : nsIntPoint result(0, 0);
5472 0 : if (aEvent->mDeltaMode == nsIDOMWheelEvent::DOM_DELTA_PIXEL) {
5473 0 : mPendingScrollAmountX += acceleratedDelta.deltaX;
5474 0 : mPendingScrollAmountY += acceleratedDelta.deltaY;
5475 : } else {
5476 0 : mPendingScrollAmountX +=
5477 0 : aScrollAmountInDevPixels.width * acceleratedDelta.deltaX;
5478 0 : mPendingScrollAmountY +=
5479 0 : aScrollAmountInDevPixels.height * acceleratedDelta.deltaY;
5480 : }
5481 0 : result.x = RoundDown(mPendingScrollAmountX);
5482 0 : result.y = RoundDown(mPendingScrollAmountY);
5483 0 : mPendingScrollAmountX -= result.x;
5484 0 : mPendingScrollAmountY -= result.y;
5485 :
5486 0 : return result;
5487 : }
5488 :
5489 : /******************************************************************/
5490 : /* mozilla::EventStateManager::WheelPrefs */
5491 : /******************************************************************/
5492 :
5493 : // static
5494 : EventStateManager::WheelPrefs*
5495 0 : EventStateManager::WheelPrefs::GetInstance()
5496 : {
5497 0 : if (!sInstance) {
5498 0 : sInstance = new WheelPrefs();
5499 : }
5500 0 : return sInstance;
5501 : }
5502 :
5503 : // static
5504 : void
5505 0 : EventStateManager::WheelPrefs::Shutdown()
5506 : {
5507 0 : delete sInstance;
5508 0 : sInstance = nullptr;
5509 0 : }
5510 :
5511 : // static
5512 : void
5513 0 : EventStateManager::WheelPrefs::OnPrefChanged(const char* aPrefName,
5514 : void* aClosure)
5515 : {
5516 : // forget all prefs, it's not problem for performance.
5517 0 : sInstance->Reset();
5518 0 : DeltaAccumulator::GetInstance()->Reset();
5519 0 : }
5520 :
5521 0 : EventStateManager::WheelPrefs::WheelPrefs()
5522 : {
5523 0 : Reset();
5524 0 : Preferences::RegisterPrefixCallback(OnPrefChanged, "mousewheel.");
5525 : Preferences::AddBoolVarCache(&sWheelEventsEnabledOnPlugins,
5526 : "plugin.mousewheel.enabled",
5527 0 : true);
5528 0 : }
5529 :
5530 0 : EventStateManager::WheelPrefs::~WheelPrefs()
5531 : {
5532 0 : Preferences::UnregisterPrefixCallback(OnPrefChanged, "mousewheel.");
5533 0 : }
5534 :
5535 : void
5536 0 : EventStateManager::WheelPrefs::Reset()
5537 : {
5538 0 : memset(mInit, 0, sizeof(mInit));
5539 :
5540 0 : }
5541 :
5542 : EventStateManager::WheelPrefs::Index
5543 0 : EventStateManager::WheelPrefs::GetIndexFor(WidgetWheelEvent* aEvent)
5544 : {
5545 0 : if (!aEvent) {
5546 0 : return INDEX_DEFAULT;
5547 : }
5548 :
5549 : Modifiers modifiers =
5550 0 : (aEvent->mModifiers & (MODIFIER_ALT |
5551 : MODIFIER_CONTROL |
5552 : MODIFIER_META |
5553 : MODIFIER_SHIFT |
5554 0 : MODIFIER_OS));
5555 :
5556 0 : switch (modifiers) {
5557 : case MODIFIER_ALT:
5558 0 : return INDEX_ALT;
5559 : case MODIFIER_CONTROL:
5560 0 : return INDEX_CONTROL;
5561 : case MODIFIER_META:
5562 0 : return INDEX_META;
5563 : case MODIFIER_SHIFT:
5564 0 : return INDEX_SHIFT;
5565 : case MODIFIER_OS:
5566 0 : return INDEX_OS;
5567 : default:
5568 : // If two or more modifier keys are pressed, we should use default
5569 : // settings.
5570 0 : return INDEX_DEFAULT;
5571 : }
5572 : }
5573 :
5574 : void
5575 0 : EventStateManager::WheelPrefs::GetBasePrefName(
5576 : EventStateManager::WheelPrefs::Index aIndex,
5577 : nsACString& aBasePrefName)
5578 : {
5579 0 : aBasePrefName.AssignLiteral("mousewheel.");
5580 0 : switch (aIndex) {
5581 : case INDEX_ALT:
5582 0 : aBasePrefName.AppendLiteral("with_alt.");
5583 0 : break;
5584 : case INDEX_CONTROL:
5585 0 : aBasePrefName.AppendLiteral("with_control.");
5586 0 : break;
5587 : case INDEX_META:
5588 0 : aBasePrefName.AppendLiteral("with_meta.");
5589 0 : break;
5590 : case INDEX_SHIFT:
5591 0 : aBasePrefName.AppendLiteral("with_shift.");
5592 0 : break;
5593 : case INDEX_OS:
5594 0 : aBasePrefName.AppendLiteral("with_win.");
5595 0 : break;
5596 : case INDEX_DEFAULT:
5597 : default:
5598 0 : aBasePrefName.AppendLiteral("default.");
5599 0 : break;
5600 : }
5601 0 : }
5602 :
5603 : void
5604 0 : EventStateManager::WheelPrefs::Init(EventStateManager::WheelPrefs::Index aIndex)
5605 : {
5606 0 : if (mInit[aIndex]) {
5607 0 : return;
5608 : }
5609 0 : mInit[aIndex] = true;
5610 :
5611 0 : nsAutoCString basePrefName;
5612 0 : GetBasePrefName(aIndex, basePrefName);
5613 :
5614 0 : nsAutoCString prefNameX(basePrefName);
5615 0 : prefNameX.AppendLiteral("delta_multiplier_x");
5616 0 : mMultiplierX[aIndex] =
5617 0 : static_cast<double>(Preferences::GetInt(prefNameX.get(), 100)) / 100;
5618 :
5619 0 : nsAutoCString prefNameY(basePrefName);
5620 0 : prefNameY.AppendLiteral("delta_multiplier_y");
5621 0 : mMultiplierY[aIndex] =
5622 0 : static_cast<double>(Preferences::GetInt(prefNameY.get(), 100)) / 100;
5623 :
5624 0 : nsAutoCString prefNameZ(basePrefName);
5625 0 : prefNameZ.AppendLiteral("delta_multiplier_z");
5626 0 : mMultiplierZ[aIndex] =
5627 0 : static_cast<double>(Preferences::GetInt(prefNameZ.get(), 100)) / 100;
5628 :
5629 0 : nsAutoCString prefNameAction(basePrefName);
5630 0 : prefNameAction.AppendLiteral("action");
5631 0 : int32_t action = Preferences::GetInt(prefNameAction.get(), ACTION_SCROLL);
5632 0 : if (action < int32_t(ACTION_NONE) || action > int32_t(ACTION_LAST)) {
5633 0 : NS_WARNING("Unsupported action pref value, replaced with 'Scroll'.");
5634 0 : action = ACTION_SCROLL;
5635 : }
5636 0 : mActions[aIndex] = static_cast<Action>(action);
5637 :
5638 : // Compute action values overridden by .override_x pref.
5639 : // At present, override is possible only for the x-direction
5640 : // because this pref is introduced mainly for tilt wheels.
5641 0 : prefNameAction.AppendLiteral(".override_x");
5642 0 : int32_t actionOverrideX = Preferences::GetInt(prefNameAction.get(), -1);
5643 0 : if (actionOverrideX < -1 || actionOverrideX > int32_t(ACTION_LAST)) {
5644 0 : NS_WARNING("Unsupported action override pref value, didn't override.");
5645 0 : actionOverrideX = -1;
5646 : }
5647 0 : mOverriddenActionsX[aIndex] = (actionOverrideX == -1)
5648 0 : ? static_cast<Action>(action)
5649 : : static_cast<Action>(actionOverrideX);
5650 : }
5651 :
5652 : void
5653 0 : EventStateManager::WheelPrefs::ApplyUserPrefsToDelta(WidgetWheelEvent* aEvent)
5654 : {
5655 0 : if (aEvent->mCustomizedByUserPrefs) {
5656 0 : return;
5657 : }
5658 :
5659 0 : Index index = GetIndexFor(aEvent);
5660 0 : Init(index);
5661 :
5662 0 : aEvent->mDeltaX *= mMultiplierX[index];
5663 0 : aEvent->mDeltaY *= mMultiplierY[index];
5664 0 : aEvent->mDeltaZ *= mMultiplierZ[index];
5665 :
5666 : // If the multiplier is 1.0 or -1.0, i.e., it doesn't change the absolute
5667 : // value, we should use lineOrPageDelta values which were set by widget.
5668 : // Otherwise, we need to compute them from accumulated delta values.
5669 0 : if (!NeedToComputeLineOrPageDelta(aEvent)) {
5670 0 : aEvent->mLineOrPageDeltaX *= static_cast<int32_t>(mMultiplierX[index]);
5671 0 : aEvent->mLineOrPageDeltaY *= static_cast<int32_t>(mMultiplierY[index]);
5672 : } else {
5673 0 : aEvent->mLineOrPageDeltaX = 0;
5674 0 : aEvent->mLineOrPageDeltaY = 0;
5675 : }
5676 :
5677 0 : aEvent->mCustomizedByUserPrefs =
5678 0 : ((mMultiplierX[index] != 1.0) || (mMultiplierY[index] != 1.0) ||
5679 0 : (mMultiplierZ[index] != 1.0));
5680 : }
5681 :
5682 : void
5683 0 : EventStateManager::WheelPrefs::CancelApplyingUserPrefsFromOverflowDelta(
5684 : WidgetWheelEvent* aEvent)
5685 : {
5686 0 : Index index = GetIndexFor(aEvent);
5687 0 : Init(index);
5688 :
5689 : // XXX If the multiplier pref value is negative, the scroll direction was
5690 : // changed and caused to scroll different direction. In such case,
5691 : // this method reverts the sign of overflowDelta. Does it make widget
5692 : // happy? Although, widget can know the pref applied delta values by
5693 : // referrencing the deltaX and deltaY of the event.
5694 :
5695 0 : if (mMultiplierX[index]) {
5696 0 : aEvent->mOverflowDeltaX /= mMultiplierX[index];
5697 : }
5698 0 : if (mMultiplierY[index]) {
5699 0 : aEvent->mOverflowDeltaY /= mMultiplierY[index];
5700 : }
5701 0 : }
5702 :
5703 : EventStateManager::WheelPrefs::Action
5704 0 : EventStateManager::WheelPrefs::ComputeActionFor(WidgetWheelEvent* aEvent)
5705 : {
5706 0 : Index index = GetIndexFor(aEvent);
5707 0 : Init(index);
5708 :
5709 : bool deltaXPreferred =
5710 0 : (Abs(aEvent->mDeltaX) > Abs(aEvent->mDeltaY) &&
5711 0 : Abs(aEvent->mDeltaX) > Abs(aEvent->mDeltaZ));
5712 0 : Action* actions = deltaXPreferred ? mOverriddenActionsX : mActions;
5713 0 : if (actions[index] == ACTION_NONE || actions[index] == ACTION_SCROLL) {
5714 0 : return actions[index];
5715 : }
5716 :
5717 : // Momentum events shouldn't run special actions.
5718 0 : if (aEvent->mIsMomentum) {
5719 : // Use the default action. Note that user might kill the wheel scrolling.
5720 0 : Init(INDEX_DEFAULT);
5721 0 : return (actions[INDEX_DEFAULT] == ACTION_SCROLL) ? ACTION_SCROLL :
5722 0 : ACTION_NONE;
5723 : }
5724 :
5725 0 : return actions[index];
5726 : }
5727 :
5728 : bool
5729 0 : EventStateManager::WheelPrefs::NeedToComputeLineOrPageDelta(
5730 : WidgetWheelEvent* aEvent)
5731 : {
5732 0 : Index index = GetIndexFor(aEvent);
5733 0 : Init(index);
5734 :
5735 0 : return (mMultiplierX[index] != 1.0 && mMultiplierX[index] != -1.0) ||
5736 0 : (mMultiplierY[index] != 1.0 && mMultiplierY[index] != -1.0);
5737 : }
5738 :
5739 : void
5740 0 : EventStateManager::WheelPrefs::GetUserPrefsForEvent(WidgetWheelEvent* aEvent,
5741 : double* aOutMultiplierX,
5742 : double* aOutMultiplierY)
5743 : {
5744 0 : Index index = GetIndexFor(aEvent);
5745 0 : Init(index);
5746 :
5747 0 : *aOutMultiplierX = mMultiplierX[index];
5748 0 : *aOutMultiplierY = mMultiplierY[index];
5749 0 : }
5750 :
5751 : // static
5752 : bool
5753 0 : EventStateManager::WheelPrefs::WheelEventsEnabledOnPlugins()
5754 : {
5755 0 : if (!sInstance) {
5756 0 : GetInstance(); // initializing sWheelEventsEnabledOnPlugins
5757 : }
5758 0 : return sWheelEventsEnabledOnPlugins;
5759 : }
5760 :
5761 : bool
5762 0 : EventStateManager::WheelEventIsScrollAction(WidgetWheelEvent* aEvent)
5763 : {
5764 0 : return aEvent->mMessage == eWheel &&
5765 0 : WheelPrefs::GetInstance()->ComputeActionFor(aEvent) == WheelPrefs::ACTION_SCROLL;
5766 : }
5767 :
5768 : void
5769 0 : EventStateManager::GetUserPrefsForWheelEvent(WidgetWheelEvent* aEvent,
5770 : double* aOutMultiplierX,
5771 : double* aOutMultiplierY)
5772 : {
5773 0 : WheelPrefs::GetInstance()->GetUserPrefsForEvent(
5774 0 : aEvent, aOutMultiplierX, aOutMultiplierY);
5775 0 : }
5776 :
5777 : bool
5778 0 : EventStateManager::WheelPrefs::IsOverOnePageScrollAllowedX(
5779 : WidgetWheelEvent* aEvent)
5780 : {
5781 0 : Index index = GetIndexFor(aEvent);
5782 0 : Init(index);
5783 0 : return Abs(mMultiplierX[index]) >=
5784 0 : MIN_MULTIPLIER_VALUE_ALLOWING_OVER_ONE_PAGE_SCROLL;
5785 : }
5786 :
5787 : bool
5788 0 : EventStateManager::WheelPrefs::IsOverOnePageScrollAllowedY(
5789 : WidgetWheelEvent* aEvent)
5790 : {
5791 0 : Index index = GetIndexFor(aEvent);
5792 0 : Init(index);
5793 0 : return Abs(mMultiplierY[index]) >=
5794 0 : MIN_MULTIPLIER_VALUE_ALLOWING_OVER_ONE_PAGE_SCROLL;
5795 : }
5796 :
5797 : /******************************************************************/
5798 : /* mozilla::EventStateManager::Prefs */
5799 : /******************************************************************/
5800 :
5801 : bool EventStateManager::Prefs::sKeyCausesActivation = true;
5802 : bool EventStateManager::Prefs::sClickHoldContextMenu = false;
5803 : int32_t EventStateManager::Prefs::sGenericAccessModifierKey = -1;
5804 : int32_t EventStateManager::Prefs::sChromeAccessModifierMask = 0;
5805 : int32_t EventStateManager::Prefs::sContentAccessModifierMask = 0;
5806 :
5807 : // static
5808 : void
5809 2 : EventStateManager::Prefs::Init()
5810 : {
5811 4 : DebugOnly<nsresult> rv = Preferences::RegisterCallback(OnChange, "dom.popup_allowed_events");
5812 2 : MOZ_ASSERT(NS_SUCCEEDED(rv),
5813 : "Failed to observe \"dom.popup_allowed_events\"");
5814 :
5815 : static bool sPrefsAlreadyCached = false;
5816 2 : if (sPrefsAlreadyCached) {
5817 0 : return;
5818 : }
5819 :
5820 4 : rv = Preferences::AddBoolVarCache(&sKeyCausesActivation,
5821 : "accessibility.accesskeycausesactivation",
5822 2 : sKeyCausesActivation);
5823 2 : MOZ_ASSERT(NS_SUCCEEDED(rv),
5824 : "Failed to observe \"accessibility.accesskeycausesactivation\"");
5825 4 : rv = Preferences::AddBoolVarCache(&sClickHoldContextMenu,
5826 : "ui.click_hold_context_menus",
5827 2 : sClickHoldContextMenu);
5828 2 : MOZ_ASSERT(NS_SUCCEEDED(rv),
5829 : "Failed to observe \"ui.click_hold_context_menus\"");
5830 4 : rv = Preferences::AddIntVarCache(&sGenericAccessModifierKey,
5831 : "ui.key.generalAccessKey",
5832 2 : sGenericAccessModifierKey);
5833 2 : MOZ_ASSERT(NS_SUCCEEDED(rv),
5834 : "Failed to observe \"ui.key.generalAccessKey\"");
5835 4 : rv = Preferences::AddIntVarCache(&sChromeAccessModifierMask,
5836 : "ui.key.chromeAccess",
5837 2 : sChromeAccessModifierMask);
5838 2 : MOZ_ASSERT(NS_SUCCEEDED(rv),
5839 : "Failed to observe \"ui.key.chromeAccess\"");
5840 4 : rv = Preferences::AddIntVarCache(&sContentAccessModifierMask,
5841 : "ui.key.contentAccess",
5842 2 : sContentAccessModifierMask);
5843 2 : MOZ_ASSERT(NS_SUCCEEDED(rv),
5844 : "Failed to observe \"ui.key.contentAccess\"");
5845 2 : sPrefsAlreadyCached = true;
5846 : }
5847 :
5848 : // static
5849 : void
5850 0 : EventStateManager::Prefs::OnChange(const char* aPrefName, void*)
5851 : {
5852 0 : nsDependentCString prefName(aPrefName);
5853 0 : if (prefName.EqualsLiteral("dom.popup_allowed_events")) {
5854 0 : Event::PopupAllowedEventsChanged();
5855 : }
5856 0 : }
5857 :
5858 : // static
5859 : void
5860 0 : EventStateManager::Prefs::Shutdown()
5861 : {
5862 0 : Preferences::UnregisterCallback(OnChange, "dom.popup_allowed_events");
5863 0 : }
5864 :
5865 : // static
5866 : int32_t
5867 0 : EventStateManager::Prefs::ChromeAccessModifierMask()
5868 : {
5869 0 : return GetAccessModifierMask(nsIDocShellTreeItem::typeChrome);
5870 : }
5871 :
5872 : // static
5873 : int32_t
5874 0 : EventStateManager::Prefs::ContentAccessModifierMask()
5875 : {
5876 0 : return GetAccessModifierMask(nsIDocShellTreeItem::typeContent);
5877 : }
5878 :
5879 : // static
5880 : int32_t
5881 0 : EventStateManager::Prefs::GetAccessModifierMask(int32_t aItemType)
5882 : {
5883 0 : switch (sGenericAccessModifierKey) {
5884 0 : case -1: break; // use the individual prefs
5885 0 : case nsIDOMKeyEvent::DOM_VK_SHIFT: return NS_MODIFIER_SHIFT;
5886 0 : case nsIDOMKeyEvent::DOM_VK_CONTROL: return NS_MODIFIER_CONTROL;
5887 0 : case nsIDOMKeyEvent::DOM_VK_ALT: return NS_MODIFIER_ALT;
5888 0 : case nsIDOMKeyEvent::DOM_VK_META: return NS_MODIFIER_META;
5889 0 : case nsIDOMKeyEvent::DOM_VK_WIN: return NS_MODIFIER_OS;
5890 0 : default: return 0;
5891 : }
5892 :
5893 0 : switch (aItemType) {
5894 : case nsIDocShellTreeItem::typeChrome:
5895 0 : return sChromeAccessModifierMask;
5896 : case nsIDocShellTreeItem::typeContent:
5897 0 : return sContentAccessModifierMask;
5898 : default:
5899 0 : return 0;
5900 : }
5901 : }
5902 :
5903 : /******************************************************************/
5904 : /* mozilla::AutoHandlingUserInputStatePusher */
5905 : /******************************************************************/
5906 :
5907 10 : AutoHandlingUserInputStatePusher::AutoHandlingUserInputStatePusher(
5908 : bool aIsHandlingUserInput,
5909 : WidgetEvent* aEvent,
5910 10 : nsIDocument* aDocument) :
5911 : mIsHandlingUserInput(aIsHandlingUserInput),
5912 10 : mIsMouseDown(aEvent && aEvent->mMessage == eMouseDown),
5913 20 : mResetFMMouseButtonHandlingState(false)
5914 : {
5915 10 : if (!aIsHandlingUserInput) {
5916 10 : return;
5917 : }
5918 0 : EventStateManager::StartHandlingUserInput();
5919 0 : if (mIsMouseDown) {
5920 0 : nsIPresShell::SetCapturingContent(nullptr, 0);
5921 0 : nsIPresShell::AllowMouseCapture(true);
5922 : }
5923 0 : if (!aDocument || !aEvent || !aEvent->IsTrusted()) {
5924 0 : return;
5925 : }
5926 0 : mResetFMMouseButtonHandlingState =
5927 0 : (aEvent->mMessage == eMouseDown || aEvent->mMessage == eMouseUp);
5928 0 : if (mResetFMMouseButtonHandlingState) {
5929 0 : nsFocusManager* fm = nsFocusManager::GetFocusManager();
5930 0 : NS_ENSURE_TRUE_VOID(fm);
5931 : // If it's in modal state, mouse button event handling may be nested.
5932 : // E.g., a modal dialog is opened at mousedown or mouseup event handler
5933 : // and the dialog is clicked. Therefore, we should store current
5934 : // mouse button event handling document if nsFocusManager already has it.
5935 : mMouseButtonEventHandlingDocument =
5936 0 : fm->SetMouseButtonHandlingDocument(aDocument);
5937 : }
5938 : }
5939 :
5940 20 : AutoHandlingUserInputStatePusher::~AutoHandlingUserInputStatePusher()
5941 : {
5942 10 : if (!mIsHandlingUserInput) {
5943 10 : return;
5944 : }
5945 0 : EventStateManager::StopHandlingUserInput();
5946 0 : if (mIsMouseDown) {
5947 0 : nsIPresShell::AllowMouseCapture(false);
5948 : }
5949 0 : if (mResetFMMouseButtonHandlingState) {
5950 0 : nsFocusManager* fm = nsFocusManager::GetFocusManager();
5951 0 : NS_ENSURE_TRUE_VOID(fm);
5952 : nsCOMPtr<nsIDocument> handlingDocument =
5953 0 : fm->SetMouseButtonHandlingDocument(mMouseButtonEventHandlingDocument);
5954 : }
5955 10 : }
5956 :
5957 9 : } // namespace mozilla
|