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