Bug 1083818 - Bypass the 10ms delay if the element being activated won't visually change. r=botond
authorKartikaya Gupta <kgupta@mozilla.com>
Mon, 15 Dec 2014 13:59:02 -0500
changeset 219832 9aabe0ab9efc606f30b83ed83e89bbc3057858eb
parent 219831 b8c76c348a694fbdb3d5b9f0bdf731370df3323a
child 219833 662d1eac297a7fe61a2399ae3f997a517bb220be
push id10419
push usercbook@mozilla.com
push dateTue, 16 Dec 2014 12:45:27 +0000
treeherderfx-team@ec87657146eb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbotond
bugs1083818
milestone37.0a1
Bug 1083818 - Bypass the 10ms delay if the element being activated won't visually change. r=botond
dom/ipc/TabChild.cpp
gfx/layers/apz/util/ActiveElementManager.cpp
gfx/layers/apz/util/ActiveElementManager.h
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -2102,16 +2102,25 @@ TabChild::RecvHandleSingleTap(const CSSP
     return true;
   }
 
   if (mTouchEndCancelled) {
     return true;
   }
 
   LayoutDevicePoint currentPoint = APZCCallbackHelper::ApplyCallbackTransform(aPoint, aGuid) * mWidget->GetDefaultScale();;
+  if (!mActiveElementManager->ActiveElementUsesStyle()) {
+    // If the active element isn't visually affected by the :active style, we
+    // have no need to wait the extra sActiveDurationMs to make the activation
+    // visually obvious to the user.
+    FireSingleTapEvent(currentPoint);
+    return true;
+  }
+
+  TABC_LOG("Active element uses style, scheduling timer for click event\n");
   nsCOMPtr<nsITimer> timer = do_CreateInstance(NS_TIMER_CONTRACTID);
   nsRefPtr<DelayedFireSingleTapEvent> callback =
     new DelayedFireSingleTapEvent(this, currentPoint, timer);
   nsresult rv = timer->InitWithCallback(callback,
                                         sActiveDurationMs,
                                         nsITimer::TYPE_ONE_SHOT);
   if (NS_FAILED(rv)) {
     // Make |callback| not hold the timer, so they will both be destructed when
--- a/gfx/layers/apz/util/ActiveElementManager.cpp
+++ b/gfx/layers/apz/util/ActiveElementManager.cpp
@@ -1,37 +1,37 @@
 /* -*- 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/EventStateManager.h"
 #include "mozilla/EventStates.h"
 #include "mozilla/Preferences.h"
-#include "mozilla/Services.h"
-#include "inIDOMUtils.h"
 #include "base/message_loop.h"
 #include "base/task.h"
 #include "mozilla/dom/Element.h"
 #include "nsIDocument.h"
+#include "nsStyleSet.h"
 
 #define AEM_LOG(...)
 // #define AEM_LOG(...) printf_stderr("AEM: " __VA_ARGS__)
 
 namespace mozilla {
 namespace layers {
 
 static int32_t sActivationDelayMs = 100;
 static bool sActivationDelayMsSet = false;
 
 ActiveElementManager::ActiveElementManager()
-  : mDomUtils(services::GetInDOMUtils()),
-    mCanBePan(false),
+  : mCanBePan(false),
     mCanBePanSet(false),
-    mSetActiveTask(nullptr)
+    mSetActiveTask(nullptr),
+    mActiveElementUsesStyle(false)
 {
   if (!sActivationDelayMsSet) {
     Preferences::AddIntVarCache(&sActivationDelayMs,
                                 "ui.touch_activation.delay_ms",
                                 sActivationDelayMs);
     sActivationDelayMsSet = true;
   }
 }
@@ -124,23 +124,61 @@ ActiveElementManager::HandleTouchEnd(boo
     // so we set the element active right away. Now it turns out the
     // action was not a click so we need to reset the active element.
     ResetActive();
   }
 
   ResetTouchBlockState();
 }
 
+bool
+ActiveElementManager::ActiveElementUsesStyle() const
+{
+  return mActiveElementUsesStyle;
+}
+
+static nsPresContext*
+GetPresContextFor(nsIContent* aContent)
+{
+  if (!aContent) {
+    return nullptr;
+  }
+  nsIPresShell* shell = aContent->OwnerDoc()->GetShell();
+  if (!shell) {
+    return nullptr;
+  }
+  return shell->GetPresContext();
+}
+
+static bool
+ElementHasActiveStyle(dom::Element* aElement)
+{
+  nsPresContext* pc = GetPresContextFor(aElement);
+  if (!pc) {
+    return false;
+  }
+  nsStyleSet* styleSet = pc->StyleSet();
+  for (dom::Element* e = aElement; e; e = e->GetParentElement()) {
+    if (styleSet->HasStateDependentStyle(pc, e, NS_EVENT_STATE_ACTIVE)) {
+      AEM_LOG("Element %p's style is dependent on the active state\n", e);
+      return true;
+    }
+  }
+  AEM_LOG("Element %p doesn't use active styles\n", aElement);
+  return false;
+}
+
 void
 ActiveElementManager::SetActive(dom::Element* aTarget)
 {
   AEM_LOG("Setting active %p\n", aTarget);
-  if (mDomUtils) {
-    nsCOMPtr<nsIDOMElement> target = do_QueryInterface(aTarget);
-    mDomUtils->SetContentState(target, NS_EVENT_STATE_ACTIVE.GetInternalValue());
+
+  if (nsPresContext* pc = GetPresContextFor(aTarget)) {
+    pc->EventStateManager()->SetContentState(aTarget, NS_EVENT_STATE_ACTIVE);
+    mActiveElementUsesStyle = ElementHasActiveStyle(aTarget);
   }
 }
 
 void
 ActiveElementManager::ResetActive()
 {
   AEM_LOG("Resetting active from %p\n", mTarget.get());
 
--- a/gfx/layers/apz/util/ActiveElementManager.h
+++ b/gfx/layers/apz/util/ActiveElementManager.h
@@ -48,18 +48,23 @@ public:
    * 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);
+  /**
+   * @return true iff the currently active element (or one of its ancestors)
+   * actually had a style for the :active pseudo-class. The currently active
+   * element is the root element if no other elements are active.
+   */
+  bool ActiveElementUsesStyle() const;
 private:
-  nsCOMPtr<inIDOMUtils> mDomUtils;
   /**
    * The target of the first touch point in the current touch block.
    */
   nsCOMPtr<dom::Element> mTarget;
   /**
    * Whether the current touch block can be a pan. Set in HandleTouchStart().
    */
   bool mCanBePan;
@@ -68,16 +73,20 @@ private:
    * 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;
+  /**
+   * See ActiveElementUsesStyle() documentation.
+   */
+  bool mActiveElementUsesStyle;
 
   // Helpers
   void TriggerElementActivation();
   void SetActive(dom::Element* aTarget);
   void ResetActive();
   void ResetTouchBlockState();
   void SetActiveTask(dom::Element* aTarget);
   void CancelTask();