/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 |