diff --git a/content/browser/screen_orientation/screen_orientation_dispatcher_host.cc b/content/browser/screen_orientation/screen_orientation_dispatcher_host.cc index ccc3e228d17ea9..3bb494d93f9001 100644 --- a/content/browser/screen_orientation/screen_orientation_dispatcher_host.cc +++ b/content/browser/screen_orientation/screen_orientation_dispatcher_host.cc @@ -23,7 +23,7 @@ bool ScreenOrientationDispatcherHost::OnMessageReceived( bool handled = true; IPC_BEGIN_MESSAGE_MAP(ScreenOrientationDispatcherHost, message) - IPC_MESSAGE_HANDLER(ScreenOrientationHostMsg_Lock, OnLockRequest) + IPC_MESSAGE_HANDLER(ScreenOrientationHostMsg_LockRequest, OnLockRequest) IPC_MESSAGE_HANDLER(ScreenOrientationHostMsg_Unlock, OnUnlockRequest) IPC_MESSAGE_UNHANDLED(handled = false) IPC_END_MESSAGE_MAP() @@ -42,10 +42,20 @@ void ScreenOrientationDispatcherHost::SetProviderForTests( } void ScreenOrientationDispatcherHost::OnLockRequest( - blink::WebScreenOrientationLockType orientation) { - if (!provider_.get()) + blink::WebScreenOrientationLockType orientation, + int request_id) { + if (!provider_) { + Send(new ScreenOrientationMsg_LockError( + request_id, + blink::WebLockOrientationCallback::ErrorTypeNotAvailable)); return; + } + // TODO(mlamouri): pass real values. + Send(new ScreenOrientationMsg_LockSuccess( + request_id, + 0, + blink::WebScreenOrientationPortraitPrimary)); provider_->LockOrientation(orientation); } diff --git a/content/browser/screen_orientation/screen_orientation_dispatcher_host.h b/content/browser/screen_orientation/screen_orientation_dispatcher_host.h index 401a9e0349bb07..363c08c6ec13cf 100644 --- a/content/browser/screen_orientation/screen_orientation_dispatcher_host.h +++ b/content/browser/screen_orientation/screen_orientation_dispatcher_host.h @@ -28,10 +28,12 @@ class CONTENT_EXPORT ScreenOrientationDispatcherHost void SetProviderForTests(ScreenOrientationProvider* provider); - private: + protected: virtual ~ScreenOrientationDispatcherHost(); - void OnLockRequest(blink::WebScreenOrientationLockType orientations); + private: + void OnLockRequest(blink::WebScreenOrientationLockType orientation, + int request_id); void OnUnlockRequest(); static ScreenOrientationProvider* CreateProvider(); diff --git a/content/browser/screen_orientation/screen_orientation_dispatcher_host_unittest.cc b/content/browser/screen_orientation/screen_orientation_dispatcher_host_unittest.cc index 67f81de005e450..b652413d4cbc76 100644 --- a/content/browser/screen_orientation/screen_orientation_dispatcher_host_unittest.cc +++ b/content/browser/screen_orientation/screen_orientation_dispatcher_host_unittest.cc @@ -7,7 +7,12 @@ #include "content/browser/screen_orientation/screen_orientation_dispatcher_host.h" #include "content/browser/screen_orientation/screen_orientation_provider.h" #include "content/common/screen_orientation_messages.h" +#include "content/public/browser/browser_context.h" +#include "content/public/test/mock_render_process_host.h" +#include "content/public/test/test_browser_context.h" +#include "content/public/test/test_browser_thread_bundle.h" #include "content/public/test/test_utils.h" +#include "ipc/ipc_test_sink.h" #include "testing/gtest/include/gtest/gtest.h" namespace content { @@ -21,7 +26,6 @@ class MockScreenOrientationProvider : public ScreenOrientationProvider { virtual void LockOrientation(blink::WebScreenOrientationLockType orientation) OVERRIDE { orientation_ = orientation; - } virtual void UnlockOrientation() OVERRIDE { @@ -45,12 +49,32 @@ class MockScreenOrientationProvider : public ScreenOrientationProvider { DISALLOW_COPY_AND_ASSIGN(MockScreenOrientationProvider); }; +class ScreenOrientationDispatcherHostWithSink FINAL : + public ScreenOrientationDispatcherHost { + public: + explicit ScreenOrientationDispatcherHostWithSink(IPC::TestSink* sink) + : ScreenOrientationDispatcherHost() , sink_(sink) {} + + virtual bool Send(IPC::Message* message) OVERRIDE { + return sink_->Send(message); + } + + private: + virtual ~ScreenOrientationDispatcherHostWithSink() { } + + IPC::TestSink* sink_; +}; + class ScreenOrientationDispatcherHostTest : public testing::Test { protected: + virtual ScreenOrientationDispatcherHost* CreateDispatcher() { + return new ScreenOrientationDispatcherHost(); + } + virtual void SetUp() OVERRIDE { provider_ = new MockScreenOrientationProvider(); - dispatcher_ = new ScreenOrientationDispatcherHost(); + dispatcher_ = CreateDispatcher(); dispatcher_->SetProviderForTests(provider_); } @@ -59,19 +83,25 @@ class ScreenOrientationDispatcherHostTest : public testing::Test { scoped_refptr dispatcher_; }; -// Test that a NULL provider is correctly handled. -TEST_F(ScreenOrientationDispatcherHostTest, NullProvider) { - dispatcher_->SetProviderForTests(NULL); +class ScreenOrientationDispatcherHostWithSinkTest : + public ScreenOrientationDispatcherHostTest { + protected: + virtual ScreenOrientationDispatcherHost* CreateDispatcher() OVERRIDE { + return new ScreenOrientationDispatcherHostWithSink(&sink_); + } - bool message_was_handled = dispatcher_->OnMessageReceived( - ScreenOrientationHostMsg_Lock( - blink::WebScreenOrientationLockPortraitPrimary)); - EXPECT_TRUE(message_was_handled); -} + const IPC::TestSink& sink() const { + return sink_; + } + + IPC::TestSink sink_; +}; // Test that when receiving a lock message, it is correctly dispatched to the // ScreenOrientationProvider. -TEST_F(ScreenOrientationDispatcherHostTest, ProviderLock) { +// We don't actually need this to be a *WithSinkTest but otherwise the IPC +// messages are detected as leaked. +TEST_F(ScreenOrientationDispatcherHostWithSinkTest, ProviderLock) { // If we change this array, update |orientationsToTestCount| below. blink::WebScreenOrientationLockType orientationsToTest[] = { blink::WebScreenOrientationLockPortraitPrimary, @@ -93,7 +123,7 @@ TEST_F(ScreenOrientationDispatcherHostTest, ProviderLock) { blink::WebScreenOrientationLockType orientation = orientationsToTest[i]; message_was_handled = dispatcher_->OnMessageReceived( - ScreenOrientationHostMsg_Lock(orientation)); + ScreenOrientationHostMsg_LockRequest(orientation, 0)); EXPECT_TRUE(message_was_handled); EXPECT_EQ(orientation, provider_->orientation()); @@ -110,4 +140,45 @@ TEST_F(ScreenOrientationDispatcherHostTest, ProviderUnlock) { EXPECT_TRUE(provider_->unlock_called()); } +// Test that when there is no provider, a LockRequest fails with the appropriate +// ErrorType. +TEST_F(ScreenOrientationDispatcherHostWithSinkTest, NoProvider_LockError) { + dispatcher_->SetProviderForTests(NULL); + + const int request_id = 3; + dispatcher_->OnMessageReceived(ScreenOrientationHostMsg_LockRequest( + blink::WebScreenOrientationLockPortraitPrimary, request_id)); + + EXPECT_EQ(1u, sink().message_count()); + + const IPC::Message* msg = sink().GetFirstMessageMatching( + ScreenOrientationMsg_LockError::ID); + EXPECT_TRUE(msg != NULL); + + Tuple2 params; + ScreenOrientationMsg_LockError::Read(msg, ¶ms); + EXPECT_EQ(request_id, params.a); + EXPECT_EQ(blink::WebLockOrientationCallback::ErrorTypeNotAvailable, params.b); +} + +// Test that when there is a provider, we always send a success response back to +// the renderer. +// TODO(mlamouri): we currently do not test the content of the message because +// it currently contains dummy values. +TEST_F(ScreenOrientationDispatcherHostWithSinkTest, WithProvider_LockSuccess) { + const int request_id = 42; + dispatcher_->OnMessageReceived(ScreenOrientationHostMsg_LockRequest( + blink::WebScreenOrientationLockPortraitPrimary, request_id)); + + EXPECT_EQ(1u, sink().message_count()); + + const IPC::Message* msg = sink().GetFirstMessageMatching( + ScreenOrientationMsg_LockSuccess::ID); + EXPECT_TRUE(msg != NULL); + + Tuple3 params; + ScreenOrientationMsg_LockSuccess::Read(msg, ¶ms); + EXPECT_EQ(request_id, params.a); +} + } // namespace content diff --git a/content/common/DEPS b/content/common/DEPS index afbcc16c777d87..7d2d0e7e355f7d 100644 --- a/content/common/DEPS +++ b/content/common/DEPS @@ -21,6 +21,7 @@ include_rules = [ "+third_party/WebKit/public/platform/WebIDBCursor.h", "+third_party/WebKit/public/platform/WebIDBDatabase.h", "+third_party/WebKit/public/platform/WebIDBTypes.h", + "+third_party/WebKit/public/platform/WebLockOrientationCallback.h", "+third_party/WebKit/public/platform/WebReferrerPolicy.h", "+third_party/WebKit/public/platform/WebScreenOrientationLockType.h", "+third_party/WebKit/public/platform/WebScreenOrientationType.h", diff --git a/content/common/screen_orientation_messages.h b/content/common/screen_orientation_messages.h index 9ea305727f4e7d..5a845e7d50fb63 100644 --- a/content/common/screen_orientation_messages.h +++ b/content/common/screen_orientation_messages.h @@ -7,6 +7,7 @@ #include "content/common/content_export.h" #include "ipc/ipc_message_macros.h" +#include "third_party/WebKit/public/platform/WebLockOrientationCallback.h" #include "third_party/WebKit/public/platform/WebScreenOrientationLockType.h" #include "third_party/WebKit/public/platform/WebScreenOrientationType.h" @@ -21,16 +22,41 @@ IPC_ENUM_TRAITS_MIN_MAX_VALUE(blink::WebScreenOrientationType, IPC_ENUM_TRAITS_MIN_MAX_VALUE(blink::WebScreenOrientationLockType, blink::WebScreenOrientationLockDefault, blink::WebScreenOrientationLockPortrait) +IPC_ENUM_TRAITS_MIN_MAX_VALUE( + blink::WebLockOrientationCallback::ErrorType, + blink::WebLockOrientationCallback::ErrorTypeNotAvailable, + blink::WebLockOrientationCallback::ErrorTypeCanceled) // The browser process informs the renderer process that the screen orientation // has changed. |orientation| contains the new screen orientation in degrees. IPC_MESSAGE_CONTROL1(ScreenOrientationMsg_OrientationChange, blink::WebScreenOrientationType /* orientation */ ) +// The browser process' response to a ScreenOrientationHostMsg_LockRequest when +// the lock actually succeeded. The message includes the new |angle| and |type| +// of orientation. The |request_id| passed when receiving the request is passed +// back so the renderer process can associate the response to the right request. +IPC_MESSAGE_CONTROL3(ScreenOrientationMsg_LockSuccess, + int, /* request_id */ + unsigned, /* angle */ + blink::WebScreenOrientationType /* type */) + +// The browser process' response to a ScreenOrientationHostMsg_LockRequest when +// the lock actually failed. The message includes the |error| type. The +// |request_id| passed when receiving the request is passed back so the renderer +// process can associate the response to the right request. +IPC_MESSAGE_CONTROL2(ScreenOrientationMsg_LockError, + int, /* request_id */ + blink::WebLockOrientationCallback::ErrorType /* error */); + // The renderer process requests the browser process to lock the screen -// orientation to the specified |orientations|. -IPC_MESSAGE_CONTROL1(ScreenOrientationHostMsg_Lock, - blink::WebScreenOrientationLockType /* orientations */ ) +// orientation to the specified |orientations|. The request contains a +// |request_id| that will have to be passed back to the renderer process when +// notifying about a success or error (see ScreenOrientationMsg_LockError and +// ScreenOrientationMsg_LockSuccess). +IPC_MESSAGE_CONTROL2(ScreenOrientationHostMsg_LockRequest, + blink::WebScreenOrientationLockType, /* orientation */ + int /* request_id */) // The renderer process requests the browser process to unlock the screen // orientation. diff --git a/content/renderer/renderer_webkitplatformsupport_impl.cc b/content/renderer/renderer_webkitplatformsupport_impl.cc index 5f9f398fc7633f..cd8628f4fa9183 100644 --- a/content/renderer/renderer_webkitplatformsupport_impl.cc +++ b/content/renderer/renderer_webkitplatformsupport_impl.cc @@ -1126,6 +1126,13 @@ void RendererWebKitPlatformSupportImpl::cancelVibration() { //------------------------------------------------------------------------------ +void RendererWebKitPlatformSupportImpl::EnsureScreenOrientationDispatcher() { + if (screen_orientation_dispatcher_) + return; + + screen_orientation_dispatcher_.reset(new ScreenOrientationDispatcher()); +} + void RendererWebKitPlatformSupportImpl::setScreenOrientationListener( blink::WebScreenOrientationListener* listener) { if (RenderThreadImpl::current() && @@ -1138,22 +1145,23 @@ void RendererWebKitPlatformSupportImpl::setScreenOrientationListener( return; } - if (!screen_orientation_dispatcher_) { - screen_orientation_dispatcher_.reset( - new ScreenOrientationDispatcher(RenderThread::Get())); - } + EnsureScreenOrientationDispatcher(); screen_orientation_dispatcher_->setListener(listener); } void RendererWebKitPlatformSupportImpl::lockOrientation( - blink::WebScreenOrientationLockType orientation) { + blink::WebScreenOrientationLockType orientation, + blink::WebLockOrientationCallback* callback) { if (RenderThreadImpl::current() && RenderThreadImpl::current()->layout_test_mode()) { g_test_screen_orientation_controller.Get().UpdateLock(orientation); return; } - RenderThread::Get()->Send(new ScreenOrientationHostMsg_Lock(orientation)); + + EnsureScreenOrientationDispatcher(); + screen_orientation_dispatcher_->LockOrientation( + orientation, scoped_ptr(callback)); } void RendererWebKitPlatformSupportImpl::unlockOrientation() { @@ -1162,7 +1170,9 @@ void RendererWebKitPlatformSupportImpl::unlockOrientation() { g_test_screen_orientation_controller.Get().ResetLock(); return; } - RenderThread::Get()->Send(new ScreenOrientationHostMsg_Unlock); + + EnsureScreenOrientationDispatcher(); + screen_orientation_dispatcher_->UnlockOrientation(); } // static diff --git a/content/renderer/renderer_webkitplatformsupport_impl.h b/content/renderer/renderer_webkitplatformsupport_impl.h index 725a8ec902c3ef..0dfb4dc2b7cd52 100644 --- a/content/renderer/renderer_webkitplatformsupport_impl.h +++ b/content/renderer/renderer_webkitplatformsupport_impl.h @@ -148,7 +148,8 @@ class CONTENT_EXPORT RendererWebKitPlatformSupportImpl virtual void cancelVibration(); virtual void setScreenOrientationListener( blink::WebScreenOrientationListener*); - virtual void lockOrientation(blink::WebScreenOrientationLockType); + virtual void lockOrientation(blink::WebScreenOrientationLockType, + blink::WebLockOrientationCallback*); virtual void unlockOrientation(); virtual void setBatteryStatusListener( blink::WebBatteryStatusListener* listener); @@ -196,6 +197,7 @@ class CONTENT_EXPORT RendererWebKitPlatformSupportImpl private: bool CheckPreparsedJsCachingEnabled() const; + void EnsureScreenOrientationDispatcher(); scoped_ptr clipboard_client_; scoped_ptr clipboard_; diff --git a/content/renderer/screen_orientation/screen_orientation_dispatcher.cc b/content/renderer/screen_orientation/screen_orientation_dispatcher.cc index 3416ec50a01a8c..280f9c79a55812 100644 --- a/content/renderer/screen_orientation/screen_orientation_dispatcher.cc +++ b/content/renderer/screen_orientation/screen_orientation_dispatcher.cc @@ -10,10 +10,12 @@ namespace content { -ScreenOrientationDispatcher::ScreenOrientationDispatcher( - RenderThread* thread) - : listener_(NULL) { - thread->AddObserver(this); +ScreenOrientationDispatcher::ScreenOrientationDispatcher() + : listener_(NULL) { + RenderThread::Get()->AddObserver(this); +} + +ScreenOrientationDispatcher::~ScreenOrientationDispatcher() { } bool ScreenOrientationDispatcher::OnControlMessageReceived( @@ -23,6 +25,10 @@ bool ScreenOrientationDispatcher::OnControlMessageReceived( IPC_BEGIN_MESSAGE_MAP(ScreenOrientationDispatcher, message) IPC_MESSAGE_HANDLER(ScreenOrientationMsg_OrientationChange, OnOrientationChange) + IPC_MESSAGE_HANDLER(ScreenOrientationMsg_LockSuccess, + OnLockSuccess) + IPC_MESSAGE_HANDLER(ScreenOrientationMsg_LockError, + OnLockError) IPC_MESSAGE_UNHANDLED(handled = false) IPC_END_MESSAGE_MAP() @@ -37,9 +43,56 @@ void ScreenOrientationDispatcher::OnOrientationChange( listener_->didChangeScreenOrientation(orientation); } +void ScreenOrientationDispatcher::OnLockSuccess( + int request_id, + unsigned angle, + blink::WebScreenOrientationType orientation) { + blink::WebLockOrientationCallback* callback = + pending_callbacks_.Lookup(request_id); + if (!callback) + return; + callback->onSuccess(angle, orientation); + pending_callbacks_.Remove(request_id); +} + +void ScreenOrientationDispatcher::OnLockError( + int request_id, + blink::WebLockOrientationCallback::ErrorType error) { + blink::WebLockOrientationCallback* callback = + pending_callbacks_.Lookup(request_id); + if (!callback) + return; + callback->onError(error); + pending_callbacks_.Remove(request_id); +} + void ScreenOrientationDispatcher::setListener( blink::WebScreenOrientationListener* listener) { listener_ = listener; } +void ScreenOrientationDispatcher::CancelPendingLocks() { + for (CallbackMap::Iterator + iterator(&pending_callbacks_); !iterator.IsAtEnd(); iterator.Advance()) { + iterator.GetCurrentValue()->onError( + blink::WebLockOrientationCallback::ErrorTypeCanceled); + pending_callbacks_.Remove(iterator.GetCurrentKey()); + } +} + +void ScreenOrientationDispatcher::LockOrientation( + blink::WebScreenOrientationLockType orientation, + scoped_ptr callback) { + CancelPendingLocks(); + + int request_id = pending_callbacks_.Add(callback.release()); + RenderThread::Get()->Send( + new ScreenOrientationHostMsg_LockRequest(orientation, request_id)); +} + +void ScreenOrientationDispatcher::UnlockOrientation() { + CancelPendingLocks(); + RenderThread::Get()->Send(new ScreenOrientationHostMsg_Unlock); +} + } // namespace content diff --git a/content/renderer/screen_orientation/screen_orientation_dispatcher.h b/content/renderer/screen_orientation/screen_orientation_dispatcher.h index 5aadd6e52887de..b34e173fc8e29d 100644 --- a/content/renderer/screen_orientation/screen_orientation_dispatcher.h +++ b/content/renderer/screen_orientation/screen_orientation_dispatcher.h @@ -5,8 +5,12 @@ #ifndef CONTENT_RENDERER_SCREEN_ORIENTATION_SCREEN_ORIENTATION_DISPATCHER_H_ #define CONTENT_RENDERER_SCREEN_ORIENTATION_SCREEN_ORIENTATION_DISPATCHER_H_ +#include "base/id_map.h" #include "base/macros.h" +#include "base/memory/scoped_ptr.h" #include "content/public/renderer/render_process_observer.h" +#include "third_party/WebKit/public/platform/WebLockOrientationCallback.h" +#include "third_party/WebKit/public/platform/WebScreenOrientationLockType.h" #include "third_party/WebKit/public/platform/WebScreenOrientationType.h" namespace blink { @@ -15,26 +19,49 @@ class WebScreenOrientationListener; namespace content { -class RenderThread; - // ScreenOrientationDispatcher listens to message from the browser process and -// dispatch the orientation change ones to the WebScreenOrientationListener. +// dispatch the orientation change ones to the WebScreenOrientationListener. It +// also does the bridge between the browser process and Blink with regards to +// lock orientation request and the handling of WebLockOrientationCallback. class CONTENT_EXPORT ScreenOrientationDispatcher : public RenderProcessObserver { public: - explicit ScreenOrientationDispatcher(RenderThread*); - virtual ~ScreenOrientationDispatcher() {} + ScreenOrientationDispatcher(); + virtual ~ScreenOrientationDispatcher(); // RenderProcessObserver virtual bool OnControlMessageReceived(const IPC::Message& message) OVERRIDE; void setListener(blink::WebScreenOrientationListener* listener); + // The |callback| is owned by ScreenOrientationDispatcher. It will be assigned + // to |pending_callbacks_| that will delete it when the entry will be removed + // from the map. + void LockOrientation(blink::WebScreenOrientationLockType orientation, + scoped_ptr callback); + + void UnlockOrientation(); + private: void OnOrientationChange(blink::WebScreenOrientationType orientation); + void OnLockSuccess(int request_id, + unsigned angle, + blink::WebScreenOrientationType orientation); + void OnLockError(int request_id, + blink::WebLockOrientationCallback::ErrorType error); + + void CancelPendingLocks(); blink::WebScreenOrientationListener* listener_; + // The pending_callbacks_ map is mostly meant to have a unique ID to associate + // with every callback going trough the dispatcher. The map will own the + // pointer in the sense that it will destroy it when Remove() will be called. + // Furthermore, we only expect to have one callback at a time in this map, + // which is what IDMap was designed for. + typedef IDMap CallbackMap; + CallbackMap pending_callbacks_; + DISALLOW_COPY_AND_ASSIGN(ScreenOrientationDispatcher); }; diff --git a/content/renderer/screen_orientation/screen_orientation_dispatcher_unittest.cc b/content/renderer/screen_orientation/screen_orientation_dispatcher_unittest.cc index acc12c2eb4dc35..85bcf01663896e 100644 --- a/content/renderer/screen_orientation/screen_orientation_dispatcher_unittest.cc +++ b/content/renderer/screen_orientation/screen_orientation_dispatcher_unittest.cc @@ -4,12 +4,15 @@ #include "screen_orientation_dispatcher.h" +#include + #include "base/logging.h" #include "base/memory/scoped_ptr.h" #include "content/common/screen_orientation_messages.h" #include "content/public/test/mock_render_thread.h" #include "content/public/test/test_utils.h" #include "testing/gtest/include/gtest/gtest.h" +#include "third_party/WebKit/public/platform/WebLockOrientationCallback.h" #include "third_party/WebKit/public/platform/WebScreenOrientationListener.h" namespace content { @@ -38,6 +41,55 @@ class MockScreenOrientationListener : DISALLOW_COPY_AND_ASSIGN(MockScreenOrientationListener); }; +// MockLockOrientationCallback is an implementation of +// WebLockOrientationCallback and takes a LockOrientationResultHolder* as a +// parameter when being constructed. The |results_| pointer is owned by the +// caller and not by the callback object. The intent being that as soon as the +// callback is resolved, it will be killed so we use the +// LockOrientationResultHolder to know in which state the callback object is at +// any time. +class MockLockOrientationCallback : + public blink::WebLockOrientationCallback { + public: + struct LockOrientationResultHolder { + LockOrientationResultHolder() + : succeeded_(false), failed_(false) {} + + bool succeeded_; + bool failed_; + unsigned angle_; + blink::WebScreenOrientationType orientation_; + blink::WebLockOrientationCallback::ErrorType error_; + }; + + static scoped_ptr CreateScoped( + LockOrientationResultHolder* results) { + return scoped_ptr( + new MockLockOrientationCallback(results)); + } + + virtual void onSuccess(unsigned angle, + blink::WebScreenOrientationType orientation) { + results_->succeeded_ = true; + results_->angle_ = angle; + results_->orientation_ = orientation; + } + + virtual void onError( + blink::WebLockOrientationCallback::ErrorType error) { + results_->failed_ = true; + results_->error_ = error; + } + + private: + explicit MockLockOrientationCallback(LockOrientationResultHolder* results) + : results_(results) {} + + virtual ~MockLockOrientationCallback() {} + + LockOrientationResultHolder* results_; +}; + MockScreenOrientationListener::MockScreenOrientationListener() : did_change_screen_orientation_(false), screen_orientation_(blink::WebScreenOrientationPortraitPrimary) { @@ -54,10 +106,20 @@ class ScreenOrientationDispatcherTest : public testing::Test { virtual void SetUp() OVERRIDE { render_thread_.reset(new MockRenderThread); listener_.reset(new MockScreenOrientationListener); - dispatcher_.reset(new ScreenOrientationDispatcher(render_thread_.get())); + dispatcher_.reset(new ScreenOrientationDispatcher); dispatcher_->setListener(listener_.get()); } + int GetFirstLockRequestIdFromSink() { + const IPC::Message* msg = render_thread_->sink().GetFirstMessageMatching( + ScreenOrientationHostMsg_LockRequest::ID); + EXPECT_TRUE(msg != NULL); + + Tuple2 params; + ScreenOrientationHostMsg_LockRequest::Read(msg, ¶ms); + return params.b; + } + scoped_ptr render_thread_; scoped_ptr listener_; scoped_ptr dispatcher_; @@ -109,4 +171,174 @@ TEST_F(ScreenOrientationDispatcherTest, ValidValues) { listener_->screen_orientation()); } +// Test that calling LockOrientation() followed by UnlockOrientation() cancel +// the LockOrientation(). +TEST_F(ScreenOrientationDispatcherTest, CancelPending_Unlocking) { + MockLockOrientationCallback::LockOrientationResultHolder callback_results; + dispatcher_->LockOrientation( + blink::WebScreenOrientationLockPortraitPrimary, + MockLockOrientationCallback::CreateScoped(&callback_results)); + dispatcher_->UnlockOrientation(); + + EXPECT_FALSE(callback_results.succeeded_); + EXPECT_TRUE(callback_results.failed_); + EXPECT_EQ(blink::WebLockOrientationCallback::ErrorTypeCanceled, + callback_results.error_); +} + +// Test that calling LockOrientation() twice cancel the first LockOrientation(). +TEST_F(ScreenOrientationDispatcherTest, CancelPending_DoubleLock) { + MockLockOrientationCallback::LockOrientationResultHolder callback_results; + // We create the object to prevent leaks but never actually use it. + MockLockOrientationCallback::LockOrientationResultHolder callback_results2; + + dispatcher_->LockOrientation( + blink::WebScreenOrientationLockPortraitPrimary, + MockLockOrientationCallback::CreateScoped(&callback_results)); + dispatcher_->LockOrientation( + blink::WebScreenOrientationLockPortraitPrimary, + MockLockOrientationCallback::CreateScoped(&callback_results2)); + + EXPECT_FALSE(callback_results.succeeded_); + EXPECT_TRUE(callback_results.failed_); + EXPECT_EQ(blink::WebLockOrientationCallback::ErrorTypeCanceled, + callback_results.error_); +} + +// Test that when a LockError message is received, the request is set as failed +// with the correct values. +TEST_F(ScreenOrientationDispatcherTest, LockRequest_Error) { + std::list errors; + errors.push_back(blink::WebLockOrientationCallback::ErrorTypeNotAvailable); + errors.push_back( + blink::WebLockOrientationCallback::ErrorTypeFullScreenRequired); + errors.push_back(blink::WebLockOrientationCallback::ErrorTypeCanceled); + + for (std::list::const_iterator + it = errors.begin(); it != errors.end(); ++it) { + render_thread_->sink().ClearMessages(); + + MockLockOrientationCallback::LockOrientationResultHolder callback_results; + dispatcher_->LockOrientation( + blink::WebScreenOrientationLockPortraitPrimary, + MockLockOrientationCallback::CreateScoped(&callback_results)); + + int request_id = GetFirstLockRequestIdFromSink(); + render_thread_->OnControlMessageReceived( + ScreenOrientationMsg_LockError(request_id, *it)); + + EXPECT_FALSE(callback_results.succeeded_); + EXPECT_TRUE(callback_results.failed_); + EXPECT_EQ(*it, callback_results.error_); + } +} + +// Test that when a LockSuccess message is received, the request is set as +// succeeded with the correct values. +TEST_F(ScreenOrientationDispatcherTest, LockRequest_Success) { + struct ScreenOrientationInformation { + unsigned angle; + blink::WebScreenOrientationType type; + } orientations[] = { + { 0, blink::WebScreenOrientationPortraitPrimary }, + { 0, blink::WebScreenOrientationLandscapePrimary }, + { 90, blink::WebScreenOrientationPortraitSecondary }, + { 90, blink::WebScreenOrientationLandscapePrimary } + }; + + int orientationsCount = 4; + + for (int i = 0; i < orientationsCount; ++i) { + render_thread_->sink().ClearMessages(); + + MockLockOrientationCallback::LockOrientationResultHolder callback_results; + dispatcher_->LockOrientation( + blink::WebScreenOrientationLockPortraitPrimary, + MockLockOrientationCallback::CreateScoped(&callback_results)); + + int request_id = GetFirstLockRequestIdFromSink(); + render_thread_->OnControlMessageReceived( + ScreenOrientationMsg_LockSuccess(request_id, + orientations[i].angle, + orientations[i].type)); + + EXPECT_TRUE(callback_results.succeeded_); + EXPECT_FALSE(callback_results.failed_); + EXPECT_EQ(orientations[i].angle, callback_results.angle_); + EXPECT_EQ(orientations[i].type, callback_results.orientation_); + } +} + +// Test an edge case: a LockSuccess is received but it matches no pending +// callback. +TEST_F(ScreenOrientationDispatcherTest, SuccessForUnknownRequest) { + MockLockOrientationCallback::LockOrientationResultHolder callback_results; + dispatcher_->LockOrientation( + blink::WebScreenOrientationLockPortraitPrimary, + MockLockOrientationCallback::CreateScoped(&callback_results)); + + int request_id = GetFirstLockRequestIdFromSink(); + render_thread_->OnControlMessageReceived(ScreenOrientationMsg_LockSuccess( + request_id + 1, + 90, + blink::WebScreenOrientationLandscapePrimary)); + + EXPECT_FALSE(callback_results.succeeded_); + EXPECT_FALSE(callback_results.failed_); +} + +// Test an edge case: a LockError is received but it matches no pending +// callback. +TEST_F(ScreenOrientationDispatcherTest, ErrorForUnknownRequest) { + MockLockOrientationCallback::LockOrientationResultHolder callback_results; + dispatcher_->LockOrientation( + blink::WebScreenOrientationLockPortraitPrimary, + MockLockOrientationCallback::CreateScoped(&callback_results)); + + int request_id = GetFirstLockRequestIdFromSink(); + render_thread_->OnControlMessageReceived(ScreenOrientationMsg_LockError( + request_id + 1, + blink::WebLockOrientationCallback::ErrorTypeCanceled)); + + EXPECT_FALSE(callback_results.succeeded_); + EXPECT_FALSE(callback_results.failed_); +} + +// Test the following scenario: +// - request1 is received by the dispatcher; +// - request2 is received by the dispatcher; +// - request1 is rejected; +// - request1 success response is received. +// Expected: request1 is still rejected, request2 has not been set as succeeded. +TEST_F(ScreenOrientationDispatcherTest, RaceScenario) { + MockLockOrientationCallback::LockOrientationResultHolder callback_results1; + MockLockOrientationCallback::LockOrientationResultHolder callback_results2; + + dispatcher_->LockOrientation( + blink::WebScreenOrientationLockPortraitPrimary, + MockLockOrientationCallback::CreateScoped(&callback_results1)); + int request_id1 = GetFirstLockRequestIdFromSink(); + + dispatcher_->LockOrientation( + blink::WebScreenOrientationLockLandscapePrimary, + MockLockOrientationCallback::CreateScoped(&callback_results2)); + + // callback_results1 must be rejected, tested in CancelPending_DoubleLock. + + render_thread_->OnControlMessageReceived(ScreenOrientationMsg_LockSuccess( + request_id1, + 0, + blink::WebScreenOrientationPortraitPrimary)); + + // First request is still rejected. + EXPECT_FALSE(callback_results1.succeeded_); + EXPECT_TRUE(callback_results1.failed_); + EXPECT_EQ(blink::WebLockOrientationCallback::ErrorTypeCanceled, + callback_results1.error_); + + // Second request is still pending. + EXPECT_FALSE(callback_results2.succeeded_); + EXPECT_FALSE(callback_results2.failed_); +} + } // namespace content