| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "ppapi/tests/test_fullscreen.h" |
| |
| #include <stdio.h> |
| #include <string.h> |
| #include <string> |
| |
| #include "ppapi/c/ppb_fullscreen.h" |
| #include "ppapi/cpp/image_data.h" |
| #include "ppapi/cpp/input_event.h" |
| #include "ppapi/cpp/instance.h" |
| #include "ppapi/cpp/module.h" |
| #include "ppapi/cpp/point.h" |
| #include "ppapi/tests/test_utils.h" |
| #include "ppapi/tests/testing_instance.h" |
| |
| REGISTER_TEST_CASE(Fullscreen); |
| |
| namespace { |
| |
| const ColorPremul kSheerBlue = { 0x88, 0x00, 0x00, 0x88 }; |
| const ColorPremul kOpaqueYellow = { 0xFF, 0xFF, 0xFF, 0x00 }; |
| const int kBytesPerPixel = sizeof(uint32_t); // 4 bytes for BGRA or RGBA. |
| |
| uint32_t FormatColor(PP_ImageDataFormat format, ColorPremul color) { |
| if (format == PP_IMAGEDATAFORMAT_BGRA_PREMUL) |
| return (color.A << 24) | (color.R << 16) | (color.G << 8) | (color.B); |
| else if (format == PP_IMAGEDATAFORMAT_RGBA_PREMUL) |
| return (color.A << 24) | (color.B << 16) | (color.G << 8) | (color.R); |
| else |
| return 0; |
| } |
| |
| bool HasMidScreen(const pp::Rect& position, const pp::Size& screen_size) { |
| static int32_t mid_x = screen_size.width() / 2; |
| static int32_t mid_y = screen_size.height() / 2; |
| return (position.Contains(mid_x, mid_y)); |
| } |
| |
| void FlushCallbackCheckImageData(void* data, int32_t result) { |
| static_cast<TestFullscreen*>(data)->CheckPluginPaint(); |
| } |
| |
| } // namespace |
| |
| TestFullscreen::TestFullscreen(TestingInstance* instance) |
| : TestCase(instance), |
| error_(), |
| screen_mode_(instance), |
| painted_color_(0), |
| fullscreen_pending_(false), |
| normal_pending_(false), |
| fullscreen_event_(instance->pp_instance()), |
| normal_event_(instance->pp_instance()) { |
| screen_mode_.GetScreenSize(&screen_size_); |
| } |
| |
| bool TestFullscreen::Init() { |
| if (screen_size_.IsEmpty()) { |
| instance_->AppendError("Failed to initialize screen_size_"); |
| return false; |
| } |
| graphics2d_ = pp::Graphics2D(instance_, screen_size_, true); |
| if (!instance_->BindGraphics(graphics2d_)) { |
| instance_->AppendError("Failed to initialize graphics2d_"); |
| return false; |
| } |
| return CheckTestingInterface(); |
| } |
| |
| void TestFullscreen::RunTests(const std::string& filter) { |
| RUN_TEST(GetScreenSize, filter); |
| RUN_TEST(NormalToFullscreenToNormal, filter); |
| } |
| |
| bool TestFullscreen::GotError() { |
| return !error_.empty(); |
| } |
| |
| std::string TestFullscreen::Error() { |
| std::string last_error = error_; |
| error_.clear(); |
| return last_error; |
| } |
| |
| // TODO(polina): consider adding custom logic to JS for this test to |
| // get screen.width and screen.height and postMessage those to this code, |
| // so the dimensions can be checked exactly. |
| std::string TestFullscreen::TestGetScreenSize() { |
| if (screen_size_.width() < 320 || screen_size_.width() > 2560) |
| return ReportError("screen_size.width()", screen_size_.width()); |
| if (screen_size_.height() < 200 || screen_size_.height() > 2048) |
| return ReportError("screen_size.height()", screen_size_.height()); |
| PASS(); |
| } |
| |
| std::string TestFullscreen::TestNormalToFullscreenToNormal() { |
| // 0. Start in normal mode. |
| if (screen_mode_.IsFullscreen()) |
| return ReportError("IsFullscreen() at start", true); |
| |
| // 1. Switch to fullscreen. |
| // This is only allowed within a context of a user gesture (e.g. mouse click). |
| if (screen_mode_.SetFullscreen(true)) |
| return ReportError("SetFullscreen(true) outside of user gesture", true); |
| // Trigger another call to SetFullscreen(true) from HandleInputEvent(). |
| // The transition is asynchronous and ends at the next DidChangeView(). |
| instance_->RequestInputEvents(PP_INPUTEVENT_CLASS_MOUSE); |
| SimulateUserGesture(); |
| // DidChangeView() will call the callback once in fullscreen mode. |
| fullscreen_event_.Wait(); |
| if (GotError()) |
| return Error(); |
| if (fullscreen_pending_) |
| return "fullscreen_pending_ has not been reset"; |
| if (!screen_mode_.IsFullscreen()) |
| return ReportError("IsFullscreen() in fullscreen", false); |
| |
| // 2. Stay in fullscreen. No change. |
| if (screen_mode_.SetFullscreen(true)) |
| return ReportError("SetFullscreen(true) in fullscreen", true); |
| if (!screen_mode_.IsFullscreen()) |
| return ReportError("IsFullscreen() in fullscreen^2", false); |
| |
| // 3. Switch to normal. |
| // The transition is asynchronous and ends at DidChangeView(). |
| // No graphics devices can be bound while in transition. |
| normal_pending_ = true; |
| if (!screen_mode_.SetFullscreen(false)) |
| return ReportError("SetFullscreen(false) in fullscreen", false); |
| // DidChangeView() will signal once out of fullscreen mode. |
| normal_event_.Wait(); |
| if (GotError()) |
| return Error(); |
| if (normal_pending_) |
| return "normal_pending_ has not been reset"; |
| if (screen_mode_.IsFullscreen()) |
| return ReportError("IsFullscreen() in normal", true); |
| |
| // 4. Stay in normal. No change. |
| if (screen_mode_.SetFullscreen(false)) |
| return ReportError("SetFullscreen(false) in normal", true); |
| if (screen_mode_.IsFullscreen()) |
| return ReportError("IsFullscreen() in normal^2", true); |
| |
| PASS(); |
| } |
| |
| void TestFullscreen::SimulateUserGesture() { |
| pp::Point plugin_center( |
| normal_position_.x() + normal_position_.width() / 2, |
| normal_position_.y() + normal_position_.height() / 2); |
| pp::Point mouse_movement; |
| pp::MouseInputEvent input_event( |
| instance_, |
| PP_INPUTEVENT_TYPE_MOUSEDOWN, |
| NowInTimeTicks(), // time_stamp |
| 0, // modifiers |
| PP_INPUTEVENT_MOUSEBUTTON_LEFT, |
| plugin_center, |
| 1, // click_count |
| mouse_movement); |
| |
| testing_interface_->SimulateInputEvent(instance_->pp_instance(), |
| input_event.pp_resource()); |
| } |
| |
| void TestFullscreen::FailFullscreenTest(const std::string& error) { |
| error_ = error; |
| fullscreen_event_.Signal(); |
| } |
| |
| void TestFullscreen::FailNormalTest(const std::string& error) { |
| error_ = error; |
| normal_event_.Signal(); |
| } |
| |
| void TestFullscreen::PassFullscreenTest() { |
| fullscreen_event_.Signal(); |
| } |
| |
| void TestFullscreen::PassNormalTest() { |
| normal_event_.Signal(); |
| } |
| |
| // Transition to fullscreen can only happen when processing a user gesture. |
| bool TestFullscreen::HandleInputEvent(const pp::InputEvent& event) { |
| // We only let mouse events through and only mouse clicks count. |
| if (event.GetType() != PP_INPUTEVENT_TYPE_MOUSEDOWN && |
| event.GetType() != PP_INPUTEVENT_TYPE_MOUSEUP) |
| return false; |
| // We got the gesture. No need to handle any more events. |
| instance_->ClearInputEventRequest(PP_INPUTEVENT_CLASS_MOUSE); |
| if (screen_mode_.IsFullscreen()) { |
| FailFullscreenTest( |
| ReportError("IsFullscreen() before fullscreen transition", true)); |
| return false; |
| } |
| fullscreen_pending_ = true; |
| if (!screen_mode_.SetFullscreen(true)) { |
| FailFullscreenTest(ReportError("SetFullscreen(true) in normal", false)); |
| return false; |
| } |
| // DidChangeView() will complete the transition to fullscreen. |
| return false; |
| } |
| |
| bool TestFullscreen::PaintPlugin(pp::Size size, ColorPremul color) { |
| painted_size_ = size; |
| PP_ImageDataFormat image_format = pp::ImageData::GetNativeImageDataFormat(); |
| painted_color_ = FormatColor(image_format, color); |
| if (painted_color_ == 0) |
| return false; |
| pp::Point origin(0, 0); |
| |
| pp::ImageData image(instance_, image_format, size, false); |
| if (image.is_null()) |
| return false; |
| uint32_t* pixels = static_cast<uint32_t*>(image.data()); |
| int num_pixels = image.stride() / kBytesPerPixel * image.size().height(); |
| for (int i = 0; i < num_pixels; i++) |
| pixels[i] = painted_color_; |
| graphics2d_.PaintImageData(image, origin); |
| pp::CompletionCallback cc(FlushCallbackCheckImageData, this); |
| if (graphics2d_.Flush(cc) != PP_OK_COMPLETIONPENDING) |
| return false; |
| |
| return true; |
| } |
| |
| void TestFullscreen::CheckPluginPaint() { |
| PP_ImageDataFormat image_format = pp::ImageData::GetNativeImageDataFormat(); |
| pp::ImageData readback(instance_, image_format, painted_size_, false); |
| pp::Point origin(0, 0); |
| if (readback.is_null() || |
| PP_TRUE != testing_interface_->ReadImageData(graphics2d_.pp_resource(), |
| readback.pp_resource(), |
| &origin.pp_point())) { |
| error_ = "Can't read plugin image"; |
| return; |
| } |
| for (int y = 0; y < painted_size_.height(); y++) { |
| for (int x = 0; x < painted_size_.width(); x++) { |
| uint32_t* readback_color = readback.GetAddr32(pp::Point(x, y)); |
| if (painted_color_ != *readback_color) { |
| error_ = "Plugin image contains incorrect pixel value"; |
| return; |
| } |
| } |
| } |
| if (screen_mode_.IsFullscreen()) |
| PassFullscreenTest(); |
| else |
| PassNormalTest(); |
| } |
| |
| // Transitions to/from fullscreen is asynchronous ending at DidChangeView. |
| // The number of calls to DidChangeView during fullscreen / normal transitions |
| // isn't specified by the API. The test waits until it the screen has |
| // transitioned to the desired state. |
| // |
| // WebKit does not change the plugin size, but Pepper does explicitly set |
| // it to screen width and height when SetFullscreen(true) is called and |
| // resets it back when ViewChanged is received indicating that we exited |
| // fullscreen. |
| // |
| // NOTE: The number of DidChangeView calls for <object> might be different. |
| // TODO(bbudge) Figure out how to test that the plugin positon eventually |
| // changes to normal_position_. |
| void TestFullscreen::DidChangeView(const pp::View& view) { |
| pp::Rect position = view.GetRect(); |
| pp::Rect clip = view.GetClipRect(); |
| |
| if (normal_position_.IsEmpty()) |
| normal_position_ = position; |
| |
| bool is_fullscreen = screen_mode_.IsFullscreen(); |
| if (fullscreen_pending_ && is_fullscreen) { |
| fullscreen_pending_ = false; |
| if (!HasMidScreen(position, screen_size_)) |
| FailFullscreenTest("DidChangeView is not in the middle of the screen"); |
| else if (position.size() != screen_size_) |
| FailFullscreenTest("DidChangeView does not have screen size"); |
| // NOTE: we cannot reliably test for clip size being equal to the screen |
| // because it might be affected by JS console, info bars, etc. |
| else if (!instance_->BindGraphics(graphics2d_)) |
| FailFullscreenTest("Failed to BindGraphics() in fullscreen"); |
| else if (!PaintPlugin(position.size(), kOpaqueYellow)) |
| FailFullscreenTest("Failed to paint plugin image in fullscreen"); |
| } else if (normal_pending_ && !is_fullscreen) { |
| normal_pending_ = false; |
| if (screen_mode_.IsFullscreen()) |
| FailNormalTest("DidChangeview is in fullscreen"); |
| else if (!instance_->BindGraphics(graphics2d_)) |
| FailNormalTest("Failed to BindGraphics() in normal"); |
| else if (!PaintPlugin(position.size(), kSheerBlue)) |
| FailNormalTest("Failed to paint plugin image in normal"); |
| } |
| } |