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