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