| // 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 "config.h" |
| #include "modules/screen_orientation/ScreenOrientationController.h" |
| |
| #include "core/events/Event.h" |
| #include "core/frame/FrameView.h" |
| #include "core/frame/LocalFrame.h" |
| #include "core/page/Page.h" |
| #include "modules/screen_orientation/ScreenOrientation.h" |
| #include "modules/screen_orientation/ScreenOrientationDispatcher.h" |
| #include "platform/LayoutTestSupport.h" |
| #include "platform/PlatformScreen.h" |
| #include "public/platform/WebScreenOrientationClient.h" |
| |
| namespace blink { |
| |
| ScreenOrientationController::~ScreenOrientationController() |
| { |
| } |
| |
| void ScreenOrientationController::persistentHostHasBeenDestroyed() |
| { |
| // Unregister lifecycle observation once page is being torn down. |
| observeContext(0); |
| } |
| |
| void ScreenOrientationController::provideTo(LocalFrame& frame, WebScreenOrientationClient* client) |
| { |
| ASSERT(RuntimeEnabledFeatures::screenOrientationEnabled()); |
| |
| ScreenOrientationController* controller = new ScreenOrientationController(frame, client); |
| WillBeHeapSupplement<LocalFrame>::provideTo(frame, supplementName(), adoptPtrWillBeNoop(controller)); |
| } |
| |
| ScreenOrientationController* ScreenOrientationController::from(LocalFrame& frame) |
| { |
| return static_cast<ScreenOrientationController*>(WillBeHeapSupplement<LocalFrame>::from(frame, supplementName())); |
| } |
| |
| ScreenOrientationController::ScreenOrientationController(LocalFrame& frame, WebScreenOrientationClient* client) |
| : PlatformEventController(frame.page()) |
| , m_client(client) |
| , m_frame(frame) |
| , 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(FrameView* view) |
| { |
| // 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; |
| |
| FloatRect rect = screenRect(view); |
| uint16_t rotation = screenOrientationAngle(view); |
| 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); |
| |
| WebScreenOrientationType orientationType = screenOrientationType(m_frame.view()); |
| if (orientationType == WebScreenOrientationUndefined) { |
| // The embedder could not provide us with an orientation, deduce it ourselves. |
| orientationType = computeOrientation(m_frame.view()); |
| } |
| ASSERT(orientationType != WebScreenOrientationUndefined); |
| |
| m_orientation->setType(orientationType); |
| m_orientation->setAngle(screenOrientationAngle(m_frame.view())); |
| } |
| |
| void ScreenOrientationController::pageVisibilityChanged() |
| { |
| notifyDispatcher(); |
| |
| if (!m_orientation || !page() || page()->visibilityState() != PageVisibilityStateVisible) |
| 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 = screenOrientationAngle(m_frame.view()); |
| |
| // 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 (m_frame == m_frame.localFrameRoot() && m_orientation->angle() != currentAngle) |
| notifyOrientationChanged(); |
| } |
| |
| void ScreenOrientationController::notifyOrientationChanged() |
| { |
| ASSERT(RuntimeEnabledFeatures::screenOrientationEnabled()); |
| |
| if (!m_orientation || !page() || page()->visibilityState() != PageVisibilityStateVisible) |
| 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. |
| Vector<RefPtr<LocalFrame> > childFrames; |
| for (Frame* child = m_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, FROM_HERE); |
| |
| // ... and child frames, if they have a ScreenOrientationController. |
| for (size_t i = 0; i < childFrames.size(); ++i) { |
| ScreenOrientationController* controller = ScreenOrientationController::from(*childFrames[i]); |
| if (controller) |
| controller->notifyOrientationChanged(); |
| } |
| } |
| |
| void ScreenOrientationController::setOrientation(ScreenOrientation* orientation) |
| { |
| m_orientation = orientation; |
| if (m_orientation) |
| updateOrientation(); |
| notifyDispatcher(); |
| } |
| |
| void ScreenOrientationController::lock(WebScreenOrientationLockType orientation, WebLockOrientationCallback* callback) |
| { |
| ASSERT(m_client); |
| m_client->lockOrientation(orientation, callback); |
| } |
| |
| void ScreenOrientationController::unlock() |
| { |
| ASSERT(m_client); |
| m_client->unlockOrientation(); |
| } |
| |
| const LocalFrame& ScreenOrientationController::frame() const |
| { |
| return m_frame; |
| } |
| |
| 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::notifyDispatcher() |
| { |
| if (m_orientation && page()->visibilityState() == PageVisibilityStateVisible) |
| startUpdating(); |
| else |
| stopUpdating(); |
| } |
| |
| void ScreenOrientationController::trace(Visitor* visitor) |
| { |
| visitor->trace(m_orientation); |
| WillBeHeapSupplement<LocalFrame>::trace(visitor); |
| } |
| |
| } // namespace blink |