• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2012 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_fullscreen.h"
6 
7 #include <stdio.h>
8 #include <string.h>
9 #include <string>
10 
11 #include "ppapi/c/ppb_fullscreen.h"
12 #include "ppapi/cpp/image_data.h"
13 #include "ppapi/cpp/input_event.h"
14 #include "ppapi/cpp/instance.h"
15 #include "ppapi/cpp/module.h"
16 #include "ppapi/cpp/point.h"
17 #include "ppapi/tests/test_utils.h"
18 #include "ppapi/tests/testing_instance.h"
19 
20 REGISTER_TEST_CASE(Fullscreen);
21 
22 namespace {
23 
24 const ColorPremul kSheerBlue = { 0x88, 0x00, 0x00, 0x88 };
25 const ColorPremul kOpaqueYellow = { 0xFF, 0xFF, 0xFF, 0x00 };
26 const int kBytesPerPixel = sizeof(uint32_t);  // 4 bytes for BGRA or RGBA.
27 
FormatColor(PP_ImageDataFormat format,ColorPremul color)28 uint32_t FormatColor(PP_ImageDataFormat format, ColorPremul color) {
29   if (format == PP_IMAGEDATAFORMAT_BGRA_PREMUL)
30     return (color.A << 24) | (color.R << 16) | (color.G << 8) | (color.B);
31   else if (format == PP_IMAGEDATAFORMAT_RGBA_PREMUL)
32     return (color.A << 24) | (color.B << 16) | (color.G << 8) | (color.R);
33   else
34     return 0;
35 }
36 
HasMidScreen(const pp::Rect & position,const pp::Size & screen_size)37 bool HasMidScreen(const pp::Rect& position, const pp::Size& screen_size) {
38   static int32_t mid_x = screen_size.width() / 2;
39   static int32_t mid_y = screen_size.height() / 2;
40   return (position.Contains(mid_x, mid_y));
41 }
42 
FlushCallbackCheckImageData(void * data,int32_t result)43 void FlushCallbackCheckImageData(void* data, int32_t result) {
44   static_cast<TestFullscreen*>(data)->CheckPluginPaint();
45 }
46 
47 }  // namespace
48 
TestFullscreen(TestingInstance * instance)49 TestFullscreen::TestFullscreen(TestingInstance* instance)
50     : TestCase(instance),
51       error_(),
52       screen_mode_(instance),
53       painted_color_(0),
54       fullscreen_pending_(false),
55       normal_pending_(false),
56       fullscreen_event_(instance->pp_instance()),
57       normal_event_(instance->pp_instance()) {
58   screen_mode_.GetScreenSize(&screen_size_);
59 }
60 
Init()61 bool TestFullscreen::Init() {
62   if (screen_size_.IsEmpty()) {
63     instance_->AppendError("Failed to initialize screen_size_");
64     return false;
65   }
66   graphics2d_ = pp::Graphics2D(instance_, screen_size_, true);
67   if (!instance_->BindGraphics(graphics2d_)) {
68     instance_->AppendError("Failed to initialize graphics2d_");
69     return false;
70   }
71   return CheckTestingInterface();
72 }
73 
RunTests(const std::string & filter)74 void TestFullscreen::RunTests(const std::string& filter) {
75   RUN_TEST(GetScreenSize, filter);
76   RUN_TEST(NormalToFullscreenToNormal, filter);
77 }
78 
GotError()79 bool TestFullscreen::GotError() {
80   return !error_.empty();
81 }
82 
Error()83 std::string TestFullscreen::Error() {
84   std::string last_error = error_;
85   error_.clear();
86   return last_error;
87 }
88 
89 // TODO(polina): consider adding custom logic to JS for this test to
90 // get screen.width and screen.height and postMessage those to this code,
91 // so the dimensions can be checked exactly.
TestGetScreenSize()92 std::string TestFullscreen::TestGetScreenSize() {
93   if (screen_size_.width() < 320 || screen_size_.width() > 2560)
94     return ReportError("screen_size.width()", screen_size_.width());
95   if (screen_size_.height() < 200 || screen_size_.height() > 2048)
96     return ReportError("screen_size.height()", screen_size_.height());
97   PASS();
98 }
99 
TestNormalToFullscreenToNormal()100 std::string TestFullscreen::TestNormalToFullscreenToNormal() {
101   // 0. Start in normal mode.
102   if (screen_mode_.IsFullscreen())
103     return ReportError("IsFullscreen() at start", true);
104 
105   // 1. Switch to fullscreen.
106   // This is only allowed within a context of a user gesture (e.g. mouse click).
107   if (screen_mode_.SetFullscreen(true))
108     return ReportError("SetFullscreen(true) outside of user gesture", true);
109   // Trigger another call to SetFullscreen(true) from HandleInputEvent().
110   // The transition is asynchronous and ends at the next DidChangeView().
111   instance_->RequestInputEvents(PP_INPUTEVENT_CLASS_MOUSE);
112   SimulateUserGesture();
113   // DidChangeView() will call the callback once in fullscreen mode.
114   fullscreen_event_.Wait();
115   if (GotError())
116     return Error();
117   if (fullscreen_pending_)
118     return "fullscreen_pending_ has not been reset";
119   if (!screen_mode_.IsFullscreen())
120     return ReportError("IsFullscreen() in fullscreen", false);
121 
122   // 2. Stay in fullscreen. No change.
123   if (screen_mode_.SetFullscreen(true))
124     return ReportError("SetFullscreen(true) in fullscreen", true);
125   if (!screen_mode_.IsFullscreen())
126     return ReportError("IsFullscreen() in fullscreen^2", false);
127 
128   // 3. Switch to normal.
129   // The transition is asynchronous and ends at DidChangeView().
130   // No graphics devices can be bound while in transition.
131   normal_pending_ = true;
132   if (!screen_mode_.SetFullscreen(false))
133     return ReportError("SetFullscreen(false) in fullscreen", false);
134   // DidChangeView() will signal once out of fullscreen mode.
135   normal_event_.Wait();
136   if (GotError())
137     return Error();
138   if (normal_pending_)
139     return "normal_pending_ has not been reset";
140   if (screen_mode_.IsFullscreen())
141     return ReportError("IsFullscreen() in normal", true);
142 
143   // 4. Stay in normal. No change.
144   if (screen_mode_.SetFullscreen(false))
145     return ReportError("SetFullscreen(false) in normal", true);
146   if (screen_mode_.IsFullscreen())
147     return ReportError("IsFullscreen() in normal^2", true);
148 
149   PASS();
150 }
151 
SimulateUserGesture()152 void TestFullscreen::SimulateUserGesture() {
153   pp::Point plugin_center(
154       normal_position_.x() + normal_position_.width() / 2,
155       normal_position_.y() + normal_position_.height() / 2);
156   pp::Point mouse_movement;
157   pp::MouseInputEvent input_event(
158       instance_,
159       PP_INPUTEVENT_TYPE_MOUSEDOWN,
160       0,  // time_stamp
161       0,  // modifiers
162       PP_INPUTEVENT_MOUSEBUTTON_LEFT,
163       plugin_center,
164       1,  // click_count
165       mouse_movement);
166 
167   testing_interface_->SimulateInputEvent(instance_->pp_instance(),
168                                          input_event.pp_resource());
169 }
170 
FailFullscreenTest(const std::string & error)171 void TestFullscreen::FailFullscreenTest(const std::string& error) {
172   error_ = error;
173   fullscreen_event_.Signal();
174 }
175 
FailNormalTest(const std::string & error)176 void TestFullscreen::FailNormalTest(const std::string& error) {
177   error_ = error;
178   normal_event_.Signal();
179 }
180 
PassFullscreenTest()181 void TestFullscreen::PassFullscreenTest() {
182   fullscreen_event_.Signal();
183 }
184 
PassNormalTest()185 void TestFullscreen::PassNormalTest() {
186   normal_event_.Signal();
187 }
188 
189 // Transition to fullscreen can only happen when processing a user gesture.
HandleInputEvent(const pp::InputEvent & event)190 bool TestFullscreen::HandleInputEvent(const pp::InputEvent& event) {
191   // We only let mouse events through and only mouse clicks count.
192   if (event.GetType() != PP_INPUTEVENT_TYPE_MOUSEDOWN &&
193       event.GetType() != PP_INPUTEVENT_TYPE_MOUSEUP)
194     return false;
195   // We got the gesture. No need to handle any more events.
196   instance_->ClearInputEventRequest(PP_INPUTEVENT_CLASS_MOUSE);
197   if (screen_mode_.IsFullscreen()) {
198     FailFullscreenTest(
199         ReportError("IsFullscreen() before fullscreen transition", true));
200     return false;
201   }
202   fullscreen_pending_ = true;
203   if (!screen_mode_.SetFullscreen(true)) {
204     FailFullscreenTest(ReportError("SetFullscreen(true) in normal", false));
205     return false;
206   }
207   // DidChangeView() will complete the transition to fullscreen.
208   return false;
209 }
210 
PaintPlugin(pp::Size size,ColorPremul color)211 bool TestFullscreen::PaintPlugin(pp::Size size, ColorPremul color) {
212   painted_size_ = size;
213   PP_ImageDataFormat image_format = pp::ImageData::GetNativeImageDataFormat();
214   painted_color_ = FormatColor(image_format, color);
215   if (painted_color_ == 0)
216     return false;
217   pp::Point origin(0, 0);
218 
219   pp::ImageData image(instance_, image_format, size, false);
220   if (image.is_null())
221     return false;
222   uint32_t* pixels = static_cast<uint32_t*>(image.data());
223   int num_pixels = image.stride() / kBytesPerPixel * image.size().height();
224   for (int i = 0; i < num_pixels; i++)
225     pixels[i] = painted_color_;
226   graphics2d_.PaintImageData(image, origin);
227   pp::CompletionCallback cc(FlushCallbackCheckImageData, this);
228   if (graphics2d_.Flush(cc) != PP_OK_COMPLETIONPENDING)
229     return false;
230 
231   return true;
232 }
233 
CheckPluginPaint()234 void TestFullscreen::CheckPluginPaint() {
235   PP_ImageDataFormat image_format = pp::ImageData::GetNativeImageDataFormat();
236   pp::ImageData readback(instance_, image_format, painted_size_, false);
237   pp::Point origin(0, 0);
238   if (readback.is_null() ||
239       PP_TRUE != testing_interface_->ReadImageData(graphics2d_.pp_resource(),
240                                                    readback.pp_resource(),
241                                                    &origin.pp_point())) {
242     error_ = "Can't read plugin image";
243     return;
244   }
245   for (int y = 0; y < painted_size_.height(); y++) {
246     for (int x = 0; x < painted_size_.width(); x++) {
247       uint32_t* readback_color = readback.GetAddr32(pp::Point(x, y));
248       if (painted_color_ != *readback_color) {
249         error_ = "Plugin image contains incorrect pixel value";
250         return;
251       }
252     }
253   }
254   if (screen_mode_.IsFullscreen())
255     PassFullscreenTest();
256   else
257     PassNormalTest();
258 }
259 
260 // Transitions to/from fullscreen is asynchronous ending at DidChangeView.
261 // The number of calls to DidChangeView during fullscreen / normal transitions
262 // isn't specified by the API. The test waits until it the screen has
263 // transitioned to the desired state.
264 //
265 // WebKit does not change the plugin size, but Pepper does explicitly set
266 // it to screen width and height when SetFullscreen(true) is called and
267 // resets it back when ViewChanged is received indicating that we exited
268 // fullscreen.
269 //
270 // NOTE: The number of DidChangeView calls for <object> might be different.
271 // TODO(bbudge) Figure out how to test that the plugin positon eventually
272 // changes to normal_position_.
DidChangeView(const pp::View & view)273 void TestFullscreen::DidChangeView(const pp::View& view) {
274   pp::Rect position = view.GetRect();
275   pp::Rect clip = view.GetClipRect();
276 
277   if (normal_position_.IsEmpty())
278     normal_position_ = position;
279 
280   bool is_fullscreen = screen_mode_.IsFullscreen();
281   if (fullscreen_pending_ && is_fullscreen) {
282     fullscreen_pending_ = false;
283     if (!HasMidScreen(position, screen_size_))
284       FailFullscreenTest("DidChangeView is not in the middle of the screen");
285     else if (position.size() != screen_size_)
286       FailFullscreenTest("DidChangeView does not have screen size");
287     // NOTE: we cannot reliably test for clip size being equal to the screen
288     // because it might be affected by JS console, info bars, etc.
289     else if (!instance_->BindGraphics(graphics2d_))
290       FailFullscreenTest("Failed to BindGraphics() in fullscreen");
291     else if (!PaintPlugin(position.size(), kOpaqueYellow))
292       FailFullscreenTest("Failed to paint plugin image in fullscreen");
293   } else if (normal_pending_ && !is_fullscreen) {
294     normal_pending_ = false;
295     if (screen_mode_.IsFullscreen())
296       FailNormalTest("DidChangeview is in fullscreen");
297     else if (!instance_->BindGraphics(graphics2d_))
298       FailNormalTest("Failed to BindGraphics() in normal");
299     else if (!PaintPlugin(position.size(), kSheerBlue))
300       FailNormalTest("Failed to paint plugin image in normal");
301   }
302 }
303