fix flashing bottom gray bar when navigating away from NTP w/ unpinned bookmark bar

cause:
- when NTP has unpinned bmb, the web contents takes up the remaining height of the tab contents area.
- when navigating away from NTP, unpinned bmb disappears, so the web contents take up the entire tab contents area, i.e. taller than when on NTP.
- this resize of web contents view causes a flashing bottom gray bar after the NTP contents are cleared and before the new page contents are completely painted.

fix is to prevent any resizing, and hence re-layout, of the new page contents:
- add a method to WebContentsDelegate to allow size specification for newly-created render views.
- WebContentsImpl calls this method when creating render views and updating their sizes.
- this prevents render views from being created at a default size only to be resized immediately.
- also added browser_tests for browser and content

BUG=264207
TEST=verify per bug rpt

Review URL: https://chromiumcodereview.appspot.com/21843003

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@219794 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index 7d999ec..5917a447 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -3552,7 +3552,7 @@
 
 void WebContentsImpl::UpdateRenderViewSizeForRenderManager() {
   // TODO(brettw) this is a hack. See WebContentsView::SizeContents.
-  gfx::Size size = view_->GetContainerSize();
+  gfx::Size size = GetSizeForNewRenderView();
   // 0x0 isn't a valid window size (minimal window size is 1x1) but it may be
   // here during container initialization and normal window size will be set
   // later. In case of tab duplication this resizing to 0x0 prevents setting
@@ -3647,7 +3647,7 @@
 
   // Now that the RenderView has been created, we need to tell it its size.
   if (rwh_view)
-    rwh_view->SetSize(view_->GetContainerSize());
+    rwh_view->SetSize(GetSizeForNewRenderView());
 
   // Make sure we use the correct starting page_id in the new RenderView.
   UpdateMaxPageIDIfNecessary(render_view_host);
@@ -3751,4 +3751,13 @@
   power_save_blockers_.clear();
 }
 
+gfx::Size WebContentsImpl::GetSizeForNewRenderView() const {
+  gfx::Size size;
+  if (delegate_)
+    size = delegate_->GetSizeForNewRenderView(this);
+  if (size.IsEmpty())
+    size = view_->GetContainerSize();
+  return size;
+}
+
 }  // namespace content
diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h
index bb56ddba2..55f377b 100644
--- a/content/browser/web_contents/web_contents_impl.h
+++ b/content/browser/web_contents/web_contents_impl.h
@@ -760,6 +760,9 @@
   // Clear all PowerSaveBlockers, leave power_save_blocker_ empty.
   void ClearAllPowerSaveBlockers();
 
+  // Helper function to invoke WebContentsDelegate::GetSizeForNewRenderView().
+  gfx::Size GetSizeForNewRenderView() const;
+
   // Data for core operation ---------------------------------------------------
 
   // Delegate for notifying our owner about stuff. Not owned by us.
diff --git a/content/browser/web_contents/web_contents_impl_browsertest.cc b/content/browser/web_contents/web_contents_impl_browsertest.cc
index 70ab5235..05e3ebe 100644
--- a/content/browser/web_contents/web_contents_impl_browsertest.cc
+++ b/content/browser/web_contents/web_contents_impl_browsertest.cc
@@ -10,7 +10,9 @@
 #include "content/public/browser/notification_details.h"
 #include "content/public/browser/notification_observer.h"
 #include "content/public/browser/notification_types.h"
+#include "content/public/browser/render_widget_host_view.h"
 #include "content/public/browser/web_contents_observer.h"
+#include "content/public/browser/web_contents_view.h"
 #include "content/public/common/content_paths.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/test_utils.h"
@@ -21,6 +23,25 @@
 
 namespace content {
 
+void ResizeWebContentsView(Shell* shell, const gfx::Size& size,
+                           bool set_start_page) {
+  // Shell::SizeTo is not implemented on Aura; WebContentsView::SizeContents
+  // works on Win and ChromeOS but not Linux - we need to resize the shell
+  // window on Linux because if we don't, the next layout of the unchanged shell
+  // window will resize WebContentsView back to the previous size.
+  // The cleaner and shorter SizeContents is preferred as more platforms convert
+  // to Aura.
+#if defined(TOOLKIT_GTK) || defined(OS_MACOSX)
+  shell->SizeTo(size.width(), size.height());
+  // If |set_start_page| is true, start with blank page to make sure resize
+  // takes effect.
+  if (set_start_page)
+    NavigateToURL(shell, GURL("about://blank"));
+#else
+  shell->web_contents()->GetView()->SizeContents(size);
+#endif  // defined(TOOLKIT_GTK) || defined(OS_MACOSX)
+}
+
 class WebContentsImplBrowserTest : public ContentBrowserTest {
  public:
   WebContentsImplBrowserTest() {}
@@ -82,6 +103,51 @@
   bool done_;
 };
 
+class RenderViewSizeDelegate : public WebContentsDelegate {
+ public:
+  void set_size_insets(const gfx::Size& size_insets) {
+    size_insets_ = size_insets;
+  }
+
+  // WebContentsDelegate:
+  virtual gfx::Size GetSizeForNewRenderView(
+      const WebContents* web_contents) const OVERRIDE {
+    gfx::Size size(web_contents->GetView()->GetContainerSize());
+    size.Enlarge(size_insets_.width(), size_insets_.height());
+    return size;
+  }
+
+ private:
+  gfx::Size size_insets_;
+};
+
+class RenderViewSizeObserver : public WebContentsObserver {
+ public:
+  RenderViewSizeObserver(Shell* shell, const gfx::Size& wcv_new_size)
+      : WebContentsObserver(shell->web_contents()),
+        shell_(shell),
+        wcv_new_size_(wcv_new_size) {
+  }
+
+  // WebContentsObserver:
+  virtual void RenderViewCreated(RenderViewHost* rvh) OVERRIDE {
+    rwhv_create_size_ = rvh->GetView()->GetViewBounds().size();
+  }
+
+  virtual void NavigateToPendingEntry(
+      const GURL& url,
+      NavigationController::ReloadType reload_type) OVERRIDE {
+    ResizeWebContentsView(shell_, wcv_new_size_, false);
+  }
+
+  gfx::Size rwhv_create_size() const { return rwhv_create_size_; }
+
+ private:
+  Shell* shell_;  // Weak ptr.
+  gfx::Size wcv_new_size_;
+  gfx::Size rwhv_create_size_;
+};
+
 // Test that DidStopLoading includes the correct URL in the details.
 IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest, DidStopLoadingDetails) {
   ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
@@ -159,4 +225,61 @@
   EXPECT_EQ(rvh->main_frame_id(), root->frame_id());
 }
 
+// TODO(sail): enable this for MAC when auto resizing of WebContentsViewCocoa is
+// fixed.
+#if defined(OS_MACOSX) || defined(OS_ANDROID)
+#define MAYBE_GetSizeForNewRenderView DISABLED_GetSizeForNewRenderView
+#else
+#define MAYBE_GetSizeForNewRenderView GetSizeForNewRenderView
+#endif
+// Test that RenderViewHost is created and updated at the size specified by
+// WebContentsDelegate::GetSizeForNewRenderView().
+IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest,
+                       MAYBE_GetSizeForNewRenderView) {
+  scoped_ptr<RenderViewSizeDelegate> delegate(new RenderViewSizeDelegate());
+  shell()->web_contents()->SetDelegate(delegate.get());
+  ASSERT_TRUE(shell()->web_contents()->GetDelegate() == delegate.get());
+
+  // When no size is set, RenderWidgetHostView adopts the size of
+  // WebContenntsView.
+  NavigateToURL(shell(), GURL("http://foo0.com"));
+  EXPECT_EQ(shell()->web_contents()->GetView()->GetContainerSize(),
+            shell()->web_contents()->GetRenderWidgetHostView()->GetViewBounds().
+                size());
+
+  // When a size is set, RenderWidgetHostView and WebContentsView honor this
+  // size.
+  gfx::Size size(300, 300);
+  gfx::Size size_insets(-10, -15);
+  ResizeWebContentsView(shell(), size, true);
+  delegate->set_size_insets(size_insets);
+  NavigateToURL(shell(), GURL("http://foo1.com"));
+  size.Enlarge(size_insets.width(), size_insets.height());
+  EXPECT_EQ(size,
+            shell()->web_contents()->GetRenderWidgetHostView()->GetViewBounds().
+                size());
+  EXPECT_EQ(size, shell()->web_contents()->GetView()->GetContainerSize());
+
+  // If WebContentsView is resized after RenderWidgetHostView is created but
+  // before pending navigation entry is committed, both RenderWidgetHostView and
+  // WebContentsView use the new size of WebContentsView.
+  gfx::Size init_size(200, 200);
+  gfx::Size new_size(100, 100);
+  size_insets = gfx::Size(-20, -30);
+  ResizeWebContentsView(shell(), init_size, true);
+  delegate->set_size_insets(size_insets);
+  RenderViewSizeObserver observer(shell(), new_size);
+  NavigateToURL(shell(), GURL("http://foo2.com"));
+  // RenderWidgetHostView is created at specified size.
+  init_size.Enlarge(size_insets.width(), size_insets.height());
+  EXPECT_EQ(init_size, observer.rwhv_create_size());
+  // RenderViewSizeObserver resizes WebContentsView in NavigateToPendingEntry,
+  // so both WebContentsView and RenderWidgetHostView adopt this new size.
+  new_size.Enlarge(size_insets.width(), size_insets.height());
+  EXPECT_EQ(new_size,
+            shell()->web_contents()->GetRenderWidgetHostView()->GetViewBounds().
+                size());
+  EXPECT_EQ(new_size, shell()->web_contents()->GetView()->GetContainerSize());
+}
+
 }  // namespace content
diff --git a/content/public/browser/web_contents_delegate.cc b/content/public/browser/web_contents_delegate.cc
index 47e30b2a..02ca86a4 100644
--- a/content/public/browser/web_contents_delegate.cc
+++ b/content/public/browser/web_contents_delegate.cc
@@ -172,4 +172,9 @@
   attached_contents_.erase(web_contents);
 }
 
+gfx::Size WebContentsDelegate::GetSizeForNewRenderView(
+    const WebContents* web_contents) const {
+  return gfx::Size();
+}
+
 }  // namespace content
diff --git a/content/public/browser/web_contents_delegate.h b/content/public/browser/web_contents_delegate.h
index 69ac871..0e019a9 100644
--- a/content/public/browser/web_contents_delegate.h
+++ b/content/public/browser/web_contents_delegate.h
@@ -94,9 +94,10 @@
 
   // Creates a new tab with the already-created WebContents 'new_contents'.
   // The window for the added contents should be reparented correctly when this
-  // method returns.  If |disposition| is NEW_POPUP, |pos| should hold the
-  // initial position. If |was_blocked| is non-NULL, then |*was_blocked| will
-  // be set to true if the popup gets blocked, and left unchanged otherwise.
+  // method returns.  If |disposition| is NEW_POPUP, |initial_pos| should hold
+  // the initial position. If |was_blocked| is non-NULL, then |*was_blocked|
+  // will be set to true if the popup gets blocked, and left unchanged
+  // otherwise.
   virtual void AddNewContents(WebContents* source,
                               WebContents* new_contents,
                               WindowOpenDisposition disposition,
@@ -433,6 +434,14 @@
       const base::FilePath& plugin_path,
       const base::Callback<void(bool)>& callback);
 
+  // Returns the size for the new render view created for the pending entry in
+  // |web_contents|; if there's no size, returns an empty size.
+  // This is optional for implementations of WebContentsDelegate; if the
+  // delegate doesn't provide a size, the current WebContentsView's size will be
+  // used.
+  virtual gfx::Size GetSizeForNewRenderView(
+      const WebContents* web_contents) const;
+
  protected:
   virtual ~WebContentsDelegate();