michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "ActiveElementManager.h" michael@0: #include "mozilla/EventStates.h" michael@0: #include "mozilla/Preferences.h" michael@0: #include "mozilla/Services.h" michael@0: #include "inIDOMUtils.h" michael@0: #include "nsIDOMDocument.h" michael@0: #include "nsIDOMElement.h" michael@0: #include "nsIDOMEventTarget.h" michael@0: #include "base/message_loop.h" michael@0: #include "base/task.h" michael@0: michael@0: namespace mozilla { michael@0: namespace layers { michael@0: michael@0: static int32_t sActivationDelayMs = 100; michael@0: static bool sActivationDelayMsSet = false; michael@0: michael@0: ActiveElementManager::ActiveElementManager() michael@0: : mDomUtils(services::GetInDOMUtils()), michael@0: mCanBePan(false), michael@0: mCanBePanSet(false), michael@0: mSetActiveTask(nullptr) michael@0: { michael@0: if (!sActivationDelayMsSet) { michael@0: Preferences::AddIntVarCache(&sActivationDelayMs, michael@0: "ui.touch_activation.delay_ms", michael@0: sActivationDelayMs); michael@0: sActivationDelayMsSet = true; michael@0: } michael@0: } michael@0: michael@0: ActiveElementManager::~ActiveElementManager() {} michael@0: michael@0: void michael@0: ActiveElementManager::SetTargetElement(nsIDOMEventTarget* aTarget) michael@0: { michael@0: if (mTarget) { michael@0: // Multiple fingers on screen (since HandleTouchEnd clears mTarget). michael@0: CancelTask(); michael@0: ResetActive(); michael@0: mTarget = nullptr; michael@0: return; michael@0: } michael@0: michael@0: mTarget = do_QueryInterface(aTarget); michael@0: TriggerElementActivation(); michael@0: } michael@0: michael@0: void michael@0: ActiveElementManager::HandleTouchStart(bool aCanBePan) michael@0: { michael@0: mCanBePan = aCanBePan; michael@0: mCanBePanSet = true; michael@0: TriggerElementActivation(); michael@0: } michael@0: michael@0: void michael@0: ActiveElementManager::TriggerElementActivation() michael@0: { michael@0: // Both HandleTouchStart() and SetTargetElement() call this. They can be michael@0: // called in either order. One will set mCanBePanSet, and the other, mTarget. michael@0: // We want to actually trigger the activation once both are set. michael@0: if (!(mTarget && mCanBePanSet)) { michael@0: return; michael@0: } michael@0: michael@0: // If the touch cannot be a pan, make mTarget :active right away. michael@0: // Otherwise, wait a bit to see if the user will pan or not. michael@0: if (!mCanBePan) { michael@0: SetActive(mTarget); michael@0: } else { michael@0: mSetActiveTask = NewRunnableMethod( michael@0: this, &ActiveElementManager::SetActiveTask, mTarget); michael@0: MessageLoop::current()->PostDelayedTask( michael@0: FROM_HERE, mSetActiveTask, sActivationDelayMs); michael@0: } michael@0: } michael@0: michael@0: void michael@0: ActiveElementManager::HandlePanStart() michael@0: { michael@0: // The user started to pan, so we don't want mTarget to be :active. michael@0: // Make it not :active, and clear any pending task to make it :active. michael@0: CancelTask(); michael@0: ResetActive(); michael@0: } michael@0: michael@0: void michael@0: ActiveElementManager::HandleTouchEnd(bool aWasClick) michael@0: { michael@0: // If the touch was a click, make mTarget :active right away. michael@0: // nsEventStateManager will reset the active element when processing michael@0: // the mouse-down event generated by the click. michael@0: CancelTask(); michael@0: if (aWasClick) { michael@0: SetActive(mTarget); michael@0: } michael@0: michael@0: // Clear mTarget for next touch. michael@0: mTarget = nullptr; michael@0: } michael@0: michael@0: void michael@0: ActiveElementManager::SetActive(nsIDOMElement* aTarget) michael@0: { michael@0: if (mDomUtils) { michael@0: mDomUtils->SetContentState(aTarget, NS_EVENT_STATE_ACTIVE.GetInternalValue());; michael@0: } michael@0: } michael@0: michael@0: void michael@0: ActiveElementManager::ResetActive() michael@0: { michael@0: // Clear the :active flag from mTarget by setting it on the document root. michael@0: if (mTarget) { michael@0: nsCOMPtr doc; michael@0: mTarget->GetOwnerDocument(getter_AddRefs(doc)); michael@0: if (doc) { michael@0: nsCOMPtr root; michael@0: doc->GetDocumentElement(getter_AddRefs(root)); michael@0: if (root) { michael@0: SetActive(root); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: ActiveElementManager::SetActiveTask(nsIDOMElement* aTarget) michael@0: { michael@0: // This gets called from mSetActiveTask's Run() method. The message loop michael@0: // deletes the task right after running it, so we need to null out michael@0: // mSetActiveTask to make sure we're not left with a dangling pointer. michael@0: mSetActiveTask = nullptr; michael@0: SetActive(aTarget); michael@0: } michael@0: michael@0: void michael@0: ActiveElementManager::CancelTask() michael@0: { michael@0: if (mSetActiveTask) { michael@0: mSetActiveTask->Cancel(); michael@0: mSetActiveTask = nullptr; michael@0: } michael@0: } michael@0: michael@0: } michael@0: }