diff --git a/mobile/android/base/gfx/GeckoLayerClient.java b/mobile/android/base/gfx/GeckoLayerClient.java index e58945626a5..eed593ebf96 100644 --- a/mobile/android/base/gfx/GeckoLayerClient.java +++ b/mobile/android/base/gfx/GeckoLayerClient.java @@ -109,6 +109,7 @@ public class GeckoLayerClient implements GeckoEventResponder, mLayerRenderer = new LayerRenderer(view); GeckoAppShell.registerGeckoEventListener("Viewport:Update", this); + GeckoAppShell.registerGeckoEventListener("Viewport:PageSize", this); GeckoAppShell.registerGeckoEventListener("Viewport:CalculateDisplayPort", this); GeckoAppShell.registerGeckoEventListener("Checkerboard:Toggle", this); @@ -242,25 +243,55 @@ public class GeckoLayerClient implements GeckoEventResponder, mGeckoViewport = viewportMetrics; } + /** + * The different types of Viewport messages handled. All viewport events + * expect a display-port to be returned, but can handle one not being + * returned. + */ + private enum ViewportMessageType { + UPDATE, // The viewport has changed and should be entirely updated + PAGE_SIZE // The viewport's page-size has changed + } + + /** Viewport message handler. */ + private void handleViewportMessage(JSONObject message, ViewportMessageType type) throws JSONException { + ViewportMetrics messageMetrics = new ViewportMetrics(message); + synchronized (mLayerController) { + final ViewportMetrics newMetrics; + ImmutableViewportMetrics oldMetrics = mLayerController.getViewportMetrics(); + + switch (type) { + default: + case UPDATE: + newMetrics = messageMetrics; + // Keep the old viewport size + newMetrics.setSize(oldMetrics.getSize()); + mLayerController.abortPanZoomAnimation(); + break; + case PAGE_SIZE: + newMetrics = new ViewportMetrics(oldMetrics); + newMetrics.setPageSize(messageMetrics.getPageSize()); + break; + } + + mLayerController.post(new Runnable() { + public void run() { + mGeckoViewport = newMetrics; + } + }); + mLayerController.setViewportMetrics(newMetrics); + mDisplayPort = calculateDisplayPort(mLayerController.getViewportMetrics()); + } + mReturnDisplayPort = mDisplayPort; + } + /** Implementation of GeckoEventResponder/GeckoEventListener. */ public void handleMessage(String event, JSONObject message) { try { if ("Viewport:Update".equals(event)) { - final ViewportMetrics newMetrics = new ViewportMetrics(message); - synchronized (mLayerController) { - // keep the old viewport size, but update everything else - ImmutableViewportMetrics oldMetrics = mLayerController.getViewportMetrics(); - newMetrics.setSize(oldMetrics.getSize()); - mLayerController.post(new Runnable() { - public void run() { - mGeckoViewport = newMetrics; - } - }); - mLayerController.setViewportMetrics(newMetrics); - mLayerController.abortPanZoomAnimation(); - mDisplayPort = calculateDisplayPort(mLayerController.getViewportMetrics()); - mReturnDisplayPort = mDisplayPort; - } + handleViewportMessage(message, ViewportMessageType.UPDATE); + } else if ("Viewport:PageSize".equals(event)) { + handleViewportMessage(message, ViewportMessageType.PAGE_SIZE); } else if ("Viewport:CalculateDisplayPort".equals(event)) { ImmutableViewportMetrics newMetrics = new ImmutableViewportMetrics(new ViewportMetrics(message)); mReturnDisplayPort = calculateDisplayPort(newMetrics); diff --git a/mobile/android/chrome/content/browser.js b/mobile/android/chrome/content/browser.js index afbfee544a2..80fa80cb3c0 100644 --- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -1515,6 +1515,7 @@ Tab.prototype = { this.browser.addEventListener("DOMWindowClose", this, true); this.browser.addEventListener("DOMWillOpenModalDialog", this, true); this.browser.addEventListener("scroll", this, true); + this.browser.addEventListener("MozScrolledAreaChanged", this, true); this.browser.addEventListener("PluginClickToPlay", this, true); this.browser.addEventListener("pagehide", this, true); this.browser.addEventListener("pageshow", this, true); @@ -1562,6 +1563,7 @@ Tab.prototype = { this.browser.removeEventListener("DOMWillOpenModalDialog", this, true); this.browser.removeEventListener("scroll", this, true); this.browser.removeEventListener("PluginClickToPlay", this, true); + this.browser.removeEventListener("MozScrolledAreaChanged", this, true); this.browser.removeEventListener("pagehide", this, true); this.browser.removeEventListener("pageshow", this, true); @@ -1689,17 +1691,16 @@ Tab.prototype = { return viewport; }, - sendViewportUpdate: function() { + sendViewportUpdate: function(aPageSizeUpdate) { let message; - if (BrowserApp.selectedTab == this) { - // for foreground tabs, send the viewport update unless the document - // displayed is different from the content document - if (!BrowserApp.isBrowserContentDocumentDisplayed()) - return; + // for foreground tabs, send the viewport update unless the document + // displayed is different from the content document. In that case, just + // calculate the display port. + if (BrowserApp.selectedTab == this && BrowserApp.isBrowserContentDocumentDisplayed()) { message = this.getViewport(); - message.type = "Viewport:Update"; + message.type = aPageSizeUpdate ? "Viewport:PageSize" : "Viewport:Update"; } else { - // for bcakground tabs, request a new display port calculation, so that + // for background tabs, request a new display port calculation, so that // when we do switch to that tab, we have the correct display port and // don't need to draw twice (once to allow the first-paint viewport to // get to java, and again once java figures out the display port). @@ -1859,6 +1860,17 @@ Tab.prototype = { break; } + case "MozScrolledAreaChanged": { + // This event is only fired for root scroll frames, and only when the + // scrolled area has actually changed, so no need to check for that. + // Just make sure it's the event for the correct root scroll frame. + if (aEvent.originalTarget != this.browser.contentDocument) + return; + + this.sendViewportUpdate(true); + break; + } + case "PluginClickToPlay": { // Keep track of the number of plugins to know whether or not to show // the hidden plugins doorhanger @@ -2194,11 +2206,12 @@ Tab.prototype = { // and then use the metadata to figure out how it needs to be updated ViewportHandler.updateMetadata(this); - // The document element must have a display port on it whenever we are about to - // paint. This is the point just before the first paint, so we set the display port - // to a default value here. Once Java is aware of this document it will overwrite - // it with a better-calculated display port. - this.setDisplayPort(0, 0, {left: 0, top: 0, right: gScreenWidth, bottom: gScreenHeight }); + // If we draw without a display-port, things can go wrong. While it's + // almost certain a display-port has been set via the + // MozScrolledAreaChanged event, make sure by sending a viewport + // update here. As it's the first paint, this will end up being a + // display-port request only. + this.sendViewportUpdate(); BrowserApp.displayedDocumentChanged(); this.contentDocumentIsDisplayed = true;