Home | History | Annotate | Download | only in screen_orientation
      1 // Copyright 2014 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "config.h"
      6 #include "modules/screen_orientation/ScreenOrientationController.h"
      7 
      8 #include "core/events/Event.h"
      9 #include "core/frame/FrameView.h"
     10 #include "core/frame/LocalFrame.h"
     11 #include "core/page/Page.h"
     12 #include "modules/screen_orientation/ScreenOrientation.h"
     13 #include "modules/screen_orientation/ScreenOrientationDispatcher.h"
     14 #include "platform/LayoutTestSupport.h"
     15 #include "platform/PlatformScreen.h"
     16 #include "public/platform/WebScreenOrientationClient.h"
     17 
     18 namespace blink {
     19 
     20 ScreenOrientationController::~ScreenOrientationController()
     21 {
     22 }
     23 
     24 void ScreenOrientationController::provideTo(LocalFrame& frame, WebScreenOrientationClient* client)
     25 {
     26     ASSERT(RuntimeEnabledFeatures::screenOrientationEnabled());
     27 
     28     ScreenOrientationController* controller = new ScreenOrientationController(frame, client);
     29     WillBeHeapSupplement<LocalFrame>::provideTo(frame, supplementName(), adoptPtrWillBeNoop(controller));
     30 }
     31 
     32 ScreenOrientationController* ScreenOrientationController::from(LocalFrame& frame)
     33 {
     34     return static_cast<ScreenOrientationController*>(WillBeHeapSupplement<LocalFrame>::from(frame, supplementName()));
     35 }
     36 
     37 ScreenOrientationController::ScreenOrientationController(LocalFrame& frame, WebScreenOrientationClient* client)
     38     : FrameDestructionObserver(&frame)
     39     , PlatformEventController(frame.page())
     40     , m_client(client)
     41     , m_dispatchEventTimer(this, &ScreenOrientationController::dispatchEventTimerFired)
     42 {
     43 }
     44 
     45 const char* ScreenOrientationController::supplementName()
     46 {
     47     return "ScreenOrientationController";
     48 }
     49 
     50 // Compute the screen orientation using the orientation angle and the screen width / height.
     51 WebScreenOrientationType ScreenOrientationController::computeOrientation(FrameView* view)
     52 {
     53     // Bypass orientation detection in layout tests to get consistent results.
     54     // FIXME: The screen dimension should be fixed when running the layout tests to avoid such
     55     // issues.
     56     if (LayoutTestSupport::isRunningLayoutTest())
     57         return WebScreenOrientationPortraitPrimary;
     58 
     59     FloatRect rect = screenRect(view);
     60     uint16_t rotation = screenOrientationAngle(view);
     61     bool isTallDisplay = rotation % 180 ? rect.height() < rect.width() : rect.height() > rect.width();
     62     switch (rotation) {
     63     case 0:
     64         return isTallDisplay ? WebScreenOrientationPortraitPrimary : WebScreenOrientationLandscapePrimary;
     65     case 90:
     66         return isTallDisplay ? WebScreenOrientationLandscapePrimary : WebScreenOrientationPortraitSecondary;
     67     case 180:
     68         return isTallDisplay ? WebScreenOrientationPortraitSecondary : WebScreenOrientationLandscapeSecondary;
     69     case 270:
     70         return isTallDisplay ? WebScreenOrientationLandscapeSecondary : WebScreenOrientationPortraitPrimary;
     71     default:
     72         ASSERT_NOT_REACHED();
     73         return WebScreenOrientationPortraitPrimary;
     74     }
     75 }
     76 
     77 void ScreenOrientationController::updateOrientation()
     78 {
     79     ASSERT(m_orientation);
     80     ASSERT(frame());
     81 
     82     FrameView* view = frame()->view();
     83     WebScreenOrientationType orientationType = screenOrientationType(view);
     84     if (orientationType == WebScreenOrientationUndefined) {
     85         // The embedder could not provide us with an orientation, deduce it ourselves.
     86         orientationType = computeOrientation(view);
     87     }
     88     ASSERT(orientationType != WebScreenOrientationUndefined);
     89 
     90     m_orientation->setType(orientationType);
     91     m_orientation->setAngle(screenOrientationAngle(view));
     92 }
     93 
     94 bool ScreenOrientationController::isActiveAndVisible() const
     95 {
     96     return m_orientation && frame() && page() && page()->visibilityState() == PageVisibilityStateVisible;
     97 }
     98 
     99 void ScreenOrientationController::pageVisibilityChanged()
    100 {
    101     notifyDispatcher();
    102 
    103     if (!isActiveAndVisible())
    104         return;
    105 
    106     // The orientation type and angle are tied in a way that if the angle has
    107     // changed, the type must have changed.
    108     unsigned short currentAngle = screenOrientationAngle(frame()->view());
    109 
    110     // FIXME: sendOrientationChangeEvent() currently send an event all the
    111     // children of the frame, so it should only be called on the frame on
    112     // top of the tree. We would need the embedder to call
    113     // sendOrientationChangeEvent on every WebFrame part of a WebView to be
    114     // able to remove this.
    115     if (frame() == frame()->localFrameRoot() && m_orientation->angle() != currentAngle)
    116         notifyOrientationChanged();
    117 }
    118 
    119 void ScreenOrientationController::notifyOrientationChanged()
    120 {
    121     ASSERT(RuntimeEnabledFeatures::screenOrientationEnabled());
    122 
    123     if (!isActiveAndVisible())
    124         return;
    125 
    126     updateOrientation();
    127 
    128     // Keep track of the frames that need to be notified before notifying the
    129     // current frame as it will prevent side effects from the change event
    130     // handlers.
    131     WillBeHeapVector<RefPtrWillBeMember<LocalFrame> > childFrames;
    132     for (Frame* child = frame()->tree().firstChild(); child; child = child->tree().nextSibling()) {
    133         if (child->isLocalFrame())
    134             childFrames.append(toLocalFrame(child));
    135     }
    136 
    137     // Notify current orientation object.
    138     if (!m_dispatchEventTimer.isActive())
    139         m_dispatchEventTimer.startOneShot(0, FROM_HERE);
    140 
    141     // ... and child frames, if they have a ScreenOrientationController.
    142     for (size_t i = 0; i < childFrames.size(); ++i) {
    143         if (ScreenOrientationController* controller = ScreenOrientationController::from(*childFrames[i]))
    144             controller->notifyOrientationChanged();
    145     }
    146 }
    147 
    148 void ScreenOrientationController::setOrientation(ScreenOrientation* orientation)
    149 {
    150     m_orientation = orientation;
    151     if (m_orientation)
    152         updateOrientation();
    153     notifyDispatcher();
    154 }
    155 
    156 void ScreenOrientationController::lock(WebScreenOrientationLockType orientation, WebLockOrientationCallback* callback)
    157 {
    158     // When detached, the client is no longer valid.
    159     if (!m_client)
    160         return;
    161     m_client->lockOrientation(orientation, callback);
    162 }
    163 
    164 void ScreenOrientationController::unlock()
    165 {
    166     // When detached, the client is no longer valid.
    167     if (!m_client)
    168         return;
    169     m_client->unlockOrientation();
    170 }
    171 
    172 void ScreenOrientationController::dispatchEventTimerFired(Timer<ScreenOrientationController>*)
    173 {
    174     if (!m_orientation)
    175         return;
    176     m_orientation->dispatchEvent(Event::create(EventTypeNames::change));
    177 }
    178 
    179 void ScreenOrientationController::didUpdateData()
    180 {
    181     // Do nothing.
    182 }
    183 
    184 void ScreenOrientationController::registerWithDispatcher()
    185 {
    186     ScreenOrientationDispatcher::instance().addController(this);
    187 }
    188 
    189 void ScreenOrientationController::unregisterWithDispatcher()
    190 {
    191     ScreenOrientationDispatcher::instance().removeController(this);
    192 }
    193 
    194 bool ScreenOrientationController::hasLastData()
    195 {
    196     return true;
    197 }
    198 
    199 void ScreenOrientationController::willDetachFrameHost()
    200 {
    201     m_client = nullptr;
    202 }
    203 
    204 void ScreenOrientationController::notifyDispatcher()
    205 {
    206     if (m_orientation && page()->visibilityState() == PageVisibilityStateVisible)
    207         startUpdating();
    208     else
    209         stopUpdating();
    210 }
    211 
    212 void ScreenOrientationController::trace(Visitor* visitor)
    213 {
    214     visitor->trace(m_orientation);
    215     FrameDestructionObserver::trace(visitor);
    216     WillBeHeapSupplement<LocalFrame>::trace(visitor);
    217 }
    218 
    219 } // namespace blink
    220