Content: Set CompletedFirstVisuallyNonEmptyPaint() before notifying observers.

Today, WebContents::CompletedFirstVisuallyNonEmptyPaint() returns false
from WebContentsObserver::DidFirstVisuallyNonEmptyPaint(). This makes
it hard to write a test that waits for first visually non-empty paint
and another condition:

class MyWebContentsObserver : public WebContentsObserver {
  void DidFirstVisuallyNonEmptyPaint() override { OnStateChange(); }
  void DidSomething() override { OnStateChange(); }
  void OnStateChange() {
    if (web_contents()->CompletedFirstVisuallyNonEmptyPaint() &&
        web_contents()->Something()) {
       // This scope will never be entered from
       // DidFirstVisuallyNonEmptyPaint().
    }
  }
};

This CL fixes the issue by making
WebContents::CompletedFirstVisuallyNonEmptyPaint() return true from
WebContentsObserver::DidFirstVisuallyNonEmptyPaint().

Bug: 831835
Change-Id: Ieffd23056fa0c0aedc56dc238cc0a343a83fdcc0
Reviewed-on: https://chromium-review.googlesource.com/c/1306333
Reviewed-by: Jochen Eisinger <[email protected]>
Commit-Queue: François Doray <[email protected]>
Cr-Commit-Position: refs/heads/master@{#603881}
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index 17ee17d..7fc1ad54 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -4866,13 +4866,15 @@
 
 void WebContentsImpl::DidFirstVisuallyNonEmptyPaint(
     RenderViewHostImpl* source) {
+  // Set |did_first_visually_non_empty_paint_| before notifying observers so
+  // they can see that CompletedFirstVisuallyNonEmptyPaint() is true.
+  did_first_visually_non_empty_paint_ = true;
+
   // TODO(nick): When this is ported to FrameHostMsg_, we should only listen if
   // |source| is the main frame.
   for (auto& observer : observers_)
     observer.DidFirstVisuallyNonEmptyPaint();
 
-  did_first_visually_non_empty_paint_ = true;
-
   if (theme_color_ != last_sent_theme_color_) {
     // Theme color should have updated by now if there was one.
     for (auto& observer : observers_)
diff --git a/content/browser/web_contents/web_contents_impl_unittest.cc b/content/browser/web_contents/web_contents_impl_unittest.cc
index 366f3859..f6e19b3f 100644
--- a/content/browser/web_contents/web_contents_impl_unittest.cc
+++ b/content/browser/web_contents/web_contents_impl_unittest.cc
@@ -299,16 +299,25 @@
     last_url_ = validated_url;
   }
 
+  void DidFirstVisuallyNonEmptyPaint() override {
+    observed_did_first_visually_non_empty_paint_ = true;
+    EXPECT_TRUE(web_contents()->CompletedFirstVisuallyNonEmptyPaint());
+  }
+
   void DidChangeThemeColor(SkColor theme_color) override {
     last_theme_color_ = theme_color;
   }
 
   const GURL& last_url() const { return last_url_; }
   SkColor last_theme_color() const { return last_theme_color_; }
+  bool observed_did_first_visually_non_empty_paint() const {
+    return observed_did_first_visually_non_empty_paint_;
+  }
 
  private:
   GURL last_url_;
   SkColor last_theme_color_;
+  bool observed_did_first_visually_non_empty_paint_ = false;
 
   DISALLOW_COPY_AND_ASSIGN(TestWebContentsObserver);
 };
@@ -3584,4 +3593,13 @@
   EXPECT_EQ(effective_flags, expected_flags);
 }
 
+TEST_F(WebContentsImplTest, DidFirstVisuallyNonEmptyPaint) {
+  TestWebContentsObserver observer(contents());
+
+  RenderWidgetHostOwnerDelegate* rwhod = test_rvh();
+  rwhod->RenderWidgetDidFirstVisuallyNonEmptyPaint();
+
+  EXPECT_TRUE(observer.observed_did_first_visually_non_empty_paint());
+}
+
 }  // namespace content