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