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