Bug 1297853 - White flash when creating a private browsing tab r=jchen,kats
authorRandall Barker <rbarker@mozilla.com>
Thu, 01 Sep 2016 09:24:39 -0700
changeset 312929 a981c221c363453eac4fefff27073ce41c1edf02
parent 312928 d4ddc0a0cf6d70a72ed580c88baf0fe4cef077bf
child 312930 eb90834c4f107ebdeb369702d66e5257ff352d36
push id30665
push usercbook@mozilla.com
push dateWed, 07 Sep 2016 15:20:43 +0000
treeherdermozilla-central@95acb9299faf [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjchen, kats
bugs1297853
milestone51.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1297853 - White flash when creating a private browsing tab r=jchen,kats
gfx/layers/Compositor.cpp
gfx/layers/Compositor.h
gfx/layers/composite/LayerManagerComposite.cpp
gfx/layers/opengl/CompositorOGL.cpp
mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
mobile/android/base/java/org/mozilla/gecko/PrivateTab.java
mobile/android/base/java/org/mozilla/gecko/Tabs.java
mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/GeckoLayerClient.java
mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/LayerView.java
widget/android/GeneratedJNIWrappers.cpp
widget/android/GeneratedJNIWrappers.h
widget/android/nsWindow.cpp
widget/android/nsWindow.h
--- a/gfx/layers/Compositor.cpp
+++ b/gfx/layers/Compositor.cpp
@@ -33,19 +33,21 @@ Compositor::Compositor(widget::Composito
   , mPixelsPerFrame(0)
   , mPixelsFilled(0)
   , mScreenRotation(ROTATION_0)
   , mWidget(aWidget)
   , mIsDestroyed(false)
 #if defined(MOZ_WIDGET_ANDROID)
   // If the default color isn't white for Fennec, there is a black
   // flash before the first page of a tab is loaded.
-  , mBeginFrameClearColor(1.0, 1.0, 1.0, 1.0)
+  , mClearColor(1.0, 1.0, 1.0, 1.0)
+  , mDefaultClearColor(1.0, 1.0, 1.0, 1.0)
 #else
-  , mBeginFrameClearColor(0.0, 0.0, 0.0, 0.0)
+  , mClearColor(0.0, 0.0, 0.0, 0.0)
+  , mDefaultClearColor(0.0, 0.0, 0.0, 0.0)
 #endif
 {
 }
 
 Compositor::~Compositor()
 {
   ReadUnlockTextures();
 }
--- a/gfx/layers/Compositor.h
+++ b/gfx/layers/Compositor.h
@@ -344,18 +344,26 @@ public:
 
   /**
    * Draw a solid color filled rect. This is a simple DrawQuad helper.
    */
   void FillRect(const gfx::Rect& aRect, const gfx::Color& color,
                     const gfx::IntRect& aClipRect = gfx::IntRect(),
                     const gfx::Matrix4x4& aTransform = gfx::Matrix4x4());
 
-  void SetBeginFrameClearColor(const gfx::Color& aColor) {
-    mBeginFrameClearColor = aColor;
+  void SetClearColor(const gfx::Color& aColor) {
+    mClearColor = aColor;
+  }
+
+  void SetDefaultClearColor(const gfx::Color& aColor) {
+    mDefaultClearColor = aColor;
+  }
+
+  void SetClearColorToDefault() {
+    mClearColor = mDefaultClearColor;
   }
 
   /*
    * Clear aRect on current render target.
    */
   virtual void ClearRect(const gfx::Rect& aRect) = 0;
 
   /**
@@ -637,17 +645,18 @@ protected:
   widget::CompositorWidget* mWidget;
 
   bool mIsDestroyed;
 
 #if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
   FenceHandle mReleaseFenceHandle;
 #endif
 
-  gfx::Color mBeginFrameClearColor;
+  gfx::Color mClearColor;
+  gfx::Color mDefaultClearColor;
 
 private:
   static LayersBackend sBackend;
 
 };
 
 // Returns the number of rects. (Up to 4)
 typedef gfx::Rect decomposedRectArrayT[4];
--- a/gfx/layers/composite/LayerManagerComposite.cpp
+++ b/gfx/layers/composite/LayerManagerComposite.cpp
@@ -927,17 +927,19 @@ LayerManagerComposite::Render(const nsIn
   IntRect actualBounds;
 
   CompositorBench(mCompositor, bounds);
 
   MOZ_ASSERT(mRoot->GetOpacity() == 1);
 #if defined(MOZ_WIDGET_ANDROID)
   LayerMetricsWrapper wrapper = GetRootContentLayer();
   if (wrapper) {
-    mCompositor->SetBeginFrameClearColor(wrapper.Metadata().GetBackgroundColor());
+    mCompositor->SetClearColor(wrapper.Metadata().GetBackgroundColor());
+  } else {
+    mCompositor->SetClearColorToDefault();
   }
 #endif
   if (mRoot->GetClipRect()) {
     clipRect = *mRoot->GetClipRect();
     IntRect rect(clipRect.x, clipRect.y, clipRect.width, clipRect.height);
     mCompositor->BeginFrame(aInvalidRegion, &rect, bounds, aOpaqueRegion, nullptr, &actualBounds);
   } else {
     gfx::IntRect rect;
@@ -997,23 +999,16 @@ LayerManagerComposite::Render(const nsIn
 
   if (composer2D) {
     composer2D->Render(mCompositor->GetWidget()->RealWidget());
   }
 
   mCompositor->GetWidget()->PostRender(this);
 
   RecordFrame();
-
-#if defined(MOZ_WIDGET_ANDROID)
-  // Reset the clear color to white so that if a page is loaded with a different background
-  // color, the page will be white while the new page is loading instead of the background
-  // color of the previous page.
-  mCompositor->SetBeginFrameClearColor(gfx::Color(1.0, 1.0, 1.0, 1.0));
-#endif
 }
 
 #if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK)
 class ScopedCompositorProjMatrix {
 public:
   ScopedCompositorProjMatrix(CompositorOGL* aCompositor, const Matrix4x4& aProjMatrix):
     mCompositor(aCompositor),
     mOriginalProjMatrix(mCompositor->GetProjMatrix())
--- a/gfx/layers/opengl/CompositorOGL.cpp
+++ b/gfx/layers/opengl/CompositorOGL.cpp
@@ -705,17 +705,17 @@ CompositorOGL::BeginFrame(const nsIntReg
 #ifdef DEBUG
   mWindowRenderTarget = mCurrentRenderTarget;
 #endif
 
   if (aClipRectOut && !aClipRectIn) {
     aClipRectOut->SetRect(0, 0, width, height);
   }
 
-  mGLContext->fClearColor(mBeginFrameClearColor.r, mBeginFrameClearColor.g, mBeginFrameClearColor.b, mBeginFrameClearColor.a);
+  mGLContext->fClearColor(mClearColor.r, mClearColor.g, mClearColor.b, mClearColor.a);
   mGLContext->fClear(LOCAL_GL_COLOR_BUFFER_BIT | LOCAL_GL_DEPTH_BUFFER_BIT);
 }
 
 void
 CompositorOGL::CreateFBOWithTexture(const gfx::IntRect& aRect, bool aCopyFromSource,
                                     GLuint aSourceFrameBuffer,
                                     GLuint *aFBO, GLuint *aTexture)
 {
--- a/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
+++ b/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
@@ -1149,18 +1149,16 @@ public abstract class GeckoApp
         // `(GeckoApplication) getApplication()` here.
         GeckoAppShell.setContextGetter(this);
         GeckoAppShell.setApplicationContext(getApplicationContext());
         GeckoAppShell.setGeckoInterface(this);
         // We need to set the notification client before launching Gecko, since Gecko could start
         // sending notifications immediately after startup, which we don't want to lose/crash on.
         GeckoAppShell.setNotificationClient(makeNotificationClient());
 
-        Tabs.getInstance().attachToContext(this);
-
         // Tell Stumbler to register a local broadcast listener to listen for preference intents.
         // We do this via intents since we can't easily access Stumbler directly,
         // as it might be compiled outside of Fennec.
         getApplicationContext().sendBroadcast(
                 new Intent(INTENT_REGISTER_STUMBLER_LISTENER)
         );
 
         // Did the OS locale change while we were backgrounded? If so,
@@ -1262,16 +1260,18 @@ public abstract class GeckoApp
         setContentView(getLayout());
 
         // Set up Gecko layout.
         mRootLayout = (RelativeLayout) findViewById(R.id.root_layout);
         mGeckoLayout = (RelativeLayout) findViewById(R.id.gecko_layout);
         mMainLayout = (RelativeLayout) findViewById(R.id.main_layout);
         mLayerView = (GeckoView) findViewById(R.id.layer_view);
 
+        Tabs.getInstance().attachToContext(this, mLayerView);
+
         // Use global layout state change to kick off additional initialization
         mMainLayout.getViewTreeObserver().addOnGlobalLayoutListener(this);
 
         if (Versions.preMarshmallow) {
             mTextSelection = new ActionBarTextSelection(
                     (TextSelectionHandle) findViewById(R.id.anchor_handle));
         } else {
             mTextSelection = new FloatingToolbarTextSelection(this, mLayerView);
@@ -2874,9 +2874,13 @@ public abstract class GeckoApp
         return IntentHelper.getHandlersForIntent(intent);
     }
 
     @Override
     public String getDefaultChromeURI() {
         // Use the chrome URI specified by Gecko's defaultChromeURI pref.
         return null;
     }
+
+    public GeckoView getGeckoView() {
+        return mLayerView;
+    }
 }
--- a/mobile/android/base/java/org/mozilla/gecko/PrivateTab.java
+++ b/mobile/android/base/java/org/mozilla/gecko/PrivateTab.java
@@ -2,18 +2,16 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko;
 
 import android.content.Context;
 
-import android.support.v4.content.ContextCompat;
-
 import org.json.JSONObject;
 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);
     }
 
--- a/mobile/android/base/java/org/mozilla/gecko/Tabs.java
+++ b/mobile/android/base/java/org/mozilla/gecko/Tabs.java
@@ -14,16 +14,17 @@ import java.util.concurrent.atomic.Atomi
 import android.support.annotation.Nullable;
 import org.json.JSONException;
 import org.json.JSONObject;
 
 import org.mozilla.gecko.annotation.JNITarget;
 import org.mozilla.gecko.annotation.RobocopTarget;
 import org.mozilla.gecko.AppConstants.Versions;
 import org.mozilla.gecko.db.BrowserDB;
+import org.mozilla.gecko.gfx.LayerView;
 import org.mozilla.gecko.mozglue.SafeIntent;
 import org.mozilla.gecko.notifications.WhatsNewReceiver;
 import org.mozilla.gecko.reader.ReaderModeUtils;
 import org.mozilla.gecko.util.GeckoEventListener;
 import org.mozilla.gecko.util.ThreadUtils;
 
 import android.accounts.Account;
 import android.accounts.AccountManager;
@@ -31,16 +32,17 @@ import android.accounts.OnAccountsUpdate
 import android.content.ContentResolver;
 import android.content.Context;
 import android.database.ContentObserver;
 import android.database.sqlite.SQLiteException;
 import android.graphics.Color;
 import android.net.Uri;
 import android.os.Handler;
 import android.provider.Browser;
+import android.support.v4.content.ContextCompat;
 import android.util.Log;
 
 public class Tabs implements GeckoEventListener {
     private static final String LOGTAG = "GeckoTabs";
 
     // mOrder and mTabs are always of the same cardinality, and contain the same values.
     private final CopyOnWriteArrayList<Tab> mOrder = new CopyOnWriteArrayList<Tab>();
 
@@ -70,18 +72,20 @@ public class Tabs implements GeckoEventL
     private static final long PERSIST_TABS_AFTER_MILLISECONDS = 1000 * 2;
 
     public static final int INVALID_TAB_ID = -1;
 
     private static final AtomicInteger sTabId = new AtomicInteger(0);
     private volatile boolean mInitialTabsAdded;
 
     private Context mAppContext;
+    private LayerView mLayerView;
     private ContentObserver mBookmarksContentObserver;
     private PersistTabsRunnable mPersistTabsRunnable;
+    private int mPrivateClearColor;
 
     private static class PersistTabsRunnable implements Runnable {
         private final BrowserDB db;
         private final Context context;
         private final Iterable<Tab> tabs;
 
         public PersistTabsRunnable(final Context context, Iterable<Tab> tabsInOrder) {
             this.context = context;
@@ -116,30 +120,34 @@ public class Tabs implements GeckoEventL
             "Link:Feed",
             "Link:OpenSearch",
             "DesktopMode:Changed",
             "Tab:ViewportMetadata",
             "Tab:StreamStart",
             "Tab:StreamStop",
             "Tab:AudioPlayingChange");
 
+        mPrivateClearColor = Color.RED;
+
     }
 
-    public synchronized void attachToContext(Context context) {
+    public synchronized void attachToContext(Context context, LayerView layerView) {
         final Context appContext = context.getApplicationContext();
         if (mAppContext == appContext) {
             return;
         }
 
         if (mAppContext != null) {
             // This should never happen.
             Log.w(LOGTAG, "The application context has changed!");
         }
 
         mAppContext = appContext;
+        mLayerView = layerView;
+        mPrivateClearColor = ContextCompat.getColor(context, R.color.tabs_tray_grey_pressed);
         mAccountManager = AccountManager.get(appContext);
 
         mAccountListener = new OnAccountsUpdateListener() {
             @Override
             public void onAccountsUpdated(Account[] accounts) {
                 queuePersistAllTabs();
             }
         };
@@ -245,16 +253,20 @@ public class Tabs implements GeckoEventL
         // handle this case.
         if (tab == null || oldTab == tab) {
             return null;
         }
 
         mSelectedTab = tab;
         notifyListeners(tab, TabEvents.SELECTED);
 
+        if (mLayerView != null) {
+            mLayerView.setClearColor(tab.isPrivate() ? mPrivateClearColor : Color.WHITE);
+        }
+
         if (oldTab != null) {
             notifyListeners(oldTab, TabEvents.UNSELECTED);
         }
 
         // Pass a message to Gecko to update tab state in BrowserApp.
         GeckoAppShell.notifyObservers("Tab:Selected", String.valueOf(tab.getId()));
         return tab;
     }
--- 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
@@ -12,16 +12,17 @@ import org.mozilla.gecko.gfx.LayerView.D
 import org.mozilla.gecko.Tab;
 import org.mozilla.gecko.Tabs;
 import org.mozilla.gecko.ZoomConstraints;
 import org.mozilla.gecko.EventDispatcher;
 import org.mozilla.gecko.util.FloatUtils;
 import org.mozilla.gecko.AppConstants;
 
 import android.content.Context;
+import android.graphics.Color;
 import android.graphics.Matrix;
 import android.graphics.PointF;
 import android.graphics.RectF;
 import android.os.SystemClock;
 import android.util.DisplayMetrics;
 import android.util.Log;
 import android.view.InputDevice;
 import android.view.MotionEvent;
@@ -101,16 +102,19 @@ class GeckoLayerClient implements LayerV
      * have the first-paint flag set, and the second paint happens concurrently with the
      * composite for the first paint, then this flag may be set to true prematurely. Fixing this
      * is possible but risky; see https://bugzilla.mozilla.org/show_bug.cgi?id=797615#c751
      */
     private volatile boolean mContentDocumentIsDisplayed;
 
     private SynthesizedEventState mPointerState;
 
+    @WrapForJNI(stubName = "ClearColor")
+    private volatile int mClearColor = Color.WHITE;
+
     public GeckoLayerClient(Context context, LayerView view, EventDispatcher eventDispatcher) {
         // we can fill these in with dummy values because they are always written
         // to before being read
         mContext = context;
         mScreenSize = new IntSize(0, 0);
         mWindowSize = new IntSize(0, 0);
         mDisplayPort = new DisplayPortMetrics();
         mRecordDrawTimes = true;
@@ -896,9 +900,13 @@ class GeckoLayerClient implements LayerV
 
     public void addDrawListener(DrawListener listener) {
         mDrawListeners.add(listener);
     }
 
     public void removeDrawListener(DrawListener listener) {
         mDrawListeners.remove(listener);
     }
+
+    public void setClearColor(int color) {
+        mClearColor = color;
+    }
 }
--- 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
@@ -789,9 +789,14 @@ public class LayerView extends ScrollVie
     public void addZoomedViewListener(ZoomedViewListener listener) {
         mRenderer.addZoomedViewListener(listener);
     }
 
     public void removeZoomedViewListener(ZoomedViewListener listener) {
         mRenderer.removeZoomedViewListener(listener);
     }
 
+    public void setClearColor(int color) {
+        if (mLayerClient != null) {
+            mLayerClient.setClearColor(color);
+        }
+    }
 }
--- a/widget/android/GeneratedJNIWrappers.cpp
+++ b/widget/android/GeneratedJNIWrappers.cpp
@@ -1357,16 +1357,29 @@ auto GeckoLayerClient::SynthesizeNativeM
 constexpr char GeckoLayerClient::SynthesizeNativeTouchPoint_t::name[];
 constexpr char GeckoLayerClient::SynthesizeNativeTouchPoint_t::signature[];
 
 auto GeckoLayerClient::SynthesizeNativeTouchPoint(int32_t a0, int32_t a1, int32_t a2, int32_t a3, double a4, int32_t a5) const -> void
 {
     return mozilla::jni::Method<SynthesizeNativeTouchPoint_t>::Call(GeckoLayerClient::mCtx, nullptr, a0, a1, a2, a3, a4, a5);
 }
 
+constexpr char GeckoLayerClient::ClearColor_t::name[];
+constexpr char GeckoLayerClient::ClearColor_t::signature[];
+
+auto GeckoLayerClient::ClearColor() const -> int32_t
+{
+    return mozilla::jni::Field<ClearColor_t>::Get(GeckoLayerClient::mCtx, nullptr);
+}
+
+auto GeckoLayerClient::ClearColor(int32_t a0) const -> void
+{
+    return mozilla::jni::Field<ClearColor_t>::Set(GeckoLayerClient::mCtx, nullptr, a0);
+}
+
 const char ImmutableViewportMetrics::name[] =
         "org/mozilla/gecko/gfx/ImmutableViewportMetrics";
 
 constexpr char ImmutableViewportMetrics::New_t::name[];
 constexpr char ImmutableViewportMetrics::New_t::signature[];
 
 auto ImmutableViewportMetrics::New(float a0, float a1, float a2, float a3, float a4, float a5, float a6, float a7, float a8, float a9, int32_t a10, int32_t a11, float a12) -> ImmutableViewportMetrics::LocalRef
 {
--- a/widget/android/GeneratedJNIWrappers.h
+++ b/widget/android/GeneratedJNIWrappers.h
@@ -4443,16 +4443,37 @@ public:
         static const mozilla::jni::CallingThread callingThread =
                 mozilla::jni::CallingThread::GECKO;
         static const mozilla::jni::DispatchTarget dispatchTarget =
                 mozilla::jni::DispatchTarget::CURRENT;
     };
 
     auto SynthesizeNativeTouchPoint(int32_t, int32_t, int32_t, int32_t, double, int32_t) const -> void;
 
+    struct ClearColor_t {
+        typedef GeckoLayerClient Owner;
+        typedef int32_t ReturnType;
+        typedef int32_t SetterType;
+        typedef mozilla::jni::Args<> Args;
+        static constexpr char name[] = "mClearColor";
+        static constexpr char signature[] =
+                "I";
+        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 ClearColor() const -> int32_t;
+
+    auto ClearColor(int32_t) const -> void;
+
     static const mozilla::jni::CallingThread callingThread =
             mozilla::jni::CallingThread::ANY;
 
 };
 
 class ImmutableViewportMetrics : public mozilla::jni::ObjectBase<ImmutableViewportMetrics>
 {
 public:
--- a/widget/android/nsWindow.cpp
+++ b/widget/android/nsWindow.cpp
@@ -3484,30 +3484,56 @@ nsWindow::SynthesizeNativeMouseMove(Layo
 
     MOZ_ASSERT(mLayerViewSupport);
     GeckoLayerClient::LocalRef client = mLayerViewSupport->GetLayerClient();
     client->SynthesizeNativeMouseEvent(sdk::MotionEvent::ACTION_HOVER_MOVE, aPoint.x, aPoint.y);
 
     return NS_OK;
 }
 
+bool
+nsWindow::PreRender(LayerManagerComposite* aManager)
+{
+    if (Destroyed()) {
+        return true;
+    }
+
+    layers::Compositor* compositor = aManager->GetCompositor();
+
+    GeckoLayerClient::LocalRef client;
+
+    if (NativePtr<LayerViewSupport>::Locked lvs{mLayerViewSupport}) {
+        client = lvs->GetLayerClient();
+    }
+
+    if (compositor && client) {
+        // Android Color is ARGB which is apparently unusual.
+        compositor->SetDefaultClearColor(gfx::Color::UnusualFromARGB((uint32_t)client->ClearColor()));
+    }
+
+    return true;
+}
 void
 nsWindow::DrawWindowUnderlay(LayerManagerComposite* aManager,
                              LayoutDeviceIntRect aRect)
 {
     if (Destroyed()) {
         return;
     }
 
     GeckoLayerClient::LocalRef client;
 
     if (NativePtr<LayerViewSupport>::Locked lvs{mLayerViewSupport}) {
         client = lvs->GetLayerClient();
     }
 
+    if (!client) {
+        return;
+    }
+
     LayerRenderer::Frame::LocalRef frame = client->CreateFrame();
     mLayerRendererFrame = frame;
     if (NS_WARN_IF(!mLayerRendererFrame)) {
         return;
     }
 
     if (!WidgetPaintsBackground()) {
         return;
--- a/widget/android/nsWindow.h
+++ b/widget/android/nsWindow.h
@@ -194,16 +194,17 @@ public:
     virtual nsIMEUpdatePreference GetIMEUpdatePreference() override;
 
     void SetSelectionDragState(bool aState);
     LayerManager* GetLayerManager(PLayerTransactionChild* aShadowManager = nullptr,
                                   LayersBackend aBackendHint = mozilla::layers::LayersBackend::LAYERS_NONE,
                                   LayerManagerPersistence aPersistence = LAYER_MANAGER_CURRENT) override;
 
     virtual bool NeedsPaint() override;
+    virtual bool PreRender(LayerManagerComposite* aManager) override;
     virtual void DrawWindowUnderlay(LayerManagerComposite* aManager, LayoutDeviceIntRect aRect) override;
     virtual void DrawWindowOverlay(LayerManagerComposite* aManager, LayoutDeviceIntRect aRect) override;
 
     virtual bool WidgetPaintsBackground() override;
 
     virtual uint32_t GetMaxTouchPoints() const override;
 
     void UpdateZoomConstraints(const uint32_t& aPresShellId,