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