Return back right way for scrolling content by mouse
[mozilla-central.git] / extensions / widgetutils / src / nsWidgetUtils.cpp
blob66923bcc8c0647f0a7331714bd6171012c4d054a
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set ts=2 sw=2 sts=2 tw=80 et cindent: */
3 /* ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
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/
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.
16 * The Original Code is Mozilla's Element Optimizeing extension.
18 * The Initial Developer of the Original Code is the Mozilla Foundation.
19 * Portions created by the Initial Developer are Copyright (C) 2006
20 * the Initial Developer. All Rights Reserved.
22 * Contributor(s):
23 * Oleg Romashin <romaxa@gmail.com> (original author)
24 * Brad Lassey <blassey@mozilla.com>
26 * Alternatively, the contents of this file may be used under the terms of
27 * either the GNU General Public License Version 2 or later (the "GPL"), or
28 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
38 * ***** END LICENSE BLOCK ***** */
40 #include "nsCURILoader.h"
41 #include "nsICategoryManager.h"
42 #include "nsIDOMAbstractView.h"
43 #include "nsIDOMDocument.h"
44 #include "nsIDOMDocumentView.h"
45 #include "nsIDOMHTMLElement.h"
46 #include "nsIDOMHTMLIFrameElement.h"
47 #include "nsIDOMNSDocument.h"
48 #include "nsIDOMNSElement.h"
49 #include "nsIDOMNSHTMLElement.h"
50 #include "nsIDOMNode.h"
51 #include "nsIDOMNodeList.h"
52 #include "nsIDOMWindow.h"
53 #include "nsIDOMWindowCollection.h"
54 #include "nsIDocument.h"
55 #include "nsIGenericFactory.h"
56 #include "nsIObserver.h"
57 #include "nsIPref.h"
58 #include "nsIPresShell.h"
59 #include "nsIStyleSheetService.h"
60 #include "nsIWebProgress.h"
61 #include "nsIWebProgressListener.h"
62 #include "nsIWindowWatcher.h"
63 #include "nsNetUtil.h"
64 #include "nsRect.h"
65 #include "nsStringGlue.h"
66 #include "nsWeakReference.h"
67 #include "nsIWebBrowser.h"
68 #include "nsIObserverService.h"
69 #include "nsIDOMEventTarget.h"
70 #include "nsPIDOMWindow.h"
71 #include "nsIDOMWindow.h"
72 //#include ".h"
73 #include "nsIDOM3EventTarget.h"
74 #include "nsIDOMKeyListener.h"
75 #include "nsIDOMCompositionListener.h"
76 #include "nsIDOMTextListener.h"
77 #include "nsIDOMMouseMotionListener.h"
78 #include "nsIDOMMouseListener.h"
79 #include "nsIDOMMouseEvent.h"
80 #include "nsIView.h"
81 #include "nsGUIEvent.h"
82 #include "nsIViewManager.h"
84 const int MIN_INT =((int) (1 << (sizeof(int) * 8 - 1)));
86 static int g_lastX=MIN_INT;
87 static int g_lastY=MIN_INT;
88 static PRBool g_panning = PR_FALSE;
89 static PRBool g_is_scrollable = PR_FALSE;
91 #define EM_MULT 16.
92 #define NS_FRAME_HAS_RELATIVE_SIZE 0x01000000
93 #define NS_FRAME_HAS_OPTIMIZEDVIEW 0x02000000
95 // TODO auto reload nsWidgetUtils in C.
96 class nsWidgetUtils : public nsIObserver,
97 public nsIDOMMouseMotionListener,
98 public nsIDOMMouseListener,
99 public nsSupportsWeakReference
101 public:
102 nsWidgetUtils();
103 virtual ~nsWidgetUtils();
104 nsresult Init(void);
105 nsresult GetDOMWindowByNode(nsIDOMNode *aNode, nsIDOMWindow * *aDOMWindow);
107 void RemoveWindowListeners(nsIDOMWindow *aDOMWin);
108 void GetChromeEventHandler(nsIDOMWindow *aDOMWin, nsIDOMEventTarget **aChromeTarget);
109 void AttachWindowListeners(nsIDOMWindow *aDOMWin);
111 // nsIDOMMouseMotionListener
112 NS_IMETHOD MouseMove(nsIDOMEvent* aDOMEvent);
113 NS_IMETHOD DragMove(nsIDOMEvent* aMouseEvent);
114 NS_IMETHOD HandleEvent(nsIDOMEvent* aDOMEvent);
116 // nsIDOMMouseListener
117 NS_IMETHOD MouseDown(nsIDOMEvent* aDOMEvent);
118 NS_IMETHOD MouseUp(nsIDOMEvent* aDOMEvent);
119 NS_IMETHOD MouseClick(nsIDOMEvent* aDOMEvent);
120 NS_IMETHOD MouseDblClick(nsIDOMEvent* aDOMEvent);
121 NS_IMETHOD MouseOver(nsIDOMEvent* aDOMEvent);
122 NS_IMETHOD MouseOut(nsIDOMEvent* aDOMEvent);
124 NS_DECL_ISUPPORTS
125 NS_DECL_NSIOBSERVER
126 nsCOMPtr<nsIWidget> mWidget;
127 nsCOMPtr<nsIViewManager> mViewManager;
128 nsCOMPtr<nsIDOMWindow> mWindow;
129 nsCOMPtr<nsIDOMNode> mNode;
132 nsWidgetUtils::nsWidgetUtils()
134 Init();
137 NS_IMETHODIMP
138 nsWidgetUtils::Init()
140 nsresult rv;
141 nsCOMPtr<nsIObserverService> obsSvc =
142 do_GetService("@mozilla.org/observer-service;1");
143 NS_ENSURE_STATE(obsSvc);
145 rv = obsSvc->AddObserver(this, "domwindowopened", PR_FALSE);
146 NS_ENSURE_SUCCESS(rv, rv);
147 rv = obsSvc->AddObserver(this, "domwindowclosed", PR_FALSE);
148 NS_ENSURE_SUCCESS(rv, rv);
151 NS_IMETHODIMP
152 nsWidgetUtils::HandleEvent(nsIDOMEvent* aDOMEvent)
154 return NS_OK;
157 static PRBool
158 IsXULNode(nsIDOMNode *aNode, PRUint32 *aType = 0)
160 PRBool retval = PR_FALSE;
161 if (!aNode) return retval;
163 nsString sorigNode;
164 aNode->GetNodeName(sorigNode);
165 if (sorigNode.EqualsLiteral("#document"))
166 return retval;
167 retval = StringBeginsWith(sorigNode, NS_LITERAL_STRING("xul:"));
169 if (!aType) return retval;
171 if (sorigNode.EqualsLiteral("xul:thumb")
172 || sorigNode.EqualsLiteral("xul:vbox")
173 || sorigNode.EqualsLiteral("xul:spacer"))
174 *aType = PR_FALSE; // Magic
175 else if (sorigNode.EqualsLiteral("xul:slider"))
176 *aType = 2; // Magic
177 else if (sorigNode.EqualsLiteral("xul:scrollbarbutton"))
178 *aType = 3; // Magic
180 return retval;
183 nsresult
184 nsWidgetUtils::GetDOMWindowByNode(nsIDOMNode *aNode, nsIDOMWindow * *aDOMWindow)
186 nsresult rv;
187 nsCOMPtr<nsIDOMDocument> nodeDoc;
188 rv = aNode->GetOwnerDocument(getter_AddRefs(nodeDoc));
189 NS_ENSURE_SUCCESS(rv, rv);
190 nsCOMPtr<nsIDOMDocumentView> docView = do_QueryInterface(nodeDoc, &rv);
191 NS_ENSURE_SUCCESS(rv, rv);
192 nsCOMPtr<nsIDOMAbstractView> absView;
193 NS_ENSURE_SUCCESS(rv, rv);
194 rv = docView->GetDefaultView(getter_AddRefs(absView));
195 NS_ENSURE_SUCCESS(rv, rv);
196 nsCOMPtr<nsIDOMWindow> window = do_QueryInterface(absView, &rv);
197 NS_ENSURE_SUCCESS(rv, rv);
198 *aDOMWindow = window;
199 NS_IF_ADDREF(*aDOMWindow);
200 return rv;
203 NS_IMETHODIMP
204 nsWidgetUtils::MouseDown(nsIDOMEvent* aDOMEvent)
206 g_is_scrollable = PR_FALSE;
207 nsCOMPtr <nsIDOMMouseEvent> mouseEvent;
208 mouseEvent = do_QueryInterface(aDOMEvent);
209 if (!mouseEvent)
210 return NS_OK;
212 ((nsIDOMMouseEvent*)mouseEvent)->GetScreenX(&g_lastX);
213 ((nsIDOMMouseEvent*)mouseEvent)->GetScreenY(&g_lastY);
215 nsCOMPtr<nsIDOMEventTarget> eventTarget;
216 aDOMEvent->GetTarget(getter_AddRefs(eventTarget));
217 if (!eventTarget)
218 return NS_OK;
219 mNode = do_QueryInterface(eventTarget);
220 if (!mNode)
221 return NS_OK;
222 PRUint32 type = 0;
223 PRBool isXul = IsXULNode(mNode, &type);
224 if (isXul) return NS_OK;
225 g_is_scrollable = PR_TRUE;
227 GetDOMWindowByNode(mNode, getter_AddRefs(mWindow));
228 if (!mWindow)
229 return NS_OK;
230 nsCOMPtr<nsIDocument> doc;
231 nsCOMPtr<nsIDOMDocument> domDoc;
232 mWindow->GetDocument(getter_AddRefs(domDoc));
233 doc = do_QueryInterface(domDoc);
234 if (!doc) return NS_OK;
235 // the only case where there could be more shells in printpreview
236 nsIPresShell *shell = doc->GetPrimaryShell();
237 NS_ENSURE_TRUE(shell, NS_ERROR_FAILURE);
238 mViewManager = shell->GetViewManager();
239 NS_ENSURE_TRUE(mViewManager, NS_ERROR_FAILURE);
240 mViewManager->GetWidget(getter_AddRefs(mWidget));
241 // Return TRUE from your signal handler to mark the event as consumed.
242 // PRBool return_val = PR_FALSE;
243 if (g_is_scrollable) {
244 aDOMEvent->StopPropagation();
245 aDOMEvent->PreventDefault();
247 return NS_OK;
250 NS_IMETHODIMP
251 nsWidgetUtils::MouseUp(nsIDOMEvent* aDOMEvent)
253 nsCOMPtr <nsIDOMMouseEvent> mouseEvent;
254 mouseEvent = do_QueryInterface(aDOMEvent);
255 if (!mouseEvent)
256 return NS_OK;
257 // Return TRUE from your signal handler to mark the event as consumed.
258 g_lastX = MIN_INT;
259 g_lastY = MIN_INT;
260 g_is_scrollable = PR_FALSE;
262 return NS_OK;
265 NS_IMETHODIMP
266 nsWidgetUtils::MouseMove(nsIDOMEvent* aDOMEvent)
268 if (!g_is_scrollable) return NS_OK;
270 nsCOMPtr<nsIDOMMouseEvent> mouseEvent = do_QueryInterface(aDOMEvent);
271 if (!mouseEvent)
272 return NS_OK;
273 int x, y;
274 ((nsIDOMMouseEvent*)mouseEvent)->GetScreenX(&x);
275 ((nsIDOMMouseEvent*)mouseEvent)->GetScreenY(&y);
277 int dx = g_lastX - x;
278 int dy = g_lastY - y;
279 if(g_lastX == MIN_INT || g_lastY == MIN_INT)
280 return NS_OK;
283 mWindow->ScrollBy(dx, dy);
284 g_lastX = x;
285 g_lastY = y;
288 nsEventStatus statusX;
289 nsMouseScrollEvent scrollEventX(PR_TRUE, NS_MOUSE_SCROLL, mWidget);
290 scrollEventX.delta = dx;
291 scrollEventX.scrollFlags = nsMouseScrollEvent::kIsHorizontal | nsMouseScrollEvent::kIsPixels;
292 mViewManager->DispatchEvent(&scrollEventX, &statusX);
293 if(statusX != nsEventStatus_eIgnore ){
294 g_lastX = x;
297 nsEventStatus statusY;
298 nsMouseScrollEvent scrollEventY(PR_TRUE, NS_MOUSE_SCROLL, mWidget);
299 scrollEventY.delta = dy;
300 scrollEventY.scrollFlags = nsMouseScrollEvent::kIsVertical | nsMouseScrollEvent::kIsPixels;
301 mViewManager->DispatchEvent(&scrollEventY, &statusY);
302 if(statusY != nsEventStatus_eIgnore ){
303 g_lastY = y;
306 return NS_OK;
309 NS_IMETHODIMP
310 nsWidgetUtils::MouseClick(nsIDOMEvent* aDOMEvent)
312 return NS_OK;
315 NS_IMETHODIMP
316 nsWidgetUtils::MouseDblClick(nsIDOMEvent* aDOMEvent)
318 return NS_OK;
321 NS_IMETHODIMP
322 nsWidgetUtils::MouseOver(nsIDOMEvent* aDOMEvent)
324 return NS_OK;
327 NS_IMETHODIMP
328 nsWidgetUtils::MouseOut(nsIDOMEvent* aDOMEvent)
330 return NS_OK;
334 NS_IMETHODIMP
335 nsWidgetUtils::DragMove(nsIDOMEvent* aDOMEvent)
337 return NS_OK;
340 void
341 nsWidgetUtils::GetChromeEventHandler(nsIDOMWindow *aDOMWin,
342 nsIDOMEventTarget **aChromeTarget)
344 nsCOMPtr<nsPIDOMWindow> privateDOMWindow(do_QueryInterface(aDOMWin));
345 nsPIDOMEventTarget* chromeEventHandler = nsnull;
346 if (privateDOMWindow) {
347 chromeEventHandler = privateDOMWindow->GetChromeEventHandler();
350 nsCOMPtr<nsIDOMEventTarget> target(do_QueryInterface(chromeEventHandler));
352 *aChromeTarget = target;
353 NS_IF_ADDREF(*aChromeTarget);
356 void
357 nsWidgetUtils::RemoveWindowListeners(nsIDOMWindow *aDOMWin)
359 nsresult rv;
360 nsCOMPtr<nsIDOMEventTarget> chromeEventHandler;
361 GetChromeEventHandler(aDOMWin, getter_AddRefs(chromeEventHandler));
362 if (!chromeEventHandler) {
363 return;
366 // Use capturing, otherwise the normal find next will get activated when ours should
367 nsCOMPtr<nsPIDOMEventTarget> piTarget(do_QueryInterface(chromeEventHandler));
369 // Remove DOM Text listener for IME text events
370 rv = piTarget->RemoveEventListenerByIID(static_cast<nsIDOMMouseListener*>(this),
371 NS_GET_IID(nsIDOMMouseListener));
372 if (NS_FAILED(rv)) {
373 NS_WARNING("Failed to add Mouse Motion listener\n");
374 return;
376 rv = piTarget->RemoveEventListenerByIID(static_cast<nsIDOMMouseMotionListener*>(this),
377 NS_GET_IID(nsIDOMMouseMotionListener));
378 if (NS_FAILED(rv)) {
379 NS_WARNING("Failed to add Mouse Motion listener\n");
380 return;
384 void
385 nsWidgetUtils::AttachWindowListeners(nsIDOMWindow *aDOMWin)
387 nsresult rv;
388 nsCOMPtr<nsIDOMEventTarget> chromeEventHandler;
389 GetChromeEventHandler(aDOMWin, getter_AddRefs(chromeEventHandler));
390 if (!chromeEventHandler) {
391 return;
394 // Use capturing, otherwise the normal find next will get activated when ours should
395 nsCOMPtr<nsPIDOMEventTarget> piTarget(do_QueryInterface(chromeEventHandler));
397 // Attach menu listeners, this will help us ignore keystrokes meant for menus
398 rv = piTarget->AddEventListenerByIID(static_cast<nsIDOMMouseListener*>(this),
399 NS_GET_IID(nsIDOMMouseListener));
400 if (NS_FAILED(rv)) {
401 NS_WARNING("Failed to add Mouse Motion listener\n");
402 return;
404 rv = piTarget->AddEventListenerByIID(static_cast<nsIDOMMouseMotionListener*>(this),
405 NS_GET_IID(nsIDOMMouseMotionListener));
406 if (NS_FAILED(rv)) {
407 NS_WARNING("Failed to add Mouse Motion listener\n");
408 return;
412 nsWidgetUtils::~nsWidgetUtils()
416 NS_IMPL_ISUPPORTS4(nsWidgetUtils, nsIObserver, nsIDOMMouseMotionListener, nsIDOMMouseListener, nsISupportsWeakReference)
418 NS_IMETHODIMP
419 nsWidgetUtils::Observe(nsISupports *aSubject, const char *aTopic, const PRUnichar *aData)
421 nsresult rv;
422 if (!strcmp(aTopic,"domwindowopened"))
424 nsCOMPtr<nsIDOMWindow> chromeWindow = do_QueryInterface(aSubject);
425 if (chromeWindow)
426 AttachWindowListeners(chromeWindow);
427 return NS_OK;
430 if (!strcmp(aTopic,"domwindowclosed"))
432 nsCOMPtr<nsIDOMWindow> chromeWindow = do_QueryInterface(aSubject);
433 RemoveWindowListeners(chromeWindow);
434 return NS_OK;
437 return NS_OK;
440 //------------------------------------------------------------------------------
441 // XPCOM REGISTRATION BELOW
442 //------------------------------------------------------------------------------
444 #define WidgetUtils_CID \
445 { 0x0ced17b6, 0x96ed, 0x4030, \
446 {0xa1, 0x34, 0x77, 0xcb, 0x66, 0x10, 0xa8, 0xf6} }
448 #define WidgetUtils_ContractID "@mozilla.org/extensions/widgetutils;1"
450 static NS_METHOD WidgetUtilsRegistration(nsIComponentManager *aCompMgr,
451 nsIFile *aPath,
452 const char *registryLocation,
453 const char *componentType,
454 const nsModuleComponentInfo *info)
456 nsresult rv;
458 nsCOMPtr<nsIServiceManager> servman = do_QueryInterface((nsISupports*)aCompMgr, &rv);
459 if (NS_FAILED(rv))
460 return rv;
462 nsCOMPtr<nsICategoryManager> catman;
463 servman->GetServiceByContractID(NS_CATEGORYMANAGER_CONTRACTID,
464 NS_GET_IID(nsICategoryManager),
465 getter_AddRefs(catman));
467 if (NS_FAILED(rv))
468 return rv;
470 char* previous = nsnull;
471 rv = catman->AddCategoryEntry("app-startup",
472 "WidgetUtils",
473 WidgetUtils_ContractID,
474 PR_TRUE,
475 PR_TRUE,
476 &previous);
477 if (previous)
478 nsMemory::Free(previous);
480 return rv;
483 static NS_METHOD WidgetUtilsUnregistration(nsIComponentManager *aCompMgr,
484 nsIFile *aPath,
485 const char *registryLocation,
486 const nsModuleComponentInfo *info)
488 nsresult rv;
490 nsCOMPtr<nsIServiceManager> servman = do_QueryInterface((nsISupports*)aCompMgr, &rv);
491 if (NS_FAILED(rv))
492 return rv;
494 nsCOMPtr<nsICategoryManager> catman;
495 servman->GetServiceByContractID(NS_CATEGORYMANAGER_CONTRACTID,
496 NS_GET_IID(nsICategoryManager),
497 getter_AddRefs(catman));
499 if (NS_FAILED(rv))
500 return rv;
502 rv = catman->DeleteCategoryEntry("app-startup",
503 "WidgetUtils",
504 PR_TRUE);
506 return rv;
509 NS_GENERIC_FACTORY_CONSTRUCTOR(nsWidgetUtils)
511 static const nsModuleComponentInfo components[] =
513 { "nsWidgetUtilsService",
514 WidgetUtils_CID,
515 WidgetUtils_ContractID,
516 nsWidgetUtilsConstructor,
517 WidgetUtilsRegistration,
518 WidgetUtilsUnregistration
522 NS_IMPL_NSGETMODULE(nsWidgetUtilsModule, components)