Bug 976772 - [e10s] Make sure <select> element appears at the right place, even in a subframe (r=felipe)
authorBill McCloskey <wmccloskey@mozilla.com>
Fri, 28 Feb 2014 17:00:17 -0800
changeset 182370 9a822b2b8810f4ce5c48b039ec3b7a24cbe2c56b
parent 182369 e4139eb421ffa24df5cff747ba54ab2882139a47
child 182371 c5a7f033a90ccb20341640aa8a9eeb8f6c559ff7
push id5439
push userffxbld
push dateMon, 17 Mar 2014 23:08:15 +0000
treeherdermozilla-aurora@c0befb3c8038 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfelipe
bugs976772
milestone30.0a1
Bug 976772 - [e10s] Make sure <select> element appears at the right place, even in a subframe (r=felipe)
toolkit/components/satchel/AutoCompleteE10S.jsm
toolkit/components/satchel/nsFormAutoComplete.js
toolkit/modules/BrowserUtils.jsm
toolkit/modules/SelectContentHelper.jsm
toolkit/modules/SelectParentHelper.jsm
--- a/toolkit/components/satchel/AutoCompleteE10S.jsm
+++ b/toolkit/components/satchel/AutoCompleteE10S.jsm
@@ -77,18 +77,20 @@ this.AutoCompleteE10S = {
 
   search: function(message) {
     let browserWindow = message.target.ownerDocument.defaultView;
     this.browser = browserWindow.gBrowser.selectedBrowser;
     this.popup = this.browser.autoCompletePopup;
     this.popup.hidden = false;
     this.popup.setAttribute("width", message.data.width);
 
-    this.x = message.data.left;
-    this.y = message.data.top + message.data.height;
+    let rect = message.data;
+    let {x, y} = this.browser.mapScreenCoordinatesFromContent(rect.left, rect.top + rect.height);
+    this.x = x;
+    this.y = y;
 
     let formAutoComplete = Cc["@mozilla.org/satchel/form-autocomplete;1"]
                              .getService(Ci.nsIFormAutoComplete);
 
     formAutoComplete.autoCompleteSearchAsync(message.data.inputName,
                                              message.data.untrimmedSearchString,
                                              null,
                                              null,
@@ -112,17 +114,17 @@ this.AutoCompleteE10S = {
       "FormAutoComplete:AutoCompleteSearchAsyncResult",
       {results: resultsArray}
     );
 
     this.popup.selectedIndex = -1;
     this.popup.invalidate();
 
     if (count > 0) {
-      this.popup.openPopup(this.browser, "overlap", this.x, this.y, true, true);
+      this.popup.openPopupAtScreen(this.x, this.y, true);
       // Bug 947503 - This openPopup call is not triggering the "popupshowing"
       // event, which autocomplete.xml uses to track the openness of the popup
       // by setting its mPopupOpen flag. This flag needs to be properly set
       // for closePopup to work. For now, we set it manually.
       this.popup.mPopupOpen = true;
     } else {
       this.popup.closePopup();
     }
--- a/toolkit/components/satchel/nsFormAutoComplete.js
+++ b/toolkit/components/satchel/nsFormAutoComplete.js
@@ -5,16 +5,18 @@
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cr = Components.results;
 
 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
 Components.utils.import("resource://gre/modules/Services.jsm");
 
+XPCOMUtils.defineLazyModuleGetter(this, "BrowserUtils",
+                                  "resource://gre/modules/BrowserUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Deprecated",
                                   "resource://gre/modules/Deprecated.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "FormHistory",
                                   "resource://gre/modules/FormHistory.jsm");
 
 function FormAutoComplete() {
     this.init();
 }
@@ -356,20 +358,20 @@ FormAutoCompleteChild.prototype = {
       // This function is deprecated
     },
 
     autoCompleteSearchAsync : function (aInputName, aUntrimmedSearchString, aField, aPreviousResult, aListener) {
       this.log("autoCompleteSearchAsync");
 
       this._pendingListener = aListener;
 
-      let rect = aField.getBoundingClientRect();
+      let rect = BrowserUtils.getElementBoundingScreenRect(aField);
 
-      let topLevelDocshell = aField.ownerDocument.defaultView
-                                   .QueryInterface(Ci.nsIInterfaceRequestor)
+      let window = aField.ownerDocument.defaultView;
+      let topLevelDocshell = window.QueryInterface(Ci.nsIInterfaceRequestor)
                                    .getInterface(Ci.nsIDocShell)
                                    .sameTypeRootTreeItem
                                    .QueryInterface(Ci.nsIDocShell);
 
       let mm = topLevelDocshell.QueryInterface(Ci.nsIInterfaceRequestor)
                                .getInterface(Ci.nsIContentFrameMessageManager);
 
       mm.sendAsyncMessage("FormHistory:AutoCompleteSearchAsync", {
--- a/toolkit/modules/BrowserUtils.jsm
+++ b/toolkit/modules/BrowserUtils.jsm
@@ -59,9 +59,32 @@ this.BrowserUtils = {
   makeURI: function(aURL, aOriginCharset, aBaseURI) {
     return Services.io.newURI(aURL, aOriginCharset, aBaseURI);
   },
 
   makeFileURI: function(aFile) {
     return Services.io.newFileURI(aFile);
   },
 
+  /**
+   * For a given DOM element, returns its position in "screen"
+   * coordinates. In a content process, the coordinates returned will
+   * be relative to the left/top of the tab. In the chrome process,
+   * the coordinates are relative to the user's screen.
+   */
+  getElementBoundingScreenRect: function(aElement) {
+    let rect = aElement.getBoundingClientRect();
+    let window = aElement.ownerDocument.defaultView;
+
+    // We need to compensate for any iframes that might shift things
+    // over. We also need to compensate for zooming.
+    let fullZoom = window.getInterface(Ci.nsIDOMWindowUtils).fullZoom;
+    rect = {
+      left: (rect.left + window.mozInnerScreenX) * fullZoom,
+      top: (rect.top + window.mozInnerScreenY) * fullZoom,
+      width: rect.width * fullZoom,
+      height: rect.height * fullZoom
+    };
+
+    return rect;
+  },
+
 };
--- a/toolkit/modules/SelectContentHelper.jsm
+++ b/toolkit/modules/SelectContentHelper.jsm
@@ -5,16 +5,19 @@
 "use strict";
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
+XPCOMUtils.defineLazyModuleGetter(this, "BrowserUtils",
+                                  "resource://gre/modules/BrowserUtils.jsm");
+
 this.EXPORTED_SYMBOLS = [
   "SelectContentHelper"
 ];
 
 this.SelectContentHelper = function (aElement, aGlobal) {
   this.element = aElement;
   this.global = aGlobal;
   this.init();
@@ -42,34 +45,17 @@ this.SelectContentHelper.prototype = {
     this.global.sendAsyncMessage("Forms:ShowDropDown", {
       rect: rect,
       options: this._buildOptionList(),
       selectedIndex: this.element.selectedIndex,
     });
   },
 
   _getBoundingContentRect: function() {
-    let { top, left, width, height } = this.element.getBoundingClientRect();
-    // We need to copy the info because the properties returned by getBoundingClientRect
-    // are not writable.
-    let rect = { left: left, top: top, width: width, height: height };
-
-    // step out of iframes and frames, offsetting scroll values
-    let view = this.element.ownerDocument.defaultView;
-    for (let frame = view; frame != this.global.content; frame = frame.parent) {
-      // adjust client coordinates' origin to be top left of iframe viewport
-      let r = frame.frameElement.getBoundingClientRect();
-      let left = frame.getComputedStyle(frame.frameElement, "").borderLeftWidth;
-      let top = frame.getComputedStyle(frame.frameElement, "").borderTopWidth;
-
-      rect.left += r.left + parseInt(left, 10);
-      rect.top += r.top + parseInt(top, 10);
-    }
-
-    return rect;
+    return BrowserUtils.getElementBoundingScreenRect(this.element);
   },
 
   _buildOptionList: function() {
     return buildOptionListForChildren(this.element);
   },
 
   receiveMessage: function(message) {
     switch (message.name) {
--- a/toolkit/modules/SelectParentHelper.jsm
+++ b/toolkit/modules/SelectParentHelper.jsm
@@ -17,17 +17,18 @@ this.SelectParentHelper = {
     populateChildren(popup, items, selectedIndex);
   },
 
   open: function(browser, popup, rect) {
     currentBrowser = browser;
     this._registerListeners(popup);
     popup.hidden = false;
 
-    popup.openPopup(currentBrowser, "overlap", rect.left, rect.top + rect.height);
+    let {x, y} = browser.mapScreenCoordinatesFromContent(rect.left, rect.top + rect.height);
+    popup.openPopupAtScreen(x, y);
   },
 
   hide: function(popup) {
     popup.hidePopup();
   },
 
   handleEvent: function(event) {
     let popup = event.currentTarget;