1 /* 2 * Copyright (C) 2011 Apple Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' 14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS 17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 23 * THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 #import "config.h" 26 #import "WebFullScreenManagerMac.h" 27 28 #if ENABLE(FULLSCREEN_API) 29 30 #import "Connection.h" 31 #import "LayerTreeContext.h" 32 #import "MessageID.h" 33 #import "WebFullScreenManagerProxyMessages.h" 34 #import "WebPage.h" 35 #import "WebProcess.h" 36 #import <QuartzCore/QuartzCore.h> 37 #import <WebCore/Frame.h> 38 #import <WebCore/FrameView.h> 39 #import <WebCore/GraphicsLayer.h> 40 #import <WebCore/Page.h> 41 #import <WebCore/Settings.h> 42 #import <WebKitSystemInterface.h> 43 44 using namespace WebCore; 45 46 typedef void (WebKit::WebFullScreenManager::*AnimationBeganFunction)(); 47 typedef void (WebKit::WebFullScreenManager::*AnimationFinishedFunction)(bool); 48 49 #if defined(BUILDING_ON_LEOPARD) 50 @interface CATransaction(SnowLeopardConvenienceFunctions) 51 + (void)setDisableActions:(BOOL)flag; 52 @end 53 54 @implementation CATransaction(SnowLeopardConvenienceFunctions) 55 + (void)setDisableActions:(BOOL)flag 56 { 57 [self setValue:[NSNumber numberWithBool:flag] forKey:kCATransactionDisableActions]; 58 } 59 @end 60 #endif 61 62 @interface WebFullScreenManagerAnimationListener : NSObject { 63 WebKit::WebFullScreenManager* _manager; 64 AnimationBeganFunction _began; 65 AnimationFinishedFunction _finished; 66 } 67 - (id)initWithManager:(WebKit::WebFullScreenManager*)manager began:(AnimationBeganFunction)began finished:(AnimationFinishedFunction)finished; 68 - (void)animationDidStart:(CAAnimation *)anim; 69 - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag; 70 - (void)invalidate; 71 @end 72 73 @implementation WebFullScreenManagerAnimationListener 74 - (id)initWithManager:(WebKit::WebFullScreenManager*)manager began:(AnimationBeganFunction)began finished:(AnimationFinishedFunction)finished 75 { 76 self = [super init]; 77 if (!self) 78 return nil; 79 80 _manager = manager; 81 _began = began; 82 _finished = finished; 83 return self; 84 } 85 86 - (void)animationDidStart:(CAAnimation *)anim 87 { 88 if (_manager && _began) 89 (_manager->*_began)(); 90 } 91 92 - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag 93 { 94 if (_manager && _finished) 95 (_manager->*_finished)(flag); 96 } 97 98 - (void)invalidate 99 { 100 _manager = 0; 101 _began = 0; 102 _finished = 0; 103 } 104 @end 105 106 namespace WebKit { 107 108 PassRefPtr<WebFullScreenManager> WebFullScreenManager::create(WebPage* page) 109 { 110 return WebFullScreenManagerMac::create(page); 111 } 112 113 PassRefPtr<WebFullScreenManagerMac> WebFullScreenManagerMac::create(WebPage* page) 114 { 115 return adoptRef(new WebFullScreenManagerMac(page)); 116 } 117 118 WebFullScreenManagerMac::WebFullScreenManagerMac(WebPage* page) 119 : WebFullScreenManager(page) 120 { 121 m_enterFullScreenListener.adoptNS([[WebFullScreenManagerAnimationListener alloc] initWithManager:this began:&WebFullScreenManagerMac::beganEnterFullScreenAnimation finished:&WebFullScreenManagerMac::finishedEnterFullScreenAnimation]); 122 m_exitFullScreenListener.adoptNS([[WebFullScreenManagerAnimationListener alloc] initWithManager:this began:&WebFullScreenManagerMac::beganExitFullScreenAnimation finished:&WebFullScreenManagerMac::finishedExitFullScreenAnimation]); 123 } 124 125 WebFullScreenManagerMac::~WebFullScreenManagerMac() 126 { 127 m_page->send(Messages::WebFullScreenManagerProxy::ExitAcceleratedCompositingMode()); 128 [m_enterFullScreenListener.get() invalidate]; 129 [m_exitFullScreenListener.get() invalidate]; 130 } 131 132 void WebFullScreenManagerMac::setRootFullScreenLayer(WebCore::GraphicsLayer* layer) 133 { 134 if (m_fullScreenRootLayer == layer) 135 return; 136 m_fullScreenRootLayer = layer; 137 138 if (!m_fullScreenRootLayer) { 139 m_page->send(Messages::WebFullScreenManagerProxy::ExitAcceleratedCompositingMode()); 140 if (m_rootLayer) { 141 m_rootLayer->removeAllChildren(); 142 m_rootLayer = 0; 143 } 144 return; 145 } 146 147 if (!m_rootLayer) { 148 mach_port_t serverPort = WebProcess::shared().compositingRenderServerPort(); 149 m_remoteLayerClient = WKCARemoteLayerClientMakeWithServerPort(serverPort); 150 151 m_rootLayer = GraphicsLayer::create(NULL); 152 #ifndef NDEBUG 153 m_rootLayer->setName("Full screen root layer"); 154 #endif 155 m_rootLayer->setDrawsContent(false); 156 m_rootLayer->setSize(getFullScreenRect().size()); 157 158 [m_rootLayer->platformLayer() setGeometryFlipped:YES]; 159 WKCARemoteLayerClientSetLayer(m_remoteLayerClient.get(), m_rootLayer->platformLayer()); 160 m_layerTreeContext.contextID = WKCARemoteLayerClientGetClientId(m_remoteLayerClient.get()); 161 m_page->send(Messages::WebFullScreenManagerProxy::EnterAcceleratedCompositingMode(m_layerTreeContext)); 162 } 163 164 m_rootLayer->removeAllChildren(); 165 166 if (m_fullScreenRootLayer) 167 m_rootLayer->addChild(m_fullScreenRootLayer); 168 169 m_rootLayer->syncCompositingStateForThisLayerOnly(); 170 m_page->corePage()->mainFrame()->view()->syncCompositingStateIncludingSubframes(); 171 } 172 173 void WebFullScreenManagerMac::beginEnterFullScreenAnimation(float duration) 174 { 175 ASSERT(m_element); 176 ASSERT(m_fullScreenRootLayer); 177 178 IntRect destinationFrame = getFullScreenRect(); 179 m_element->document()->setFullScreenRendererSize(destinationFrame.size()); 180 m_rootLayer->syncCompositingStateForThisLayerOnly(); 181 m_page->corePage()->mainFrame()->view()->syncCompositingStateIncludingSubframes(); 182 183 // FIXME: Once we gain the ability to do native WebKit animations of generated 184 // content, this can change to use them. Meanwhile, we'll have to animate the 185 // CALayer directly: 186 CALayer* caLayer = m_fullScreenRootLayer->platformLayer(); 187 188 // Create a transformation matrix that will transform the renderer layer such that 189 // the fullscreen element appears to move from its starting position and size to its 190 // final one. 191 CGPoint destinationPosition = [caLayer position]; 192 CGPoint layerAnchor = [caLayer anchorPoint]; 193 CGPoint initialPosition = CGPointMake( 194 m_initialFrame.x() + m_initialFrame.width() * layerAnchor.x, 195 m_initialFrame.y() + m_initialFrame.height() * layerAnchor.y); 196 CATransform3D shrinkTransform = CATransform3DMakeScale( 197 static_cast<CGFloat>(m_initialFrame.width()) / destinationFrame.width(), 198 static_cast<CGFloat>(m_initialFrame.height()) / destinationFrame.height(), 1); 199 CATransform3D shiftTransform = CATransform3DMakeTranslation( 200 initialPosition.x - destinationPosition.x, 201 // Drawing is flipped here, and so much be the translation transformation 202 destinationPosition.y - initialPosition.y, 0); 203 CATransform3D finalTransform = CATransform3DConcat(shrinkTransform, shiftTransform); 204 205 // Use a CABasicAnimation here for the zoom effect. We want to be notified that the animation has 206 // completed by way of the CAAnimation delegate. 207 CABasicAnimation* zoomAnimation = [CABasicAnimation animationWithKeyPath:@"transform"]; 208 [zoomAnimation setFromValue:[NSValue valueWithCATransform3D:finalTransform]]; 209 [zoomAnimation setToValue:[NSValue valueWithCATransform3D:CATransform3DIdentity]]; 210 [zoomAnimation setDelegate:m_enterFullScreenListener.get()]; 211 [zoomAnimation setDuration:duration]; 212 [zoomAnimation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]]; 213 [zoomAnimation setFillMode:kCAFillModeForwards]; 214 215 // Disable implicit animations and set the layer's transformation matrix to its final state. 216 [CATransaction begin]; 217 [CATransaction setDisableActions:YES]; 218 [caLayer addAnimation:zoomAnimation forKey:@"zoom"]; 219 [CATransaction commit]; 220 } 221 222 void WebFullScreenManagerMac::beginExitFullScreenAnimation(float duration) 223 { 224 ASSERT(m_element); 225 ASSERT(m_fullScreenRootLayer); 226 227 IntRect destinationFrame = getFullScreenRect(); 228 m_element->document()->setFullScreenRendererSize(destinationFrame.size()); 229 m_rootLayer->syncCompositingStateForThisLayerOnly(); 230 m_page->corePage()->mainFrame()->view()->syncCompositingStateIncludingSubframes(); 231 232 // FIXME: Once we gain the ability to do native WebKit animations of generated 233 // content, this can change to use them. Meanwhile, we'll have to animate the 234 // CALayer directly: 235 CALayer* caLayer = m_fullScreenRootLayer->platformLayer(); 236 237 // Create a transformation matrix that will transform the renderer layer such that 238 // the fullscreen element appears to move from its starting position and size to its 239 // final one. 240 CGPoint destinationPosition = [(CALayer*)[caLayer presentationLayer] position]; 241 CGRect destinationBounds = NSRectToCGRect([[caLayer presentationLayer] bounds]); 242 CGPoint layerAnchor = [caLayer anchorPoint]; 243 CGPoint initialPosition = CGPointMake( 244 m_initialFrame.x() + m_initialFrame.width() * layerAnchor.x, 245 m_initialFrame.y() + m_initialFrame.height() * layerAnchor.y); 246 CATransform3D shrinkTransform = CATransform3DMakeScale( 247 static_cast<CGFloat>(m_initialFrame.width()) / destinationBounds.size.width, 248 static_cast<CGFloat>(m_initialFrame.height()) / destinationBounds.size.height, 1); 249 CATransform3D shiftTransform = CATransform3DMakeTranslation( 250 initialPosition.x - destinationPosition.x, 251 // Drawing is flipped here, and so must be the translation transformation 252 destinationPosition.y - initialPosition.y, 0); 253 CATransform3D finalTransform = CATransform3DConcat(shrinkTransform, shiftTransform); 254 255 CATransform3D initialTransform = [(CALayer*)[caLayer presentationLayer] transform]; 256 257 // Use a CABasicAnimation here for the zoom effect. We want to be notified that the animation has 258 // completed by way of the CAAnimation delegate. 259 CABasicAnimation* zoomAnimation = [CABasicAnimation animationWithKeyPath:@"transform"]; 260 [zoomAnimation setFromValue:[NSValue valueWithCATransform3D:initialTransform]]; 261 [zoomAnimation setToValue:[NSValue valueWithCATransform3D:finalTransform]]; 262 [zoomAnimation setDelegate:m_exitFullScreenListener.get()]; 263 [zoomAnimation setDuration:duration]; 264 [zoomAnimation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]]; 265 [zoomAnimation setFillMode:kCAFillModeForwards]; 266 267 // Disable implicit animations and set the layer's transformation matrix to its final state. 268 [CATransaction begin]; 269 [CATransaction setDisableActions:YES]; 270 [caLayer addAnimation:zoomAnimation forKey:@"zoom"]; 271 [caLayer setTransform:finalTransform]; 272 [CATransaction commit]; 273 } 274 275 } // namespace WebKit 276 277 #endif // ENABLE(FULLSCREEN_API) 278