Bug 571371 - [e10s] Move BrowserView event handling to message-based API [r=vingtetun]
authorMark Finkle <mfinkle@mozilla.com>
Fri, 11 Jun 2010 10:23:24 -0400
changeset 1626 62a2be294c46bd78dbbc167f09fd0a31adc93775
parent 1625 435719d9cefd62f7df772fb4632bd2d78d323d0a
child 1627 db7aa064fd6c70614158c3a20b7039f3186afc2d
push id1463
push usermfinkle@mozilla.com
push dateFri, 11 Jun 2010 14:23:35 +0000
reviewersvingtetun
bugs571371
Bug 571371 - [e10s] Move BrowserView event handling to message-based API [r=vingtetun]
chrome/content/BrowserView.js
chrome/content/bindings/browser.xml
chrome/content/browser-ui.js
chrome/content/browser.js
chrome/content/content.js
--- a/chrome/content/BrowserView.js
+++ b/chrome/content/BrowserView.js
@@ -210,23 +210,22 @@ BrowserView.Util = {
   },
 
   resizeContainerToViewport: function resizeContainerToViewport(container, viewportRect) {
     container.style.width = viewportRect.width  + 'px';
     container.style.height = viewportRect.height + 'px';
   },
 
   ensureMozScrolledAreaEvent: function ensureMozScrolledAreaEvent(aBrowser, aWidth, aHeight) {
-    let event = document.createEvent("Event");
-    event.initEvent("MozScrolledAreaChanged", true, false);
-    event.x = 0;
-    event.y = 0;
-    event.width = aWidth;
-    event.height = aHeight;
-    aBrowser.dispatchEvent(event);
+    let message = {};
+    message.target = aBrowser;
+    message.name = "Browser:MozScrolledAreaChanged";
+    message.json = { width: aWidth, height: aHeight };
+
+    Browser._browserView.updateScrolledArea(message);
   }
 };
 
 BrowserView.prototype = {
 
   // -----------------------------------------------------------
   // Public instance methods
   //
@@ -267,18 +266,20 @@ BrowserView.prototype = {
     this._tileManager = new TileManager(this._appendTile, this._removeTile, this, cacheSize);
     this._visibleRectFactory = visibleRectFactory;
 
     this._idleServiceObserver = new BrowserView.IdleServiceObserver(this);
     this._idleService = Cc["@mozilla.org/widget/idleservice;1"].getService(Ci.nsIIdleService);
     this._idleService.addIdleObserver(this._idleServiceObserver, kBrowserViewPrefetchBeginIdleWait);
     this._idleServiceWait = kBrowserViewPrefetchBeginIdleWait;
 
-    let browsers = document.getElementById("browsers");
-    browsers.addEventListener("MozScrolledAreaChanged", this.handleMozScrolledAreaChanged, false);
+    let self = this;
+    messageManager.addMessageListener("Browser:MozScrolledAreaChanged", this);
+    messageManager.addMessageListener("Browser:MozAfterPaint", this);
+    messageManager.addMessageListener("Browser:PageScroll", this);
   },
 
   uninit: function uninit() {
     this.setBrowser(null, null);
     this._idleService.removeIdleObserver(this._idleServiceObserver, this._idleServiceWait);
   },
 
   /** When aggressive, spend more time rendering tiles. */
@@ -469,116 +470,111 @@ BrowserView.prototype = {
     if (browser && !browserViewportState) {
       throw "Cannot set non-null browser with null BrowserViewportState";
     }
 
     let oldBrowser = this._browser;
     let browserChanged = (oldBrowser !== browser);
 
     if (oldBrowser) {
-      oldBrowser.removeEventListener("MozAfterPaint", this.handleMozAfterPaint, false);
-      oldBrowser.removeEventListener("scroll", this.handlePageScroll, false);
       oldBrowser.setAttribute("type", "content");
       oldBrowser.docShell.isOffScreenBrowser = false;
     }
 
     this._browser = browser;
     this._contentWindow = (browser) ? browser.contentWindow : null;
     this._browserViewportState = browserViewportState;
 
     if (browser) {
       browser.setAttribute("type", "content-primary");
 
       this.beginBatchOperation();
 
-      browser.addEventListener("MozAfterPaint", this.handleMozAfterPaint, false);
-      browser.addEventListener("scroll", this.handlePageScroll, false);
-
       browser.docShell.isOffScreenBrowser = true;
-
       if (browserChanged)
         this._viewportChanged(true, true);
 
       this.commitBatchOperation();
     }
   },
 
   getBrowser: function getBrowser() {
     return this._browser;
   },
 
-  handleMozAfterPaint: function handleMozAfterPaint(ev) {
-    let browser = this._browser;
+  receiveMessage: function receiveMessage(aMessage) {
+    switch (aMessage.name) {
+      case "Browser:MozAfterPaint":
+        this.updateDirtyTiles(aMessage);
+        break;
+      case "Browser:PageScroll":
+        this.updatePageScroll(aMessage);
+        break;
+      case "Browser:MozScrolledAreaChanged":
+        this.updateScrolledArea(aMessage);
+        break;
+    }
+    
+    return {};
+  },
+
+  updateDirtyTiles: function updateDirtyTiles(aMessage) {
+    let browser = aMessage.target;
+    if (browser != this._browser)
+      return;
+    
+    let rects = aMessage.json.rects;
+
     let tm = this._tileManager;
     let vs = this._browserViewportState;
 
-    let { x: scrollX, y: scrollY } = BrowserView.Util.getContentScrollOffset(browser);
-    let clientRects = ev.clientRects;
-
-    let rects = [];
+    let dirtyRects = [];
     // loop backwards to avoid xpconnect penalty for .length
-    for (let i = clientRects.length - 1; i >= 0; --i) {
-      let e = clientRects.item(i);
-      let r = new Rect(e.left + scrollX,
-                            e.top + scrollY,
-                            e.width, e.height);
-
+    for (let i = rects.length - 1; i >= 0; --i) {
+      let r = Rect.fromRect(rects[i]);
       r = this.browserToViewportRect(r);
       r.expandToIntegers();
 
-      if (r.right < 0 || r.bottom < 0)
-        continue;
-
       r.restrictTo(vs.viewportRect);
       if (!r.isEmpty())
-        rects.push(r);
+        dirtyRects.push(r);
     }
 
-    tm.dirtyRects(rects, this.isRendering(), true);
+    tm.dirtyRects(dirtyRects, this.isRendering(), true);
   },
 
   /** If browser scrolls, pan content to new scroll area. */
-  handlePageScroll: function handlePageScroll(aEvent) {
-    if (aEvent.target != this._browser.contentDocument || this._ignorePageScroll)
+  updatePageScroll: function updatePageScroll(aMessage) {
+    if (aMessage.target != this._browser || this._ignorePageScroll)
       return;
+
     // XXX shouldn't really make calls to Browser
     Browser.scrollContentToBrowser();
   },
 
   _ignorePageScroll: false,
   ignorePageScroll: function ignorePageScroll(aIgnoreScroll) {
     this._ignorePageScroll = aIgnoreScroll;
   },
 
-  handleMozScrolledAreaChanged: function handleMozScrolledAreaChanged(ev) {
-    let tab = Browser.getTabForDocument(ev.originalTarget) ||
-             (ev.target.contentDocument && Browser.getTabForDocument(ev.target.contentDocument));
-    if (!tab)
-      return;
+  updateScrolledArea: function updateScrolledArea(aMessage) {
+    let browser = aMessage.target;
+    if (!browser)
+      throw "MozScrolledAreaChanged: Could not find browser";
 
-    let browser = tab.browser;
+    let json = aMessage.json;
+    let tab = Browser.getTabForBrowser(browser);
     let bvs = tab.browserViewportState;
-    let { x: scrollX, y: scrollY } = BrowserView.Util.getContentScrollOffset(browser);
-    let x = ev.x + scrollX;
-    let y = ev.y + scrollY;
-    let w = ev.width;
-    let h = ev.height;
-
-    // Adjust width and height from the incoming event properties so that we
-    // ignore changes to width and height contributed by growth in page
-    // quadrants other than x > 0 && y > 0.
-    if (x < 0) w += x;
-    if (y < 0) h += y;
 
     let vis = this.getVisibleRect();
     let viewport = bvs.viewportRect;
     let oldRight = viewport.right;
     let oldBottom = viewport.bottom;
-    viewport.right  = bvs.zoomLevel * w;
-    viewport.bottom = bvs.zoomLevel * h;
+    viewport.right  = bvs.zoomLevel * json.width;
+    viewport.bottom = bvs.zoomLevel * json.height;
 
     if (browser == this._browser) {
       // Page has now loaded enough to allow zooming.
       let sizeChanged = oldRight != viewport.right || oldBottom != viewport.bottom;
       this._viewportChanged(sizeChanged, false);
       this.updateDefaultZoom();
       if (vis.right > viewport.right || vis.bottom > viewport.bottom) {
         // Content has shrunk outside of the visible rectangle.
--- a/chrome/content/bindings/browser.xml
+++ b/chrome/content/bindings/browser.xml
@@ -99,33 +99,35 @@
 
             case "DOMTitleChanged":
               this._contentTitle = aMessage.json.title;
               break;
 
             case "DOMLinkAdded":
               let link = aMessage.json;
               if (link.location != this._documentURI)
-                return;
+                return {};
 
               let linkType = this._getLinkType(link);
               switch(linkType) {
                 case "icon":
                   let iconURI = gIOService.newURI(link.href, link.charset, null);
                   if (!iconURI.schemeIs("javascript") && !gFaviconService.isFailedFavicon(iconURI)) {
                     gFaviconService.setAndLoadFaviconForPage(this.currentURI, iconURI, true);
                     this.mIconURL = iconURI.spec;
                   }
                   break;
                 case "search":
                   this._searchEngines.push({ title: link.title, href: link.href });
                   break;
               }
               break;
          }
+
+         return {};
         ]]></body>
       </method>
 
       <method name="_getLinkType">
         <parameter name="aLink" />
         <body><![CDATA[
           let type = "";
           if (/\bicon\b/i(aLink.rel)) {
--- a/chrome/content/browser-ui.js
+++ b/chrome/content/browser-ui.js
@@ -792,16 +792,18 @@ var BrowserUI = {
           element.setAttribute("referrer", json.referrer);
           DownloadsView._updateTime(element);
           DownloadsView._updateStatus(element);
         }
         catch(e) {}
         gObserverService.notifyObservers(download, "dl-done", null);
         break;
     }
+
+    return {};
   },
 
   supportsCommand : function(cmd) {
     var isSupported = false;
     switch (cmd) {
       case "cmd_back":
       case "cmd_forward":
       case "cmd_reload":
--- a/chrome/content/browser.js
+++ b/chrome/content/browser.js
@@ -571,17 +571,17 @@ var Browser = {
       }
       gPrefService.clearUserPref("extensions.disabledAddons");
     }
 
     // Force commonly used border-images into the image cache
     ImagePreloader.cache();
 
     messageManager.addMessageListener("FennecViewportMetadata", this);
-    messageManager.addMessageListener("MozApplicationManifest", OfflineApps);
+    messageManager.addMessageListener("Browser:MozApplicationManifest", OfflineApps);
 
     this._pluginObserver = new PluginObserver(bv);
 
     // broadcast a UIReady message so add-ons know we are finished with startup
     let event = document.createEvent("Events");
     event.initEvent("UIReady", true, false);
     window.dispatchEvent(event);
   },
@@ -2979,17 +2979,17 @@ var OfflineApps = {
     let currentURI = gIOService.newURI(aRequest.location, aRequest.charset, null);
     let manifestURI = gIOService.newURI(aRequest.manifest, aRequest.charset, currentURI);
 
     let updateService = Cc["@mozilla.org/offlinecacheupdate-service;1"].getService(Ci.nsIOfflineCacheUpdateService);
     updateService.scheduleUpdate(manifestURI, currentURI);
   },
 
   receiveMessage: function receiveMessage(aMessage) {
-    if (aMessage.name == "MozApplicationManifest") {
+    if (aMessage.name == "Browser:MozApplicationManifest") {
       this.offlineAppRequested(aMessage.json);
     }
   }
 };
 
 function Tab() {
   this._id = null;
   this._browser = null;
--- a/chrome/content/content.js
+++ b/chrome/content/content.js
@@ -228,23 +228,26 @@ Coalescer.prototype = {
         if (win.parent != win) // We are only interested in root scroll pane changes
 	  return;
         this.sizeChange(scrollOffset, aEvent.x, aEvent.y, aEvent.width, aEvent.height);
         break;
       }
       case "MozApplicationManifest": {
         let doc = aEvent.originalTarget;
 
-        sendAsyncMessage("MozApplicationManifest", {
+        sendAsyncMessage("Browser:MozApplicationManifest", {
           location: doc.documentURIObject.spec,
           manifest: doc.documentElement.getAttribute("manifest"),
           charset: doc.characterSet
         });
         break;
       }
+      case "scroll":
+        sendSyncMessage("Browser:PageScroll", {});
+        break;
     }
   },
 
   /** Next scroll size change event will invalidate all previous content. See constructor. */
   emptyPage: function emptyPage() {
     this._incremental = false;
   },
 
@@ -255,18 +258,18 @@ Coalescer.prototype = {
   stopCoalescing: function stopCoalescing() {
     this._timer.flush();
   },
 
   sizeChange: function sizeChange(scrollOffset, x, y, width, height) {
     // Adjust width and height from the incoming event properties so that we
     // ignore changes to width and height contributed by growth in page
     // quadrants other than x > 0 && y > 0.
-    var x = x + scrollOffset.x;
-    var y = y + scrollOffset.y;
+    x = x + scrollOffset.x;
+    y = y + scrollOffset.y;
     this._pendingSizeChange = {
       width: width + (x < 0 ? x : 0),
       height: height + (y < 0 ? y : 0)
     };
 
     // Clear any pending dirty rectangles since entire viewport will be invalidated
     // anyways.
     var rect = this._pendingDirtyRect;
@@ -287,30 +290,34 @@ Coalescer.prototype = {
       }
 
       if (!this._timer.isPending())
         this.flush()
     }
   },
 
   flush: function flush() {
-    var dirtyRect = this._pendingDirtyRect;
-    var sizeChange = this._pendingSizeChange;
+    let dirtyRect = this._pendingDirtyRect;
+    let sizeChange = this._pendingSizeChange;
     if (sizeChange) {
-      sendMessage("FennecMozScrolledAreaChanged", sizeChange.width, sizeChange.height);
+      sendSyncMessage("Browser:MozScrolledAreaChanged", { width: sizeChange.width, height: sizeChange.height });
       if (!this._incremental)
-        sendMessage("FennecMozAfterPaint", [new Rect(0, 0, sizeChange.width, sizeChange.height)]);
+        sendSyncMessage("Browser:MozAfterPaint", { rects: [ { left: 0, top: 0, right: sizeChange.width, bottom: sizeChange.height } ] });
+
       this._pendingSizeChange = null;
+
       // After first size change has been issued, assume subsequent size changes are only incremental
       // changes to the current page.
       this._incremental = true;
     }
     else if (!dirtyRect.isEmpty()) {
       // No size change has occurred, but areas have been dirtied.
-      sendMessage("FennecMozAfterPaint", [dirtyRect]);
+      sendSyncMessage("Browser:MozAfterPaint", { rects: [dirtyRect] });
+
+      // Reset the rect to empty
       dirtyRect.top = dirtyRect.bottom;
       dirtyRect.left = dirtyRect.right;
     }
   }
 };
 
 
 /**
@@ -677,16 +684,17 @@ function Content() {
   addMessageListener("Browser:Mouseup", this);
   addMessageListener("Browser:CancelMouse", this);
   addMessageListener("Browser:SaveAs", this);
 
   this._coalescer = new Coalescer();
   addEventListener("MozAfterPaint", this._coalescer, false);
   addEventListener("MozScrolledAreaChanged", this._coalescer, false);
   addEventListener("MozApplicationManifest", this._coalescer, false);
+  addEventListener("scroll", this._coalescer, false);
 
   this._progressController = new ProgressController(this);
   this._progressController.start();
 
   this._contentFormManager = new ContentFormManager();
 
   this._mousedownTimeout = new Util.Timeout();
 }