Home | History | Annotate | Download | only in tests
      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