blob: a1a1186ce118d50d4a20e9979df15ce5073eba52 [file] [log] [blame]
// Copyright 2014 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 "modules/screen_orientation/ScreenOrientationController.h"
#include "core/events/Event.h"
#include "core/frame/FrameHost.h"
#include "core/frame/FrameView.h"
#include "core/frame/LocalFrame.h"
#include "core/page/ChromeClient.h"
#include "core/page/Page.h"
#include "modules/screen_orientation/ScreenOrientation.h"
#include "modules/screen_orientation/ScreenOrientationDispatcher.h"
#include "platform/LayoutTestSupport.h"
#include "public/platform/WebScreenInfo.h"
#include "public/platform/modules/screen_orientation/WebScreenOrientationClient.h"
namespace blink {
ScreenOrientationController::~ScreenOrientationController()
{
}
void ScreenOrientationController::provideTo(LocalFrame& frame, WebScreenOrientationClient* client)
{
ASSERT(RuntimeEnabledFeatures::screenOrientationEnabled());
ScreenOrientationController* controller = new ScreenOrientationController(frame, client);
Supplement<LocalFrame>::provideTo(frame, supplementName(), controller);
}
ScreenOrientationController* ScreenOrientationController::from(LocalFrame& frame)
{
return static_cast<ScreenOrientationController*>(Supplement<LocalFrame>::from(frame, supplementName()));
}
ScreenOrientationController::ScreenOrientationController(LocalFrame& frame, WebScreenOrientationClient* client)
: LocalFrameLifecycleObserver(&frame)
, PlatformEventController(frame.page())
, m_client(client)
, m_dispatchEventTimer(this, &ScreenOrientationController::dispatchEventTimerFired)
{
}
const char* ScreenOrientationController::supplementName()
{
return "ScreenOrientationController";
}
// Compute the screen orientation using the orientation angle and the screen width / height.
WebScreenOrientationType ScreenOrientationController::computeOrientation(const IntRect& rect, uint16_t rotation)
{
// Bypass orientation detection in layout tests to get consistent results.
// FIXME: The screen dimension should be fixed when running the layout tests to avoid such
// issues.
if (LayoutTestSupport::isRunningLayoutTest())
return WebScreenOrientationPortraitPrimary;
bool isTallDisplay = rotation % 180 ? rect.height() < rect.width() : rect.height() > rect.width();
switch (rotation) {
case 0:
return isTallDisplay ? WebScreenOrientationPortraitPrimary : WebScreenOrientationLandscapePrimary;
case 90:
return isTallDisplay ? WebScreenOrientationLandscapePrimary : WebScreenOrientationPortraitSecondary;
case 180:
return isTallDisplay ? WebScreenOrientationPortraitSecondary : WebScreenOrientationLandscapeSecondary;
case 270:
return isTallDisplay ? WebScreenOrientationLandscapeSecondary : WebScreenOrientationPortraitPrimary;
default:
ASSERT_NOT_REACHED();
return WebScreenOrientationPortraitPrimary;
}
}
void ScreenOrientationController::updateOrientation()
{
ASSERT(m_orientation);
ASSERT(frame());
ASSERT(frame()->host());
ChromeClient& chromeClient = frame()->host()->chromeClient();
WebScreenInfo screenInfo = chromeClient.screenInfo();
WebScreenOrientationType orientationType = screenInfo.orientationType;
if (orientationType == WebScreenOrientationUndefined) {
// The embedder could not provide us with an orientation, deduce it ourselves.
orientationType = computeOrientation(chromeClient.screenInfo().rect, screenInfo.orientationAngle);
}
ASSERT(orientationType != WebScreenOrientationUndefined);
m_orientation->setType(orientationType);
m_orientation->setAngle(screenInfo.orientationAngle);
}
bool ScreenOrientationController::isActiveAndVisible() const
{
return m_orientation && frame() && page() && page()->isPageVisible();
}
void ScreenOrientationController::pageVisibilityChanged()
{
notifyDispatcher();
if (!isActiveAndVisible())
return;
// The orientation type and angle are tied in a way that if the angle has
// changed, the type must have changed.
unsigned short currentAngle = frame()->host()->chromeClient().screenInfo().orientationAngle;
// FIXME: sendOrientationChangeEvent() currently send an event all the
// children of the frame, so it should only be called on the frame on
// top of the tree. We would need the embedder to call
// sendOrientationChangeEvent on every WebFrame part of a WebView to be
// able to remove this.
if (frame() == frame()->localFrameRoot() && m_orientation->angle() != currentAngle)
notifyOrientationChanged();
}
void ScreenOrientationController::notifyOrientationChanged()
{
ASSERT(RuntimeEnabledFeatures::screenOrientationEnabled());
if (!isActiveAndVisible())
return;
updateOrientation();
// Keep track of the frames that need to be notified before notifying the
// current frame as it will prevent side effects from the change event
// handlers.
HeapVector<Member<LocalFrame>> childFrames;
for (Frame* child = frame()->tree().firstChild(); child; child = child->tree().nextSibling()) {
if (child->isLocalFrame())
childFrames.append(toLocalFrame(child));
}
// Notify current orientation object.
if (!m_dispatchEventTimer.isActive())
m_dispatchEventTimer.startOneShot(0, BLINK_FROM_HERE);
// ... and child frames, if they have a ScreenOrientationController.
for (size_t i = 0; i < childFrames.size(); ++i) {
if (ScreenOrientationController* controller = ScreenOrientationController::from(*childFrames[i]))
controller->notifyOrientationChanged();
}
}
void ScreenOrientationController::setOrientation(ScreenOrientation* orientation)
{
m_orientation = orientation;
if (m_orientation)
updateOrientation();
notifyDispatcher();
}
void ScreenOrientationController::lock(WebScreenOrientationLockType orientation, WebLockOrientationCallback* callback)
{
// When detached, the client is no longer valid.
if (!m_client)
return;
m_client->lockOrientation(orientation, callback);
}
void ScreenOrientationController::unlock()
{
// When detached, the client is no longer valid.
if (!m_client)
return;
m_client->unlockOrientation();
}
void ScreenOrientationController::dispatchEventTimerFired(Timer<ScreenOrientationController>*)
{
if (!m_orientation)
return;
m_orientation->dispatchEvent(Event::create(EventTypeNames::change));
}
void ScreenOrientationController::didUpdateData()
{
// Do nothing.
}
void ScreenOrientationController::registerWithDispatcher()
{
ScreenOrientationDispatcher::instance().addController(this);
}
void ScreenOrientationController::unregisterWithDispatcher()
{
ScreenOrientationDispatcher::instance().removeController(this);
}
bool ScreenOrientationController::hasLastData()
{
return true;
}
void ScreenOrientationController::contextDestroyed()
{
m_client = nullptr;
LocalFrameLifecycleObserver::contextDestroyed();
}
void ScreenOrientationController::notifyDispatcher()
{
if (m_orientation && page()->isPageVisible())
startUpdating();
else
stopUpdating();
}
DEFINE_TRACE(ScreenOrientationController)
{
visitor->trace(m_orientation);
LocalFrameLifecycleObserver::trace(visitor);
Supplement<LocalFrame>::trace(visitor);
PlatformEventController::trace(visitor);
}
} // namespace blink