// Copyright (c) 2011 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 "ppapi/tests/test_view.h" #include #include "ppapi/c/pp_time.h" #include "ppapi/c/private/ppb_testing_private.h" #include "ppapi/cpp/completion_callback.h" #include "ppapi/tests/testing_instance.h" REGISTER_TEST_CASE(View); // When waiting for view changed events, wait no longer than this. #if !defined(THREAD_SANITIZER) static int kViewChangeTimeoutSec = 5; #else // ThreadSanitizer may slow the interaction down significantly. static int kViewChangeTimeoutSec = 30; #endif TestView::TestView(TestingInstance* instance) : TestCase(instance), post_quit_on_view_changed_(false) { } void TestView::DidChangeView(const pp::View& view) { last_view_ = view; page_visibility_log_.push_back(view.IsPageVisible()); if (post_quit_on_view_changed_) { post_quit_on_view_changed_ = false; testing_interface_->QuitMessageLoop(instance_->pp_instance()); } } bool TestView::Init() { return CheckTestingInterface(); } void TestView::RunTests(const std::string& filter) { RUN_TEST(CreatedVisible, filter); RUN_TEST(CreatedInvisible, filter); RUN_TEST(PageHideShow, filter); RUN_TEST(SizeChange, filter); RUN_TEST(ClipChange, filter); RUN_TEST(ScrollOffsetChange, filter); } bool TestView::WaitUntilViewChanged() { // Schedule a callback so this step times out if we don't get a ViewChanged // in a reasonable amount of time. pp::CompletionCallbackFactory factory(this); pp::CompletionCallback timeout = factory.NewCallback(&TestView::QuitMessageLoop); pp::Module::Get()->core()->CallOnMainThread( kViewChangeTimeoutSec * 1000, timeout); size_t old_page_visibility_change_count = page_visibility_log_.size(); // Run a nested message loop. It will exit either on ViewChanged or if the // timeout happens. post_quit_on_view_changed_ = true; testing_interface_->RunMessageLoop(instance_->pp_instance()); post_quit_on_view_changed_ = false; // We know we got a view changed event if something was appended to the log. return page_visibility_log_.size() > old_page_visibility_change_count; } void TestView::QuitMessageLoop(int32_t result) { testing_interface_->QuitMessageLoop(instance_->pp_instance()); } std::string TestView::TestCreatedVisible() { ASSERT_FALSE(page_visibility_log_.empty()); ASSERT_TRUE(page_visibility_log_[0]); PASS(); } std::string TestView::TestCreatedInvisible() { ASSERT_FALSE(page_visibility_log_.empty()); if (page_visibility_log_[0]) { // Add more error message since this test has some extra requirements. instance_->AppendError("Initial page is set to visible. NOTE: " "This test must be run in a background tab. " "Either run in the UI test which does this, or you can middle-click " "on the test link to run manually."); } ASSERT_FALSE(page_visibility_log_[0]); PASS(); } std::string TestView::TestPageHideShow() { // Initial state should be visible. ASSERT_FALSE(page_visibility_log_.empty()); ASSERT_TRUE(page_visibility_log_[0]); // Now that we're alive, tell the test knows it can change our visibility. instance_->ReportProgress("TestPageHideShow:Created"); // Wait until we get a hide event, being careful to handle spurious // notifications of ViewChanged. PP_Time begin_time = pp::Module::Get()->core()->GetTime(); while (WaitUntilViewChanged() && page_visibility_log_[page_visibility_log_.size() - 1] && pp::Module::Get()->core()->GetTime() - begin_time < kViewChangeTimeoutSec) { } if (page_visibility_log_[page_visibility_log_.size() - 1]) { // Didn't get a view changed event that changed visibility (though there // may have been some that didn't change visibility). // Add more error message since this test has some extra requirements. return "Didn't receive a hide event in timeout. NOTE: " "This test requires tab visibility to change and won't pass if you " "just run it in a browser. Normally the UI test should handle " "this. You can also run manually by waiting 2 secs, creating a new " "tab, waiting 2 more secs, and closing the new tab."; } // Tell the test so it can show us again. instance_->ReportProgress("TestPageHideShow:Hidden"); // Wait until we get a show event. begin_time = pp::Module::Get()->core()->GetTime(); while (WaitUntilViewChanged() && !page_visibility_log_[page_visibility_log_.size() - 1] && pp::Module::Get()->core()->GetTime() - begin_time < kViewChangeTimeoutSec) { } ASSERT_TRUE(page_visibility_log_[page_visibility_log_.size() - 1]); PASS(); } std::string TestView::TestSizeChange() { pp::Rect original_rect = last_view_.GetRect(); pp::Rect desired_rect = original_rect; desired_rect.set_width(original_rect.width() + 10); desired_rect.set_height(original_rect.height() + 12); std::ostringstream script_stream; script_stream << "var plugin = document.getElementById('plugin');"; script_stream << "plugin.setAttribute('width', " << desired_rect.width() << ");"; script_stream << "plugin.setAttribute('height', " << desired_rect.height() << ");"; instance_->EvalScript(script_stream.str()); PP_Time begin_time = pp::Module::Get()->core()->GetTime(); while (WaitUntilViewChanged() && last_view_.GetRect() != desired_rect && pp::Module::Get()->core()->GetTime() - begin_time < kViewChangeTimeoutSec) { } ASSERT_TRUE(last_view_.GetRect() == desired_rect); PASS(); } std::string TestView::TestClipChange() { pp::Rect original_rect = last_view_.GetRect(); // Original clip should be the full frame. pp::Rect original_clip = last_view_.GetClipRect(); ASSERT_TRUE(original_clip.x() == 0); ASSERT_TRUE(original_clip.y() == 0); ASSERT_TRUE(original_clip.width() == original_rect.width()); ASSERT_TRUE(original_clip.height() == original_rect.height()); int clip_amount = original_rect.height() / 2; // It might be nice to set the position to be absolute and set the location, // but this will cause WebKit to actually tear down the plugin and recreate // it. So instead we add a big div to cause the document to be scrollable, // and scroll it down. std::ostringstream script_stream; script_stream << "var big = document.createElement('div');" << "big.setAttribute('style', 'position:absolute; left:100px; " "top:0px; width:1px; height:5000px;');" << "document.body.appendChild(big);" << "window.scrollBy(0, " << original_rect.y() + clip_amount << ");"; instance_->EvalScript(script_stream.str()); pp::Rect desired_clip = original_clip; desired_clip.set_y(clip_amount); desired_clip.set_height(desired_clip.height() - desired_clip.y()); PP_Time begin_time = pp::Module::Get()->core()->GetTime(); while (WaitUntilViewChanged() && last_view_.GetClipRect() != desired_clip && pp::Module::Get()->core()->GetTime() - begin_time < kViewChangeTimeoutSec) { } ASSERT_TRUE(last_view_.GetClipRect() == desired_clip); PASS(); } std::string TestView::TestScrollOffsetChange() { instance_->EvalScript("document.body.style.width = '5000px';" "document.body.style.height = '5000px';"); instance_->EvalScript("window.scrollTo(5, 1);"); PP_Time begin_time = pp::Module::Get()->core()->GetTime(); while (WaitUntilViewChanged() && last_view_.GetScrollOffset() != pp::Point(5, 1) && pp::Module::Get()->core()->GetTime() - begin_time < kViewChangeTimeoutSec) { } ASSERT_EQ(pp::Point(5, 1), last_view_.GetScrollOffset()); instance_->EvalScript("window.scrollTo(0, 0);"); begin_time = pp::Module::Get()->core()->GetTime(); while (WaitUntilViewChanged() && last_view_.GetScrollOffset() != pp::Point(0, 0) && pp::Module::Get()->core()->GetTime() - begin_time < kViewChangeTimeoutSec) { } ASSERT_EQ(pp::Point(0, 0), last_view_.GetScrollOffset()); PASS(); }