From 1f49af4fc0d86f472af171048542842504d6ee92 Mon Sep 17 00:00:00 2001 From: Wes Kocher Date: Thu, 25 Aug 2016 11:29:35 -0700 Subject: [PATCH] Backed out 2 changesets (bug 1297850) for robocop bustage a=backout CLOSED TREE Backed out changeset e83c9eb279a9 (bug 1297850) Backed out changeset 979694026137 (bug 1297850) --- gfx/layers/client/ClientLayerManager.cpp | 28 +++ gfx/layers/client/ClientLayerManager.h | 16 ++ .../base/java/org/mozilla/gecko/GeckoApp.java | 8 + .../java/org/mozilla/gecko/PrivateTab.java | 5 + .../base/java/org/mozilla/gecko/Tab.java | 41 ++++ .../base/java/org/mozilla/gecko/Tabs.java | 13 ++ .../org/mozilla/gecko/prompts/Prompt.java | 4 + .../java/org/mozilla/gecko/GeckoAppShell.java | 14 ++ .../mozilla/gecko/gfx/GeckoLayerClient.java | 213 ++++++++++++++++++ .../java/org/mozilla/gecko/gfx/LayerView.java | 36 +++ .../gecko/gfx/NativePanZoomController.java | 47 ++++ .../mozilla/gecko/gfx/PanZoomController.java | 13 ++ .../org/mozilla/gecko/gfx/PanZoomTarget.java | 1 + widget/android/AndroidBridge.cpp | 27 +++ widget/android/AndroidBridge.h | 3 + widget/android/GeneratedJNINatives.h | 6 +- widget/android/GeneratedJNIWrappers.cpp | 19 ++ widget/android/GeneratedJNIWrappers.h | 63 ++++++ widget/android/nsWindow.cpp | 19 ++ 19 files changed, 575 insertions(+), 1 deletion(-) diff --git a/gfx/layers/client/ClientLayerManager.cpp b/gfx/layers/client/ClientLayerManager.cpp index 0acc69260566..68d5d2ca48d9 100644 --- a/gfx/layers/client/ClientLayerManager.cpp +++ b/gfx/layers/client/ClientLayerManager.cpp @@ -795,6 +795,34 @@ ClientLayerManager::GetBackendName(nsAString& aName) } } +bool +ClientLayerManager::ProgressiveUpdateCallback(bool aHasPendingNewThebesContent, + FrameMetrics& aMetrics, + bool aDrawingCritical) +{ +#ifdef MOZ_WIDGET_ANDROID + MOZ_ASSERT(aMetrics.IsScrollable()); + // This is derived from the code in + // gfx/layers/ipc/CompositorBridgeParent.cpp::TransformShadowTree. + CSSToLayerScale paintScale = aMetrics.LayersPixelsPerCSSPixel().ToScaleFactor(); + const CSSRect& metricsDisplayPort = + (aDrawingCritical && !aMetrics.GetCriticalDisplayPort().IsEmpty()) ? + aMetrics.GetCriticalDisplayPort() : aMetrics.GetDisplayPort(); + LayerRect displayPort = (metricsDisplayPort + aMetrics.GetScrollOffset()) * paintScale; + + ParentLayerPoint scrollOffset; + CSSToParentLayerScale zoom; + bool ret = AndroidBridge::Bridge()->ProgressiveUpdateCallback( + aHasPendingNewThebesContent, displayPort, paintScale.scale, aDrawingCritical, + scrollOffset, zoom); + aMetrics.SetScrollOffset(scrollOffset / zoom); + aMetrics.SetZoom(CSSToParentLayerScale2D(zoom)); + return ret; +#else + return false; +#endif +} + bool ClientLayerManager::AsyncPanZoomEnabled() const { diff --git a/gfx/layers/client/ClientLayerManager.h b/gfx/layers/client/ClientLayerManager.h index ad9f9ea71ea0..6fd1d8ca1304 100644 --- a/gfx/layers/client/ClientLayerManager.h +++ b/gfx/layers/client/ClientLayerManager.h @@ -155,6 +155,22 @@ public: // Disable component alpha layers with the software compositor. virtual bool ShouldAvoidComponentAlphaLayers() override { return !IsCompositingCheap(); } + /** + * Called for each iteration of a progressive tile update. Updates + * aMetrics with the current scroll offset and scale being used to composite + * the primary scrollable layer in this manager, to determine what area + * intersects with the target composition bounds. + * aDrawingCritical will be true if the current drawing operation is using + * the critical displayport. + * Returns true if the update should continue, or false if it should be + * cancelled. + * This is only called if gfxPlatform::UseProgressiveTilePainting() returns + * true. + */ + bool ProgressiveUpdateCallback(bool aHasPendingNewThebesContent, + FrameMetrics& aMetrics, + bool aDrawingCritical); + bool InConstruction() { return mPhase == PHASE_CONSTRUCTION; } #ifdef DEBUG bool InDrawing() { return mPhase == PHASE_DRAWING; } diff --git a/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java b/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java index 19688e72360e..7f037d8d735c 100644 --- a/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java +++ b/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java @@ -367,6 +367,14 @@ public abstract class GeckoApp mFormAssistPopup.hide(); break; + case LOADED: + // Sync up the layer view and the tab if the tab is + // currently displayed. + LayerView layerView = mLayerView; + if (layerView != null && Tabs.getInstance().isSelectedTab(tab)) + layerView.setBackgroundColor(tab.getBackgroundColor()); + break; + case DESKTOP_MODE_CHANGE: if (Tabs.getInstance().isSelectedTab(tab)) invalidateOptionsMenu(); diff --git a/mobile/android/base/java/org/mozilla/gecko/PrivateTab.java b/mobile/android/base/java/org/mozilla/gecko/PrivateTab.java index f3a9bcb25cb0..cca46f17f0b2 100644 --- a/mobile/android/base/java/org/mozilla/gecko/PrivateTab.java +++ b/mobile/android/base/java/org/mozilla/gecko/PrivateTab.java @@ -13,6 +13,11 @@ import org.mozilla.gecko.db.BrowserDB; public class PrivateTab extends Tab { public PrivateTab(Context context, int id, String url, boolean external, int parentId, String title) { super(context, id, url, external, parentId, title); + + // Init background to private_toolbar_grey to ensure flicker-free + // private tab creation. Page loads will reset it to white as expected. + final int bgColor = ContextCompat.getColor(context, R.color.tabs_tray_grey_pressed); + setBackgroundColor(bgColor); } @Override diff --git a/mobile/android/base/java/org/mozilla/gecko/Tab.java b/mobile/android/base/java/org/mozilla/gecko/Tab.java index 425f7083b83a..e033915f9f7d 100644 --- a/mobile/android/base/java/org/mozilla/gecko/Tab.java +++ b/mobile/android/base/java/org/mozilla/gecko/Tab.java @@ -74,6 +74,7 @@ public class Tab { private ZoomConstraints mZoomConstraints; private boolean mIsRTL; private final ArrayList mPluginViews; + private int mBackgroundColor; private int mState; private Bitmap mThumbnailBitmap; private boolean mDesktopMode; @@ -115,6 +116,8 @@ public class Tab { public static final int LOAD_PROGRESS_LOADED = 80; public static final int LOAD_PROGRESS_STOP = 100; + private static final int DEFAULT_BACKGROUND_COLOR = Color.WHITE; + public enum ErrorType { CERT_ERROR, // Pages with certificate problems BLOCKED, // Pages blocked for phishing or malware warnings @@ -140,6 +143,11 @@ public class Tab { mState = shouldShowProgress(url) ? STATE_LOADING : STATE_SUCCESS; mLoadProgress = LOAD_PROGRESS_INIT; + // At startup, the background is set to a color specified by LayerView + // when the LayerView is created. Shortly after, this background color + // will be used before the tab's content is shown. + mBackgroundColor = DEFAULT_BACKGROUND_COLOR; + updateBookmark(); } @@ -690,6 +698,7 @@ public class Tab { setSiteLogins(null); setZoomConstraints(new ZoomConstraints(true)); setHasTouchListeners(false); + setBackgroundColor(DEFAULT_BACKGROUND_COLOR); setErrorType(ErrorType.NONE); setLoadProgressIfLoading(LOAD_PROGRESS_LOCATION_CHANGE); @@ -794,6 +803,38 @@ public class Tab { return mPluginViews.toArray(new View[mPluginViews.size()]); } + public int getBackgroundColor() { + return mBackgroundColor; + } + + /** Sets a new color for the background. */ + public void setBackgroundColor(int color) { + mBackgroundColor = color; + } + + /** Parses and sets a new color for the background. */ + public void setBackgroundColor(String newColor) { + setBackgroundColor(parseColorFromGecko(newColor)); + } + + // Parses a color from an RGB triple of the form "rgb([0-9]+, [0-9]+, [0-9]+)". If the color + // cannot be parsed, returns white. + private static int parseColorFromGecko(String string) { + if (sColorPattern == null) { + sColorPattern = Pattern.compile("rgb\\((\\d+),\\s*(\\d+),\\s*(\\d+)\\)"); + } + + Matcher matcher = sColorPattern.matcher(string); + if (!matcher.matches()) { + return Color.WHITE; + } + + int r = Integer.parseInt(matcher.group(1)); + int g = Integer.parseInt(matcher.group(2)); + int b = Integer.parseInt(matcher.group(3)); + return Color.rgb(r, g, b); + } + public void setDesktopMode(boolean enabled) { mDesktopMode = enabled; } diff --git a/mobile/android/base/java/org/mozilla/gecko/Tabs.java b/mobile/android/base/java/org/mozilla/gecko/Tabs.java index 2c7451048924..e4b80342639a 100644 --- a/mobile/android/base/java/org/mozilla/gecko/Tabs.java +++ b/mobile/android/base/java/org/mozilla/gecko/Tabs.java @@ -111,6 +111,7 @@ public class Tabs implements GeckoEventListener { "Content:StateChange", "Content:LoadError", "Content:PageShow", + "DOMContentLoaded", "DOMTitleChanged", "Link:Favicon", "Link:Feed", @@ -517,6 +518,18 @@ public class Tabs implements GeckoEventListener { tab.setLoadedFromCache(message.getBoolean("fromCache")); tab.updateUserRequested(message.getString("userRequested")); notifyListeners(tab, TabEvents.PAGE_SHOW); + } else if (event.equals("DOMContentLoaded")) { + tab.handleContentLoaded(); + String backgroundColor = message.getString("bgColor"); + if (backgroundColor != null) { + tab.setBackgroundColor(backgroundColor); + } else { + // Default to white if no color is given + tab.setBackgroundColor(Color.WHITE); + } + tab.setErrorType(message.optString("errorType")); + tab.setMetadata(message.optJSONObject("metadata")); + notifyListeners(tab, Tabs.TabEvents.LOADED); } else if (event.equals("DOMTitleChanged")) { tab.updateTitle(message.getString("title")); } else if (event.equals("Link:Favicon")) { diff --git a/mobile/android/base/java/org/mozilla/gecko/prompts/Prompt.java b/mobile/android/base/java/org/mozilla/gecko/prompts/Prompt.java index 11121b2cce7d..401c9a999174 100644 --- a/mobile/android/base/java/org/mozilla/gecko/prompts/Prompt.java +++ b/mobile/android/base/java/org/mozilla/gecko/prompts/Prompt.java @@ -179,6 +179,10 @@ public class Prompt implements OnClickListener, OnCancelListener, OnItemClickLis private void create(String title, String text, PromptListItem[] listItems, int choiceMode) throws IllegalStateException { + final LayerView view = GeckoAppShell.getLayerView(); + if (view != null) { + view.abortPanning(); + } AlertDialog.Builder builder = new AlertDialog.Builder(mContext); if (!TextUtils.isEmpty(title)) { diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAppShell.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAppShell.java index 3e256db3beef..8d3913873f91 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAppShell.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAppShell.java @@ -1143,6 +1143,20 @@ public class GeckoAppShell }); } + @WrapForJNI(calledFrom = "gecko") + public static void notifyDefaultPrevented(final boolean defaultPrevented) { + ThreadUtils.postToUiThread(new Runnable() { + @Override + public void run() { + LayerView view = getLayerView(); + PanZoomController controller = (view == null ? null : view.getPanZoomController()); + if (controller != null) { + controller.notifyDefaultActionPrevented(defaultPrevented); + } + } + }); + } + @WrapForJNI(calledFrom = "gecko") public static boolean isNetworkLinkUp() { ConnectivityManager cm = (ConnectivityManager) diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/GeckoLayerClient.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/GeckoLayerClient.java index 6786b7a683e7..696ee6807733 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/GeckoLayerClient.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/GeckoLayerClient.java @@ -89,6 +89,8 @@ class GeckoLayerClient implements LayerView.Listener, PanZoomTarget * fields. */ private volatile ImmutableViewportMetrics mViewportMetrics; + private ZoomConstraints mZoomConstraints; + private volatile boolean mGeckoIsReady; private final PanZoomController mPanZoomController; @@ -123,8 +125,11 @@ class GeckoLayerClient implements LayerView.Listener, PanZoomTarget DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics(); mViewportMetrics = new ImmutableViewportMetrics(displayMetrics) .setViewportSize(view.getWidth(), view.getHeight()); + mZoomConstraints = new ZoomConstraints(false); + Tab tab = Tabs.getInstance().getSelectedTab(); if (tab != null) { + mZoomConstraints = tab.getZoomConstraints(); mViewportMetrics = mViewportMetrics.setIsRTL(tab.getIsRTL()); } @@ -178,6 +183,24 @@ class GeckoLayerClient implements LayerView.Listener, PanZoomTarget mGeckoIsReady = false; } + /** + * Returns true if this client is fine with performing a redraw operation or false if it + * would prefer that the action didn't take place. + */ + private boolean getRedrawHint() { + if (mForceRedraw) { + mForceRedraw = false; + return true; + } + + if (!mPanZoomController.getRedrawHint()) { + return false; + } + + return DisplayPortCalculator.aboutToCheckerboard(mViewportMetrics, + mPanZoomController.getVelocityVector(), mDisplayPort); + } + public LayerView getView() { return mView; } @@ -295,11 +318,24 @@ class GeckoLayerClient implements LayerView.Listener, PanZoomTarget post(new Runnable() { @Override public void run() { + mPanZoomController.pageRectUpdated(); mView.requestRender(); } }); } + /** Aborts any pan/zoom animation that is currently in progress. */ + private void abortPanZoomAnimation() { + if (mPanZoomController != null) { + post(new Runnable() { + @Override + public void run() { + mPanZoomController.abortAnimation(); + } + }); + } + } + /** * The different types of Viewport messages handled. All viewport events * expect a display-port to be returned, but can handle one not being @@ -310,6 +346,57 @@ class GeckoLayerClient implements LayerView.Listener, PanZoomTarget PAGE_SIZE // The viewport's page-size has changed } + /** Viewport message handler. */ + private DisplayPortMetrics handleViewportMessage(ImmutableViewportMetrics messageMetrics, ViewportMessageType type) { + synchronized (getLock()) { + ImmutableViewportMetrics newMetrics; + ImmutableViewportMetrics oldMetrics = getViewportMetrics(); + + switch (type) { + default: + case UPDATE: + // Keep the old viewport size + newMetrics = messageMetrics.setViewportSize(oldMetrics.viewportRectWidth, oldMetrics.viewportRectHeight); + if (mToolbarAnimator.isResizing()) { + // If we're in the middle of a resize, we don't want to clobber + // the scroll offset, so grab the one from the oldMetrics and + // keep using that. We also don't want to abort animations, + // because at that point we're guaranteed to not be animating + // anyway, and calling abortPanZoomAnimation has a nasty + // side-effect of clmaping and clobbering the metrics, which + // we don't want here. + newMetrics = newMetrics.setViewportOrigin(oldMetrics.viewportRectLeft, oldMetrics.viewportRectTop); + break; + } + if (!oldMetrics.fuzzyEquals(newMetrics)) { + abortPanZoomAnimation(); + } + break; + case PAGE_SIZE: + // adjust the page dimensions to account for differences in zoom + // between the rendered content (which is what Gecko tells us) + // and our zoom level (which may have diverged). + float scaleFactor = oldMetrics.zoomFactor / messageMetrics.zoomFactor; + newMetrics = oldMetrics.setPageRect(RectUtils.scale(messageMetrics.getPageRect(), scaleFactor), messageMetrics.getCssPageRect()); + break; + } + + // Update the Gecko-side viewport metrics. Make sure to do this + // before modifying the metrics below. + final ImmutableViewportMetrics geckoMetrics = newMetrics.clamp(); + post(new Runnable() { + @Override + public void run() { + mGeckoViewport = geckoMetrics; + } + }); + + setViewportMetrics(newMetrics, type == ViewportMessageType.UPDATE); + mDisplayPort = DisplayPortCalculator.calculate(getViewportMetrics(), null); + } + return mDisplayPort; + } + @WrapForJNI(calledFrom = "gecko") void contentDocumentChanged() { mContentDocumentIsDisplayed = false; @@ -320,6 +407,112 @@ class GeckoLayerClient implements LayerView.Listener, PanZoomTarget return mContentDocumentIsDisplayed; } + // This is called on the Gecko thread to determine if we're still interested + // in the update of this display-port to continue. We can return true here + // to abort the current update and continue with any subsequent ones. This + // is useful for slow-to-render pages when the display-port starts lagging + // behind enough that continuing to draw it is wasted effort. + @WrapForJNI + public ProgressiveUpdateData progressiveUpdateCallback(boolean aHasPendingNewThebesContent, + float x, float y, float width, float height, + float resolution, boolean lowPrecision) { + // Reset the checkerboard risk flag when switching to low precision + // rendering. + if (lowPrecision && !mLastProgressiveUpdateWasLowPrecision) { + // Skip low precision rendering until we're at risk of checkerboarding. + if (!mProgressiveUpdateWasInDanger) { + mProgressiveUpdateData.abort = true; + return mProgressiveUpdateData; + } + mProgressiveUpdateWasInDanger = false; + } + mLastProgressiveUpdateWasLowPrecision = lowPrecision; + + // Grab a local copy of the last display-port sent to Gecko and the + // current viewport metrics to avoid races when accessing them. + DisplayPortMetrics displayPort = mDisplayPort; + ImmutableViewportMetrics viewportMetrics = mViewportMetrics; + mProgressiveUpdateData.setViewport(viewportMetrics); + mProgressiveUpdateData.abort = false; + + // Always abort updates if the resolution has changed. There's no use + // in drawing at the incorrect resolution. + if (!FloatUtils.fuzzyEquals(resolution, viewportMetrics.zoomFactor)) { + Log.d(LOGTAG, "Aborting draw due to resolution change: " + resolution + " != " + viewportMetrics.zoomFactor); + mProgressiveUpdateData.abort = true; + return mProgressiveUpdateData; + } + + // Store the high precision displayport for comparison when doing low + // precision updates. + if (!lowPrecision) { + if (!FloatUtils.fuzzyEquals(resolution, mProgressiveUpdateDisplayPort.resolution) || + !FloatUtils.fuzzyEquals(x, mProgressiveUpdateDisplayPort.getLeft()) || + !FloatUtils.fuzzyEquals(y, mProgressiveUpdateDisplayPort.getTop()) || + !FloatUtils.fuzzyEquals(x + width, mProgressiveUpdateDisplayPort.getRight()) || + !FloatUtils.fuzzyEquals(y + height, mProgressiveUpdateDisplayPort.getBottom())) { + mProgressiveUpdateDisplayPort = + new DisplayPortMetrics(x, y, x + width, y + height, resolution); + } + } + + // If we're not doing low precision draws and we're about to + // checkerboard, enable low precision drawing. + if (!lowPrecision && !mProgressiveUpdateWasInDanger) { + if (DisplayPortCalculator.aboutToCheckerboard(viewportMetrics, + mPanZoomController.getVelocityVector(), mProgressiveUpdateDisplayPort)) { + mProgressiveUpdateWasInDanger = true; + } + } + + // XXX All sorts of rounding happens inside Gecko that becomes hard to + // account exactly for. Given we align the display-port to tile + // boundaries (and so they rarely vary by sub-pixel amounts), just + // check that values are within a couple of pixels of the + // display-port bounds. + + // Never abort drawing if we can't be sure we've sent a more recent + // display-port. If we abort updating when we shouldn't, we can end up + // with blank regions on the screen and we open up the risk of entering + // an endless updating cycle. + if (Math.abs(displayPort.getLeft() - mProgressiveUpdateDisplayPort.getLeft()) <= 2 && + Math.abs(displayPort.getTop() - mProgressiveUpdateDisplayPort.getTop()) <= 2 && + Math.abs(displayPort.getBottom() - mProgressiveUpdateDisplayPort.getBottom()) <= 2 && + Math.abs(displayPort.getRight() - mProgressiveUpdateDisplayPort.getRight()) <= 2) { + return mProgressiveUpdateData; + } + + // Abort updates when the display-port no longer contains the visible + // area of the page (that is, the viewport cropped by the page + // boundaries). + // XXX This makes the assumption that we never let the visible area of + // the page fall outside of the display-port. + if (Math.max(viewportMetrics.viewportRectLeft, viewportMetrics.pageRectLeft) + 1 < x || + Math.max(viewportMetrics.viewportRectTop, viewportMetrics.pageRectTop) + 1 < y || + Math.min(viewportMetrics.viewportRectRight(), viewportMetrics.pageRectRight) - 1 > x + width || + Math.min(viewportMetrics.viewportRectBottom(), viewportMetrics.pageRectBottom) - 1 > y + height) { + Log.d(LOGTAG, "Aborting update due to viewport not in display-port"); + mProgressiveUpdateData.abort = true; + + // Enable low-precision drawing, as we're likely to be in danger if + // this situation has been encountered. + mProgressiveUpdateWasInDanger = true; + + return mProgressiveUpdateData; + } + + // Abort drawing stale low-precision content if there's a more recent + // display-port in the pipeline. + if (lowPrecision && !aHasPendingNewThebesContent) { + mProgressiveUpdateData.abort = true; + } + return mProgressiveUpdateData; + } + + void setZoomConstraints(ZoomConstraints constraints) { + mZoomConstraints = constraints; + } + void setIsRTL(boolean aIsRTL) { synchronized (getLock()) { ImmutableViewportMetrics newMetrics = getViewportMetrics().setIsRTL(aIsRTL); @@ -362,6 +555,20 @@ class GeckoLayerClient implements LayerView.Listener, PanZoomTarget setViewportMetrics(newMetrics); + if (tab != null) { + mView.setBackgroundColor(tab.getBackgroundColor()); + setZoomConstraints(tab.getZoomConstraints()); + } + + // At this point, we have just switched to displaying a different document than we + // we previously displaying. This means we need to abort any panning/zooming animations + // that are in progress and send an updated display port request to browser.js as soon + // as possible. The call to PanZoomController.abortAnimation accomplishes this by calling the + // forceRedraw function, which sends the viewport to gecko. The display port request is + // actually a full viewport update, which is fine because if browser.js has somehow moved to + // be out of sync with this first-paint viewport, then we force them back in sync. + abortPanZoomAnimation(); + // Indicate that the document is about to be composited so the // LayerView background can be removed. if (mView.getPaintState() == LayerView.PAINT_START) { @@ -705,6 +912,12 @@ class GeckoLayerClient implements LayerView.Listener, PanZoomTarget return mViewportMetrics; } + /** Implementation of PanZoomTarget */ + @Override + public ZoomConstraints getZoomConstraints() { + return mZoomConstraints; + } + /** Implementation of PanZoomTarget */ @Override public FullScreenState getFullScreenState() { diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/LayerView.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/LayerView.java index 1852b93e5148..97c7fe2bfc53 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/LayerView.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/LayerView.java @@ -55,6 +55,7 @@ public class LayerView extends ScrollView implements Tabs.OnTabsChangedListener private LayerRenderer mRenderer; /* Must be a PAINT_xxx constant */ private int mPaintState; + private int mBackgroundColor; private FullScreenState mFullScreenState; private SurfaceView mSurfaceView; @@ -170,6 +171,7 @@ public class LayerView extends ScrollView implements Tabs.OnTabsChangedListener super(context, attrs); mPaintState = PAINT_START; + mBackgroundColor = Color.WHITE; mFullScreenState = FullScreenState.NONE; if (Versions.feature14Plus) { @@ -375,6 +377,12 @@ public class LayerView extends ScrollView implements Tabs.OnTabsChangedListener return mLayerClient.getViewportMetrics(); } + public void abortPanning() { + if (mPanZoomController != null) { + mPanZoomController.abortPanning(); + } + } + public PointF convertViewPointToLayerPoint(PointF viewPoint) { return mLayerClient.convertViewPointToLayerPoint(viewPoint); } @@ -383,16 +391,43 @@ public class LayerView extends ScrollView implements Tabs.OnTabsChangedListener return mLayerClient.getMatrixForLayerRectToViewRect(); } + int getBackgroundColor() { + return mBackgroundColor; + } + + @Override + public void setBackgroundColor(int newColor) { + mBackgroundColor = newColor; + requestRender(); + } + void setSurfaceBackgroundColor(int newColor) { if (mSurfaceView != null) { mSurfaceView.setBackgroundColor(newColor); } } + public void setZoomConstraints(ZoomConstraints constraints) { + mLayerClient.setZoomConstraints(constraints); + } + public void setIsRTL(boolean aIsRTL) { mLayerClient.setIsRTL(aIsRTL); } + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) { + if (!mLayerClient.isGeckoReady()) { + // If gecko isn't loaded yet, don't try sending events to the + // native code because it's just going to crash + return true; + } + if (mPanZoomController != null && mPanZoomController.onKeyEvent(event)) { + return true; + } + return false; + } + public void requestRender() { if (mCompositorCreated) { mCompositor.syncInvalidateAndScheduleComposite(); @@ -760,6 +795,7 @@ public class LayerView extends ScrollView implements Tabs.OnTabsChangedListener @Override public void onTabChanged(Tab tab, Tabs.TabEvents msg, String data) { if (msg == Tabs.TabEvents.VIEWPORT_CHANGE && Tabs.getInstance().isSelectedTab(tab) && mLayerClient != null) { + setZoomConstraints(tab.getZoomConstraints()); setIsRTL(tab.getIsRTL()); } } diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/NativePanZoomController.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/NativePanZoomController.java index 2db0d1ac1619..c733bd1894a1 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/NativePanZoomController.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/NativePanZoomController.java @@ -190,11 +190,58 @@ class NativePanZoomController extends JNIObject implements PanZoomController { } } + @Override + public boolean onKeyEvent(KeyEvent event) { + // FIXME implement this + return false; + } + @Override public void onMotionEventVelocity(final long aEventTime, final float aSpeedY) { handleMotionEventVelocity(aEventTime, aSpeedY); } + @Override + public PointF getVelocityVector() { + // FIXME implement this + return new PointF(0, 0); + } + + @Override + public void pageRectUpdated() { + // no-op in APZC, I think + } + + @Override + public void abortPanning() { + // no-op in APZC, I think + } + + @Override + public void notifyDefaultActionPrevented(boolean prevented) { + // no-op: This could get called if accessibility is enabled and the events + // are sent to Gecko directly without going through APZ. In this case + // we just want to ignore this callback. + } + + @WrapForJNI(stubName = "AbortAnimation", calledFrom = "ui") + private native void nativeAbortAnimation(); + + @Override // PanZoomController + public void abortAnimation() + { + if (!mDestroyed) { + nativeAbortAnimation(); + } + } + + @Override // PanZoomController + public boolean getRedrawHint() + { + // FIXME implement this + return true; + } + @Override @WrapForJNI(calledFrom = "ui") // PanZoomController public void destroy() { if (mDestroyed || !mTarget.isGeckoReady()) { diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/PanZoomController.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/PanZoomController.java index be999729d9a4..d0ca251e0e24 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/PanZoomController.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/PanZoomController.java @@ -14,6 +14,10 @@ import android.view.MotionEvent; import android.view.View; public interface PanZoomController { + // The distance the user has to pan before we recognize it as such (e.g. to avoid 1-pixel pans + // between the touch-down and touch-up of a click). In units of density-independent pixels. + public static final float PAN_THRESHOLD = 1 / 16f * GeckoAppShell.getDpi(); + // Threshold for sending touch move events to content public static final float CLICK_THRESHOLD = 1 / 50f * GeckoAppShell.getDpi(); @@ -27,7 +31,16 @@ public interface PanZoomController { public boolean onTouchEvent(MotionEvent event); public boolean onMotionEvent(MotionEvent event); + public boolean onKeyEvent(KeyEvent event); public void onMotionEventVelocity(final long aEventTime, final float aSpeedY); + public void notifyDefaultActionPrevented(boolean prevented); + + public boolean getRedrawHint(); + public PointF getVelocityVector(); + + public void pageRectUpdated(); + public void abortPanning(); + public void abortAnimation(); public void setOverscrollHandler(final Overscroll controller); diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/PanZoomTarget.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/PanZoomTarget.java index f3e7231aba6c..7d5b8b1eab4d 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/PanZoomTarget.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/PanZoomTarget.java @@ -12,6 +12,7 @@ import android.graphics.PointF; public interface PanZoomTarget { public ImmutableViewportMetrics getViewportMetrics(); + public ZoomConstraints getZoomConstraints(); public FullScreenState getFullScreenState(); public PointF getVisibleEndOfLayerView(); diff --git a/widget/android/AndroidBridge.cpp b/widget/android/AndroidBridge.cpp index 3d393564a4b7..53440efb71fc 100644 --- a/widget/android/AndroidBridge.cpp +++ b/widget/android/AndroidBridge.cpp @@ -1402,6 +1402,33 @@ AndroidBridge::IsContentDocumentDisplayed() return mLayerClient->IsContentDocumentDisplayed(); } +bool +AndroidBridge::ProgressiveUpdateCallback(bool aHasPendingNewThebesContent, + const LayerRect& aDisplayPort, float aDisplayResolution, + bool aDrawingCritical, ParentLayerPoint& aScrollOffset, + CSSToParentLayerScale& aZoom) +{ + if (!mLayerClient) { + ALOG_BRIDGE("Exceptional Exit: %s", __PRETTY_FUNCTION__); + return false; + } + + ProgressiveUpdateData::LocalRef progressiveUpdateData = + mLayerClient->ProgressiveUpdateCallback(aHasPendingNewThebesContent, + (float)aDisplayPort.x, + (float)aDisplayPort.y, + (float)aDisplayPort.width, + (float)aDisplayPort.height, + aDisplayResolution, + !aDrawingCritical); + + aScrollOffset.x = progressiveUpdateData->X(); + aScrollOffset.y = progressiveUpdateData->Y(); + aZoom.scale = progressiveUpdateData->Scale(); + + return progressiveUpdateData->Abort(); +} + class AndroidBridge::DelayedTask { using TimeStamp = mozilla::TimeStamp; diff --git a/widget/android/AndroidBridge.h b/widget/android/AndroidBridge.h index f66724b28c42..14958c716459 100644 --- a/widget/android/AndroidBridge.h +++ b/widget/android/AndroidBridge.h @@ -151,6 +151,9 @@ public: void ContentDocumentChanged(); bool IsContentDocumentDisplayed(); + bool ProgressiveUpdateCallback(bool aHasPendingNewThebesContent, const LayerRect& aDisplayPort, float aDisplayResolution, bool aDrawingCritical, + mozilla::ParentLayerPoint& aScrollOffset, mozilla::CSSToParentLayerScale& aZoom); + void SetLayerClient(java::GeckoLayerClient::Param jobj); const java::GeckoLayerClient::Ref& GetLayerClient() { return mLayerClient; } diff --git a/widget/android/GeneratedJNINatives.h b/widget/android/GeneratedJNINatives.h index 88921209b288..fe1cad852851 100644 --- a/widget/android/GeneratedJNINatives.h +++ b/widget/android/GeneratedJNINatives.h @@ -541,7 +541,7 @@ template class NativePanZoomController::Natives : public mozilla::jni::NativeImpl { public: - static const JNINativeMethod methods[7]; + static const JNINativeMethod methods[8]; }; template @@ -571,6 +571,10 @@ const JNINativeMethod NativePanZoomController::Natives::methods[] = { mozilla::jni::NativeStub ::template Wrap<&Impl::HandleScrollEvent>), + mozilla::jni::MakeNativeMethod( + mozilla::jni::NativeStub + ::template Wrap<&Impl::AbortAnimation>), + mozilla::jni::MakeNativeMethod( mozilla::jni::NativeStub ::template Wrap<&Impl::SetIsLongpressEnabled>) diff --git a/widget/android/GeneratedJNIWrappers.cpp b/widget/android/GeneratedJNIWrappers.cpp index f29fe54b9f58..4bf3ae38650f 100644 --- a/widget/android/GeneratedJNIWrappers.cpp +++ b/widget/android/GeneratedJNIWrappers.cpp @@ -588,6 +588,14 @@ constexpr char GeckoAppShell::NotifyObservers_t::signature[]; constexpr char GeckoAppShell::NotifyAlertListener_t::name[]; constexpr char GeckoAppShell::NotifyAlertListener_t::signature[]; +constexpr char GeckoAppShell::NotifyDefaultPrevented_t::name[]; +constexpr char GeckoAppShell::NotifyDefaultPrevented_t::signature[]; + +auto GeckoAppShell::NotifyDefaultPrevented(bool a0) -> void +{ + return mozilla::jni::Method::Call(GeckoAppShell::Context(), nullptr, a0); +} + constexpr char GeckoAppShell::NotifyUriVisited_t::name[]; constexpr char GeckoAppShell::NotifyUriVisited_t::signature[]; @@ -1300,6 +1308,14 @@ auto GeckoLayerClient::OnGeckoReady() const -> void return mozilla::jni::Method::Call(GeckoLayerClient::mCtx, nullptr); } +constexpr char GeckoLayerClient::ProgressiveUpdateCallback_t::name[]; +constexpr char GeckoLayerClient::ProgressiveUpdateCallback_t::signature[]; + +auto GeckoLayerClient::ProgressiveUpdateCallback(bool a0, float a1, float a2, float a3, float a4, float a5, bool a6) const -> mozilla::jni::Object::LocalRef +{ + return mozilla::jni::Method::Call(GeckoLayerClient::mCtx, nullptr, a0, a1, a2, a3, a4, a5, a6); +} + constexpr char GeckoLayerClient::SetFirstPaintViewport_t::name[]; constexpr char GeckoLayerClient::SetFirstPaintViewport_t::signature[]; @@ -1490,6 +1506,9 @@ constexpr char NativePanZoomController::HandleMouseEvent_t::signature[]; constexpr char NativePanZoomController::HandleScrollEvent_t::name[]; constexpr char NativePanZoomController::HandleScrollEvent_t::signature[]; +constexpr char NativePanZoomController::AbortAnimation_t::name[]; +constexpr char NativePanZoomController::AbortAnimation_t::signature[]; + constexpr char NativePanZoomController::SetIsLongpressEnabled_t::name[]; constexpr char NativePanZoomController::SetIsLongpressEnabled_t::signature[]; diff --git a/widget/android/GeneratedJNIWrappers.h b/widget/android/GeneratedJNIWrappers.h index 202e325a35be..e52b1ac85e16 100644 --- a/widget/android/GeneratedJNIWrappers.h +++ b/widget/android/GeneratedJNIWrappers.h @@ -1571,6 +1571,26 @@ public: mozilla::jni::DispatchTarget::GECKO; }; + struct NotifyDefaultPrevented_t { + typedef GeckoAppShell Owner; + typedef void ReturnType; + typedef void SetterType; + typedef mozilla::jni::Args< + bool> Args; + static constexpr char name[] = "notifyDefaultPrevented"; + static constexpr char signature[] = + "(Z)V"; + static const bool isStatic = true; + static const mozilla::jni::ExceptionMode exceptionMode = + mozilla::jni::ExceptionMode::ABORT; + static const mozilla::jni::CallingThread callingThread = + mozilla::jni::CallingThread::GECKO; + static const mozilla::jni::DispatchTarget dispatchTarget = + mozilla::jni::DispatchTarget::CURRENT; + }; + + static auto NotifyDefaultPrevented(bool) -> void; + struct NotifyUriVisited_t { typedef GeckoAppShell Owner; typedef void ReturnType; @@ -4238,6 +4258,32 @@ public: auto OnGeckoReady() const -> void; + struct ProgressiveUpdateCallback_t { + typedef GeckoLayerClient Owner; + typedef mozilla::jni::Object::LocalRef ReturnType; + typedef mozilla::jni::Object::Param SetterType; + typedef mozilla::jni::Args< + bool, + float, + float, + float, + float, + float, + bool> Args; + static constexpr char name[] = "progressiveUpdateCallback"; + static constexpr char signature[] = + "(ZFFFFFZ)Lorg/mozilla/gecko/gfx/ProgressiveUpdateData;"; + static const bool isStatic = false; + static const mozilla::jni::ExceptionMode exceptionMode = + mozilla::jni::ExceptionMode::ABORT; + static const mozilla::jni::CallingThread callingThread = + mozilla::jni::CallingThread::ANY; + static const mozilla::jni::DispatchTarget dispatchTarget = + mozilla::jni::DispatchTarget::CURRENT; + }; + + auto ProgressiveUpdateCallback(bool, float, float, float, float, float, bool) const -> mozilla::jni::Object::LocalRef; + struct SetFirstPaintViewport_t { typedef GeckoLayerClient Owner; typedef void ReturnType; @@ -4934,6 +4980,23 @@ public: mozilla::jni::DispatchTarget::CURRENT; }; + struct AbortAnimation_t { + typedef NativePanZoomController Owner; + typedef void ReturnType; + typedef void SetterType; + typedef mozilla::jni::Args<> Args; + static constexpr char name[] = "nativeAbortAnimation"; + static constexpr char signature[] = + "()V"; + static const bool isStatic = false; + static const mozilla::jni::ExceptionMode exceptionMode = + mozilla::jni::ExceptionMode::ABORT; + static const mozilla::jni::CallingThread callingThread = + mozilla::jni::CallingThread::UI; + static const mozilla::jni::DispatchTarget dispatchTarget = + mozilla::jni::DispatchTarget::CURRENT; + }; + struct SetIsLongpressEnabled_t { typedef NativePanZoomController Owner; typedef void ReturnType; diff --git a/widget/android/nsWindow.cpp b/widget/android/nsWindow.cpp index e7f6b6328a60..c603941a6aea 100644 --- a/widget/android/nsWindow.cpp +++ b/widget/android/nsWindow.cpp @@ -537,6 +537,25 @@ public: } public: + void AbortAnimation() + { + MOZ_ASSERT(AndroidBridge::IsJavaUiThread()); + + RefPtr controller; + RefPtr compositor; + + if (LockedWindowPtr window{mWindow}) { + controller = window->mAPZC; + compositor = window->GetCompositorBridgeParent(); + } + + if (controller && compositor) { + // TODO: Pass in correct values for presShellId and viewId. + controller->CancelAnimation(ScrollableLayerGuid( + compositor->RootLayerTreeId(), 0, 0)); + } + } + void AdjustScrollForSurfaceShift(float aX, float aY) { MOZ_ASSERT(AndroidBridge::IsJavaUiThread());