blob: dbeed629a02df14a7bef88672134a0d5b8a1d270 [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 "platform/ScopedOrientationChangeIndicator.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) {
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)
: DOMWindowProperty(&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 && m_client && page() && page()->isPageVisible();
}
void ScreenOrientationController::pageVisibilityChanged() {
notifyDispatcher();
if (!isActiveAndVisible())
return;
DCHECK(frame());
DCHECK(frame()->host());
// 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() {
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(TimerBase*) {
if (!m_orientation)
return;
ScopedOrientationChangeIndicator orientationChangeIndicator;
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::frameDestroyed() {
m_client = nullptr;
DOMWindowProperty::frameDestroyed();
}
void ScreenOrientationController::notifyDispatcher() {
if (m_orientation && page()->isPageVisible())
startUpdating();
else
stopUpdating();
}
DEFINE_TRACE(ScreenOrientationController) {
visitor->trace(m_orientation);
DOMWindowProperty::trace(visitor);
Supplement<LocalFrame>::trace(visitor);
PlatformEventController::trace(visitor);
}
} // namespace blink