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