Rename minimumLayoutSize to viewLayoutSize
[WebKit-https.git] / Source / WebKit / WebProcess / WebPage / mac / TiledCoreAnimationDrawingArea.mm
1 /*
2  * Copyright (C) 2011-2018 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
26 #import "config.h"
27 #import "TiledCoreAnimationDrawingArea.h"
28
29 #if !PLATFORM(IOS)
30
31 #import "ColorSpaceData.h"
32 #import "DrawingAreaProxyMessages.h"
33 #import "LayerHostingContext.h"
34 #import "LayerTreeContext.h"
35 #import "Logging.h"
36 #import "ViewGestureControllerMessages.h"
37 #import "WebFrame.h"
38 #import "WebPage.h"
39 #import "WebPageCreationParameters.h"
40 #import "WebPageProxyMessages.h"
41 #import "WebProcess.h"
42 #import <QuartzCore/QuartzCore.h>
43 #import <WebCore/DebugPageOverlays.h>
44 #import <WebCore/Frame.h>
45 #import <WebCore/FrameView.h>
46 #import <WebCore/GraphicsContext.h>
47 #import <WebCore/GraphicsLayerCA.h>
48 #import <WebCore/InspectorController.h>
49 #import <WebCore/Page.h>
50 #import <WebCore/PlatformCAAnimationCocoa.h>
51 #import <WebCore/RenderLayerBacking.h>
52 #import <WebCore/RenderLayerCompositor.h>
53 #import <WebCore/RenderView.h>
54 #import <WebCore/ScrollbarTheme.h>
55 #import <WebCore/Settings.h>
56 #import <WebCore/TiledBacking.h>
57 #import <WebCore/WebActionDisablingCALayerDelegate.h>
58 #import <pal/spi/cocoa/QuartzCoreSPI.h>
59 #import <wtf/MachSendRight.h>
60 #import <wtf/MainThread.h>
61
62 #if ENABLE(ASYNC_SCROLLING)
63 #import <WebCore/AsyncScrollingCoordinator.h>
64 #import <WebCore/ScrollingThread.h>
65 #import <WebCore/ScrollingTree.h>
66 #endif
67
68 @interface CATransaction (Details)
69 + (void)synchronize;
70 @end
71
72 using namespace WebCore;
73
74 namespace WebKit {
75
76 TiledCoreAnimationDrawingArea::TiledCoreAnimationDrawingArea(WebPage& webPage, const WebPageCreationParameters& parameters)
77     : DrawingArea(DrawingAreaTypeTiledCoreAnimation, webPage)
78     , m_layerTreeStateIsFrozen(false)
79     , m_layerFlushScheduler(this)
80     , m_isPaintingSuspended(!(parameters.activityState & ActivityState::IsVisible))
81     , m_transientZoomScale(1)
82     , m_sendDidUpdateActivityStateTimer(RunLoop::main(), this, &TiledCoreAnimationDrawingArea::didUpdateActivityStateTimerFired)
83     , m_wantsDidUpdateActivityState(false)
84     , m_viewOverlayRootLayer(nullptr)
85 {
86     m_webPage.corePage()->settings().setForceCompositingMode(true);
87
88     m_hostingLayer = [CALayer layer];
89     [m_hostingLayer setDelegate:[WebActionDisablingCALayerDelegate shared]];
90     [m_hostingLayer setFrame:m_webPage.bounds()];
91     [m_hostingLayer setOpaque:YES];
92     [m_hostingLayer setGeometryFlipped:YES];
93
94     updateLayerHostingContext();
95     setColorSpace(parameters.colorSpace);
96
97     attachDrawingArea();
98 }
99
100 TiledCoreAnimationDrawingArea::~TiledCoreAnimationDrawingArea()
101 {
102     m_layerFlushScheduler.invalidate();
103 }
104
105
106 void TiledCoreAnimationDrawingArea::attachDrawingArea()
107 {
108     LayerTreeContext layerTreeContext;
109     layerTreeContext.contextID = m_layerHostingContext->contextID();
110     m_webPage.send(Messages::DrawingAreaProxy::EnterAcceleratedCompositingMode(0, layerTreeContext));
111 }
112
113 void TiledCoreAnimationDrawingArea::setNeedsDisplay()
114 {
115 }
116
117 void TiledCoreAnimationDrawingArea::setNeedsDisplayInRect(const IntRect& rect)
118 {
119 }
120
121 void TiledCoreAnimationDrawingArea::scroll(const IntRect& scrollRect, const IntSize& scrollDelta)
122 {
123     updateScrolledExposedRect();
124 }
125
126 void TiledCoreAnimationDrawingArea::setRootCompositingLayer(GraphicsLayer* graphicsLayer)
127 {
128     CALayer *rootLayer = graphicsLayer ? graphicsLayer->platformLayer() : nil;
129
130     if (m_layerTreeStateIsFrozen) {
131         m_pendingRootLayer = rootLayer;
132         return;
133     }
134
135     setRootCompositingLayer(rootLayer);
136 }
137
138 void TiledCoreAnimationDrawingArea::forceRepaint()
139 {
140     if (m_layerTreeStateIsFrozen)
141         return;
142
143     for (Frame* frame = &m_webPage.corePage()->mainFrame(); frame; frame = frame->tree().traverseNext()) {
144         FrameView* frameView = frame->view();
145         if (!frameView || !frameView->tiledBacking())
146             continue;
147
148         frameView->tiledBacking()->forceRepaint();
149     }
150
151     flushLayers();
152     [CATransaction flush];
153     [CATransaction synchronize];
154 }
155
156 bool TiledCoreAnimationDrawingArea::forceRepaintAsync(CallbackID callbackID)
157 {
158     if (m_layerTreeStateIsFrozen)
159         return false;
160
161     dispatchAfterEnsuringUpdatedScrollPosition([this, callbackID] {
162         m_webPage.drawingArea()->forceRepaint();
163         m_webPage.send(Messages::WebPageProxy::VoidCallback(callbackID));
164     });
165     return true;
166 }
167
168 void TiledCoreAnimationDrawingArea::setLayerTreeStateIsFrozen(bool layerTreeStateIsFrozen)
169 {
170     if (m_layerTreeStateIsFrozen == layerTreeStateIsFrozen)
171         return;
172
173     m_layerTreeStateIsFrozen = layerTreeStateIsFrozen;
174     if (m_layerTreeStateIsFrozen)
175         m_layerFlushScheduler.suspend();
176     else
177         m_layerFlushScheduler.resume();
178 }
179
180 bool TiledCoreAnimationDrawingArea::layerTreeStateIsFrozen() const
181 {
182     return m_layerTreeStateIsFrozen;
183 }
184
185 void TiledCoreAnimationDrawingArea::scheduleCompositingLayerFlush()
186 {
187     m_layerFlushScheduler.schedule();
188 }
189
190 void TiledCoreAnimationDrawingArea::scheduleCompositingLayerFlushImmediately()
191 {
192     scheduleCompositingLayerFlush();
193 }
194
195 void TiledCoreAnimationDrawingArea::updatePreferences(const WebPreferencesStore&)
196 {
197     Settings& settings = m_webPage.corePage()->settings();
198
199 #if ENABLE(ASYNC_SCROLLING)
200     if (AsyncScrollingCoordinator* scrollingCoordinator = downcast<AsyncScrollingCoordinator>(m_webPage.corePage()->scrollingCoordinator())) {
201         bool scrollingPerformanceLoggingEnabled = m_webPage.scrollingPerformanceLoggingEnabled();
202         
203         RefPtr<ScrollingTree> scrollingTree = scrollingCoordinator->scrollingTree();
204         ScrollingThread::dispatch([scrollingTree, scrollingPerformanceLoggingEnabled] {
205             scrollingTree->setScrollingPerformanceLoggingEnabled(scrollingPerformanceLoggingEnabled);
206         });
207     }
208 #endif
209
210     // Fixed position elements need to be composited and create stacking contexts
211     // in order to be scrolled by the ScrollingCoordinator.
212     settings.setAcceleratedCompositingForFixedPositionEnabled(true);
213
214     DebugPageOverlays::settingsChanged(*m_webPage.corePage());
215
216     bool showTiledScrollingIndicator = settings.showTiledScrollingIndicator();
217     if (showTiledScrollingIndicator == !!m_debugInfoLayer)
218         return;
219
220     updateDebugInfoLayer(showTiledScrollingIndicator);
221 }
222
223 void TiledCoreAnimationDrawingArea::updateRootLayers()
224 {
225     if (!m_rootLayer) {
226         [m_hostingLayer setSublayers:@[ ]];
227         return;
228     }
229
230     [m_hostingLayer setSublayers:m_viewOverlayRootLayer ? @[ m_rootLayer.get(), m_viewOverlayRootLayer->platformLayer() ] : @[ m_rootLayer.get() ]];
231     
232     if (m_debugInfoLayer)
233         [m_hostingLayer addSublayer:m_debugInfoLayer.get()];
234 }
235
236 void TiledCoreAnimationDrawingArea::attachViewOverlayGraphicsLayer(Frame* frame, GraphicsLayer* viewOverlayRootLayer)
237 {
238     if (!frame->isMainFrame())
239         return;
240
241     m_viewOverlayRootLayer = viewOverlayRootLayer;
242     updateRootLayers();
243 }
244
245 void TiledCoreAnimationDrawingArea::mainFrameContentSizeChanged(const IntSize& size)
246 {
247
248 }
249
250 void TiledCoreAnimationDrawingArea::updateIntrinsicContentSizeIfNeeded()
251 {
252     if (!m_webPage.viewLayoutSize().width())
253         return;
254
255     FrameView* frameView = m_webPage.mainFrameView();
256     if (!frameView)
257         return;
258
259     if (frameView->needsLayout())
260         return;
261
262     IntSize contentSize = frameView->autoSizingIntrinsicContentSize();
263     if (m_lastSentIntrinsicContentSize == contentSize)
264         return;
265
266     m_lastSentIntrinsicContentSize = contentSize;
267     m_webPage.send(Messages::DrawingAreaProxy::IntrinsicContentSizeDidChange(contentSize));
268 }
269
270 void TiledCoreAnimationDrawingArea::setShouldScaleViewToFitDocument(bool shouldScaleView)
271 {
272     if (m_shouldScaleViewToFitDocument == shouldScaleView)
273         return;
274
275     m_shouldScaleViewToFitDocument = shouldScaleView;
276     scheduleCompositingLayerFlush();
277 }
278
279 void TiledCoreAnimationDrawingArea::scaleViewToFitDocumentIfNeeded()
280 {
281     const int maximumDocumentWidthForScaling = 1440;
282     const float minimumViewScale = 0.1;
283
284     if (!m_shouldScaleViewToFitDocument)
285         return;
286
287     LOG(Resize, "TiledCoreAnimationDrawingArea %p scaleViewToFitDocumentIfNeeded", this);
288     m_webPage.layoutIfNeeded();
289
290     int viewWidth = m_webPage.size().width();
291     int documentWidth = m_webPage.mainFrameView()->renderView()->unscaledDocumentRect().width();
292
293     bool documentWidthChanged = m_lastDocumentSizeForScaleToFit.width() != documentWidth;
294     bool viewWidthChanged = m_lastViewSizeForScaleToFit.width() != viewWidth;
295
296     LOG(Resize, "  documentWidthChanged=%d, viewWidthChanged=%d", documentWidthChanged, viewWidthChanged);
297
298     if (!documentWidthChanged && !viewWidthChanged)
299         return;
300
301     // The view is now bigger than the document, so we'll re-evaluate whether we have to scale.
302     if (m_isScalingViewToFitDocument && viewWidth >= m_lastDocumentSizeForScaleToFit.width())
303         m_isScalingViewToFitDocument = false;
304
305     // Our current understanding of the document width is still up to date, and we're in scaling mode.
306     // Update the viewScale without doing an extra layout to re-determine the document width.
307     if (m_isScalingViewToFitDocument) {
308         if (!documentWidthChanged) {
309             m_lastViewSizeForScaleToFit = m_webPage.size();
310             float viewScale = (float)viewWidth / (float)m_lastDocumentSizeForScaleToFit.width();
311             if (viewScale < minimumViewScale) {
312                 viewScale = minimumViewScale;
313                 documentWidth = std::ceil(viewWidth / viewScale);
314             }
315             IntSize fixedLayoutSize(documentWidth, std::ceil((m_webPage.size().height() - m_webPage.corePage()->topContentInset()) / viewScale));
316             m_webPage.setFixedLayoutSize(fixedLayoutSize);
317             m_webPage.scaleView(viewScale);
318
319             LOG(Resize, "  using fixed layout at %dx%d. document width %d unchanged, scaled to %.4f to fit view width %d", fixedLayoutSize.width(), fixedLayoutSize.height(), documentWidth, viewScale, viewWidth);
320             return;
321         }
322     
323         IntSize fixedLayoutSize = m_webPage.fixedLayoutSize();
324         if (documentWidth > fixedLayoutSize.width()) {
325             LOG(Resize, "  page laid out wider than fixed layout width. Not attempting to re-scale");
326             return;
327         }
328     }
329
330     LOG(Resize, "  doing unconstrained layout");
331
332     // Lay out at the view size.
333     m_webPage.setUseFixedLayout(false);
334     m_webPage.layoutIfNeeded();
335
336     IntSize documentSize = m_webPage.mainFrameView()->renderView()->unscaledDocumentRect().size();
337     m_lastViewSizeForScaleToFit = m_webPage.size();
338     m_lastDocumentSizeForScaleToFit = documentSize;
339
340     documentWidth = documentSize.width();
341
342     float viewScale = 1;
343
344     LOG(Resize, "  unscaled document size %dx%d. need to scale down: %d", documentSize.width(), documentSize.height(), documentWidth && documentWidth < maximumDocumentWidthForScaling && viewWidth < documentWidth);
345
346     // Avoid scaling down documents that don't fit in a certain width, to allow
347     // sites that want horizontal scrollbars to continue to have them.
348     if (documentWidth && documentWidth < maximumDocumentWidthForScaling && viewWidth < documentWidth) {
349         // If the document doesn't fit in the view, scale it down but lay out at the view size.
350         m_isScalingViewToFitDocument = true;
351         m_webPage.setUseFixedLayout(true);
352         viewScale = (float)viewWidth / (float)documentWidth;
353         if (viewScale < minimumViewScale) {
354             viewScale = minimumViewScale;
355             documentWidth = std::ceil(viewWidth / viewScale);
356         }
357         IntSize fixedLayoutSize(documentWidth, std::ceil((m_webPage.size().height() - m_webPage.corePage()->topContentInset()) / viewScale));
358         m_webPage.setFixedLayoutSize(fixedLayoutSize);
359
360         LOG(Resize, "  using fixed layout at %dx%d. document width %d, scaled to %.4f to fit view width %d", fixedLayoutSize.width(), fixedLayoutSize.height(), documentWidth, viewScale, viewWidth);
361     }
362
363     m_webPage.scaleView(viewScale);
364 }
365
366 void TiledCoreAnimationDrawingArea::dispatchAfterEnsuringUpdatedScrollPosition(WTF::Function<void ()>&& function)
367 {
368 #if ENABLE(ASYNC_SCROLLING)
369     if (!m_webPage.corePage()->scrollingCoordinator()) {
370         function();
371         return;
372     }
373
374     m_webPage.ref();
375     m_webPage.corePage()->scrollingCoordinator()->commitTreeStateIfNeeded();
376
377     if (!m_layerTreeStateIsFrozen)
378         m_layerFlushScheduler.suspend();
379
380     // It is possible for the drawing area to be destroyed before the bound block
381     // is invoked, so grab a reference to the web page here so we can access the drawing area through it.
382     // (The web page is already kept alive by dispatchAfterEnsuringUpdatedScrollPosition).
383     WebPage* webPage = &m_webPage;
384
385     ScrollingThread::dispatchBarrier([this, webPage, function = WTFMove(function)] {
386         DrawingArea* drawingArea = webPage->drawingArea();
387         if (!drawingArea)
388             return;
389
390         function();
391
392         if (!m_layerTreeStateIsFrozen)
393             m_layerFlushScheduler.resume();
394
395         webPage->deref();
396     });
397 #else
398     function();
399 #endif
400 }
401
402 void TiledCoreAnimationDrawingArea::sendPendingNewlyReachedLayoutMilestones()
403 {
404     if (!m_pendingNewlyReachedLayoutMilestones)
405         return;
406
407     m_webPage.send(Messages::WebPageProxy::DidReachLayoutMilestone(m_pendingNewlyReachedLayoutMilestones));
408     m_pendingNewlyReachedLayoutMilestones = 0;
409 }
410
411 void TiledCoreAnimationDrawingArea::addTransactionCallbackID(CallbackID callbackID)
412 {
413     m_pendingCallbackIDs.append(callbackID);
414     scheduleCompositingLayerFlush();
415 }
416
417 bool TiledCoreAnimationDrawingArea::flushLayers()
418 {
419     ASSERT(!m_layerTreeStateIsFrozen);
420
421     @autoreleasepool {
422         scaleViewToFitDocumentIfNeeded();
423
424         m_webPage.layoutIfNeeded();
425         m_webPage.flushPendingEditorStateUpdate();
426
427         updateIntrinsicContentSizeIfNeeded();
428
429         if (m_pendingRootLayer) {
430             setRootCompositingLayer(m_pendingRootLayer.get());
431             m_pendingRootLayer = nullptr;
432         }
433
434         FloatRect visibleRect = [m_hostingLayer frame];
435         if (m_scrolledViewExposedRect)
436             visibleRect.intersect(m_scrolledViewExposedRect.value());
437
438         // Because our view-relative overlay root layer is not attached to the main GraphicsLayer tree, we need to flush it manually.
439         if (m_viewOverlayRootLayer)
440             m_viewOverlayRootLayer->flushCompositingState(visibleRect);
441
442         RefPtr<WebPage> retainedPage = &m_webPage;
443         [CATransaction addCommitHandler:[retainedPage] {
444             if (Page* corePage = retainedPage->corePage()) {
445                 if (Frame* coreFrame = retainedPage->mainFrame())
446                     corePage->inspectorController().didComposite(*coreFrame);
447             }
448             if (auto drawingArea = static_cast<TiledCoreAnimationDrawingArea*>(retainedPage->drawingArea()))
449                 drawingArea->sendPendingNewlyReachedLayoutMilestones();
450         } forPhase:kCATransactionPhasePostCommit];
451
452         bool returnValue = m_webPage.mainFrameView()->flushCompositingStateIncludingSubframes();
453 #if ENABLE(ASYNC_SCROLLING)
454         if (ScrollingCoordinator* scrollingCoordinator = m_webPage.corePage()->scrollingCoordinator())
455             scrollingCoordinator->commitTreeStateIfNeeded();
456 #endif
457
458         // If we have an active transient zoom, we want the zoom to win over any changes
459         // that WebCore makes to the relevant layers, so re-apply our changes after flushing.
460         if (m_transientZoomScale != 1)
461             applyTransientZoomToLayers(m_transientZoomScale, m_transientZoomOrigin);
462
463         if (!m_pendingCallbackIDs.isEmpty()) {
464             m_webPage.send(Messages::DrawingAreaProxy::DispatchPresentationCallbacksAfterFlushingLayers(m_pendingCallbackIDs));
465             m_pendingCallbackIDs.clear();
466         }
467
468         return returnValue;
469     }
470 }
471
472 void TiledCoreAnimationDrawingArea::activityStateDidChange(ActivityState::Flags changed, bool wantsDidUpdateActivityState, const Vector<CallbackID>& nextActivityStateChangeCallbackIDs)
473 {
474     m_nextActivityStateChangeCallbackIDs.appendVector(nextActivityStateChangeCallbackIDs);
475     m_wantsDidUpdateActivityState |= wantsDidUpdateActivityState;
476
477     if (changed & ActivityState::IsVisible) {
478         if (m_webPage.isVisible())
479             resumePainting();
480         else
481             suspendPainting();
482     }
483
484     if (m_wantsDidUpdateActivityState || !m_nextActivityStateChangeCallbackIDs.isEmpty())
485         m_sendDidUpdateActivityStateTimer.startOneShot(0_s);
486 }
487
488 void TiledCoreAnimationDrawingArea::didUpdateActivityStateTimerFired()
489 {
490     [CATransaction flush];
491
492     if (m_wantsDidUpdateActivityState)
493         m_webPage.send(Messages::WebPageProxy::DidUpdateActivityState());
494
495     for (auto callbackID : m_nextActivityStateChangeCallbackIDs)
496         m_webPage.send(Messages::WebPageProxy::VoidCallback(callbackID));
497
498     m_nextActivityStateChangeCallbackIDs.clear();
499     m_wantsDidUpdateActivityState = false;
500 }
501
502 void TiledCoreAnimationDrawingArea::suspendPainting()
503 {
504     ASSERT(!m_isPaintingSuspended);
505     m_isPaintingSuspended = true;
506
507     [m_hostingLayer setValue:@YES forKey:@"NSCAViewRenderPaused"];
508     [[NSNotificationCenter defaultCenter] postNotificationName:@"NSCAViewRenderDidPauseNotification" object:nil userInfo:[NSDictionary dictionaryWithObject:m_hostingLayer.get() forKey:@"layer"]];
509 }
510
511 void TiledCoreAnimationDrawingArea::resumePainting()
512 {
513     if (!m_isPaintingSuspended) {
514         // FIXME: We can get a call to resumePainting when painting is not suspended.
515         // This happens when sending a synchronous message to create a new page. See <rdar://problem/8976531>.
516         return;
517     }
518     m_isPaintingSuspended = false;
519
520     [m_hostingLayer setValue:@NO forKey:@"NSCAViewRenderPaused"];
521     [[NSNotificationCenter defaultCenter] postNotificationName:@"NSCAViewRenderDidResumeNotification" object:nil userInfo:[NSDictionary dictionaryWithObject:m_hostingLayer.get() forKey:@"layer"]];
522 }
523
524 void TiledCoreAnimationDrawingArea::setViewExposedRect(std::optional<WebCore::FloatRect> viewExposedRect)
525 {
526     m_viewExposedRect = viewExposedRect;
527     updateScrolledExposedRect();
528 }
529
530 void TiledCoreAnimationDrawingArea::updateScrolledExposedRect()
531 {
532     FrameView* frameView = m_webPage.mainFrameView();
533     if (!frameView)
534         return;
535
536     m_scrolledViewExposedRect = m_viewExposedRect;
537
538 #if !PLATFORM(IOS)
539     if (m_viewExposedRect) {
540         ScrollOffset scrollOffset = frameView->scrollOffsetFromPosition(frameView->scrollPosition());
541         m_scrolledViewExposedRect.value().moveBy(scrollOffset);
542     }
543 #endif
544
545     frameView->setViewExposedRect(m_scrolledViewExposedRect);
546 }
547
548 void TiledCoreAnimationDrawingArea::updateGeometry(const IntSize& viewSize, bool flushSynchronously, const WTF::MachSendRight& fencePort)
549 {
550     m_inUpdateGeometry = true;
551
552     IntSize size = viewSize;
553     IntSize contentSize = IntSize(-1, -1);
554
555     if (!m_webPage.viewLayoutSize().width() || m_webPage.autoSizingShouldExpandToViewHeight())
556         m_webPage.setSize(size);
557
558     FrameView* frameView = m_webPage.mainFrameView();
559
560     if (m_webPage.autoSizingShouldExpandToViewHeight() && frameView)
561         frameView->setAutoSizeFixedMinimumHeight(viewSize.height());
562
563     m_webPage.layoutIfNeeded();
564
565     if (m_webPage.viewLayoutSize().width() && frameView) {
566         contentSize = frameView->autoSizingIntrinsicContentSize();
567         size = contentSize;
568     }
569
570     if (!m_layerTreeStateIsFrozen)
571         flushLayers();
572
573     [CATransaction begin];
574     [CATransaction setDisableActions:YES];
575
576     [m_hostingLayer setFrame:CGRectMake(0, 0, viewSize.width(), viewSize.height())];
577
578     [CATransaction commit];
579
580     if (flushSynchronously)
581         [CATransaction flush];
582
583     m_webPage.send(Messages::DrawingAreaProxy::DidUpdateGeometry());
584
585     m_inUpdateGeometry = false;
586
587     m_layerHostingContext->setFencePort(fencePort.sendRight());
588 }
589
590 void TiledCoreAnimationDrawingArea::setDeviceScaleFactor(float deviceScaleFactor)
591 {
592     m_webPage.setDeviceScaleFactor(deviceScaleFactor);
593 }
594
595 void TiledCoreAnimationDrawingArea::setLayerHostingMode(LayerHostingMode)
596 {
597     updateLayerHostingContext();
598
599     // Finally, inform the UIProcess that the context has changed.
600     LayerTreeContext layerTreeContext;
601     layerTreeContext.contextID = m_layerHostingContext->contextID();
602     m_webPage.send(Messages::DrawingAreaProxy::UpdateAcceleratedCompositingMode(0, layerTreeContext));
603 }
604
605 void TiledCoreAnimationDrawingArea::setColorSpace(const ColorSpaceData& colorSpace)
606 {
607     m_layerHostingContext->setColorSpace(colorSpace.cgColorSpace.get());
608 }
609
610 void TiledCoreAnimationDrawingArea::updateLayerHostingContext()
611 {
612     RetainPtr<CGColorSpaceRef> colorSpace;
613
614     // Invalidate the old context.
615     if (m_layerHostingContext) {
616         colorSpace = m_layerHostingContext->colorSpace();
617         m_layerHostingContext->invalidate();
618         m_layerHostingContext = nullptr;
619     }
620
621     // Create a new context and set it up.
622     switch (m_webPage.layerHostingMode()) {
623     case LayerHostingMode::InProcess:
624         m_layerHostingContext = LayerHostingContext::createForPort(WebProcess::singleton().compositingRenderServerPort());
625         break;
626 #if HAVE(OUT_OF_PROCESS_LAYER_HOSTING)
627     case LayerHostingMode::OutOfProcess:
628         m_layerHostingContext = LayerHostingContext::createForExternalHostingProcess();
629         break;
630 #endif
631     }
632
633     if (m_rootLayer)
634         m_layerHostingContext->setRootLayer(m_hostingLayer.get());
635
636     if (colorSpace)
637         m_layerHostingContext->setColorSpace(colorSpace.get());
638 }
639
640 void TiledCoreAnimationDrawingArea::setRootCompositingLayer(CALayer *layer)
641 {
642     ASSERT(!m_layerTreeStateIsFrozen);
643
644     [CATransaction begin];
645     [CATransaction setDisableActions:YES];
646
647     bool hadRootLayer = !!m_rootLayer;
648     m_rootLayer = layer;
649     [m_rootLayer setSublayerTransform:m_transform];
650
651     updateRootLayers();
652
653     if (hadRootLayer != !!layer)
654         m_layerHostingContext->setRootLayer(layer ? m_hostingLayer.get() : nil);
655
656     updateDebugInfoLayer(layer && m_webPage.corePage()->settings().showTiledScrollingIndicator());
657
658     [CATransaction commit];
659 }
660
661 TiledBacking* TiledCoreAnimationDrawingArea::mainFrameTiledBacking() const
662 {
663     FrameView* frameView = m_webPage.mainFrameView();
664     return frameView ? frameView->tiledBacking() : nullptr;
665 }
666
667 void TiledCoreAnimationDrawingArea::updateDebugInfoLayer(bool showLayer)
668 {
669     if (m_debugInfoLayer) {
670         [m_debugInfoLayer removeFromSuperlayer];
671         m_debugInfoLayer = nil;
672     }
673     
674     if (showLayer) {
675         if (TiledBacking* tiledBacking = mainFrameTiledBacking()) {
676             if (PlatformCALayer* indicatorLayer = tiledBacking->tiledScrollingIndicatorLayer())
677                 m_debugInfoLayer = indicatorLayer->platformLayer();
678         }
679
680         if (m_debugInfoLayer) {
681             [m_debugInfoLayer setName:@"Debug Info"];
682             [m_hostingLayer addSublayer:m_debugInfoLayer.get()];
683         }
684     }
685 }
686
687 bool TiledCoreAnimationDrawingArea::shouldUseTiledBackingForFrameView(const FrameView& frameView)
688 {
689     return frameView.frame().isMainFrame() || m_webPage.corePage()->settings().asyncFrameScrollingEnabled();
690 }
691
692 PlatformCALayer* TiledCoreAnimationDrawingArea::layerForTransientZoom() const
693 {
694     RenderLayerBacking* renderViewBacking = m_webPage.mainFrameView()->renderView()->layer()->backing();
695
696     if (GraphicsLayer* contentsContainmentLayer = renderViewBacking->contentsContainmentLayer())
697         return downcast<GraphicsLayerCA>(*contentsContainmentLayer).platformCALayer();
698
699     return downcast<GraphicsLayerCA>(*renderViewBacking->graphicsLayer()).platformCALayer();
700 }
701
702 PlatformCALayer* TiledCoreAnimationDrawingArea::shadowLayerForTransientZoom() const
703 {
704     RenderLayerCompositor& renderLayerCompositor = m_webPage.mainFrameView()->renderView()->compositor();
705
706     if (GraphicsLayer* shadowGraphicsLayer = renderLayerCompositor.layerForContentShadow())
707         return downcast<GraphicsLayerCA>(*shadowGraphicsLayer).platformCALayer();
708
709     return nullptr;
710 }
711     
712 static FloatPoint shadowLayerPositionForFrame(FrameView& frameView, FloatPoint origin)
713 {
714     // FIXME: correct for b-t documents?
715     FloatPoint position = frameView.positionForRootContentLayer();
716     return position + origin.expandedTo(FloatPoint());
717 }
718
719 static FloatRect shadowLayerBoundsForFrame(FrameView& frameView, float transientScale)
720 {
721     FloatRect clipLayerFrame(frameView.renderView()->documentRect());
722     FloatRect shadowLayerFrame = clipLayerFrame;
723     
724     shadowLayerFrame.scale(transientScale / frameView.frame().page()->pageScaleFactor());
725     shadowLayerFrame.intersect(clipLayerFrame);
726     
727     return shadowLayerFrame;
728 }
729
730 void TiledCoreAnimationDrawingArea::applyTransientZoomToLayers(double scale, FloatPoint origin)
731 {
732     // FIXME: Scrollbars should stay in-place and change height while zooming.
733
734     if (!m_hostingLayer)
735         return;
736
737     TransformationMatrix transform;
738     transform.translate(origin.x(), origin.y());
739     transform.scale(scale);
740
741     PlatformCALayer* zoomLayer = layerForTransientZoom();
742     zoomLayer->setTransform(transform);
743     zoomLayer->setAnchorPoint(FloatPoint3D());
744     zoomLayer->setPosition(FloatPoint3D());
745     
746     if (PlatformCALayer* shadowLayer = shadowLayerForTransientZoom()) {
747         FrameView& frameView = *m_webPage.mainFrameView();
748         shadowLayer->setBounds(shadowLayerBoundsForFrame(frameView, scale));
749         shadowLayer->setPosition(shadowLayerPositionForFrame(frameView, origin));
750     }
751
752     m_transientZoomScale = scale;
753     m_transientZoomOrigin = origin;
754 }
755
756 void TiledCoreAnimationDrawingArea::adjustTransientZoom(double scale, FloatPoint origin)
757 {
758     scale *= m_webPage.viewScaleFactor();
759
760     applyTransientZoomToLayers(scale, origin);
761
762     double currentPageScale = m_webPage.totalScaleFactor();
763     if (scale > currentPageScale)
764         return;
765
766     FrameView* frameView = m_webPage.mainFrameView();
767     FloatRect tileCoverageRect = frameView->visibleContentRectIncludingScrollbars();
768     tileCoverageRect.moveBy(-origin);
769     tileCoverageRect.scale(currentPageScale / scale);
770     frameView->renderView()->layer()->backing()->tiledBacking()->prepopulateRect(tileCoverageRect);
771 }
772
773 static RetainPtr<CABasicAnimation> transientZoomSnapAnimationForKeyPath(String keyPath)
774 {
775     const float transientZoomSnapBackDuration = 0.25;
776
777     RetainPtr<CABasicAnimation> animation = [CABasicAnimation animationWithKeyPath:keyPath];
778     [animation setDuration:transientZoomSnapBackDuration];
779     [animation setFillMode:kCAFillModeForwards];
780     [animation setRemovedOnCompletion:false];
781     [animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]];
782
783     return animation;
784 }
785
786 void TiledCoreAnimationDrawingArea::commitTransientZoom(double scale, FloatPoint origin)
787 {
788     scale *= m_webPage.viewScaleFactor();
789
790     FrameView& frameView = *m_webPage.mainFrameView();
791     FloatRect visibleContentRect = frameView.visibleContentRectIncludingScrollbars();
792
793     FloatPoint constrainedOrigin = visibleContentRect.location();
794     constrainedOrigin.moveBy(-origin);
795
796     IntSize scaledTotalContentsSize = frameView.totalContentsSize();
797     scaledTotalContentsSize.scale(scale / m_webPage.totalScaleFactor());
798
799     // Scaling may have exposed the overhang area, so we need to constrain the final
800     // layer position exactly like scrolling will once it's committed, to ensure that
801     // scrolling doesn't make the view jump.
802     constrainedOrigin = ScrollableArea::constrainScrollPositionForOverhang(roundedIntRect(visibleContentRect), scaledTotalContentsSize, roundedIntPoint(constrainedOrigin), frameView.scrollOrigin(), frameView.headerHeight(), frameView.footerHeight());
803     constrainedOrigin.moveBy(-visibleContentRect.location());
804     constrainedOrigin = -constrainedOrigin;
805
806     if (m_transientZoomScale == scale && roundedIntPoint(m_transientZoomOrigin) == roundedIntPoint(constrainedOrigin)) {
807         // We're already at the right scale and position, so we don't need to animate.
808         applyTransientZoomToPage(scale, origin);
809         return;
810     }
811
812     TransformationMatrix transform;
813     transform.translate(constrainedOrigin.x(), constrainedOrigin.y());
814     transform.scale(scale);
815
816     RetainPtr<CABasicAnimation> renderViewAnimationCA = transientZoomSnapAnimationForKeyPath("transform");
817     RefPtr<PlatformCAAnimation> renderViewAnimation = PlatformCAAnimationCocoa::create(renderViewAnimationCA.get());
818     renderViewAnimation->setToValue(transform);
819
820     RetainPtr<CALayer> shadowCALayer;
821     if (PlatformCALayer* shadowLayer = shadowLayerForTransientZoom())
822         shadowCALayer = shadowLayer->platformLayer();
823
824     RefPtr<PlatformCALayer> zoomLayer = layerForTransientZoom();
825     RefPtr<WebPage> page = &m_webPage;
826
827     [CATransaction begin];
828     [CATransaction setCompletionBlock:[zoomLayer, shadowCALayer, page, scale, origin] () {
829         zoomLayer->removeAnimationForKey("transientZoomCommit");
830         if (shadowCALayer)
831             [shadowCALayer removeAllAnimations];
832
833         if (TiledCoreAnimationDrawingArea* drawingArea = static_cast<TiledCoreAnimationDrawingArea*>(page->drawingArea()))
834             drawingArea->applyTransientZoomToPage(scale, origin);
835     }];
836
837     zoomLayer->addAnimationForKey("transientZoomCommit", *renderViewAnimation);
838
839     if (shadowCALayer) {
840         FloatRect shadowBounds = shadowLayerBoundsForFrame(frameView, scale);
841         RetainPtr<CGPathRef> shadowPath = adoptCF(CGPathCreateWithRect(shadowBounds, NULL));
842
843         RetainPtr<CABasicAnimation> shadowBoundsAnimation = transientZoomSnapAnimationForKeyPath("bounds");
844         [shadowBoundsAnimation setToValue:[NSValue valueWithRect:shadowBounds]];
845         RetainPtr<CABasicAnimation> shadowPositionAnimation = transientZoomSnapAnimationForKeyPath("position");
846         [shadowPositionAnimation setToValue:[NSValue valueWithPoint:shadowLayerPositionForFrame(frameView, constrainedOrigin)]];
847         RetainPtr<CABasicAnimation> shadowPathAnimation = transientZoomSnapAnimationForKeyPath("shadowPath");
848         [shadowPathAnimation setToValue:(id)shadowPath.get()];
849
850         [shadowCALayer addAnimation:shadowBoundsAnimation.get() forKey:@"transientZoomCommitShadowBounds"];
851         [shadowCALayer addAnimation:shadowPositionAnimation.get() forKey:@"transientZoomCommitShadowPosition"];
852         [shadowCALayer addAnimation:shadowPathAnimation.get() forKey:@"transientZoomCommitShadowPath"];
853     }
854
855     [CATransaction commit];
856 }
857
858 void TiledCoreAnimationDrawingArea::applyTransientZoomToPage(double scale, FloatPoint origin)
859 {
860     // If the page scale is already the target scale, setPageScaleFactor() will short-circuit
861     // and not apply the transform, so we can't depend on it to do so.
862     TransformationMatrix finalTransform;
863     finalTransform.scale(scale);
864     layerForTransientZoom()->setTransform(finalTransform);
865     
866     FrameView& frameView = *m_webPage.mainFrameView();
867
868     if (PlatformCALayer* shadowLayer = shadowLayerForTransientZoom()) {
869         shadowLayer->setBounds(shadowLayerBoundsForFrame(frameView, 1));
870         shadowLayer->setPosition(shadowLayerPositionForFrame(frameView, FloatPoint()));
871     }
872
873     FloatPoint unscrolledOrigin(origin);
874     FloatRect unobscuredContentRect = frameView.unobscuredContentRectIncludingScrollbars();
875     unscrolledOrigin.moveBy(-unobscuredContentRect.location());
876     m_webPage.scalePage(scale / m_webPage.viewScaleFactor(), roundedIntPoint(-unscrolledOrigin));
877     m_transientZoomScale = 1;
878     flushLayers();
879 }
880
881 void TiledCoreAnimationDrawingArea::addFence(const MachSendRight& fencePort)
882 {
883     m_layerHostingContext->setFencePort(fencePort.sendRight());
884 }
885
886 bool TiledCoreAnimationDrawingArea::dispatchDidReachLayoutMilestone(WebCore::LayoutMilestones layoutMilestones)
887 {
888     m_pendingNewlyReachedLayoutMilestones |= layoutMilestones;
889     return true;
890 }
891
892 } // namespace WebKit
893
894 #endif // !PLATFORM(IOS)