diff --git a/gfx/layers/ipc/CompositorParent.cpp b/gfx/layers/ipc/CompositorParent.cpp index b711b63ed63..0c3a12163b0 100644 --- a/gfx/layers/ipc/CompositorParent.cpp +++ b/gfx/layers/ipc/CompositorParent.cpp @@ -52,9 +52,9 @@ namespace mozilla { namespace layers { CompositorParent::CompositorParent(nsIWidget* aWidget) - : mPaused(false) - , mWidget(aWidget) + : mWidget(aWidget) , mCurrentCompositeTask(NULL) + , mPaused(false) { MOZ_COUNT_CTOR(CompositorParent); } @@ -104,13 +104,11 @@ CompositorParent::PauseComposition() void CompositorParent::ResumeComposition() { - if (mPaused) { - mPaused = false; + mPaused = false; #ifdef MOZ_WIDGET_ANDROID - static_cast(mLayerManager.get())->gl()->RenewSurface(); + static_cast(mLayerManager.get())->gl()->RenewSurface(); #endif - } } void @@ -184,13 +182,49 @@ CompositorParent::Composite() printf_stderr("Correcting for position fixed %i, %i\n", -mScrollOffset.x, -mScrollOffset.y); worldTransform.Translate(offset); worldTransform.Scale(mXScale, mYScale, 1.0f); - Layer* root = mLayerManager->GetRoot(); - root->AsShadowLayer()->SetShadowTransform(worldTransform); +#ifdef MOZ_WIDGET_ANDROID + Layer* layer = GetPrimaryScrollableLayer(); +#else + Layer* layer = mLayerManager->GetRoot(); +#endif + layer->AsShadowLayer()->SetShadowTransform(worldTransform); mLayerManager->EndEmptyTransaction(); mLastCompose = mozilla::TimeStamp::Now(); } +#ifdef MOZ_WIDGET_ANDROID +// Do a breadth-first search to find the first layer in the tree with a +// displayport set. +Layer* +CompositorParent::GetPrimaryScrollableLayer() +{ + Layer* root = mLayerManager->GetRoot(); + + nsTArray queue; + queue.AppendElement(root); + for (int i = 0; i < queue.Length(); i++) { + ContainerLayer* containerLayer = queue[i]->AsContainerLayer(); + if (!containerLayer) { + continue; + } + + const FrameMetrics& frameMetrics = containerLayer->GetFrameMetrics(); + if (!frameMetrics.mDisplayPort.IsEmpty()) { + return containerLayer; + } + + Layer* child = containerLayer->GetFirstChild(); + while (child) { + queue.AppendElement(child); + child = child->GetNextSibling(); + } + } + + return root; +} +#endif + // Go down shadow layer tree, setting properties to match their non-shadow // counterparts. static void @@ -383,12 +417,6 @@ CompositorParent::TestScroll() PLayersParent* CompositorParent::AllocPLayers(const LayersBackend &backendType) { -#ifdef MOZ_WIDGET_ANDROID - // Registering with the compositor will create the surface view that - // the layer manager expects to attach to. - //RegisterCompositorWithJava(); -#endif - if (backendType == LayerManager::LAYERS_OPENGL) { nsRefPtr layerManager = new LayerManagerOGL(mWidget); mWidget = NULL; @@ -417,14 +445,6 @@ CompositorParent::DeallocPLayers(PLayersParent* actor) return true; } -#ifdef MOZ_WIDGET_ANDROID -void -CompositorParent::RegisterCompositorWithJava() -{ - mozilla::AndroidBridge::Bridge()->RegisterCompositor(); -} -#endif - } // namespace layers } // namespace mozilla diff --git a/gfx/layers/ipc/CompositorParent.h b/gfx/layers/ipc/CompositorParent.h index 27b28a9a518..25f03f1ccc4 100644 --- a/gfx/layers/ipc/CompositorParent.h +++ b/gfx/layers/ipc/CompositorParent.h @@ -114,18 +114,17 @@ private: // Platform specific functions #ifdef MOZ_WIDGET_ANDROID - /** - * Register the compositor thread with the Java native thread. - * This will replace the temporary compositor with the real - * Gecko compositor thread. - **/ - void RegisterCompositorWithJava(); - /** * Asks Java for the viewport position and updates the world transform * accordingly. */ void RequestViewTransform(); + + /** + * Does a breadth-first search to find the first layer in the tree with a + * displayport set. + */ + Layer* GetPrimaryScrollableLayer(); #endif nsRefPtr mLayerManager; diff --git a/gfx/layers/opengl/LayerManagerOGL.cpp b/gfx/layers/opengl/LayerManagerOGL.cpp index c61754fd401..15381cbc1f7 100644 --- a/gfx/layers/opengl/LayerManagerOGL.cpp +++ b/gfx/layers/opengl/LayerManagerOGL.cpp @@ -782,18 +782,7 @@ LayerManagerOGL::Render() mGLContext->fEnable(LOCAL_GL_SCISSOR_TEST); - static int i = 0; - i++; - i=i%3; - if( i == 0 ) { - mGLContext->fClearColor(1.0, 0.0, 0.0, 0.0); - } else if( i == 1 ) { - mGLContext->fClearColor(0.0, 0.0, 0.0, 0.0); - } else if( i == 2 ) { - mGLContext->fClearColor(1.0, 1.0, 0.0, 0.0); - } else { - mGLContext->fClearColor(0.0, 1.0, 0.0, 0.0); - } + mGLContext->fClearColor(1.0, 1.0, 1.0, 0.0); mGLContext->fClear(LOCAL_GL_COLOR_BUFFER_BIT | LOCAL_GL_DEPTH_BUFFER_BIT); // Render our layers. diff --git a/layout/base/FrameLayerBuilder.cpp b/layout/base/FrameLayerBuilder.cpp index fac2737d7f0..3df09799b2e 100644 --- a/layout/base/FrameLayerBuilder.cpp +++ b/layout/base/FrameLayerBuilder.cpp @@ -871,6 +871,8 @@ ContainerState::CreateOrRecycleThebesLayer(nsIFrame* aActiveScrolledRoot) matrix.Translate(gfxPoint(pixOffset.x, pixOffset.y)); layer->SetTransform(gfx3DMatrix::From2D(matrix)); + // FIXME: Temporary workaround for bug 681192 and bug 724786. Uncomment this code before review! +#if 0 // Calculate exact position of the top-left of the active scrolled root. // This might not be 0,0 due to the snapping in ScaleToNearestPixels. gfxPoint activeScrolledRootTopLeft = scaledOffset - matrix.GetTranslation(); @@ -882,6 +884,7 @@ ContainerState::CreateOrRecycleThebesLayer(nsIFrame* aActiveScrolledRoot) nsIntRect invalidate = layer->GetValidRegion().GetBounds(); layer->InvalidateRegion(invalidate); } +#endif return layer.forget(); } diff --git a/layout/generic/nsGfxScrollFrame.cpp b/layout/generic/nsGfxScrollFrame.cpp index 547d931e0c1..f78d2dd08a6 100644 --- a/layout/generic/nsGfxScrollFrame.cpp +++ b/layout/generic/nsGfxScrollFrame.cpp @@ -2043,16 +2043,18 @@ nsGfxScrollFrameInner::BuildDisplayList(nsDisplayListBuilder* aBuilder, NS_ENSURE_SUCCESS(rv, rv); // Since making new layers is expensive, only use nsDisplayScrollLayer - // if the area is scrollable. + // if the area is scrollable and there's a displayport (or we're the content + // process). nsRect scrollRange = GetScrollRange(); ScrollbarStyles styles = GetScrollbarStylesFromFrame(); mShouldBuildLayer = - (XRE_GetProcessType() == GeckoProcessType_Content && + ((XRE_GetProcessType() == GeckoProcessType_Content || usingDisplayport) && (styles.mHorizontal != NS_STYLE_OVERFLOW_HIDDEN || styles.mVertical != NS_STYLE_OVERFLOW_HIDDEN) && (scrollRange.width > 0 || scrollRange.height > 0) && - (!mIsRoot || !mOuter->PresContext()->IsRootContentDocument())); + (usingDisplayport || !mIsRoot || + !mOuter->PresContext()->IsRootContentDocument())); if (ShouldBuildLayer()) { // ScrollLayerWrapper must always be created because it initializes the diff --git a/mobile/android/base/gfx/FlexibleGLSurfaceView.java b/mobile/android/base/gfx/FlexibleGLSurfaceView.java index 8a1883fdb27..a07ea8a888b 100644 --- a/mobile/android/base/gfx/FlexibleGLSurfaceView.java +++ b/mobile/android/base/gfx/FlexibleGLSurfaceView.java @@ -157,8 +157,7 @@ public class FlexibleGLSurfaceView extends SurfaceView implements SurfaceHolder. } if (mListener != null) { - mListener.compositionResumeRequested(); - mListener.renderRequested(); + mListener.surfaceChanged(width, height); } } @@ -203,6 +202,7 @@ public class FlexibleGLSurfaceView extends SurfaceView implements SurfaceHolder. void renderRequested(); void compositionPauseRequested(); void compositionResumeRequested(); + void surfaceChanged(int width, int height); } public static class FlexibleGLSurfaceViewException extends RuntimeException { diff --git a/mobile/android/base/gfx/GeckoGLLayerClient.java b/mobile/android/base/gfx/GeckoGLLayerClient.java index 3ba9992b6e0..2207067a796 100644 --- a/mobile/android/base/gfx/GeckoGLLayerClient.java +++ b/mobile/android/base/gfx/GeckoGLLayerClient.java @@ -203,5 +203,13 @@ public class GeckoGLLayerClient extends GeckoLayerClient Log.e(LOGTAG, "### Scheduling ResumeComposition"); GeckoAppShell.scheduleResumeComposition(); } + + public void surfaceChanged(int width, int height) { + compositionPauseRequested(); + LayerController layerController = getLayerController(); + layerController.setViewportSize(new FloatSize(width, height)); + compositionResumeRequested(); + renderRequested(); + } } diff --git a/mobile/android/base/gfx/GeckoLayerClient.java b/mobile/android/base/gfx/GeckoLayerClient.java index 42e79e9c13f..648f0665862 100644 --- a/mobile/android/base/gfx/GeckoLayerClient.java +++ b/mobile/android/base/gfx/GeckoLayerClient.java @@ -74,7 +74,7 @@ public abstract class GeckoLayerClient extends LayerClient implements GeckoEvent /* The viewport that Gecko will display when drawing is finished */ protected ViewportMetrics mNewGeckoViewport; - private static final long MIN_VIEWPORT_CHANGE_DELAY = 200L; + private static final long MIN_VIEWPORT_CHANGE_DELAY = 25L; private long mLastViewportChangeTime; private boolean mPendingViewportAdjust; private boolean mViewportSizeChanged; @@ -265,8 +265,10 @@ public abstract class GeckoLayerClient extends LayerClient implements GeckoEvent } private void adjustViewportWithThrottling() { - if (!getLayerController().getRedrawHint()) - return; + // FIXME: Allow redraw while a finger is down, but only if we're about to checkerboard. + // This requires fixing aboutToCheckerboard() to know about the new buffer size. + /*if (!getLayerController().getRedrawHint()) + return;*/ if (mPendingViewportAdjust) return; diff --git a/mobile/android/chrome/content/browser.js b/mobile/android/chrome/content/browser.js index 1bc6fbc2744..50d5755b240 100644 --- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -102,6 +102,12 @@ const kElementsReceivingInput = { video: true }; +// How many pixels on each side to buffer. +const kBufferAmount = 300; + +// Whether we're using GL layers. +const kUsingGLLayers = true; + function dump(a) { Cc["@mozilla.org/consoleservice;1"].getService(Ci.nsIConsoleService).logStringMessage(a); } @@ -863,27 +869,20 @@ var BrowserApp = { top: focusedRect.top - tab.viewportExcess.y, bottom: focusedRect.bottom - tab.viewportExcess.y }; - let transformChanged = false; if (focusedRect.right >= visibleContentWidth && focusedRect.left > 0) { // the element is too far off the right side, so we need to scroll to the right more tab.viewportExcess.x += Math.min(focusedRect.left, focusedRect.right - visibleContentWidth); - transformChanged = true; } else if (focusedRect.left < 0) { // the element is too far off the left side, so we need to scroll to the left more tab.viewportExcess.x += focusedRect.left; - transformChanged = true; } if (focusedRect.bottom >= visibleContentHeight && focusedRect.top > 0) { // the element is too far down, so we need to scroll down more tab.viewportExcess.y += Math.min(focusedRect.top, focusedRect.bottom - visibleContentHeight); - transformChanged = true; } else if (focusedRect.top < 0) { // the element is too far up, so we need to scroll up more tab.viewportExcess.y += focusedRect.top; - transformChanged = true; } - if (transformChanged) - tab.updateTransform(); // finally, let java know where we ended up tab.sendViewportUpdate(); } @@ -1409,6 +1408,8 @@ let gTabIDFactory = 0; let gScreenWidth = 1; let gScreenHeight = 1; +let gBrowserWidth = null; + function Tab(aURL, aParams) { this.browser = null; this.vbox = null; @@ -1438,14 +1439,19 @@ Tab.prototype = { this.browser = document.createElement("browser"); this.browser.setAttribute("type", "content-targetable"); this.setBrowserSize(980, 480); - this.browser.style.MozTransformOrigin = "0 0"; + this.browser.style.width = gScreenWidth + "px"; + this.browser.style.height = gScreenHeight + "px"; this.vbox.appendChild(this.browser); this.browser.stop(); - // Turn off clipping so we can buffer areas outside of the browser element. let frameLoader = this.browser.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader; - frameLoader.clipSubdocument = false; + if (kUsingGLLayers) { + frameLoader.renderMode = Ci.nsIFrameLoader.RENDER_MODE_ASYNC_SCROLL; + } else { + // Turn off clipping so we can buffer areas outside of the browser element. + frameLoader.clipSubdocument = false; + } this.id = ++gTabIDFactory; @@ -1568,27 +1574,19 @@ Tab.prototype = { this._viewport.height = gScreenHeight = aViewport.height; dump("### gScreenWidth = " + gScreenWidth + "\n"); - let transformChanged = false; - if ((aViewport.offsetX != this._viewport.offsetX) || (excessX != this.viewportExcess.x)) { this._viewport.offsetX = aViewport.offsetX; this.viewportExcess.x = excessX; - transformChanged = true; } if ((aViewport.offsetY != this._viewport.offsetY) || (excessY != this.viewportExcess.y)) { this._viewport.offsetY = aViewport.offsetY; this.viewportExcess.y = excessY; - transformChanged = true; } if (Math.abs(aViewport.zoom - this._viewport.zoom) >= 1e-6) { this._viewport.zoom = aViewport.zoom; - transformChanged = true; } - - if (transformChanged) - this.updateTransform(); }, screenshot: function(aSrc, aDst) { @@ -1619,20 +1617,6 @@ Tab.prototype = { }, Ci.nsIThread.DISPATCH_NORMAL); }, - updateTransform: function() { - let hasZoom = (Math.abs(this._viewport.zoom - 1.0) >= 1e-6); - let x = this._viewport.offsetX + Math.round(-this.viewportExcess.x * this._viewport.zoom); - let y = this._viewport.offsetY + Math.round(-this.viewportExcess.y * this._viewport.zoom); - - let transform = - "translate(" + x + "px, " + - y + "px)"; - if (hasZoom) - transform += " scale(" + this._viewport.zoom + ")"; - - this.browser.style.MozTransform = transform; - }, - get viewport() { // Update the viewport to current dimensions this._viewport.x = (this.browser.contentWindow.scrollX + @@ -1747,6 +1731,14 @@ Tab.prototype = { if (this._pluginCount && !this._pluginOverlayShowing) PluginHelper.showDoorHanger(this); + // FIXME: This should not be in DOMContentLoaded; it should happen earlier. + let cwu = this.browser.contentWindow.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindowUtils); + cwu.setDisplayPortForElement(-kBufferAmount, -kBufferAmount, + gScreenWidth + kBufferAmount * 2, + gScreenHeight + kBufferAmount * 2, + this.browser.contentDocument.documentElement); + break; } @@ -2068,7 +2060,7 @@ Tab.prototype = { let minScale = this.getPageZoomLevel(screenW); viewportH = Math.max(viewportH, screenH / minScale); - let oldBrowserWidth = parseInt(this.browser.style.width); + let oldBrowserWidth = gBrowserWidth; this.setBrowserSize(viewportW, viewportH); // Avoid having the scroll position jump around after device rotation. @@ -2079,7 +2071,7 @@ Tab.prototype = { // If the browser width changes, we change the zoom proportionally. This ensures sensible // behavior when rotating the device on pages with automatically-resizing viewports. - if (viewportW == oldBrowserWidth) + if (oldBrowserWidth == null || viewportW == oldBrowserWidth) return; let viewport = this.viewport; @@ -2092,9 +2084,8 @@ Tab.prototype = { if ("defaultZoom" in md && md.defaultZoom) return md.defaultZoom; - let browserWidth = parseInt(this.browser.style.width); dump("### getDefaultZoomLevel gScreenWidth=" + gScreenWidth); - return gScreenWidth / browserWidth; + return gScreenWidth / gBrowserWidth; }, getPageZoomLevel: function getPageZoomLevel() { @@ -2107,8 +2098,8 @@ Tab.prototype = { }, setBrowserSize: function(aWidth, aHeight) { - this.browser.style.width = aWidth + "px"; - this.browser.style.height = aHeight + "px"; + // TODO: Use nsIDOMWindowUtils::SetCSSViewport() here. + gBrowserWidth = aWidth; }, getRequestLoadContext: function(aRequest) {