Bug 976605 - Introduce ActiveElementManager. r=kats
☠☠ backed out by d6254d12592d ☠ ☠
authorBotond Ballo <botond@mozilla.com>
Wed, 09 Apr 2014 16:21:27 -0400
changeset 197158 55843a5551ecb32484255979d95c3ef55cb2978e
parent 197157 27ceedad1d1c0f06f8df7e6cc303b08cc88f1bf4
child 197159 5fb0bcdb92d5abf730038b9f63a9541f6bc854bb
push id3624
push userasasaki@mozilla.com
push dateMon, 09 Jun 2014 21:49:01 +0000
treeherdermozilla-beta@b1a5da15899a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskats
bugs976605
milestone31.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 976605 - Introduce ActiveElementManager. r=kats
widget/xpwidgets/ActiveElementManager.cpp
widget/xpwidgets/ActiveElementManager.h
widget/xpwidgets/moz.build
new file mode 100644
--- /dev/null
+++ b/widget/xpwidgets/ActiveElementManager.cpp
@@ -0,0 +1,151 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ActiveElementManager.h"
+#include "mozilla/EventStates.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/Services.h"
+#include "inIDOMUtils.h"
+#include "nsIDOMDocument.h"
+#include "nsIDOMElement.h"
+#include "nsIDOMEventTarget.h"
+#include "base/message_loop.h"
+#include "base/task.h"
+
+namespace mozilla {
+namespace widget {
+
+static int32_t sActivationDelayMs = 100;
+static bool sActivationDelayMsSet = false;
+
+ActiveElementManager::ActiveElementManager()
+  : mDomUtils(services::GetInDOMUtils()),
+    mCanBePan(false),
+    mCanBePanSet(false),
+    mSetActiveTask(nullptr)
+{
+  if (!sActivationDelayMsSet) {
+    Preferences::AddIntVarCache(&sActivationDelayMs,
+                                "ui.touch_activation.delay_ms",
+                                sActivationDelayMs);
+    sActivationDelayMsSet = true;
+  }
+}
+
+ActiveElementManager::~ActiveElementManager() {}
+
+void
+ActiveElementManager::SetTargetElement(nsIDOMEventTarget* aTarget)
+{
+  if (mTarget) {
+    // Multiple fingers on screen (since HandleTouchEnd clears mTarget).
+    ResetActive();
+    return;
+  }
+
+  mTarget = do_QueryInterface(aTarget);
+  TriggerElementActivation();
+}
+
+void
+ActiveElementManager::HandleTouchStart(bool aCanBePan)
+{
+  mCanBePan = aCanBePan;
+  mCanBePanSet = true;
+  TriggerElementActivation();
+}
+
+void
+ActiveElementManager::TriggerElementActivation()
+{
+  // Both HandleTouchStart() and SetTargetElement() call this. They can be
+  // called in either order. One will set mCanBePanSet, and the other, mTarget.
+  // We want to actually trigger the activation once both are set.
+  if (!(mTarget && mCanBePanSet)) {
+    return;
+  }
+
+  // If the touch cannot be a pan, make mTarget :active right away.
+  // Otherwise, wait a bit to see if the user will pan or not.
+  if (!mCanBePan) {
+    SetActive(mTarget);
+  } else {
+    mSetActiveTask = NewRunnableMethod(
+        this, &ActiveElementManager::SetActiveTask, mTarget);
+    MessageLoop::current()->PostDelayedTask(
+        FROM_HERE, mSetActiveTask, sActivationDelayMs);
+  }
+}
+
+void
+ActiveElementManager::HandlePanStart()
+{
+  // The user started to pan, so we don't want mTarget to be :active.
+  // Make it not :active, and clear any pending task to make it :active.
+  CancelTask();
+  ResetActive();
+}
+
+void
+ActiveElementManager::HandleTouchEnd(bool aWasClick)
+{
+  // If the touch was a click, make mTarget :active right away.
+  // nsEventStateManager will reset the active element when processing
+  // the mouse-down event generated by the click.
+  CancelTask();
+  if (aWasClick) {
+    SetActive(mTarget);
+  }
+
+  // Clear mTarget for next touch.
+  mTarget = nullptr;
+}
+
+void
+ActiveElementManager::SetActive(nsIDOMElement* aTarget)
+{
+  if (mDomUtils) {
+    mDomUtils->SetContentState(aTarget, NS_EVENT_STATE_ACTIVE.GetInternalValue());;
+  }
+}
+
+void
+ActiveElementManager::ResetActive()
+{
+  // Clear the :active flag from mTarget by setting it on the document root.
+  if (mTarget) {
+    nsCOMPtr<nsIDOMDocument> doc;
+    mTarget->GetOwnerDocument(getter_AddRefs(doc));
+    if (doc) {
+      nsCOMPtr<nsIDOMElement> root;
+      doc->GetDocumentElement(getter_AddRefs(root));
+      if (root) {
+        SetActive(root);
+      }
+    }
+  }
+}
+
+void
+ActiveElementManager::SetActiveTask(nsIDOMElement* aTarget)
+{
+  // This gets called from mSetActiveTask's Run() method. The message loop
+  // deletes the task right after running it, so we need to null out
+  // mSetActiveTask to make sure we're not left with a dangling pointer.
+  mSetActiveTask = nullptr;
+  SetActive(aTarget);
+}
+
+void
+ActiveElementManager::CancelTask()
+{
+  if (mSetActiveTask) {
+    mSetActiveTask->Cancel();
+    mSetActiveTask = nullptr;
+  }
+}
+
+}
+}
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/widget/xpwidgets/ActiveElementManager.h
@@ -0,0 +1,85 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef __mozilla_widget_ActiveElementManager_h__
+#define __mozilla_widget_ActiveElementManager_h__
+
+#include "nsCOMPtr.h"
+#include "nsISupportsImpl.h"
+
+class inIDOMUtils;
+class nsIDOMEventTarget;
+class nsIDOMElement;
+class CancelableTask;
+
+namespace mozilla {
+namespace widget {
+
+/**
+ * Manages setting and clearing the ':active' CSS pseudostate in the presence
+ * of touch input.
+ */
+class ActiveElementManager {
+public:
+  NS_INLINE_DECL_REFCOUNTING(ActiveElementManager)
+
+  ActiveElementManager();
+  ~ActiveElementManager();
+
+  /**
+   * Specify the target of a touch. Typically this should be called right
+   * before HandleTouchStart(), but we give callers the flexibility to specify
+   * the target later if they don't know it at the time they call
+   * HandleTouchStart().
+   * |aTarget| may be nullptr.
+   */
+  void SetTargetElement(nsIDOMEventTarget* aTarget);
+  /**
+   * Handle a touch-start event.
+   * @param aCanBePan whether the touch can be a pan
+   */
+  void HandleTouchStart(bool aCanBePan);
+  /**
+   * Handle the start of panning.
+   */
+  void HandlePanStart();
+  /**
+   * Handle a touch-end or touch-cancel event.
+   * @param aWasClick whether the touch was a click
+   */
+  void HandleTouchEnd(bool aWasClick);
+private:
+  nsCOMPtr<inIDOMUtils> mDomUtils;
+  /**
+   * The target of the first touch point in the current touch block.
+   */
+  nsCOMPtr<nsIDOMElement> mTarget;
+  /**
+   * Whether the current touch block can be a pan. Set in HandleTouchStart().
+   */
+  bool mCanBePan;
+  /**
+   * Whether mCanBePan has been set for the current touch block.
+   * We need to keep track of this to allow HandleTouchStart() and
+   * SetTargetElement() to be called in either order.
+   */
+  bool mCanBePanSet;
+  /**
+   * A task for calling SetActive() after a timeout.
+   */
+  CancelableTask* mSetActiveTask;
+
+  // Helpers
+  void TriggerElementActivation();
+  void SetActive(nsIDOMElement* aTarget);
+  void ResetActive();
+  void SetActiveTask(nsIDOMElement* aTarget);
+  void CancelTask();
+};
+
+}
+}
+
+#endif /*__mozilla_widget_ActiveElementManager_h__ */
--- a/widget/xpwidgets/moz.build
+++ b/widget/xpwidgets/moz.build
@@ -1,23 +1,25 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 EXPORTS += [
+    'ActiveElementManager.h',
     'APZCCallbackHelper.h',
     'ContentHelper.h',
     'GfxDriverInfo.h',
     'GfxInfoBase.h',
     'GfxInfoCollector.h',
 ]
 
 UNIFIED_SOURCES += [
+    'ActiveElementManager.cpp',
     'APZCCallbackHelper.cpp',
     'ContentHelper.cpp',
     'GfxDriverInfo.cpp',
     'GfxInfoBase.cpp',
     'GfxInfoCollector.cpp',
     'GfxInfoWebGL.cpp',
     'InputData.cpp',
     'nsBaseAppShell.cpp',