1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "ppapi/tests/test_view.h" 6 7 #include <sstream> 8 9 #include "ppapi/c/pp_time.h" 10 #include "ppapi/c/private/ppb_testing_private.h" 11 #include "ppapi/cpp/completion_callback.h" 12 #include "ppapi/tests/testing_instance.h" 13 14 REGISTER_TEST_CASE(View); 15 16 // When waiting for view changed events, wait no longer than this. 17 #if !defined(THREAD_SANITIZER) 18 static int kViewChangeTimeoutSec = 5; 19 #else 20 // ThreadSanitizer may slow the interaction down significantly. 21 static int kViewChangeTimeoutSec = 30; 22 #endif 23 24 TestView::TestView(TestingInstance* instance) 25 : TestCase(instance), 26 post_quit_on_view_changed_(false) { 27 } 28 29 void TestView::DidChangeView(const pp::View& view) { 30 last_view_ = view; 31 page_visibility_log_.push_back(view.IsPageVisible()); 32 33 if (post_quit_on_view_changed_) { 34 post_quit_on_view_changed_ = false; 35 testing_interface_->QuitMessageLoop(instance_->pp_instance()); 36 } 37 } 38 39 bool TestView::Init() { 40 return CheckTestingInterface(); 41 } 42 43 void TestView::RunTests(const std::string& filter) { 44 RUN_TEST(CreatedVisible, filter); 45 RUN_TEST(CreatedInvisible, filter); 46 RUN_TEST(PageHideShow, filter); 47 RUN_TEST(SizeChange, filter); 48 RUN_TEST(ClipChange, filter); 49 RUN_TEST(ScrollOffsetChange, filter); 50 } 51 52 bool TestView::WaitUntilViewChanged() { 53 // Schedule a callback so this step times out if we don't get a ViewChanged 54 // in a reasonable amount of time. 55 pp::CompletionCallbackFactory<TestView> factory(this); 56 pp::CompletionCallback timeout = 57 factory.NewCallback(&TestView::QuitMessageLoop); 58 pp::Module::Get()->core()->CallOnMainThread( 59 kViewChangeTimeoutSec * 1000, timeout); 60 61 size_t old_page_visibility_change_count = page_visibility_log_.size(); 62 63 // Run a nested message loop. It will exit either on ViewChanged or if the 64 // timeout happens. 65 post_quit_on_view_changed_ = true; 66 testing_interface_->RunMessageLoop(instance_->pp_instance()); 67 post_quit_on_view_changed_ = false; 68 69 // We know we got a view changed event if something was appended to the log. 70 return page_visibility_log_.size() > old_page_visibility_change_count; 71 } 72 73 void TestView::QuitMessageLoop(int32_t result) { 74 testing_interface_->QuitMessageLoop(instance_->pp_instance()); 75 } 76 77 std::string TestView::TestCreatedVisible() { 78 ASSERT_FALSE(page_visibility_log_.empty()); 79 ASSERT_TRUE(page_visibility_log_[0]); 80 PASS(); 81 } 82 83 std::string TestView::TestCreatedInvisible() { 84 ASSERT_FALSE(page_visibility_log_.empty()); 85 86 if (page_visibility_log_[0]) { 87 // Add more error message since this test has some extra requirements. 88 instance_->AppendError("Initial page is set to visible. NOTE: " 89 "This test must be run in a background tab. " 90 "Either run in the UI test which does this, or you can middle-click " 91 "on the test link to run manually."); 92 } 93 ASSERT_FALSE(page_visibility_log_[0]); 94 PASS(); 95 } 96 97 std::string TestView::TestPageHideShow() { 98 // Initial state should be visible. 99 ASSERT_FALSE(page_visibility_log_.empty()); 100 ASSERT_TRUE(page_visibility_log_[0]); 101 102 // Now that we're alive, tell the test knows it can change our visibility. 103 instance_->ReportProgress("TestPageHideShow:Created"); 104 105 // Wait until we get a hide event, being careful to handle spurious 106 // notifications of ViewChanged. 107 PP_Time begin_time = pp::Module::Get()->core()->GetTime(); 108 while (WaitUntilViewChanged() && 109 page_visibility_log_[page_visibility_log_.size() - 1] && 110 pp::Module::Get()->core()->GetTime() - begin_time < 111 kViewChangeTimeoutSec) { 112 } 113 if (page_visibility_log_[page_visibility_log_.size() - 1]) { 114 // Didn't get a view changed event that changed visibility (though there 115 // may have been some that didn't change visibility). 116 // Add more error message since this test has some extra requirements. 117 return "Didn't receive a hide event in timeout. NOTE: " 118 "This test requires tab visibility to change and won't pass if you " 119 "just run it in a browser. Normally the UI test should handle " 120 "this. You can also run manually by waiting 2 secs, creating a new " 121 "tab, waiting 2 more secs, and closing the new tab."; 122 } 123 124 // Tell the test so it can show us again. 125 instance_->ReportProgress("TestPageHideShow:Hidden"); 126 127 // Wait until we get a show event. 128 begin_time = pp::Module::Get()->core()->GetTime(); 129 while (WaitUntilViewChanged() && 130 !page_visibility_log_[page_visibility_log_.size() - 1] && 131 pp::Module::Get()->core()->GetTime() - begin_time < 132 kViewChangeTimeoutSec) { 133 } 134 ASSERT_TRUE(page_visibility_log_[page_visibility_log_.size() - 1]); 135 136 PASS(); 137 } 138 139 std::string TestView::TestSizeChange() { 140 pp::Rect original_rect = last_view_.GetRect(); 141 142 pp::Rect desired_rect = original_rect; 143 desired_rect.set_width(original_rect.width() + 10); 144 desired_rect.set_height(original_rect.height() + 12); 145 146 std::ostringstream script_stream; 147 script_stream << "var plugin = document.getElementById('plugin');"; 148 script_stream << "plugin.setAttribute('width', " 149 << desired_rect.width() << ");"; 150 script_stream << "plugin.setAttribute('height', " 151 << desired_rect.height() << ");"; 152 153 instance_->EvalScript(script_stream.str()); 154 155 PP_Time begin_time = pp::Module::Get()->core()->GetTime(); 156 while (WaitUntilViewChanged() && last_view_.GetRect() != desired_rect && 157 pp::Module::Get()->core()->GetTime() - begin_time < 158 kViewChangeTimeoutSec) { 159 } 160 ASSERT_TRUE(last_view_.GetRect() == desired_rect); 161 162 PASS(); 163 } 164 165 std::string TestView::TestClipChange() { 166 pp::Rect original_rect = last_view_.GetRect(); 167 168 // Original clip should be the full frame. 169 pp::Rect original_clip = last_view_.GetClipRect(); 170 ASSERT_TRUE(original_clip.x() == 0); 171 ASSERT_TRUE(original_clip.y() == 0); 172 ASSERT_TRUE(original_clip.width() == original_rect.width()); 173 ASSERT_TRUE(original_clip.height() == original_rect.height()); 174 175 int clip_amount = original_rect.height() / 2; 176 177 // It might be nice to set the position to be absolute and set the location, 178 // but this will cause WebKit to actually tear down the plugin and recreate 179 // it. So instead we add a big div to cause the document to be scrollable, 180 // and scroll it down. 181 std::ostringstream script_stream; 182 script_stream 183 << "var big = document.createElement('div');" 184 << "big.setAttribute('style', 'position:absolute; left:100px; " 185 "top:0px; width:1px; height:5000px;');" 186 << "document.body.appendChild(big);" 187 << "window.scrollBy(0, " << original_rect.y() + clip_amount << ");"; 188 189 instance_->EvalScript(script_stream.str()); 190 191 pp::Rect desired_clip = original_clip; 192 desired_clip.set_y(clip_amount); 193 desired_clip.set_height(desired_clip.height() - desired_clip.y()); 194 195 PP_Time begin_time = pp::Module::Get()->core()->GetTime(); 196 while (WaitUntilViewChanged() && last_view_.GetClipRect() != desired_clip && 197 pp::Module::Get()->core()->GetTime() - begin_time < 198 kViewChangeTimeoutSec) { 199 } 200 ASSERT_TRUE(last_view_.GetClipRect() == desired_clip); 201 PASS(); 202 } 203 204 std::string TestView::TestScrollOffsetChange() { 205 instance_->EvalScript("document.body.style.width = '5000px';" 206 "document.body.style.height = '5000px';"); 207 instance_->EvalScript("window.scrollTo(5, 1);"); 208 209 PP_Time begin_time = pp::Module::Get()->core()->GetTime(); 210 while (WaitUntilViewChanged() && 211 last_view_.GetScrollOffset() != pp::Point(5, 1) && 212 pp::Module::Get()->core()->GetTime() - begin_time < 213 kViewChangeTimeoutSec) { 214 } 215 ASSERT_EQ(pp::Point(5, 1), last_view_.GetScrollOffset()); 216 217 instance_->EvalScript("window.scrollTo(0, 0);"); 218 219 begin_time = pp::Module::Get()->core()->GetTime(); 220 while (WaitUntilViewChanged() && 221 last_view_.GetScrollOffset() != pp::Point(0, 0) && 222 pp::Module::Get()->core()->GetTime() - begin_time < 223 kViewChangeTimeoutSec) { 224 } 225 ASSERT_EQ(pp::Point(0, 0), last_view_.GetScrollOffset()); 226 227 PASS(); 228 } 229