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