| // Copyright 2015 The Chromium Authors. All rights reserved. |
| // 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/run_loop.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_view.h" |
| #include "content/public/renderer/render_view_observer.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" |
| |
| namespace content { |
| |
| class CommitObserver : public RenderViewObserver { |
| public: |
| CommitObserver(RenderView* render_view) |
| : RenderViewObserver(render_view), quit_closures_(), commit_count_(0) {} |
| |
| void DidCommitCompositorFrame() override { |
| commit_count_++; |
| for (base::Closure* closure : quit_closures_) { |
| closure->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; |
| base::Closure quit_closure = |
| base::Bind(&CommitObserver::QuitAfterCommit, base::Unretained(this), |
| commit_number, runner); |
| quit_closures_.insert(&quit_closure); |
| runner->Run(); |
| quit_closures_.erase(&quit_closure); |
| } |
| } |
| |
| int GetCommitCount() { return commit_count_; } |
| |
| private: |
| // RenderViewObserver implementation. |
| void OnDestruct() override { delete this; } |
| |
| std::set<base::Closure*> quit_closures_; |
| int commit_count_; |
| }; |
| |
| 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::MessageLoop::current()->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). |
| NavigateToURL(shell(), GURL("about:blank")); |
| CommitObserver observer(RenderView::FromRoutingID( |
| shell()->web_contents()->GetRenderViewHost()->GetRoutingID())); |
| |
| // Wait for the commit corresponding to the load. |
| |
| PostTaskToInProcessRendererAndWait(base::Bind( |
| &VisualStateTest::WaitForCommit, base::Unretained(this), &observer, 1)); |
| |
| // Try our best to check that there are no pending updates or commits. |
| PostTaskToInProcessRendererAndWait( |
| base::Bind(&VisualStateTest::AssertIsIdle, base::Unretained(this))); |
| |
| // Insert a visual state callback. |
| shell()->web_contents()->GetMainFrame()->InsertVisualStateCallback(base::Bind( |
| &VisualStateTest::InvokeVisualStateCallback, base::Unretained(this))); |
| |
| // Verify that the callback is invoked and a new commit completed. |
| PostTaskToInProcessRendererAndWait(base::Bind( |
| &VisualStateTest::WaitForCommit, base::Unretained(this), &observer, 2)); |
| EXPECT_EQ(1, GetCallbackCount()); |
| } |
| |
| } // namespace content |