Coverage Report

Created: 2018-09-25 14:53

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