Merge mozilla-central into cedar
authorEhsan Akhgari <ehsan@mozilla.com>
Sat, 09 Apr 2011 23:26:53 -0400
changeset 67791 56d25b561e19d465520d6d369d80b66b194df30c
parent 67790 f15bfa1cb14b56e067e1cfcf2f2eb75a6b27691d (current diff)
parent 67754 fe3f7889918b2c2a7a37e75d78d6fd436d3949a0 (diff)
child 67792 57af1004637a729e035380ea1afb54236cbc7eff
push id1
push userroot
push dateTue, 26 Apr 2011 22:38:44 +0000
treeherdermozilla-beta@bfdb6e623a36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone2.2a1pre
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
Merge mozilla-central into cedar
configure.in
--- a/configure.in
+++ b/configure.in
@@ -6209,16 +6209,24 @@ dnl ====================================
 dnl = Universalchardet
 dnl ========================================================
 MOZ_ARG_DISABLE_BOOL(universalchardet,
 [  --disable-universalchardet
                           Disable universal encoding detection],
   MOZ_UNIVERSALCHARDET=,
   MOZ_UNIVERSALCHARDET=1 )
 
+if test -n "${JAVA_BIN_PATH}"; then
+  dnl Look for javac and jar in the specified path.
+  JAVA_PATH="$JAVA_BIN_PATH"
+else
+  dnl No path specified, so look for javac and jar in $JAVA_HOME & $PATH.
+  JAVA_PATH="$JAVA_HOME/bin:$PATH"
+fi
+
 MOZ_PATH_PROG(JAVA, java, :, [$JAVA_PATH])
 MOZ_PATH_PROG(JAVAC, javac, :, [$JAVA_PATH])
 MOZ_PATH_PROG(JAR, jar, :, [$JAVA_PATH])
 
 if test -n "${JAVA_BIN_PATH}" -o "$OS_TARGET" = Android; then
   if test -z "$JAVA" -o "$JAVA" = ":" -o -z "$JAVAC" -o "$JAVAC" = ":" -o -z "$JAR" -o "$JAR" = ":"; then
     AC_MSG_ERROR([The programs java, javac and jar were not found.  Set \$JAVA_HOME to your java sdk directory or use --with-java-bin-path={java-bin-dir}])
   fi
--- a/gfx/layers/Layers.h
+++ b/gfx/layers/Layers.h
@@ -758,22 +758,16 @@ public:
 
   /**
    * Dynamic downcast to a Thebes layer. Returns null if this is not
    * a ThebesLayer.
    */
   virtual ThebesLayer* AsThebesLayer() { return nsnull; }
 
   /**
-   * Dynamic cast to a ContainerLayer. Returns null if this is not
-   * a ContainerLayer.
-   */
-  virtual ContainerLayer* AsContainerLayer() { return nsnull; }
-
-  /**
    * Dynamic cast to a ShadowLayer.  Return null if this is not a
    * ShadowLayer.  Can be used anytime.
    */
   virtual ShadowLayer* AsShadowLayer() { return nsnull; }
 
   // These getters can be used anytime.  They return the effective
   // values that should be used when drawing this layer to screen,
   // accounting for this layer possibly being a shadow.
@@ -1030,18 +1024,16 @@ public:
    */
   void SetFrameMetrics(const FrameMetrics& aFrameMetrics)
   {
     mFrameMetrics = aFrameMetrics;
   }
 
   // These getters can be used anytime.
 
-  virtual ContainerLayer* AsContainerLayer() { return this; }
-
   virtual Layer* GetFirstChild() { return mFirstChild; }
   virtual Layer* GetLastChild() { return mLastChild; }
   const FrameMetrics& GetFrameMetrics() { return mFrameMetrics; }
 
   MOZ_LAYER_DECL_NAME("ContainerLayer", TYPE_CONTAINER)
 
   /**
    * ContainerLayer backends need to override ComputeEffectiveTransforms
--- a/layout/base/FrameLayerBuilder.h
+++ b/layout/base/FrameLayerBuilder.h
@@ -54,19 +54,17 @@ class nsRootPresContext;
 namespace mozilla {
 
 enum LayerState {
   LAYER_NONE,
   LAYER_INACTIVE,
   LAYER_ACTIVE,
   // Force an active layer even if it causes incorrect rendering, e.g.
   // when the layer has rounded rect clips.
-  LAYER_ACTIVE_FORCE,
-  // Special layer that is metadata only.
-  LAYER_ACTIVE_EMPTY
+  LAYER_ACTIVE_FORCE
 };
 
 /**
  * The FrameLayerBuilder belongs to an nsDisplayListBuilder and is
  * responsible for converting display lists into layer trees.
  * 
  * The most important API in this class is BuildContainerLayerFor. This
  * method takes a display list as input and constructs a ContainerLayer
--- a/layout/base/nsDisplayItemTypes.h
+++ b/layout/base/nsDisplayItemTypes.h
@@ -79,17 +79,16 @@ enum Type {
   TYPE_PAGE_SEQUENCE,
   TYPE_PLUGIN,
   TYPE_PLUGIN_READBACK,
   TYPE_PRINT_PREVIEW_BACKGROUND,
   TYPE_PRINT_PLUGIN,
   TYPE_REMOTE,
   TYPE_REMOTE_SHADOW,
   TYPE_SCROLL_LAYER,
-  TYPE_SCROLL_INFO_LAYER,
   TYPE_SELECTION_OVERLAY,
   TYPE_SOLID_COLOR,
   TYPE_TABLE_CELL_BACKGROUND,
   TYPE_TABLE_CELL_SELECTION,
   TYPE_TABLE_ROW_BACKGROUND,
   TYPE_TABLE_ROW_GROUP_BACKGROUND,
   TYPE_TABLE_BORDER_BACKGROUND,
   TYPE_TEXT,
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -1831,35 +1831,16 @@ nsDisplayScrollLayer::ComputeVisibility(
 
 #ifdef NS_BUILD_REFCNT_LOGGING
 nsDisplayScrollLayer::~nsDisplayScrollLayer()
 {
   MOZ_COUNT_DTOR(nsDisplayScrollLayer);
 }
 #endif
 
-nsDisplayScrollInfoLayer::nsDisplayScrollInfoLayer(
-  nsDisplayListBuilder* aBuilder,
-  nsDisplayList* aList,
-  nsIFrame* aForFrame,
-  nsIFrame* aViewportFrame)
-  : nsDisplayScrollLayer(aBuilder, aList, aForFrame, aViewportFrame)
-{
-#ifdef NS_BUILD_REFCNT_LOGGING
-  MOZ_COUNT_CTOR(nsDisplayScrollInfoLayer);
-#endif
-}
-
-#ifdef NS_BUILD_REFCNT_LOGGING
-nsDisplayScrollInfoLayer::~nsDisplayScrollInfoLayer()
-{
-  MOZ_COUNT_DTOR(nsDisplayScrollInfoLayer);
-}
-#endif
-
 nsDisplayClip::nsDisplayClip(nsDisplayListBuilder* aBuilder,
                              nsIFrame* aFrame, nsDisplayItem* aItem,
                              const nsRect& aRect)
    : nsDisplayWrapList(aBuilder, aFrame, aItem) {
   MOZ_COUNT_CTOR(nsDisplayClip);
   mClip = SnapBounds(aBuilder->IsSnappingEnabled() && !aBuilder->IsInTransform(),
                      aBuilder->CurrentPresContext(), aRect);
 }
--- a/layout/base/nsDisplayList.h
+++ b/layout/base/nsDisplayList.h
@@ -1832,40 +1832,16 @@ public:
     // This causes incorrect rendering for rounded clips!
     return mozilla::LAYER_ACTIVE_FORCE;
   }
 private:
   nsIFrame* mViewportFrame;
 };
 
 /**
- * Like nsDisplayScrollLayer, but only has metadata on the scroll frame. This
- * creates a layer that has no Thebes child layer, but still allows the
- * compositor process to know of the scroll frame's existence.
- */
-class nsDisplayScrollInfoLayer : public nsDisplayScrollLayer
-{
-public:
-  nsDisplayScrollInfoLayer(nsDisplayListBuilder* aBuilder, nsDisplayList* aList,
-                           nsIFrame* aForFrame, nsIFrame* aViewportFrame);
-  NS_DISPLAY_DECL_NAME("ScrollInfoLayer", TYPE_SCROLL_INFO_LAYER)
-
-#ifdef NS_BUILD_REFCNT_LOGGING
-  virtual ~nsDisplayScrollInfoLayer();
-#endif
-
-  virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder,
-                                   LayerManager* aManager)
-  {
-    return mozilla::LAYER_ACTIVE_EMPTY;
-  }
-
-};
-
-/**
  * nsDisplayClip can clip a list of items, but we take a single item
  * initially and then later merge other items into it when we merge
  * adjacent matching nsDisplayClips
  */
 class nsDisplayClip : public nsDisplayWrapList {
 public:
   /**
    * @param aFrame the frame that should be considered the underlying
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -1956,68 +1956,55 @@ nsGfxScrollFrameInner::BuildDisplayList(
   // Not all our descendants will be clipped by overflow clipping, but all
   // the ones that aren't clipped will be out of flow frames that have already
   // had dirty rects saved for them by their parent frames calling
   // MarkOutOfFlowChildrenForDisplayList, so it's safe to restrict our
   // dirty rect here.
   dirtyRect.IntersectRect(aDirtyRect, mScrollPort);
 
   // Override the dirty rectangle if the displayport has been set.
-  PRBool usingDisplayport =
-    nsLayoutUtils::GetDisplayPort(mOuter->GetContent(), &dirtyRect);
+  nsLayoutUtils::GetDisplayPort(mOuter->GetContent(), &dirtyRect);
 
   nsDisplayListCollection set;
 
   nsPresContext* presContext = mOuter->PresContext();
 
   // Since making new layers is expensive, only use nsDisplayScrollLayer
   // if the area is scrollable.
   //
   // Scroll frames can be generated with a scroll range that is 0, 0.
   // Furthermore, it is not worth the memory tradeoff to allow asynchronous
   // scrolling of small scroll frames. We use an arbitrary minimum scroll
   // range of 20 pixels to eliminate many gfx scroll frames from becoming a
   // layer.
   //
+  PRInt32 appUnitsPerDevPixel = presContext->AppUnitsPerDevPixel();
   nsRect scrollRange = GetScrollRange();
   ScrollbarStyles styles = GetScrollbarStylesFromFrame();
   mShouldBuildLayer =
      (XRE_GetProcessType() == GeckoProcessType_Content &&
      (styles.mHorizontal != NS_STYLE_OVERFLOW_HIDDEN ||
       styles.mVertical != NS_STYLE_OVERFLOW_HIDDEN) &&
-     (!mIsRoot || !mOuter->PresContext()->IsRootContentDocument()));
+     (scrollRange.width >= NSIntPixelsToAppUnits(20, appUnitsPerDevPixel) ||
+      scrollRange.height >= NSIntPixelsToAppUnits(20, appUnitsPerDevPixel))) &&
+     (!mIsRoot || !mOuter->PresContext()->IsRootContentDocument());
 
   if (ShouldBuildLayer()) {
+    // Note that using StackingContext breaks z order, so the resulting
+    // rendering can be incorrect for weird edge cases!
+
     nsDisplayList list;
-    if (usingDisplayport) {
-      // Once a displayport is set, assume that scrolling needs to be fast
-      // so create a layer with all the content inside. The compositor
-      // process will be able to scroll the content asynchronously.
-      //
-      // Note that using StackingContext breaks z order, so the resulting
-      // rendering can be incorrect for weird edge cases!
-
-      rv = mScrolledFrame->BuildDisplayListForStackingContext(
-        aBuilder, dirtyRect + mOuter->GetOffsetTo(mScrolledFrame), &list);
-
-      nsDisplayScrollLayer* layerItem = new (aBuilder) nsDisplayScrollLayer(
-        aBuilder, &list, mScrolledFrame, mOuter);
-      set.Content()->AppendNewToTop(layerItem);
-    } else {
-      // If there is no displayport set, there is no reason here to force a
-      // layer that needs a memory-expensive allocation, but the compositor
-      // process would still like to know that it exists.
-
-      nsDisplayScrollLayer* layerItem = new (aBuilder) nsDisplayScrollInfoLayer(
-        aBuilder, &list, mScrolledFrame, mOuter);
-      set.Content()->AppendNewToTop(layerItem);
-
-      rv = mOuter->BuildDisplayListForChild(aBuilder, mScrolledFrame, dirtyRect, set);
-    }
-  } else {
+    rv = mScrolledFrame->BuildDisplayListForStackingContext(
+      aBuilder, dirtyRect + mOuter->GetOffsetTo(mScrolledFrame), &list);
+
+    nsDisplayScrollLayer* layerItem = new (aBuilder) nsDisplayScrollLayer(
+      aBuilder, &list, mScrolledFrame, mOuter);
+    set.Content()->AppendNewToTop(layerItem);
+  } else
+  {
     rv = mOuter->BuildDisplayListForChild(aBuilder, mScrolledFrame, dirtyRect, set);
   }
 
   NS_ENSURE_SUCCESS(rv, rv);
   nsRect clip;
   clip = mScrollPort + aBuilder->ToReferenceFrame(mOuter);
 
   nscoord radii[8];
--- a/layout/ipc/RenderFrameParent.cpp
+++ b/layout/ipc/RenderFrameParent.cpp
@@ -134,18 +134,23 @@ FindViewForId(const ViewMap& aMap, ViewI
 {
   ViewMap::const_iterator iter = aMap.find(aId);
   return iter != aMap.end() ? iter->second : NULL;
 }
 
 static const FrameMetrics*
 GetFrameMetrics(Layer* aLayer)
 {
-  ContainerLayer* container = aLayer->AsContainerLayer();
-  return container ? &container->GetFrameMetrics() : NULL;
+  // Children are not container layers, so they don't have frame metrics. Give
+  // them a blank metric.
+  if (!aLayer->GetFirstChild())
+    return NULL;
+
+  ContainerLayer* container = static_cast<ContainerLayer*>(aLayer);
+  return &container->GetFrameMetrics();
 }
 
 static nsIntPoint
 GetRootFrameOffset(nsIFrame* aContainerFrame, nsDisplayListBuilder* aBuilder)
 {
   nscoord auPerDevPixel = aContainerFrame->PresContext()->AppUnitsPerDevPixel();
 
   // Offset to the content rect in case we have borders or padding
@@ -336,19 +341,20 @@ IsTempLayerManager(LayerManager* aManage
 // aXScale and aYScale are used to calculate any values that need to be in
 // chrome-document CSS pixels and aren't part of the rendering loop, such as
 // the initial scroll offset for a new view.
 static void
 BuildViewMap(ViewMap& oldContentViews, ViewMap& newContentViews,
              nsFrameLoader* aFrameLoader, Layer* aLayer,
              float aXScale = 1, float aYScale = 1)
 {
-  ContainerLayer* container = aLayer->AsContainerLayer();
-  if (!container)
+  if (!aLayer->GetFirstChild())
     return;
+
+  ContainerLayer* container = static_cast<ContainerLayer*>(aLayer);
   const FrameMetrics metrics = container->GetFrameMetrics();
   const ViewID scrollId = metrics.mScrollId;
 
   if (metrics.IsScrollable()) {
     nscoord auPerDevPixel = aFrameLoader->GetPrimaryFrameOfOwningContent()
                                         ->PresContext()->AppUnitsPerDevPixel();
     nsContentView* view = FindViewForId(oldContentViews, scrollId);
     if (view) {
--- a/mobile/app/mobile.js
+++ b/mobile/app/mobile.js
@@ -390,19 +390,19 @@ pref("javascript.options.methodjit.chrom
 
 pref("dom.max_chrome_script_run_time", 0); // disable slow script dialog for chrome
 pref("dom.max_script_run_time", 20);
 
 // JS error console
 pref("devtools.errorconsole.enabled", false);
 
 // kinetic tweakables
-pref("browser.ui.kinetic.updateInterval", 16);
-pref("browser.ui.kinetic.exponentialC", 1400);
-pref("browser.ui.kinetic.polynomialC", 100);
+pref("browser.ui.kinetic.updateInterval", 30);
+pref("browser.ui.kinetic.decelerationRate", 20);
+pref("browser.ui.kinetic.speedSensitivity", 80);
 pref("browser.ui.kinetic.swipeLength", 160);
 
 // zooming
 pref("browser.ui.zoom.pageFitGranularity", 9); // don't zoom to fit by less than 1/9 (11%)
 pref("browser.ui.zoom.animationDuration", 200); // ms duration of double-tap zoom animation
 pref("browser.ui.zoom.reflow", false); // Change text wrapping on double-tap
 pref("browser.ui.zoom.reflow.fontSize", 720);
 
--- a/mobile/chrome/content/input.js
+++ b/mobile/chrome/content/input.js
@@ -55,23 +55,16 @@ const kOverTapWait = 150;
 const kLongTapWait = 500;
 
 // maximum drag distance in inches while axis locking can still be reverted
 const kAxisLockRevertThreshold = 0.8;
 
 // Same as NS_EVENT_STATE_ACTIVE from nsIEventStateManager.h
 const kStateActive = 0x00000001;
 
-// After a drag begins, kinetic panning is stopped if the drag doesn't become
-// a pan in 300 milliseconds.
-const kStopKineticPanOnDragTimeout = 300;
-
-// Max velocity of a pan. This is in pixels/millisecond.
-const kMaxVelocity = 6;
-
 /**
  * MouseModule
  *
  * Handles all touch-related input such as dragging and tapping.
  *
  * The Fennec chrome DOM tree has elements that are augmented dynamically with
  * custom JS properties that tell the MouseModule they have custom support for
  * either dragging or clicking.  These JS properties are JS objects that expose
@@ -207,31 +200,29 @@ MouseModule.prototype = {
     // stop kinetic panning if targetScrollbox has changed
     if (this._kinetic.isActive() && this._dragger != dragger)
       this._kinetic.end();
 
     this._targetScrollbox = targetScrollInterface ? targetScrollInterface.element : targetScrollbox;
     this._targetScrollInterface = targetScrollInterface;
 
     // Do tap
-    if (!this._kinetic.isActive()) {
-      let event = document.createEvent("Events");
-      event.initEvent("TapDown", true, true);
-      event.clientX = aEvent.clientX;
-      event.clientY = aEvent.clientY;
-      let success = aEvent.target.dispatchEvent(event);
-      if (success) {
-        this._recordEvent(aEvent);
-        this._target = aEvent.target;
-        this._mouseOverTimeout.once(kOverTapWait);
-        this._longClickTimeout.once(kLongTapWait);
-      } else {
-        // cancel all pending content clicks
-        this._cleanClickBuffer();
-      }
+    let event = document.createEvent("Events");
+    event.initEvent("TapDown", true, true);
+    event.clientX = aEvent.clientX;
+    event.clientY = aEvent.clientY;
+    let success = aEvent.target.dispatchEvent(event);
+    if (success) {
+      this._recordEvent(aEvent);
+      this._target = aEvent.target;
+      this._mouseOverTimeout.once(kOverTapWait);
+      this._longClickTimeout.once(kLongTapWait);
+    } else {
+      // cancel all pending content clicks
+      this._cleanClickBuffer();
     }
 
     // Do pan
     if (dragger) {
       let draggable = dragger.isDraggable(targetScrollbox, targetScrollInterface);
       dragData.locked = !draggable.x || !draggable.y;
       if (draggable.x || draggable.y) {
         this._dragger = dragger;
@@ -332,18 +323,21 @@ MouseModule.prototype = {
       //
       let [sX, sY] = dragData.panPosition();
       this.dX += dragData.prevPanX - sX;
       this.dY += dragData.prevPanY - sY;
 
       if (dragData.isPan()) {
         // Only pan when mouse event isn't part of a click. Prevent jittering on tap.
         this._kinetic.addData(sX - dragData.prevPanX, sY - dragData.prevPanY);
-        this._dragBy(this.dX, this.dY);
-        // dragBy will reset dX and dY values to 0.
+        if (!this._waitingForPaint) {
+          this._dragBy(this.dX, this.dY);
+          this.dX = 0;
+          this.dY = 0;
+        }
 
         // Let everyone know when mousemove begins a pan
         if (!oldIsPan && dragData.isPan()) {
           this._mouseOverTimeout.clear();
           this._longClickTimeout.clear();
 
           let event = document.createEvent("Events");
           event.initEvent("PanBegin", true, false);
@@ -362,67 +356,55 @@ MouseModule.prototype = {
 
   /**
    * Inform our dragger of a dragStart.
    */
   _doDragStart: function _doDragStart(aEvent, aDraggable) {
     let dragData = this._dragData;
     dragData.setDragStart(aEvent.screenX, aEvent.screenY, aDraggable);
     this._kinetic.addData(0, 0);
-    this._dragStartTime = Date.now();
     if (!this._kinetic.isActive())
       this._dragger.dragStart(aEvent.clientX, aEvent.clientY, aEvent.target, this._targetScrollInterface);
   },
 
   /** Finish a drag. */
   _doDragStop: function _doDragStop() {
     let dragData = this._dragData;
     if (!dragData.dragging)
       return;
 
     dragData.endDrag();
 
     // Note: it is possible for kinetic scrolling to be active from a
     // mousedown/mouseup event previous to this one. In this case, we
     // want the kinetic panner to tell our drag interface to stop.
 
-    if (dragData.isPan()) {
-      if (Date.now() - this._dragStartTime > kStopKineticPanOnDragTimeout)
-        this._kinetic._velocity.set(0, 0);
+    if (!dragData.isPan() && !this._kinetic.isActive()) {
+      // There was no pan and no kinetic scrolling, so just stop dragger.
+      this._dragger.dragStop(0, 0, this._targetScrollInterface);
+      this._dragger = null;
+    } else if (dragData.isPan()) {
       // Start kinetic pan.
       this._kinetic.start();
-    } else {
-      this._kinetic.end();
-      this._dragger.dragStop(0, 0, this._targetScrollInterface);
-      this._dragger = null;
     }
   },
 
   /**
    * Used by _onMouseMove() above and by KineticController's timer to do the
    * actual dragMove signalling to the dragger.  We'd put this in _onMouseMove()
    * but then KineticController would be adding to its own data as it signals
    * the dragger of dragMove()s.
    */
   _dragBy: function _dragBy(dX, dY, aIsKinetic) {
-    let dragged = true;
     let dragData = this._dragData;
-    if (!this._waitingForPaint || aIsKinetic) {
-      let dragData = this._dragData;
-      dragged = this._dragger.dragMove(dX, dY, this._targetScrollInterface, aIsKinetic);
-      if (dragged && !this._waitingForPaint) {
-        this._waitingForPaint = true;
-        mozRequestAnimationFrame(this);
-      }
-      this.dX = 0;
-      this.dY = 0;
+    let dragged = this._dragger.dragMove(dX, dY, this._targetScrollInterface, aIsKinetic);
+    if (dragged && !this._waitingForPaint) {
+      this._waitingForPaint = true;
+      mozRequestAnimationFrame(this);
     }
-    if (!dragData.isPan())
-      this._kinetic.pause();
-
     return dragged;
   },
 
   /** Callback for kinetic scroller. */
   _kineticStop: function _kineticStop() {
     // Kinetic panning could finish while user is panning, so don't finish
     // the pan just yet.
     let dragData = this._dragData;
@@ -829,130 +811,122 @@ function KineticController(aPanBy, aEndC
   this._velocity = new Point(0, 0);
   this._acceleration = new Point(0, 0);
   this._time = 0;
   this._timeStart = 0;
 
   // How often do we change the position of the scroll pane?  Too often and panning may jerk near
   // the end. Too little and panning will be choppy. In milliseconds.
   this._updateInterval = Services.prefs.getIntPref("browser.ui.kinetic.updateInterval");
-  // Constants that affect the "friction" of the scroll pane.
-  this._exponentialC = Services.prefs.getIntPref("browser.ui.kinetic.exponentialC");
-  this._polynomialC = Services.prefs.getIntPref("browser.ui.kinetic.polynomialC") / 1000000;
+  // "Friction" of the scroll pane. The lower, the less friction and the further distance traveled.
+  this._decelerationRate = Services.prefs.getIntPref("browser.ui.kinetic.decelerationRate") / 10000;
+  // A multiplier for the initial velocity of the movement.
+  this._speedSensitivity = Services.prefs.getIntPref("browser.ui.kinetic.speedSensitivity") / 100;
   // Number of milliseconds that can contain a swipe. Movements earlier than this are disregarded.
   this._swipeLength = Services.prefs.getIntPref("browser.ui.kinetic.swipeLength");
 
   this._reset();
 }
 
 KineticController.prototype = {
   _reset: function _reset() {
     this._active = false;
-    this._paused = false;
     this.momentumBuffer = [];
     this._velocity.set(0, 0);
   },
 
   isActive: function isActive() {
     return this._active;
   },
 
   _startTimer: function _startTimer() {
-    let self = this;
+    // Use closed form of a parabola to calculate each position for panning.
+    // x(t) = v0*t + .5*t^2*a
+    // where: v0 is initial velocity
+    //        a is acceleration
+    //        t is time elapsed
+    //
+    // x(t)
+    //  ^
+    //  |                |
+    //  |
+    //  |                |
+    //  |           ....^^^^....
+    //  |      ...^^     |      ^^...
+    //  |  ...^                      ^...
+    //  |..              |               ..
+    //   -----------------------------------> t
+    //  t0             tf=-v0/a
+    //
+    // Using this formula, distance moved is independent of the time between each frame, unlike time
+    // step approaches. Once the time is up, set the position to x(tf) and stop the timer.
 
-    let lastp = this._position;  // track last position vector because pan takes deltas
+    let lastx = this._position;  // track last position vector because pan takes differences
     let v0 = this._velocity;  // initial velocity
     let a = this._acceleration;  // acceleration
-    let c = this._exponentialC;
-    let p = new Point(0, 0);
-    let dx, dy, t, realt;
 
-    function calcP(v0, a, t) {
-      // Important traits for this function:
-      //   p(t=0) is 0
-      //   p'(t=0) is v0
-      //
-      // We use exponential to get a smoother stop, but by itself exponential
-      // is too smooth at the end. Adding a polynomial with the appropriate
-      // weight helps to balance
-      return v0 * Math.exp(-t / c) * -c + a * t * t + v0 * c;
-    }
-
-    this._calcV = function(v0, a, t) {
-      return v0 * Math.exp(-t / c) + 2 * a * t;
-    }
+    // Temporary "bins" so that we don't create new objects during pan.
+    let aBin = new Point(0, 0);
+    let v0Bin = new Point(0, 0);
+    let self = this;
 
     let callback = {
       onBeforePaint: function kineticHandleEvent(timeStamp) {
-        // Someone called end() on us between timer intervals
-        // or we are paused.
-        if (!self.isActive() || self._paused)
+        if (!self.isActive())  // someone called end() on us between timer intervals
           return;
 
         // To make animation end fast enough but to keep smoothness, average the ideal
         // time frame (smooth animation) with the actual time lapse (end fast enough).
         // Animation will never take longer than 2 times the ideal length of time.
-        realt = timeStamp - self._initialTime;
+        let realt = timeStamp - self._initialTime;
         self._time += self._updateInterval;
-        t = (self._time + realt) / 2;
+        let t = (self._time + realt) / 2;
 
-        // Calculate new position.
-        p.x = calcP(v0.x, a.x, t);
-        p.y = calcP(v0.y, a.y, t);
-        dx = Math.round(p.x - lastp.x);
-        dy = Math.round(p.y - lastp.y);
+        // Calculate new position using x(t) formula.
+        let x = v0Bin.set(v0).scale(t).add(aBin.set(a).scale(0.5 * t * t));
+        let dx = x.x - lastx.x;
+        let dy = x.y - lastx.y;
+        lastx.set(x);
 
-        // Test to see if movement is finished for each component.
-        if (dx * a.x > 0) {
-          dx = 0;
-          lastp.x = 0;
+        // Test to see if movement is finished for each component. As seen in graph, we want the
+        // final position to be at tf.
+        if (t >= -v0.x / a.x) {
+          // Plug in t=-v0/a into x(t) to get final position.
+          dx = -v0.x * v0.x / 2 / a.x - lastx.x;
+          // Reset components. Next frame: a's component will be 0 and t >= NaN will be false.
+          lastx.x = 0;
           v0.x = 0;
           a.x = 0;
         }
         // Symmetric to above case.
-        if (dy * a.y > 0) {
-          dy = 0;
-          lastp.y = 0;
+        if (t >= -v0.y / a.y) {
+          dy = -v0.y * v0.y / 2 / a.y - lastx.y;
+          lastx.y = 0;
           v0.y = 0;
           a.y = 0;
         }
 
-        if (v0.x == 0 && v0.y == 0) {
+        let panned = false;
+        try { panned = self._panBy(Math.round(-dx), Math.round(-dy), true); } catch (e) {}
+        if (!panned)
           self.end();
-        } else {
-          let panStop = false;
-          if (dx != 0 || dy != 0) {
-            try { panStop = !self._panBy(-dx, -dy, true); } catch (e) {}
-            lastp.add(dx, dy);
-          }
-
-          if (panStop)
-            self.end();
-          else
-            mozRequestAnimationFrame(this);
-        }
+        else
+          mozRequestAnimationFrame(this);
       }
     };
 
     this._active = true;
-    this._paused = false;
     mozRequestAnimationFrame(callback);
   },
 
   start: function start() {
     function sign(x) {
       return x ? ((x > 0) ? 1 : -1) : 0;
     }
 
-    function clampFromZero(x, closerToZero, furtherFromZero) {
-      if (x >= 0)
-        return Math.max(closerToZero, Math.min(furtherFromZero, x));
-      return Math.min(-closerToZero, Math.max(-furtherFromZero, x));
-    }
-
     let mb = this.momentumBuffer;
     let mblen = this.momentumBuffer.length;
 
     let lastTime = mb[mblen - 1].t;
     let distanceX = 0;
     let distanceY = 0;
     let swipeLength = this._swipeLength;
 
@@ -961,53 +935,34 @@ KineticController.prototype = {
     for (let i = 0; i < mblen; i++) {
       me = mb[i];
       if (lastTime - me.t < swipeLength) {
         distanceX += me.dx;
         distanceY += me.dy;
       }
     }
 
-    let currentVelocityX = 0;
-    let currentVelocityY = 0;
-
-    if (this.isActive()) {
-      // If active, then we expect this._calcV to be defined.
-      let currentTime = Date.now() - this._initialTime;
-      currentVelocityX = Util.clamp(this._calcV(this._velocity.x, this._acceleration.x, currentTime), -kMaxVelocity, kMaxVelocity);
-      currentVelocityY = Util.clamp(this._calcV(this._velocity.y, this._acceleration.y, currentTime), -kMaxVelocity, kMaxVelocity);
-    }
-
-    if (currentVelocityX * this._velocity.x <= 0)
-      currentVelocityX = 0;
-    if (currentVelocityY * this._velocity.y <= 0)
-      currentVelocityY = 0;
-
-    let swipeTime = Math.min(swipeLength, lastTime - mb[0].t);
-    this._velocity.x = clampFromZero((distanceX / swipeTime) + currentVelocityX, Math.abs(currentVelocityX), 6);
-    this._velocity.y = clampFromZero((distanceY / swipeTime) + currentVelocityY, Math.abs(currentVelocityY), 6);
+    // Only allow kinetic scrolling to speed up if kinetic scrolling is active.
+    this._velocity.x = (distanceX < 0 ? Math.min : Math.max)((distanceX / swipeLength) * this._speedSensitivity, this._velocity.x);
+    this._velocity.y = (distanceY < 0 ? Math.min : Math.max)((distanceY / swipeLength) * this._speedSensitivity, this._velocity.y);
 
     // Set acceleration vector to opposite signs of velocity
-    this._acceleration.set(this._velocity.clone().map(sign).scale(-this._polynomialC));
+    this._acceleration.set(this._velocity.clone().map(sign).scale(-this._decelerationRate));
 
     this._position.set(0, 0);
     this._initialTime = mozAnimationStartTime;
     this._time = 0;
     this.momentumBuffer = [];
 
-    if (!this.isActive() || this._paused)
+    if (!this.isActive())
       this._startTimer();
 
     return true;
   },
 
-  pause: function pause() {
-    this._paused = true;
-  },
-
   end: function end() {
     if (this.isActive()) {
       if (this._beforeEnd)
         this._beforeEnd();
       this._reset();
     }
   },