1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133

content / renderer / visual_state_browsertest.cc [blame]

// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/command_line.h"
#include "base/containers/flat_map.h"
#include "base/functional/bind.h"
#include "base/run_loop.h"
#include "base/task/current_thread.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/content_switches.h"
#include "content/public/renderer/render_frame.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/content_browser_test.h"
#include "content/public/test/content_browser_test_utils.h"
#include "content/public/test/test_utils.h"
#include "content/renderer/render_frame_impl.h"
#include "content/shell/browser/shell.h"
#include "third_party/blink/public/web/web_view_observer.h"

namespace content {

class CommitObserver : public blink::WebViewObserver {
 public:
  explicit CommitObserver(blink::WebView* web_view)
      : blink::WebViewObserver(web_view) {}

  void DidCommitCompositorFrame() override {
    commit_count_++;
    for (const auto& pair : quit_closures_) {
      pair.second.Run();
    }
  }

  void QuitAfterCommit(int commit_number,
                       scoped_refptr<MessageLoopRunner> runner) {
    if (commit_number >= commit_count_) runner->Quit();
  }

  void WaitForCommitNumber(int commit_number) {
    if (commit_number > commit_count_) {
      scoped_refptr<MessageLoopRunner> runner = new MessageLoopRunner;
      quit_closures_[commit_number] =
          base::BindRepeating(&CommitObserver::QuitAfterCommit,
                              base::Unretained(this), commit_number, runner);
      runner->Run();
      quit_closures_.erase(commit_number);
    }
  }

  int GetCommitCount() { return commit_count_; }

 private:
  // blink::WebViewObserver implementation.
  void OnDestruct() override { delete this; }

  base::flat_map<int, base::RepeatingClosure> quit_closures_;
  int commit_count_ = 0;
};

class VisualStateTest : public ContentBrowserTest {
 public:
  VisualStateTest() : callback_count_(0) {}

  void SetUpCommandLine(base::CommandLine* command_line) override {
    command_line->AppendSwitch(switches::kSingleProcess);
  }

  void WaitForCommit(CommitObserver *observer, int commit_number) {
    observer->WaitForCommitNumber(commit_number);
    EXPECT_EQ(commit_number, observer->GetCommitCount());
  }

  void AssertIsIdle() {
    ASSERT_TRUE(base::CurrentThread::Get()->IsIdleForTesting());
  }

  void InvokeVisualStateCallback(bool result) {
    EXPECT_TRUE(result);
    callback_count_++;
  }

  int GetCallbackCount() { return callback_count_; }

 private:
  int callback_count_;
};

// This test verifies that visual state callbacks do not deadlock. In other
// words, the visual state callback should be received even if there are no
// pending updates or commits.
// Disabled due to cross-platform flakes; http://crbug.com/462580.
IN_PROC_BROWSER_TEST_F(VisualStateTest, DISABLED_CallbackDoesNotDeadlock) {
  // This test relies on the fact that loading "about:blank" only requires a
  // single commit. We first load "about:blank" and wait for this single
  // commit. At that point we know that the page has stabilized and no
  // further commits are expected. We then insert a visual state callback
  // and verify that this causes an additional commit in order to deliver
  // the callback.
  // Unfortunately, if loading "about:blank" changes and starts requiring
  // two commits then this test will prove nothing. We could detect this
  // with a high level of confidence if we used a timeout, but that's
  // discouraged (see https://codereview.chromium.org/939673002).
  EXPECT_TRUE(NavigateToURL(shell(), GURL("about:blank")));
  CommitObserver observer(
      blink::WebLocalFrame::FromFrameToken(
          shell()->web_contents()->GetPrimaryMainFrame()->GetFrameToken())
          ->View());

  // Wait for the commit corresponding to the load.

  PostTaskToInProcessRendererAndWait(base::BindOnce(
      &VisualStateTest::WaitForCommit, base::Unretained(this), &observer, 1));

  // Try our best to check that there are no pending updates or commits.
  PostTaskToInProcessRendererAndWait(
      base::BindOnce(&VisualStateTest::AssertIsIdle, base::Unretained(this)));

  // Insert a visual state callback.
  shell()->web_contents()->GetPrimaryMainFrame()->InsertVisualStateCallback(
      base::BindOnce(&VisualStateTest::InvokeVisualStateCallback,
                     base::Unretained(this)));

  // Verify that the callback is invoked and a new commit completed.
  PostTaskToInProcessRendererAndWait(base::BindOnce(
      &VisualStateTest::WaitForCommit, base::Unretained(this), &observer, 2));
  EXPECT_EQ(1, GetCallbackCount());
}

}  // namespace content