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 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 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 43 void FlushCallbackCheckImageData(void* data, int32_t result) { 44 static_cast<TestFullscreen*>(data)->CheckPluginPaint(); 45 } 46 47 } // namespace 48 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 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 74 void TestFullscreen::RunTests(const std::string& filter) { 75 RUN_TEST(GetScreenSize, filter); 76 RUN_TEST(NormalToFullscreenToNormal, filter); 77 } 78 79 bool TestFullscreen::GotError() { 80 return !error_.empty(); 81 } 82 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. 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 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 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 171 void TestFullscreen::FailFullscreenTest(const std::string& error) { 172 error_ = error; 173 fullscreen_event_.Signal(); 174 } 175 176 void TestFullscreen::FailNormalTest(const std::string& error) { 177 error_ = error; 178 normal_event_.Signal(); 179 } 180 181 void TestFullscreen::PassFullscreenTest() { 182 fullscreen_event_.Signal(); 183 } 184 185 void TestFullscreen::PassNormalTest() { 186 normal_event_.Signal(); 187 } 188 189 // Transition to fullscreen can only happen when processing a user gesture. 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 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 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_. 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