| // 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 |