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