Bug 799094 - Clip text selection handles by containing iframes. r=margaret
authorKartikaya Gupta <kgupta@mozilla.com>
Thu, 18 Oct 2012 13:41:40 -0400
changeset 110685 2e05402e25b2d45e16839d4a4f6621ca9255156d
parent 110684 bc0f08257eda7eaa4ed0adcb2cf77fc3f3eab7b9
child 110686 67728fe694e2e494386e6ebd16f05d4179bbfe17
push id23708
push userryanvm@gmail.com
push dateFri, 19 Oct 2012 01:28:59 +0000
treeherdermozilla-central@fd5556088388 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmargaret
bugs799094
milestone19.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 799094 - Clip text selection handles by containing iframes. r=margaret
mobile/android/base/TextSelection.java
mobile/android/chrome/content/browser.js
--- a/mobile/android/base/TextSelection.java
+++ b/mobile/android/base/TextSelection.java
@@ -106,22 +106,23 @@ class TextSelection extends Layer implem
                 });
             } else if (event.equals("TextSelection:PositionHandles")) {
                 final JSONArray positions = message.getJSONArray("positions");
                 GeckoApp.mAppContext.mMainHandler.post(new Runnable() {
                     public void run() {
                         try {
                             for (int i=0; i < positions.length(); i++) {
                                 JSONObject position = positions.getJSONObject(i);
-                                String handle = position.getString("handle");
                                 int left = position.getInt("left");
                                 int top = position.getInt("top");
 
-                                getHandle(handle).positionFromGecko(left, top);
-                             }
+                                TextSelectionHandle handle = getHandle(position.getString("handle"));
+                                handle.setVisibility(position.getBoolean("hidden") ? View.GONE : View.VISIBLE);
+                                handle.positionFromGecko(left, top);
+                            }
                         } catch (Exception e) { }
                     }
                 });
             }
         } catch (Exception e) {
             Log.e(LOGTAG, "Exception handling message \"" + event + "\":", e);
         }
     }
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -2030,29 +2030,52 @@ var SelectionHandler = {
 
   positionHandles: function sh_positionHandles() {
     // Translate coordinates to account for selections in sub-frames. We can't cache
     // this because the top-level page may have scrolled since selection started.
     let offset = this._getViewOffset();
     let scrollX = {}, scrollY = {};
     this._view.top.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils).getScrollXY(false, scrollX, scrollY);
 
+    // the checkHidden function tests to see if the given point is hidden inside an
+    // iframe/subdocument. this is so that if we select some text inside an iframe and
+    // scroll the iframe so the selection is out of view, we hide the handles rather
+    // than having them float on top of the main page content.
+    let checkHidden = function(x, y) {
+      return false;
+    };
+    if (this._view.frameElement) {
+      let bounds = this._view.frameElement.getBoundingClientRect();
+      checkHidden = function(x, y) {
+        return x < 0 || y < 0 || x > bounds.width || y > bounds.height;
+      }
+    }
+
     let positions = null;
     if (this._activeType == this.TYPE_CURSOR) {
       let cursor = this._cwu.sendQueryContentEvent(this._cwu.QUERY_CARET_RECT, this._target.selectionEnd, 0, 0, 0);
+      let x = cursor.left;
+      let y = cursor.top + cursor.height;
       positions = [ { handle: this.HANDLE_TYPE_MIDDLE,
-                     left: cursor.left + offset.x + scrollX.value,
-                     top: cursor.top + cursor.height + offset.y + scrollY.value } ];
+                     left: x + offset.x + scrollX.value,
+                     top: y + offset.y + scrollY.value,
+                     hidden: checkHidden(x, y) } ];
     } else {
+      let sx = this.cache.start.x;
+      let sy = this.cache.start.y;
+      let ex = this.cache.end.x;
+      let ey = this.cache.end.y;
       positions = [ { handle: this.HANDLE_TYPE_START,
-                     left: this.cache.start.x + offset.x + scrollX.value,
-                     top: this.cache.start.y + offset.y + scrollY.value },
+                     left: sx + offset.x + scrollX.value,
+                     top: sy + offset.y + scrollY.value,
+                     hidden: checkHidden(sx, sy) },
                    { handle: this.HANDLE_TYPE_END,
-                     left: this.cache.end.x + offset.x + scrollX.value,
-                     top: this.cache.end.y + offset.y + scrollY.value } ];
+                     left: ex + offset.x + scrollX.value,
+                     top: ey + offset.y + scrollY.value,
+                     hidden: checkHidden(ex, ey) } ];
     }
 
     sendMessageToJava({
       gecko: {
         type: "TextSelection:PositionHandles",
         positions: positions
       }
     });