Bug 1066504 - Allow timeline range selection to continue even after mouseout of the graph window. r=vporof
authorBrian Grinstead <bgrinstead@mozilla.com>
Wed, 18 Mar 2015 13:29:00 -0400
changeset 265594 e6349d80d3b5f6a25a6bcb620679b7da87a95711
parent 265593 466d2f2a7b7e51cca2ce024a23baeb8723c8ae76
child 265595 2166c79040a7115e681d96b757949aaf8e571c45
push id830
push userraliiev@mozilla.com
push dateFri, 19 Jun 2015 19:24:37 +0000
treeherdermozilla-release@932614382a68 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersvporof
bugs1066504
milestone39.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 1066504 - Allow timeline range selection to continue even after mouseout of the graph window. r=vporof
browser/devtools/shared/test/browser_graphs-07a.js
browser/devtools/shared/widgets/Graphs.jsm
--- a/browser/devtools/shared/test/browser_graphs-07a.js
+++ b/browser/devtools/shared/test/browser_graphs-07a.js
@@ -12,24 +12,28 @@ add_task(function*() {
   yield performTest();
   gBrowser.removeCurrentTab();
 });
 
 function* performTest() {
   let [host, win, doc] = yield createHost();
   let graph = new LineGraphWidget(doc.body, "fps");
   yield graph.once("ready");
+  testGraph(graph, normalDragStop);
+  yield graph.destroy();
 
-  testGraph(graph);
+  let graph2 = new LineGraphWidget(doc.body, "fps");
+  yield graph2.once("ready");
+  testGraph(graph2, buggyDragStop);
+  yield graph2.destroy();
 
-  yield graph.destroy();
   host.destroy();
 }
 
-function testGraph(graph) {
+function testGraph(graph, dragStop) {
   graph.setData(TEST_DATA);
 
   info("Making a selection.");
 
   dragStart(graph, 300);
   ok(graph.hasSelectionInProgress(),
     "The selection should start (1).");
   is(graph.getSelection().start, 300,
@@ -181,21 +185,32 @@ function click(graph, x, y = 1) {
 
 function dragStart(graph, x, y = 1) {
   x /= window.devicePixelRatio;
   y /= window.devicePixelRatio;
   graph._onMouseMove({ clientX: x, clientY: y });
   graph._onMouseDown({ clientX: x, clientY: y });
 }
 
-function dragStop(graph, x, y = 1) {
+function normalDragStop(graph, x, y = 1) {
   x /= window.devicePixelRatio;
   y /= window.devicePixelRatio;
   graph._onMouseMove({ clientX: x, clientY: y });
   graph._onMouseUp({ clientX: x, clientY: y });
 }
 
+function buggyDragStop(graph, x, y = 1) {
+  x /= window.devicePixelRatio;
+  y /= window.devicePixelRatio;
+
+  // Only fire a mousemove instead of a mouseup.
+  // This happens when the mouseup happens outside of the toolbox,
+  // see Bug 1066504.
+  graph._onMouseMove({ clientX: x, clientY: y });
+  graph._onMouseMove({ clientX: x, clientY: y, buttons: 0 });
+}
+
 function scroll(graph, wheel, x, y = 1) {
   x /= window.devicePixelRatio;
   y /= window.devicePixelRatio;
   graph._onMouseMove({ clientX: x, clientY: y });
   graph._onMouseWheel({ clientX: x, clientY: y, detail: wheel });
 }
--- a/browser/devtools/shared/widgets/Graphs.jsm
+++ b/browser/devtools/shared/widgets/Graphs.jsm
@@ -176,16 +176,17 @@ this.AbstractCanvasGraph = function(pare
     this._height = canvas.height = bounds.height * this._pixelRatio;
     this._ctx = canvas.getContext("2d");
     this._ctx.mozImageSmoothingEnabled = false;
 
     this._cursor = new GraphCursor();
     this._selection = new GraphArea();
     this._selectionDragger = new GraphAreaDragger();
     this._selectionResizer = new GraphAreaResizer();
+    this._isMouseActive = false;
 
     this._onAnimationFrame = this._onAnimationFrame.bind(this);
     this._onMouseMove = this._onMouseMove.bind(this);
     this._onMouseDown = this._onMouseDown.bind(this);
     this._onMouseUp = this._onMouseUp.bind(this);
     this._onMouseWheel = this._onMouseWheel.bind(this);
     this._onMouseOut = this._onMouseOut.bind(this);
     this._onResize = this._onResize.bind(this);
@@ -947,31 +948,40 @@ AbstractCanvasGraph.prototype = {
 
     return { left: x, top: y };
   },
 
   /**
    * Listener for the "mousemove" event on the graph's container.
    */
   _onMouseMove: function(e) {
+    let resizer = this._selectionResizer;
+    let dragger = this._selectionDragger;
+
+    // If a mouseup happened outside the toolbox and the current operation
+    // is causing the selection changed, then end it.
+    if (e.buttons == 0 && (this.hasSelectionInProgress() ||
+                           resizer.margin != null ||
+                           dragger.origin != null)) {
+      return this._onMouseUp(e);
+    }
+
     let offset = this._getContainerOffset();
     let mouseX = (e.clientX - offset.left) * this._pixelRatio;
     let mouseY = (e.clientY - offset.top) * this._pixelRatio;
     this._cursor.x = mouseX;
     this._cursor.y = mouseY;
 
-    let resizer = this._selectionResizer;
     if (resizer.margin != null) {
       this._selection[resizer.margin] = mouseX;
       this._shouldRedraw = true;
       this.emit("selecting");
       return;
     }
 
-    let dragger = this._selectionDragger;
     if (dragger.origin != null) {
       this._selection.start = dragger.anchor.start - dragger.origin + mouseX;
       this._selection.end = dragger.anchor.end - dragger.origin + mouseX;
       this._shouldRedraw = true;
       this.emit("selecting");
       return;
     }
 
@@ -1008,16 +1018,17 @@ AbstractCanvasGraph.prototype = {
 
     this._shouldRedraw = true;
   },
 
   /**
    * Listener for the "mousedown" event on the graph's container.
    */
   _onMouseDown: function(e) {
+    this._isMouseActive = true;
     let offset = this._getContainerOffset();
     let mouseX = (e.clientX - offset.left) * this._pixelRatio;
 
     switch (this._canvas.getAttribute("input")) {
       case "hovering-background":
       case "hovering-region":
         if (!this.selectionEnabled) {
           break;
@@ -1046,16 +1057,17 @@ AbstractCanvasGraph.prototype = {
     this._shouldRedraw = true;
     this.emit("mousedown");
   },
 
   /**
    * Listener for the "mouseup" event on the graph's container.
    */
   _onMouseUp: function(e) {
+    this._isMouseActive = false;
     let offset = this._getContainerOffset();
     let mouseX = (e.clientX - offset.left) * this._pixelRatio;
 
     switch (this._canvas.getAttribute("input")) {
       case "hovering-background":
       case "hovering-region":
         if (!this.selectionEnabled) {
           break;
@@ -1156,31 +1168,27 @@ AbstractCanvasGraph.prototype = {
       selection.end = midPoint + GRAPH_WHEEL_MIN_SELECTION_WIDTH / 2;
     }
 
     this._shouldRedraw = true;
     this.emit("selecting");
     this.emit("scroll");
   },
 
-  /**
+   /**
    * Listener for the "mouseout" event on the graph's container.
+   * Clear any active cursors if a drag isn't happening.
    */
-  _onMouseOut: function() {
-    if (this.hasSelectionInProgress()) {
-      this.dropSelection();
+  _onMouseOut: function(e) {
+    if (!this._isMouseActive) {
+      this._cursor.x = null;
+      this._cursor.y = null;
+      this._canvas.removeAttribute("input");
+      this._shouldRedraw = true;
     }
-
-    this._cursor.x = null;
-    this._cursor.y = null;
-    this._selectionResizer.margin = null;
-    this._selectionDragger.origin = null;
-
-    this._canvas.removeAttribute("input");
-    this._shouldRedraw = true;
   },
 
   /**
    * Listener for the "resize" event on the graph's parent node.
    */
   _onResize: function() {
     if (this.hasData()) {
       setNamedTimeout(this._uid, GRAPH_RESIZE_EVENTS_DRAIN, this.refresh);