summaryrefslogtreecommitdiff
path: root/Source/WebCore/page/FrameView.cpp
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
committerLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
commit1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch)
tree46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WebCore/page/FrameView.cpp
parent32761a6cee1d0dee366b885b7b9c777e67885688 (diff)
downloadWebKitGtk-tarball-master.tar.gz
Diffstat (limited to 'Source/WebCore/page/FrameView.cpp')
-rw-r--r--Source/WebCore/page/FrameView.cpp3129
1 files changed, 2085 insertions, 1044 deletions
diff --git a/Source/WebCore/page/FrameView.cpp b/Source/WebCore/page/FrameView.cpp
index 804c0420f..725c62f32 100644
--- a/Source/WebCore/page/FrameView.cpp
+++ b/Source/WebCore/page/FrameView.cpp
@@ -3,7 +3,7 @@
* 1999 Lars Knoll <knoll@kde.org>
* 1999 Antti Koivisto <koivisto@kde.org>
* 2000 Dirk Mueller <mueller@kde.org>
- * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2004-2017 Apple Inc. All rights reserved.
* (C) 2006 Graham Dennis (graham.dennis@gmail.com)
* (C) 2006 Alexey Proskuryakov (ap@nypop.com)
* Copyright (C) 2009 Google Inc. All rights reserved.
@@ -28,40 +28,55 @@
#include "FrameView.h"
#include "AXObjectCache.h"
-#include "AnimationController.h"
#include "BackForwardController.h"
+#include "CSSAnimationController.h"
#include "CachedImage.h"
#include "CachedResourceLoader.h"
#include "Chrome.h"
#include "ChromeClient.h"
#include "DOMWindow.h"
+#include "DebugPageOverlays.h"
#include "DocumentMarkerController.h"
#include "EventHandler.h"
+#include "EventNames.h"
#include "FloatRect.h"
#include "FocusController.h"
-#include "FontCache.h"
-#include "FontLoader.h"
#include "FrameLoader.h"
#include "FrameLoaderClient.h"
#include "FrameSelection.h"
#include "FrameTree.h"
#include "GraphicsContext.h"
+#include "HTMLBodyElement.h"
#include "HTMLDocument.h"
+#include "HTMLEmbedElement.h"
#include "HTMLFrameElement.h"
#include "HTMLFrameSetElement.h"
+#include "HTMLHtmlElement.h"
+#include "HTMLIFrameElement.h"
#include "HTMLNames.h"
+#include "HTMLObjectElement.h"
#include "HTMLPlugInImageElement.h"
+#include "ImageDocument.h"
#include "InspectorClient.h"
#include "InspectorController.h"
#include "InspectorInstrumentation.h"
+#include "Logging.h"
#include "MainFrame.h"
+#include "MemoryCache.h"
+#include "MemoryPressureHandler.h"
#include "OverflowEvent.h"
+#include "Page.h"
+#include "PageCache.h"
+#include "PageOverlayController.h"
#include "ProgressTracker.h"
#include "RenderEmbeddedObject.h"
#include "RenderFullScreen.h"
#include "RenderIFrame.h"
+#include "RenderInline.h"
#include "RenderLayer.h"
#include "RenderLayerBacking.h"
+#include "RenderLayerCompositor.h"
+#include "RenderSVGRoot.h"
#include "RenderScrollbar.h"
#include "RenderScrollbarPart.h"
#include "RenderStyle.h"
@@ -69,47 +84,35 @@
#include "RenderTheme.h"
#include "RenderView.h"
#include "RenderWidget.h"
+#include "SVGDocument.h"
+#include "SVGSVGElement.h"
+#include "ScriptedAnimationController.h"
#include "ScrollAnimator.h"
#include "ScrollingCoordinator.h"
#include "Settings.h"
#include "StyleResolver.h"
+#include "StyleScope.h"
#include "TextResourceDecoder.h"
#include "TextStream.h"
+#include "TiledBacking.h"
+#include "WheelEventTestTrigger.h"
#include <wtf/CurrentTime.h>
#include <wtf/Ref.h>
-#include <wtf/TemporaryChange.h>
-
-#if USE(ACCELERATED_COMPOSITING)
-#include "RenderLayerCompositor.h"
-#include "TiledBacking.h"
-#endif
+#include <wtf/SetForScope.h>
+#include <wtf/SystemTracing.h>
-#if ENABLE(SVG)
-#include "RenderSVGRoot.h"
-#include "SVGDocument.h"
-#include "SVGSVGElement.h"
-#endif
-
-#if USE(TILED_BACKING_STORE)
+#if USE(COORDINATED_GRAPHICS)
#include "TiledBackingStore.h"
#endif
-#if ENABLE(TEXT_AUTOSIZING)
-#include "TextAutosizer.h"
+#if ENABLE(CSS_SCROLL_SNAP)
+#include "AxisScrollSnapOffsets.h"
#endif
#if PLATFORM(IOS)
#include "DocumentLoader.h"
-#include "Logging.h"
-#include "MemoryCache.h"
-#include "MemoryPressureHandler.h"
-#include "SystemMemory.h"
-#include "TileCache.h"
-#endif
-
-#if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
-#include "HTMLMediaElement.h"
+#include "LegacyTileCache.h"
#endif
namespace WebCore {
@@ -128,7 +131,7 @@ static RenderLayer::UpdateLayerPositionsFlags updateLayerPositionFlags(RenderLay
flags &= ~RenderLayer::CheckForRepaint;
flags |= RenderLayer::NeedsFullRepaintInBacking;
}
- if (isRelayoutingSubtree && layer->isPaginated())
+ if (isRelayoutingSubtree && layer->enclosingPaginationLayer(RenderLayer::IncludeCompositedPaginatedLayers))
flags |= RenderLayer::UpdatePagination;
return flags;
}
@@ -160,33 +163,107 @@ Pagination::Mode paginationModeForRenderStyle(const RenderStyle& style)
return Pagination::BottomToTopPaginated;
}
+class SubtreeLayoutStateMaintainer {
+public:
+ SubtreeLayoutStateMaintainer(RenderElement* subtreeLayoutRoot)
+ : m_layoutRoot(subtreeLayoutRoot)
+ {
+ if (m_layoutRoot) {
+ RenderView& view = m_layoutRoot->view();
+ view.pushLayoutState(*m_layoutRoot);
+ if (shouldDisableLayoutStateForSubtree()) {
+ view.disableLayoutState();
+ m_didDisableLayoutState = true;
+ }
+ }
+ }
+
+ ~SubtreeLayoutStateMaintainer()
+ {
+ if (m_layoutRoot) {
+ RenderView& view = m_layoutRoot->view();
+ view.popLayoutState(*m_layoutRoot);
+ if (m_didDisableLayoutState)
+ view.enableLayoutState();
+ }
+ }
+
+ bool shouldDisableLayoutStateForSubtree()
+ {
+ for (auto* renderer = m_layoutRoot; renderer; renderer = renderer->container()) {
+ if (renderer->hasTransform() || renderer->hasReflection())
+ return true;
+ }
+ return false;
+ }
+
+private:
+ RenderElement* m_layoutRoot { nullptr };
+ bool m_didDisableLayoutState { false };
+};
+
+#ifndef NDEBUG
+class RenderTreeNeedsLayoutChecker {
+public :
+ RenderTreeNeedsLayoutChecker(const RenderElement& layoutRoot)
+ : m_layoutRoot(layoutRoot)
+ {
+ }
+
+ ~RenderTreeNeedsLayoutChecker()
+ {
+ auto reportNeedsLayoutError = [] (const RenderObject& renderer) {
+ WTFReportError(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, "post-layout: dirty renderer(s)");
+ renderer.showRenderTreeForThis();
+ ASSERT_NOT_REACHED();
+ };
+
+ if (m_layoutRoot.needsLayout()) {
+ reportNeedsLayoutError(m_layoutRoot);
+ return;
+ }
+
+ for (auto* descendant = m_layoutRoot.firstChild(); descendant; descendant = descendant->nextInPreOrder(&m_layoutRoot)) {
+ if (!descendant->needsLayout())
+ continue;
+
+ reportNeedsLayoutError(*descendant);
+ return;
+ }
+ }
+
+private:
+ const RenderElement& m_layoutRoot;
+};
+#endif
+
FrameView::FrameView(Frame& frame)
- : m_frame(&frame)
+ : m_frame(frame)
, m_canHaveScrollbars(true)
- , m_layoutTimer(this, &FrameView::layoutTimerFired)
- , m_layoutRoot(0)
+ , m_layoutTimer(*this, &FrameView::layoutTimerFired)
, m_layoutPhase(OutsideLayout)
, m_inSynchronousPostLayout(false)
- , m_postLayoutTasksTimer(this, &FrameView::postLayoutTimerFired)
- , m_updateEmbeddedObjectsTimer(this, &FrameView::updateEmbeddedObjectsTimerFired)
+ , m_postLayoutTasksTimer(*this, &FrameView::performPostLayoutTasks)
+ , m_updateEmbeddedObjectsTimer(*this, &FrameView::updateEmbeddedObjectsTimerFired)
, m_isTransparent(false)
, m_baseBackgroundColor(Color::white)
, m_mediaType("screen")
, m_overflowStatusDirty(true)
- , m_viewportRenderer(0)
, m_wasScrolledByUser(false)
, m_inProgrammaticScroll(false)
, m_safeToPropagateScrollToParent(true)
+ , m_delayedScrollEventTimer(*this, &FrameView::sendScrollEvent)
, m_isTrackingRepaints(false)
, m_shouldUpdateWhileOffscreen(true)
- , m_exposedRect(FloatRect::infiniteRect())
- , m_deferSetNeedsLayouts(0)
+ , m_deferSetNeedsLayoutCount(0)
, m_setNeedsLayoutWasDeferred(false)
, m_speculativeTilingEnabled(false)
- , m_speculativeTilingEnableTimer(this, &FrameView::speculativeTilingEnableTimerFired)
+ , m_speculativeTilingEnableTimer(*this, &FrameView::speculativeTilingEnableTimerFired)
#if PLATFORM(IOS)
, m_useCustomFixedPositionLayoutRect(false)
+ , m_useCustomSizeForResizeEvent(false)
#endif
+ , m_hasOverrideViewportSize(false)
, m_shouldAutoSize(false)
, m_inAutoSize(false)
, m_didRunAutosize(false)
@@ -195,29 +272,40 @@ FrameView::FrameView(Frame& frame)
, m_footerHeight(0)
, m_milestonesPendingPaint(0)
, m_visualUpdatesAllowedByClient(true)
+ , m_hasFlippedBlockRenderers(false)
, m_scrollPinningBehavior(DoNotPin)
{
init();
- if (frame.isMainFrame()) {
- ScrollableArea::setVerticalScrollElasticity(ScrollElasticityAllowed);
- ScrollableArea::setHorizontalScrollElasticity(ScrollElasticityAutomatic);
+#if ENABLE(RUBBER_BANDING)
+ ScrollElasticity verticalElasticity = ScrollElasticityNone;
+ ScrollElasticity horizontalElasticity = ScrollElasticityNone;
+ if (m_frame->isMainFrame()) {
+ verticalElasticity = m_frame->page() ? m_frame->page()->verticalScrollElasticity() : ScrollElasticityAllowed;
+ horizontalElasticity = m_frame->page() ? m_frame->page()->horizontalScrollElasticity() : ScrollElasticityAllowed;
+ } else if (m_frame->settings().rubberBandingForSubScrollableRegionsEnabled()) {
+ verticalElasticity = ScrollElasticityAutomatic;
+ horizontalElasticity = ScrollElasticityAutomatic;
}
+
+ ScrollableArea::setVerticalScrollElasticity(verticalElasticity);
+ ScrollableArea::setHorizontalScrollElasticity(horizontalElasticity);
+#endif
}
-PassRefPtr<FrameView> FrameView::create(Frame& frame)
+Ref<FrameView> FrameView::create(Frame& frame)
{
- RefPtr<FrameView> view = adoptRef(new FrameView(frame));
+ Ref<FrameView> view = adoptRef(*new FrameView(frame));
view->show();
- return view.release();
+ return view;
}
-PassRefPtr<FrameView> FrameView::create(Frame& frame, const IntSize& initialSize)
+Ref<FrameView> FrameView::create(Frame& frame, const IntSize& initialSize)
{
- RefPtr<FrameView> view = adoptRef(new FrameView(frame));
+ Ref<FrameView> view = adoptRef(*new FrameView(frame));
view->Widget::setFrameRect(IntRect(view->location(), initialSize));
view->show();
- return view.release();
+ return view;
}
FrameView::~FrameView()
@@ -245,10 +333,8 @@ void FrameView::reset()
m_cannotBlitToWindow = false;
m_isOverlapped = false;
m_contentIsOpaque = false;
- m_borderX = 30;
- m_borderY = 30;
m_layoutTimer.stop();
- m_layoutRoot = 0;
+ m_layoutRoot = nullptr;
m_delayedLayout = false;
m_needsFullRepaint = true;
m_layoutSchedulingEnabled = true;
@@ -262,6 +348,7 @@ void FrameView::reset()
m_firstLayoutCallbackPending = false;
m_wasScrolledByUser = false;
m_safeToPropagateScrollToParent = true;
+ m_delayedScrollEventTimer.stop();
m_lastViewportSize = IntSize();
m_lastZoomFactor = 1.0f;
m_isTrackingRepaints = false;
@@ -273,13 +360,17 @@ void FrameView::reset()
m_visuallyNonEmptyPixelCount = 0;
m_isVisuallyNonEmpty = false;
m_firstVisuallyNonEmptyLayoutCallbackPending = true;
- m_maintainScrollPositionAnchor = 0;
+ m_needsDeferredScrollbarsUpdate = false;
+ m_maintainScrollPositionAnchor = nullptr;
}
void FrameView::removeFromAXObjectCache()
{
- if (AXObjectCache* cache = axObjectCache())
+ if (AXObjectCache* cache = axObjectCache()) {
+ if (HTMLFrameOwnerElement* owner = frame().ownerElement())
+ cache->childrenChanged(owner->renderer());
cache->remove(this);
+ }
}
void FrameView::resetScrollbars()
@@ -312,12 +403,12 @@ void FrameView::init()
// Propagate the marginwidth/height and scrolling modes to the view.
Element* ownerElement = frame().ownerElement();
- if (ownerElement && (ownerElement->hasTagName(frameTag) || ownerElement->hasTagName(iframeTag))) {
- HTMLFrameElementBase* frameElt = toHTMLFrameElementBase(ownerElement);
- if (frameElt->scrollingMode() == ScrollbarAlwaysOff)
+ if (is<HTMLFrameElementBase>(ownerElement)) {
+ HTMLFrameElementBase& frameElement = downcast<HTMLFrameElementBase>(*ownerElement);
+ if (frameElement.scrollingMode() == ScrollbarAlwaysOff)
setCanHaveScrollbars(false);
- LayoutUnit marginWidth = frameElt->marginWidth();
- LayoutUnit marginHeight = frameElt->marginHeight();
+ LayoutUnit marginWidth = frameElement.marginWidth();
+ LayoutUnit marginHeight = frameElement.marginHeight();
if (marginWidth != -1)
setMarginWidth(marginWidth);
if (marginHeight != -1)
@@ -338,7 +429,7 @@ void FrameView::prepareForDetach()
if (frame().page()) {
if (ScrollingCoordinator* scrollingCoordinator = frame().page()->scrollingCoordinator())
- scrollingCoordinator->willDestroyScrollableArea(this);
+ scrollingCoordinator->willDestroyScrollableArea(*this);
}
}
@@ -358,7 +449,14 @@ void FrameView::detachCustomScrollbars()
void FrameView::recalculateScrollbarOverlayStyle()
{
ScrollbarOverlayStyle oldOverlayStyle = scrollbarOverlayStyle();
- ScrollbarOverlayStyle overlayStyle = ScrollbarOverlayStyleDefault;
+ std::optional<ScrollbarOverlayStyle> clientOverlayStyle = frame().page() ? frame().page()->chrome().client().preferredScrollbarOverlayStyle() : ScrollbarOverlayStyleDefault;
+ if (clientOverlayStyle) {
+ if (clientOverlayStyle.value() != oldOverlayStyle)
+ setScrollbarOverlayStyle(clientOverlayStyle.value());
+ return;
+ }
+
+ ScrollbarOverlayStyle computedOverlayStyle = ScrollbarOverlayStyleDefault;
Color backgroundColor = documentBackgroundColor();
if (backgroundColor.isValid()) {
@@ -367,12 +465,12 @@ void FrameView::recalculateScrollbarOverlayStyle()
// heuristic.
double hue, saturation, lightness;
backgroundColor.getHSL(hue, saturation, lightness);
- if (lightness <= .5 && backgroundColor.alpha() > 0)
- overlayStyle = ScrollbarOverlayStyleLight;
+ if (lightness <= .5 && backgroundColor.isVisible())
+ computedOverlayStyle = ScrollbarOverlayStyleLight;
}
- if (oldOverlayStyle != overlayStyle)
- setScrollbarOverlayStyle(overlayStyle);
+ if (oldOverlayStyle != computedOverlayStyle)
+ setScrollbarOverlayStyle(computedOverlayStyle);
}
void FrameView::clear()
@@ -386,11 +484,20 @@ void FrameView::clear()
#if PLATFORM(IOS)
// To avoid flashes of white, disable tile updates immediately when view is cleared at the beginning of a page load.
// Tiling will be re-enabled from UIKit via [WAKWindow setTilingMode:] when we have content to draw.
- if (TileCache* tileCache = this->tileCache())
- tileCache->setTilingMode(TileCache::Disabled);
+ if (LegacyTileCache* tileCache = legacyTileCache())
+ tileCache->setTilingMode(LegacyTileCache::Disabled);
#endif
}
+#if PLATFORM(IOS)
+void FrameView::didReplaceMultipartContent()
+{
+ // Re-enable tile updates that were disabled in clear().
+ if (LegacyTileCache* tileCache = legacyTileCache())
+ tileCache->setTilingMode(LegacyTileCache::Normal);
+}
+#endif
+
bool FrameView::didFirstLayout() const
{
return !m_firstLayout;
@@ -399,63 +506,50 @@ bool FrameView::didFirstLayout() const
void FrameView::invalidateRect(const IntRect& rect)
{
if (!parent()) {
- if (HostWindow* window = hostWindow())
- window->invalidateContentsAndRootView(rect, false /*immediate*/);
+ if (auto* page = frame().page())
+ page->chrome().invalidateContentsAndRootView(rect);
return;
}
- RenderWidget* renderer = frame().ownerRenderer();
+ auto* renderer = frame().ownerRenderer();
if (!renderer)
return;
IntRect repaintRect = rect;
- repaintRect.move(renderer->borderLeft() + renderer->paddingLeft(),
- renderer->borderTop() + renderer->paddingTop());
+ repaintRect.move(renderer->borderLeft() + renderer->paddingLeft(), renderer->borderTop() + renderer->paddingTop());
renderer->repaintRectangle(repaintRect);
}
void FrameView::setFrameRect(const IntRect& newRect)
{
+ Ref<FrameView> protectedThis(*this);
IntRect oldRect = frameRect();
if (newRect == oldRect)
return;
-#if ENABLE(TEXT_AUTOSIZING)
- // Autosized font sizes depend on the width of the viewing area.
- if (newRect.width() != oldRect.width()) {
- Page* page = frame().page();
- if (frame().isMainFrame() && page->settings().textAutosizingEnabled()) {
- for (Frame* frame = &page->mainFrame(); frame; frame = frame->tree().traverseNext())
- frame().document()->textAutosizer()->recalculateMultipliers();
- }
- }
-#endif
-
ScrollView::setFrameRect(newRect);
updateScrollableAreaSet();
-#if USE(ACCELERATED_COMPOSITING)
if (RenderView* renderView = this->renderView()) {
if (renderView->usesCompositing())
renderView->compositor().frameViewDidChangeSize();
}
-#endif
- if (!frameFlatteningEnabled())
- sendResizeEventIfNeeded();
+ if (frame().isMainFrame())
+ frame().mainFrame().pageOverlayController().didChangeViewSize();
+
+ viewportContentsChanged();
}
-#if ENABLE(REQUEST_ANIMATION_FRAME)
bool FrameView::scheduleAnimation()
{
- if (HostWindow* window = hostWindow()) {
- window->scheduleAnimation();
- return true;
- }
- return false;
+ auto* page = frame().page();
+ if (!page)
+ return false;
+ page->chrome().scheduleAnimation();
+ return true;
}
-#endif
void FrameView::setMarginWidth(LayoutUnit w)
{
@@ -512,42 +606,67 @@ void FrameView::updateCanHaveScrollbars()
setCanHaveScrollbars(true);
}
-PassRefPtr<Scrollbar> FrameView::createScrollbar(ScrollbarOrientation orientation)
+Ref<Scrollbar> FrameView::createScrollbar(ScrollbarOrientation orientation)
{
- if (!frame().settings().allowCustomScrollbarInMainFrame() && frame().isMainFrame())
- return ScrollView::createScrollbar(orientation);
-
// FIXME: We need to update the scrollbar dynamically as documents change (or as doc elements and bodies get discovered that have custom styles).
Document* doc = frame().document();
// Try the <body> element first as a scrollbar source.
- Element* body = doc ? doc->body() : 0;
+ HTMLElement* body = doc ? doc->bodyOrFrameset() : nullptr;
if (body && body->renderer() && body->renderer()->style().hasPseudoStyle(SCROLLBAR))
- return RenderScrollbar::createCustomScrollbar(this, orientation, body);
+ return RenderScrollbar::createCustomScrollbar(*this, orientation, body);
// If the <body> didn't have a custom style, then the root element might.
- Element* docElement = doc ? doc->documentElement() : 0;
+ Element* docElement = doc ? doc->documentElement() : nullptr;
if (docElement && docElement->renderer() && docElement->renderer()->style().hasPseudoStyle(SCROLLBAR))
- return RenderScrollbar::createCustomScrollbar(this, orientation, docElement);
+ return RenderScrollbar::createCustomScrollbar(*this, orientation, docElement);
// If we have an owning iframe/frame element, then it can set the custom scrollbar also.
RenderWidget* frameRenderer = frame().ownerRenderer();
if (frameRenderer && frameRenderer->style().hasPseudoStyle(SCROLLBAR))
- return RenderScrollbar::createCustomScrollbar(this, orientation, 0, &frame());
+ return RenderScrollbar::createCustomScrollbar(*this, orientation, nullptr, &frame());
// Nobody set a custom style, so we just use a native scrollbar.
return ScrollView::createScrollbar(orientation);
}
+void FrameView::didRestoreFromPageCache()
+{
+ // When restoring from page cache, the main frame stays in place while subframes get swapped in.
+ // We update the scrollable area set to ensure that scrolling data structures get invalidated.
+ updateScrollableAreaSet();
+}
+
+void FrameView::willDestroyRenderTree()
+{
+ detachCustomScrollbars();
+ m_layoutRoot = nullptr;
+}
+
+void FrameView::didDestroyRenderTree()
+{
+ ASSERT(!m_layoutRoot);
+ ASSERT(m_widgetsInRenderTree.isEmpty());
+
+ // If the render tree is destroyed below FrameView::updateEmbeddedObjects(), there will still be a null sentinel in the set.
+ // Everything else should have removed itself as the tree was felled.
+ ASSERT(!m_embeddedObjectsToUpdate || m_embeddedObjectsToUpdate->isEmpty() || (m_embeddedObjectsToUpdate->size() == 1 && m_embeddedObjectsToUpdate->first() == nullptr));
+
+ ASSERT(!m_viewportConstrainedObjects || m_viewportConstrainedObjects->isEmpty());
+ ASSERT(!m_slowRepaintObjects || m_slowRepaintObjects->isEmpty());
+
+ ASSERT(!frame().animation().hasAnimations());
+}
+
void FrameView::setContentsSize(const IntSize& size)
{
if (size == contentsSize())
return;
- m_deferSetNeedsLayouts++;
+ m_deferSetNeedsLayoutCount++;
ScrollView::setContentsSize(size);
- ScrollView::contentsResized();
+ contentsResized();
Page* page = frame().page();
if (!page)
@@ -555,12 +674,17 @@ void FrameView::setContentsSize(const IntSize& size)
updateScrollableAreaSet();
- page->chrome().contentsSizeChanged(&frame(), size); // Notify only.
+ page->chrome().contentsSizeChanged(frame(), size); // Notify only.
- ASSERT(m_deferSetNeedsLayouts);
- m_deferSetNeedsLayouts--;
+ if (frame().isMainFrame()) {
+ frame().mainFrame().pageOverlayController().didChangeDocumentSize();
+ PageCache::singleton().markPagesForContentsSizeChanged(*page);
+ }
+
+ ASSERT(m_deferSetNeedsLayoutCount);
+ m_deferSetNeedsLayoutCount--;
- if (!m_deferSetNeedsLayouts)
+ if (!m_deferSetNeedsLayoutCount)
m_setNeedsLayoutWasDeferred = false; // FIXME: Find a way to make the deferred layout actually happen.
}
@@ -575,11 +699,13 @@ void FrameView::adjustViewSize()
const IntRect rect = renderView->documentRect();
const IntSize& size = rect.size();
ScrollView::setScrollOrigin(IntPoint(-rect.x(), -rect.y()), !frame().document()->printing(), size == contentsSize());
-
+
+ LOG_WITH_STREAM(Layout, stream << "FrameView " << this << " adjustViewSize: unscaled document rect changed to " << renderView->unscaledDocumentRect() << " (scaled to " << size << ")");
+
setContentsSize(size);
}
-void FrameView::applyOverflowToViewport(RenderElement* o, ScrollbarMode& hMode, ScrollbarMode& vMode)
+void FrameView::applyOverflowToViewport(const RenderElement& renderer, ScrollbarMode& hMode, ScrollbarMode& vMode)
{
// Handle the overflow:hidden/scroll case for the body/html elements. WinIE treats
// overflow:hidden and overflow:scroll on <body> as applying to the document's
@@ -592,18 +718,17 @@ void FrameView::applyOverflowToViewport(RenderElement* o, ScrollbarMode& hMode,
bool overrideHidden = frame().isMainFrame() && ((frame().frameScaleFactor() > 1) || headerHeight() || footerHeight());
- EOverflow overflowX = o->style().overflowX();
- EOverflow overflowY = o->style().overflowY();
+ EOverflow overflowX = renderer.style().overflowX();
+ EOverflow overflowY = renderer.style().overflowY();
-#if ENABLE(SVG)
- if (o->isSVGRoot()) {
- // overflow is ignored in stand-alone SVG documents.
- if (!toRenderSVGRoot(o)->isEmbeddedThroughFrameContainingSVGDocument())
- return;
- overflowX = OHIDDEN;
- overflowY = OHIDDEN;
+ if (is<RenderSVGRoot>(renderer)) {
+ // FIXME: evaluate if we can allow overflow for these cases too.
+ // Overflow is always hidden when stand-alone SVG documents are embedded.
+ if (downcast<RenderSVGRoot>(renderer).isEmbeddedThroughFrameContainingSVGDocument()) {
+ overflowX = OHIDDEN;
+ overflowY = OHIDDEN;
+ }
}
-#endif
switch (overflowX) {
case OHIDDEN:
@@ -640,41 +765,38 @@ void FrameView::applyOverflowToViewport(RenderElement* o, ScrollbarMode& hMode,
// Don't set it at all. Values of OPAGEDX and OPAGEDY are handled by applyPaginationToViewPort().
;
}
-
- m_viewportRenderer = o;
}
void FrameView::applyPaginationToViewport()
{
- Document* document = frame().document();
- auto documentElement = document->documentElement();
- RenderElement* documentRenderer = documentElement ? documentElement->renderer() : nullptr;
- RenderElement* documentOrBodyRenderer = documentRenderer;
- auto body = document->body();
- if (body && body->renderer()) {
- if (body->hasTagName(bodyTag))
- documentOrBodyRenderer = documentRenderer->style().overflowX() == OVISIBLE && documentElement->hasTagName(htmlTag) ? body->renderer() : documentRenderer;
+ auto* document = frame().document();
+ auto* documentElement = document ? document->documentElement() : nullptr;
+ if (!documentElement || !documentElement->renderer()) {
+ setPagination(Pagination());
+ return;
}
- Pagination pagination;
+ auto& documentRenderer = *documentElement->renderer();
+ auto* documentOrBodyRenderer = &documentRenderer;
- if (!documentOrBodyRenderer) {
- setPagination(pagination);
- return;
+ auto* body = document->body();
+ if (body && body->renderer()) {
+ documentOrBodyRenderer = documentRenderer.style().overflowX() == OVISIBLE && is<HTMLHtmlElement>(*documentElement) ?
+ body->renderer() : &documentRenderer;
}
+ Pagination pagination;
EOverflow overflowY = documentOrBodyRenderer->style().overflowY();
if (overflowY == OPAGEDX || overflowY == OPAGEDY) {
pagination.mode = WebCore::paginationModeForRenderStyle(documentOrBodyRenderer->style());
pagination.gap = static_cast<unsigned>(documentOrBodyRenderer->style().columnGap());
}
-
setPagination(pagination);
}
void FrameView::calculateScrollbarModesForLayout(ScrollbarMode& hMode, ScrollbarMode& vMode, ScrollbarModesCalculationStrategy strategy)
{
- m_viewportRenderer = 0;
+ m_viewportRendererType = ViewportRendererType::None;
const HTMLFrameOwnerElement* owner = frame().ownerElement();
if (owner && (owner->scrollingMode() == ScrollbarAlwaysOff)) {
@@ -685,51 +807,75 @@ void FrameView::calculateScrollbarModesForLayout(ScrollbarMode& hMode, Scrollbar
if (m_canHaveScrollbars || strategy == RulesFromWebContentOnly) {
hMode = ScrollbarAuto;
- // Seamless documents begin with heights of 0; we special case that here
- // to correctly render documents that don't need scrollbars.
- IntSize fullVisibleSize = visibleContentRectIncludingScrollbars(LegacyIOSDocumentVisibleRect).size();
- bool isSeamlessDocument = frame().document() && frame().document()->shouldDisplaySeamlesslyWithParent();
- vMode = (isSeamlessDocument && !fullVisibleSize.height()) ? ScrollbarAlwaysOff : ScrollbarAuto;
+ vMode = ScrollbarAuto;
} else {
hMode = ScrollbarAlwaysOff;
vMode = ScrollbarAlwaysOff;
}
- if (!m_layoutRoot) {
- Document* document = frame().document();
- auto documentElement = document->documentElement();
- RenderElement* rootRenderer = documentElement ? documentElement->renderer() : nullptr;
- auto body = document->body();
- if (body && body->renderer()) {
- if (body->hasTagName(framesetTag) && !frameFlatteningEnabled()) {
- vMode = ScrollbarAlwaysOff;
- hMode = ScrollbarAlwaysOff;
- } else if (body->hasTagName(bodyTag)) {
- // It's sufficient to just check the X overflow,
- // since it's illegal to have visible in only one direction.
- RenderElement* o = rootRenderer->style().overflowX() == OVISIBLE && document->documentElement()->hasTagName(htmlTag) ? body->renderer() : rootRenderer;
- applyOverflowToViewport(o, hMode, vMode);
+ if (m_layoutRoot)
+ return;
+
+ auto* document = frame().document();
+ if (!document)
+ return;
+
+ auto* documentElement = document->documentElement();
+ if (!documentElement)
+ return;
+
+ auto* bodyOrFrameset = document->bodyOrFrameset();
+ auto* rootRenderer = documentElement->renderer();
+ if (!bodyOrFrameset || !bodyOrFrameset->renderer()) {
+ if (rootRenderer) {
+ applyOverflowToViewport(*rootRenderer, hMode, vMode);
+ m_viewportRendererType = ViewportRendererType::Document;
+ }
+ return;
+ }
+
+ if (is<HTMLFrameSetElement>(*bodyOrFrameset) && !frameFlatteningEnabled()) {
+ vMode = ScrollbarAlwaysOff;
+ hMode = ScrollbarAlwaysOff;
+ return;
+ }
+
+ if (is<HTMLBodyElement>(*bodyOrFrameset) && rootRenderer) {
+ // It's sufficient to just check the X overflow,
+ // since it's illegal to have visible in only one direction.
+ if (rootRenderer->style().overflowX() == OVISIBLE && is<HTMLHtmlElement>(documentElement)) {
+ auto* bodyRenderer = bodyOrFrameset->renderer();
+ if (bodyRenderer) {
+ applyOverflowToViewport(*bodyRenderer, hMode, vMode);
+ m_viewportRendererType = ViewportRendererType::Body;
}
- } else if (rootRenderer)
- applyOverflowToViewport(rootRenderer, hMode, vMode);
- }
+ } else {
+ applyOverflowToViewport(*rootRenderer, hMode, vMode);
+ m_viewportRendererType = ViewportRendererType::Document;
+ }
+ }
}
-#if USE(ACCELERATED_COMPOSITING)
-void FrameView::updateCompositingLayersAfterStyleChange()
+void FrameView::willRecalcStyle()
{
RenderView* renderView = this->renderView();
if (!renderView)
return;
+ renderView->compositor().willRecalcStyle();
+}
+
+bool FrameView::updateCompositingLayersAfterStyleChange()
+{
+ RenderView* renderView = this->renderView();
+ if (!renderView)
+ return false;
+
// If we expect to update compositing after an incipient layout, don't do so here.
if (inPreLayoutStyleUpdate() || layoutPending() || renderView->needsLayout())
- return;
+ return false;
- RenderLayerCompositor& compositor = renderView->compositor();
- // This call will make sure the cached hasAcceleratedCompositing is updated from the pref
- compositor.cacheAcceleratedCompositingFlags();
- compositor.updateCompositingLayers(CompositingUpdateAfterStyleChange);
+ return renderView->compositor().didRecalcStyleWithNoPendingLayout();
}
void FrameView::updateCompositingLayersAfterLayout()
@@ -755,32 +901,11 @@ void FrameView::clearBackingStores()
compositor.clearBackingForAllLayers();
}
-void FrameView::restoreBackingStores()
-{
- RenderView* renderView = this->renderView();
- if (!renderView)
- return;
-
- RenderLayerCompositor& compositor = renderView->compositor();
- compositor.enableCompositingMode(true);
- compositor.updateCompositingLayers(CompositingUpdateAfterLayout);
-}
-
-bool FrameView::usesCompositedScrolling() const
-{
- RenderView* renderView = this->renderView();
- if (!renderView)
- return false;
- if (frame().settings().compositedScrollingForFramesEnabled())
- return renderView->compositor().inForcedCompositingMode();
- return false;
-}
-
GraphicsLayer* FrameView::layerForScrolling() const
{
RenderView* renderView = this->renderView();
if (!renderView)
- return 0;
+ return nullptr;
return renderView->compositor().scrollLayer();
}
@@ -788,7 +913,7 @@ GraphicsLayer* FrameView::layerForHorizontalScrollbar() const
{
RenderView* renderView = this->renderView();
if (!renderView)
- return 0;
+ return nullptr;
return renderView->compositor().layerForHorizontalScrollbar();
}
@@ -796,7 +921,7 @@ GraphicsLayer* FrameView::layerForVerticalScrollbar() const
{
RenderView* renderView = this->renderView();
if (!renderView)
- return 0;
+ return nullptr;
return renderView->compositor().layerForVerticalScrollbar();
}
@@ -804,7 +929,7 @@ GraphicsLayer* FrameView::layerForScrollCorner() const
{
RenderView* renderView = this->renderView();
if (!renderView)
- return 0;
+ return nullptr;
return renderView->compositor().layerForScrollCorner();
}
@@ -812,11 +937,11 @@ TiledBacking* FrameView::tiledBacking() const
{
RenderView* renderView = this->renderView();
if (!renderView)
- return 0;
+ return nullptr;
RenderLayerBacking* backing = renderView->layer()->backing();
if (!backing)
- return 0;
+ return nullptr;
return backing->graphicsLayer()->tiledBacking();
}
@@ -831,7 +956,16 @@ uint64_t FrameView::scrollLayerID() const
if (!backing)
return 0;
- return backing->scrollLayerID();
+ return backing->scrollingNodeIDForRole(Scrolling);
+}
+
+ScrollableArea* FrameView::scrollableAreaForScrollLayerID(uint64_t nodeID) const
+{
+ RenderView* renderView = this->renderView();
+ if (!renderView)
+ return nullptr;
+
+ return renderView->compositor().scrollableAreaForScrollLayerID(nodeID);
}
#if ENABLE(RUBBER_BANDING)
@@ -839,7 +973,7 @@ GraphicsLayer* FrameView::layerForOverhangAreas() const
{
RenderView* renderView = this->renderView();
if (!renderView)
- return 0;
+ return nullptr;
return renderView->compositor().layerForOverhangAreas();
}
@@ -847,7 +981,7 @@ GraphicsLayer* FrameView::setWantsLayerForTopOverHangArea(bool wantsLayer) const
{
RenderView* renderView = this->renderView();
if (!renderView)
- return 0;
+ return nullptr;
return renderView->compositor().updateLayerForTopOverhangArea(wantsLayer);
}
@@ -856,14 +990,56 @@ GraphicsLayer* FrameView::setWantsLayerForBottomOverHangArea(bool wantsLayer) co
{
RenderView* renderView = this->renderView();
if (!renderView)
- return 0;
+ return nullptr;
return renderView->compositor().updateLayerForBottomOverhangArea(wantsLayer);
}
#endif // ENABLE(RUBBER_BANDING)
-bool FrameView::flushCompositingStateForThisFrame(Frame* rootFrameForFlush)
+#if ENABLE(CSS_SCROLL_SNAP)
+void FrameView::updateSnapOffsets()
+{
+ if (!frame().document())
+ return;
+
+ // FIXME: Should we allow specifying snap points through <html> tags too?
+ HTMLElement* body = frame().document()->bodyOrFrameset();
+ if (!renderView() || !body || !body->renderer())
+ return;
+
+ updateSnapOffsetsForScrollableArea(*this, *body, *renderView(), body->renderer()->style());
+}
+
+bool FrameView::isScrollSnapInProgress() const
+{
+ if (scrollbarsSuppressed())
+ return false;
+
+ // If the scrolling thread updates the scroll position for this FrameView, then we should return
+ // ScrollingCoordinator::isScrollSnapInProgress().
+ if (Page* page = frame().page()) {
+ if (ScrollingCoordinator* scrollingCoordinator = page->scrollingCoordinator()) {
+ if (!scrollingCoordinator->shouldUpdateScrollLayerPositionSynchronously(*this))
+ return scrollingCoordinator->isScrollSnapInProgress();
+ }
+ }
+
+ // If the main thread updates the scroll position for this FrameView, we should return
+ // ScrollAnimator::isScrollSnapInProgress().
+ if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
+ return scrollAnimator->isScrollSnapInProgress();
+
+ return false;
+}
+
+void FrameView::updateScrollingCoordinatorScrollSnapProperties() const
+{
+ renderView()->compositor().updateScrollSnapPropertiesWithFrameView(*this);
+}
+#endif
+
+bool FrameView::flushCompositingStateForThisFrame(const Frame& rootFrameForFlush)
{
RenderView* renderView = this->renderView();
if (!renderView)
@@ -877,12 +1053,11 @@ bool FrameView::flushCompositingStateForThisFrame(Frame* rootFrameForFlush)
return false;
#if PLATFORM(IOS)
- if (TileCache* tileCache = this->tileCache())
+ if (LegacyTileCache* tileCache = legacyTileCache())
tileCache->doPendingRepaints();
#endif
- renderView->compositor().flushPendingLayerChanges(rootFrameForFlush == &frame());
-
+ renderView->compositor().flushPendingLayerChanges(&rootFrameForFlush == m_frame.ptr());
return true;
}
@@ -896,22 +1071,22 @@ GraphicsLayer* FrameView::graphicsLayerForPlatformWidget(PlatformWidget platform
{
// To find the Widget that corresponds with platformWidget we have to do a linear
// search of our child widgets.
- Widget* foundWidget = nullptr;
+ const Widget* foundWidget = nullptr;
for (auto& widget : children()) {
if (widget->platformWidget() != platformWidget)
continue;
- foundWidget = widget.get();
+ foundWidget = widget.ptr();
break;
}
if (!foundWidget)
return nullptr;
- auto* renderWidget = RenderWidget::find(foundWidget);
+ auto* renderWidget = RenderWidget::find(*foundWidget);
if (!renderWidget)
return nullptr;
- RenderLayer* widgetLayer = renderWidget->layer();
+ auto* widgetLayer = renderWidget->layer();
if (!widgetLayer || !widgetLayer->isComposited())
return nullptr;
@@ -925,7 +1100,36 @@ void FrameView::scheduleLayerFlushAllowingThrottling()
return;
view->compositor().scheduleLayerFlush(true /* canThrottle */);
}
-#endif // USE(ACCELERATED_COMPOSITING)
+
+LayoutRect FrameView::fixedScrollableAreaBoundsInflatedForScrolling(const LayoutRect& uninflatedBounds) const
+{
+ LayoutPoint scrollPosition;
+ LayoutSize topLeftExpansion;
+ LayoutSize bottomRightExpansion;
+
+ if (frame().settings().visualViewportEnabled()) {
+ // FIXME: this is wrong under zooming; uninflatedBounds is scaled but the scroll positions are not.
+ scrollPosition = layoutViewportRect().location();
+ topLeftExpansion = scrollPosition - unscaledMinimumScrollPosition();
+ bottomRightExpansion = unscaledMaximumScrollPosition() - scrollPosition;
+ } else {
+ scrollPosition = scrollPositionRespectingCustomFixedPosition();
+ topLeftExpansion = scrollPosition - minimumScrollPosition();
+ bottomRightExpansion = maximumScrollPosition() - scrollPosition;
+ }
+
+ return LayoutRect(uninflatedBounds.location() - topLeftExpansion, uninflatedBounds.size() + topLeftExpansion + bottomRightExpansion);
+}
+
+LayoutPoint FrameView::scrollPositionRespectingCustomFixedPosition() const
+{
+#if PLATFORM(IOS)
+ if (!frame().settings().visualViewportEnabled())
+ return useCustomFixedPositionLayoutRect() ? customFixedPositionLayoutRect().location() : scrollPosition();
+#endif
+
+ return scrollPositionForFixedPosition();
+}
void FrameView::setHeaderHeight(int headerHeight)
{
@@ -947,103 +1151,101 @@ void FrameView::setFooterHeight(int footerHeight)
renderView->setNeedsLayout();
}
-bool FrameView::hasCompositedContent() const
+float FrameView::topContentInset(TopContentInsetType contentInsetTypeToReturn) const
{
-#if USE(ACCELERATED_COMPOSITING)
- if (RenderView* renderView = this->renderView())
- return renderView->compositor().inCompositingMode();
-#endif
- return false;
-}
+ if (platformWidget() && contentInsetTypeToReturn == TopContentInsetType::WebCoreOrPlatformContentInset)
+ return platformTopContentInset();
-bool FrameView::hasCompositedContentIncludingDescendants() const
+ if (!frame().isMainFrame())
+ return 0;
+
+ Page* page = frame().page();
+ return page ? page->topContentInset() : 0;
+}
+
+void FrameView::topContentInsetDidChange(float newTopContentInset)
{
-#if USE(ACCELERATED_COMPOSITING)
- for (Frame* frame = m_frame.get(); frame; frame = frame->tree().traverseNext(m_frame.get())) {
- RenderView* renderView = frame->contentRenderer();
- if (RenderLayerCompositor* compositor = renderView ? &renderView->compositor() : 0) {
- if (compositor->inCompositingMode())
- return true;
+ RenderView* renderView = this->renderView();
+ if (!renderView)
+ return;
- if (!RenderLayerCompositor::allowsIndependentlyCompositedFrames(this))
- break;
- }
- }
-#endif
- return false;
+ if (platformWidget())
+ platformSetTopContentInset(newTopContentInset);
+
+ layout();
+
+ updateScrollbars(scrollPosition());
+ if (renderView->usesCompositing())
+ renderView->compositor().frameViewDidChangeSize();
+
+ if (TiledBacking* tiledBacking = this->tiledBacking())
+ tiledBacking->setTopContentInset(newTopContentInset);
}
-bool FrameView::hasCompositingAncestor() const
+void FrameView::topContentDirectionDidChange()
{
-#if USE(ACCELERATED_COMPOSITING)
- for (Frame* frame = this->frame().tree().parent(); frame; frame = frame->tree().parent()) {
- if (FrameView* view = frame->view()) {
- if (view->hasCompositedContent())
- return true;
- }
- }
-#endif
+ m_needsDeferredScrollbarsUpdate = true;
+}
+
+void FrameView::handleDeferredScrollbarsUpdateAfterDirectionChange()
+{
+ if (!m_needsDeferredScrollbarsUpdate)
+ return;
+
+ m_needsDeferredScrollbarsUpdate = false;
+
+ ASSERT(m_layoutPhase == InPostLayerPositionsUpdatedAfterLayout);
+ updateScrollbars(scrollPosition());
+ positionScrollbarLayers();
+}
+
+bool FrameView::hasCompositedContent() const
+{
+ if (RenderView* renderView = this->renderView())
+ return renderView->compositor().inCompositingMode();
return false;
}
// Sometimes (for plug-ins) we need to eagerly go into compositing mode.
void FrameView::enterCompositingMode()
{
-#if USE(ACCELERATED_COMPOSITING)
if (RenderView* renderView = this->renderView()) {
renderView->compositor().enableCompositingMode();
if (!needsLayout())
renderView->compositor().scheduleCompositingLayerUpdate();
}
-#endif
}
bool FrameView::isEnclosedInCompositingLayer() const
{
-#if USE(ACCELERATED_COMPOSITING)
auto frameOwnerRenderer = frame().ownerRenderer();
if (frameOwnerRenderer && frameOwnerRenderer->containerForRepaint())
return true;
if (FrameView* parentView = parentFrameView())
return parentView->isEnclosedInCompositingLayer();
-#endif
return false;
}
bool FrameView::flushCompositingStateIncludingSubframes()
{
-#if USE(ACCELERATED_COMPOSITING)
- bool allFramesFlushed = flushCompositingStateForThisFrame(&frame());
-
- for (Frame* child = frame().tree().firstChild(); child; child = child->tree().traverseNext(&frame())) {
- bool flushed = child->view()->flushCompositingStateForThisFrame(&frame());
+ InspectorInstrumentation::willComposite(frame());
+
+ bool allFramesFlushed = flushCompositingStateForThisFrame(frame());
+
+ for (Frame* child = frame().tree().firstRenderedChild(); child; child = child->tree().traverseNextRendered(m_frame.ptr())) {
+ if (!child->view())
+ continue;
+ bool flushed = child->view()->flushCompositingStateForThisFrame(frame());
allFramesFlushed &= flushed;
}
return allFramesFlushed;
-#else // USE(ACCELERATED_COMPOSITING)
- return true;
-#endif
}
bool FrameView::isSoftwareRenderable() const
{
-#if USE(ACCELERATED_COMPOSITING)
RenderView* renderView = this->renderView();
return !renderView || !renderView->compositor().has3DContent();
-#else
- return true;
-#endif
-}
-
-void FrameView::didMoveOnscreen()
-{
- contentAreaDidShow();
-}
-
-void FrameView::willMoveOffscreen()
-{
- contentAreaDidHide();
}
void FrameView::setIsInWindow(bool isInWindow)
@@ -1052,14 +1254,8 @@ void FrameView::setIsInWindow(bool isInWindow)
renderView->setIsInWindow(isInWindow);
}
-RenderObject* FrameView::layoutRoot(bool onlyDuringLayout) const
-{
- return onlyDuringLayout && layoutPending() ? 0 : m_layoutRoot;
-}
-
inline void FrameView::forceLayoutParentViewIfNeeded()
{
-#if ENABLE(SVG)
RenderWidget* ownerRenderer = frame().ownerRenderer();
if (!ownerRenderer)
return;
@@ -1068,50 +1264,59 @@ inline void FrameView::forceLayoutParentViewIfNeeded()
if (!contentBox)
return;
- RenderSVGRoot* svgRoot = toRenderSVGRoot(contentBox);
- if (svgRoot->everHadLayout() && !svgRoot->needsLayout())
+ auto& svgRoot = downcast<RenderSVGRoot>(*contentBox);
+ if (svgRoot.everHadLayout() && !svgRoot.needsLayout())
return;
+ LOG(Layout, "FrameView %p forceLayoutParentViewIfNeeded scheduling layout on parent FrameView %p", this, &ownerRenderer->view().frameView());
+
// If the embedded SVG document appears the first time, the ownerRenderer has already finished
// layout without knowing about the existence of the embedded SVG document, because RenderReplaced
- // embeddedContentBox() returns 0, as long as the embedded document isn't loaded yet. Before
+ // embeddedContentBox() returns nullptr, as long as the embedded document isn't loaded yet. Before
// bothering to lay out the SVG document, mark the ownerRenderer needing layout and ask its
// FrameView for a layout. After that the RenderEmbeddedObject (ownerRenderer) carries the
// correct size, which RenderSVGRoot::computeReplacedLogicalWidth/Height rely on, when laying
// out for the first time, or when the RenderSVGRoot size has changed dynamically (eg. via <script>).
- Ref<FrameView> frameView(ownerRenderer->view().frameView());
- // Mark the owner renderer as needing layout.
ownerRenderer->setNeedsLayoutAndPrefWidthsRecalc();
-
- // Synchronously enter layout, to layout the view containing the host object/embed/iframe.
- frameView->layout();
-#endif
+ ownerRenderer->view().frameView().scheduleRelayout();
}
void FrameView::layout(bool allowSubtree)
{
- if (isInLayout())
+ ASSERT_WITH_SECURITY_IMPLICATION(!frame().document()->inRenderTreeUpdate());
+
+ LOG(Layout, "FrameView %p (%dx%d) layout, main frameview %d, allowSubtree=%d", this, size().width(), size().height(), frame().isMainFrame(), allowSubtree);
+ if (isInRenderTreeLayout()) {
+ LOG(Layout, " in layout, bailing");
+ return;
+ }
+
+ if (layoutDisallowed()) {
+ LOG(Layout, " layout is disallowed, bailing");
return;
+ }
+
+ // Protect the view from being deleted during layout (in recalcStyle).
+ Ref<FrameView> protectedThis(*this);
// Many of the tasks performed during layout can cause this function to be re-entered,
// so save the layout phase now and restore it on exit.
- TemporaryChange<LayoutPhase> layoutPhaseRestorer(m_layoutPhase, InPreLayout);
-
- // Protect the view from being deleted during layout (in recalcStyle)
- Ref<FrameView> protect(*this);
+ SetForScope<LayoutPhase> layoutPhaseRestorer(m_layoutPhase, InPreLayout);
// Every scroll that happens during layout is programmatic.
- TemporaryChange<bool> changeInProgrammaticScroll(m_inProgrammaticScroll, true);
+ SetForScope<bool> changeInProgrammaticScroll(m_inProgrammaticScroll, true);
bool inChildFrameLayoutWithFrameFlattening = isInChildFrameWithFrameFlattening();
if (inChildFrameLayoutWithFrameFlattening) {
startLayoutAtMainFrameViewIfNeeded(allowSubtree);
RenderElement* root = m_layoutRoot ? m_layoutRoot : frame().document()->renderView();
- if (!root->needsLayout())
+ if (!root || !root->needsLayout())
return;
}
+
+ TraceScope tracingScope(LayoutStart, LayoutEnd);
#if PLATFORM(IOS)
if (updateFixedPositionLayoutRect())
@@ -1127,40 +1332,37 @@ void FrameView::layout(bool allowSubtree)
if (isPainting())
return;
- InspectorInstrumentationCookie cookie = InspectorInstrumentation::willLayout(&frame());
-
- if (!allowSubtree && m_layoutRoot) {
- m_layoutRoot->markContainingBlocksForLayout(false);
- m_layoutRoot = 0;
- }
+ InspectorInstrumentationCookie cookie = InspectorInstrumentation::willLayout(frame());
+ AnimationUpdateBlock animationUpdateBlock(&frame().animation());
+
+ if (!allowSubtree && m_layoutRoot)
+ convertSubtreeLayoutToFullLayout();
ASSERT(frame().view() == this);
ASSERT(frame().document());
Document& document = *frame().document();
- ASSERT(!document.inPageCache());
-
- bool subtree;
- RenderElement* root;
+ ASSERT(document.pageCacheState() == Document::NotInPageCache);
{
- TemporaryChange<bool> changeSchedulingEnabled(m_layoutSchedulingEnabled, false);
+ SetForScope<bool> changeSchedulingEnabled(m_layoutSchedulingEnabled, false);
if (!m_nestedLayoutCount && !m_inSynchronousPostLayout && m_postLayoutTasksTimer.isActive() && !inChildFrameLayoutWithFrameFlattening) {
// This is a new top-level layout. If there are any remaining tasks from the previous
// layout, finish them now.
- TemporaryChange<bool> inSynchronousPostLayoutChange(m_inSynchronousPostLayout, true);
+ SetForScope<bool> inSynchronousPostLayoutChange(m_inSynchronousPostLayout, true);
performPostLayoutTasks();
}
m_layoutPhase = InPreLayoutStyleUpdate;
// Viewport-dependent media queries may cause us to need completely different style information.
- StyleResolver* styleResolver = document.styleResolverIfExists();
- if (!styleResolver || styleResolver->affectedByViewportChange()) {
- document.styleResolverChanged(DeferRecalcStyle);
+ auto* styleResolver = document.styleScope().resolverIfExists();
+ if (!styleResolver || styleResolver->hasMediaQueriesAffectedByViewportChange()) {
+ LOG(Layout, " hasMediaQueriesAffectedByViewportChange, enqueueing style recalc");
+ document.styleScope().didChangeStyleSheetEnvironment();
// FIXME: This instrumentation event is not strictly accurate since cached media query results do not persist across StyleResolver rebuilds.
- InspectorInstrumentation::mediaQueryResultChanged(&document);
+ InspectorInstrumentation::mediaQueryResultChanged(document);
} else
document.evaluateMediaQueryList();
@@ -1171,54 +1373,52 @@ void FrameView::layout(bool allowSubtree)
// Always ensure our style info is up-to-date. This can happen in situations where
// the layout beats any sort of style recalc update that needs to occur.
document.updateStyleIfNeeded();
- m_layoutPhase = InPreLayout;
-
- subtree = m_layoutRoot;
-
- // If there is only one ref to this view left, then its going to be destroyed as soon as we exit,
+ // If there is only one ref to this view left, then its going to be destroyed as soon as we exit,
// so there's no point to continuing to layout
if (hasOneRef())
return;
- root = subtree ? m_layoutRoot : document.renderView();
- if (!root) {
- // FIXME: Do we need to set m_size here?
- return;
- }
-
// Close block here so we can set up the font cache purge preventer, which we will still
// want in scope even after we want m_layoutSchedulingEnabled to be restored again.
// The next block sets m_layoutSchedulingEnabled back to false once again.
}
- FontCachePurgePreventer fontCachePurgePreventer;
- RenderLayer* layer;
+ m_layoutPhase = InPreLayout;
+
+ RenderLayer* layer = nullptr;
+ bool subtree = false;
+ RenderElement* root = nullptr;
++m_nestedLayoutCount;
{
- TemporaryChange<bool> changeSchedulingEnabled(m_layoutSchedulingEnabled, false);
+ SetForScope<bool> changeSchedulingEnabled(m_layoutSchedulingEnabled, false);
+
+ autoSizeIfEnabled();
+
+ root = m_layoutRoot ? m_layoutRoot : document.renderView();
+ if (!root)
+ return;
+ subtree = m_layoutRoot;
if (!m_layoutRoot) {
- HTMLElement* body = document.body();
+ auto* body = document.bodyOrFrameset();
if (body && body->renderer()) {
- if (body->hasTagName(framesetTag) && !frameFlatteningEnabled()) {
+ if (is<HTMLFrameSetElement>(*body) && !frameFlatteningEnabled()) {
body->renderer()->setChildNeedsLayout();
- } else if (body->hasTagName(bodyTag)) {
- if (!m_firstLayout && m_size.height() != layoutHeight() && body->renderer()->enclosingBox()->stretchesToViewport())
+ } else if (is<HTMLBodyElement>(*body)) {
+ if (!m_firstLayout && m_size.height() != layoutHeight() && body->renderer()->enclosingBox().stretchesToViewport())
body->renderer()->setChildNeedsLayout();
}
}
-#ifdef INSTRUMENT_LAYOUT_SCHEDULING
+#if !LOG_DISABLED
if (m_firstLayout && !frame().ownerElement())
- printf("Elapsed time before first layout: %lld\n", document.elapsedTime().count());
-#endif
+ LOG(Layout, "FrameView %p elapsed time before first layout: %.3fs\n", this, document.timeSinceDocumentCreation().value());
+#endif
}
- autoSizeIfEnabled();
-
- m_needsFullRepaint = !subtree && (m_firstLayout || toRenderView(*root).printing());
+ m_needsFullRepaint = !subtree && (m_firstLayout || downcast<RenderView>(*root).printing());
if (!subtree) {
ScrollbarMode hMode;
@@ -1231,11 +1431,7 @@ void FrameView::layout(bool allowSubtree)
m_firstLayout = false;
m_firstLayoutCallbackPending = true;
- if (useFixedLayout() && !fixedLayoutSize().isEmpty() && delegatesScrolling())
- m_lastViewportSize = fixedLayoutSize();
- else
- m_lastViewportSize = visibleContentRectIncludingScrollbars().size();
-
+ m_lastViewportSize = sizeForResizeEvent();
m_lastZoomFactor = root->style().zoom();
// Set the initial vMode to AlwaysOn if we're auto.
@@ -1244,7 +1440,9 @@ void FrameView::layout(bool allowSubtree)
// Set the initial hMode to AlwaysOff if we're auto.
if (hMode == ScrollbarAuto)
setHorizontalScrollbarMode(ScrollbarAlwaysOff); // This causes a horizontal scrollbar to disappear.
-
+ Page* page = frame().page();
+ if (page && page->expectsWheelEventTriggers())
+ scrollAnimator().setWheelEventTestTrigger(page->testTrigger());
setScrollbarModes(hMode, vMode);
setScrollbarsSuppressed(false, true);
} else
@@ -1252,14 +1450,15 @@ void FrameView::layout(bool allowSubtree)
}
LayoutSize oldSize = m_size;
-
m_size = layoutSize();
if (oldSize != m_size) {
+ LOG(Layout, " layout size changed from %.3fx%.3f to %.3fx%.3f", oldSize.width().toFloat(), oldSize.height().toFloat(), m_size.width().toFloat(), m_size.height().toFloat());
m_needsFullRepaint = true;
if (!m_firstLayout) {
- RenderBox* rootRenderer = document.documentElement() ? document.documentElement()->renderBox() : 0;
- RenderBox* bodyRenderer = rootRenderer && document.body() ? document.body()->renderBox() : 0;
+ RenderBox* rootRenderer = document.documentElement() ? document.documentElement()->renderBox() : nullptr;
+ auto* body = document.bodyOrFrameset();
+ RenderBox* bodyRenderer = rootRenderer && body ? body->renderBox() : nullptr;
if (bodyRenderer && bodyRenderer->stretchesToViewport())
bodyRenderer->setChildNeedsLayout();
else if (rootRenderer && rootRenderer->stretchesToViewport())
@@ -1271,53 +1470,49 @@ void FrameView::layout(bool allowSubtree)
}
layer = root->enclosingLayer();
+ SubtreeLayoutStateMaintainer subtreeLayoutStateMaintainer(m_layoutRoot);
- bool disableLayoutState = false;
- if (subtree) {
- disableLayoutState = root->view().shouldDisableLayoutStateForSubtree(root);
- root->view().pushLayoutState(*root);
- }
- LayoutStateDisabler layoutStateDisabler(disableLayoutState ? &root->view() : 0);
RenderView::RepaintRegionAccumulator repaintRegionAccumulator(&root->view());
ASSERT(m_layoutPhase == InPreLayout);
- m_layoutPhase = InLayout;
+ m_layoutPhase = InRenderTreeLayout;
forceLayoutParentViewIfNeeded();
- ASSERT(m_layoutPhase == InLayout);
-
- root->layout();
-#if ENABLE(IOS_TEXT_AUTOSIZING)
- float minZoomFontSize = frame().settings().minimumZoomFontSize();
- float visWidth = frame().page()->mainFrame().textAutosizingWidth();
- if (minZoomFontSize && visWidth && !root->view().printing()) {
- root->adjustComputedFontSizesOnBlocks(minZoomFontSize, visWidth);
- bool needsLayout = root->needsLayout();
- if (needsLayout)
- root->layout();
- }
+ ASSERT(m_layoutPhase == InRenderTreeLayout);
+#ifndef NDEBUG
+ RenderTreeNeedsLayoutChecker checker(*root);
#endif
+ root->layout();
+ ASSERT(!root->view().renderTreeIsBeingMutatedInternally());
+
#if ENABLE(TEXT_AUTOSIZING)
- if (document.textAutosizer()->processSubtree(root) && root->needsLayout())
- root->layout();
+ if (frame().settings().textAutosizingEnabled() && !root->view().printing()) {
+ float minimumZoomFontSize = frame().settings().minimumZoomFontSize();
+ float textAutosizingWidth = frame().page() ? frame().page()->textAutosizingWidth() : 0;
+ if (int overrideWidth = frame().settings().textAutosizingWindowSizeOverride().width())
+ textAutosizingWidth = overrideWidth;
+
+ LOG(TextAutosizing, "Text Autosizing: minimumZoomFontSize=%.2f textAutosizingWidth=%.2f", minimumZoomFontSize, textAutosizingWidth);
+
+ if (minimumZoomFontSize && textAutosizingWidth) {
+ root->adjustComputedFontSizesOnBlocks(minimumZoomFontSize, textAutosizingWidth);
+ if (root->needsLayout())
+ root->layout();
+ }
+ }
#endif
- ASSERT(m_layoutPhase == InLayout);
-
- if (subtree)
- root->view().popLayoutState(*root);
-
- m_layoutRoot = 0;
-
- // Close block here to end the scope of changeSchedulingEnabled and layoutStateDisabler.
+ ASSERT(m_layoutPhase == InRenderTreeLayout);
+ m_layoutRoot = nullptr;
+ // Close block here to end the scope of changeSchedulingEnabled and SubtreeLayoutStateMaintainer.
}
m_layoutPhase = InViewSizeAdjust;
bool neededFullRepaint = m_needsFullRepaint;
- if (!subtree && !toRenderView(*root).printing())
+ if (!subtree && !downcast<RenderView>(*root).printing())
adjustViewSize();
m_layoutPhase = InPostLayout;
@@ -1328,15 +1523,19 @@ void FrameView::layout(bool allowSubtree)
if (m_needsFullRepaint)
root->view().repaintRootContents();
+ root->view().releaseProtectedRenderWidgets();
+
+ ASSERT(!root->needsLayout());
+
layer->updateLayerPositionsAfterLayout(renderView()->layer(), updateLayerPositionFlags(layer, subtree, m_needsFullRepaint));
-#if USE(ACCELERATED_COMPOSITING)
updateCompositingLayersAfterLayout();
-#endif
-
+
+ m_layoutPhase = InPostLayerPositionsUpdatedAfterLayout;
+
m_layoutCount++;
-#if PLATFORM(MAC) || PLATFORM(WIN) || PLATFORM(GTK) || PLATFORM(EFL)
+#if PLATFORM(COCOA) || PLATFORM(WIN) || PLATFORM(GTK)
if (AXObjectCache* cache = root->document().existingAXObjectCache())
cache->postNotification(root, AXObjectCache::AXLayoutComplete);
#endif
@@ -1349,19 +1548,23 @@ void FrameView::layout(bool allowSubtree)
document.dirtyTouchEventRects();
#endif
- ASSERT(!root->needsLayout());
-
updateCanBlitOnScrollRecursively();
+ handleDeferredScrollUpdateAfterContentSizeChange();
+
+ handleDeferredScrollbarsUpdateAfterDirectionChange();
+
if (document.hasListenerType(Document::OVERFLOWCHANGED_LISTENER))
updateOverflowStatus(layoutWidth() < contentsWidth(), layoutHeight() < contentsHeight());
+ frame().document()->markers().invalidateRectsForAllMarkers();
+
if (!m_postLayoutTasksTimer.isActive()) {
if (!m_inSynchronousPostLayout) {
if (inChildFrameLayoutWithFrameFlattening)
updateWidgetPositions();
else {
- TemporaryChange<bool> inSynchronousPostLayoutChange(m_inSynchronousPostLayout, true);
+ SetForScope<bool> inSynchronousPostLayoutChange(m_inSynchronousPostLayout, true);
performPostLayoutTasks(); // Calls resumeScheduledEvents().
}
}
@@ -1372,37 +1575,33 @@ void FrameView::layout(bool allowSubtree)
// can make us need to update again, and we can get stuck in a nasty cycle unless
// we call it through the timer here.
m_postLayoutTasksTimer.startOneShot(0);
- if (needsLayout())
- layout();
}
+ if (needsLayout())
+ layout();
}
- InspectorInstrumentation::didLayout(cookie, root);
+ InspectorInstrumentation::didLayout(cookie, *root);
+ DebugPageOverlays::didLayout(frame());
--m_nestedLayoutCount;
+}
- if (m_nestedLayoutCount)
- return;
-
- if (Page* page = frame().page())
- page->chrome().client().layoutUpdated(&frame());
+bool FrameView::shouldDeferScrollUpdateAfterContentSizeChange()
+{
+ return (m_layoutPhase < InPostLayout) && (m_layoutPhase != OutsideLayout);
}
RenderBox* FrameView::embeddedContentBox() const
{
-#if ENABLE(SVG)
RenderView* renderView = this->renderView();
if (!renderView)
return nullptr;
RenderObject* firstChild = renderView->firstChild();
- if (!firstChild || !firstChild->isBox())
- return nullptr;
// Curently only embedded SVG documents participate in the size-negotiation logic.
- if (toRenderBox(firstChild)->isSVGRoot())
- return toRenderBox(firstChild);
-#endif
+ if (is<RenderSVGRoot>(firstChild))
+ return downcast<RenderSVGRoot>(firstChild);
return nullptr;
}
@@ -1410,12 +1609,12 @@ RenderBox* FrameView::embeddedContentBox() const
void FrameView::addEmbeddedObjectToUpdate(RenderEmbeddedObject& embeddedObject)
{
if (!m_embeddedObjectsToUpdate)
- m_embeddedObjectsToUpdate = adoptPtr(new ListHashSet<RenderEmbeddedObject*>);
+ m_embeddedObjectsToUpdate = std::make_unique<ListHashSet<RenderEmbeddedObject*>>();
HTMLFrameOwnerElement& element = embeddedObject.frameOwnerElement();
- if (isHTMLObjectElement(element) || isHTMLEmbedElement(element)) {
+ if (is<HTMLObjectElement>(element) || is<HTMLEmbedElement>(element)) {
// Tell the DOM element that it needs a widget update.
- HTMLPlugInImageElement& pluginElement = toHTMLPlugInImageElement(element);
+ HTMLPlugInImageElement& pluginElement = downcast<HTMLPlugInImageElement>(element);
if (!pluginElement.needsCheckForSizeChange())
pluginElement.setNeedsWidgetUpdate(true);
}
@@ -1440,7 +1639,7 @@ String FrameView::mediaType() const
{
// See if we have an override type.
String overrideType = frame().loader().client().overrideMediaType();
- InspectorInstrumentation::applyEmulatedMedia(&frame(), &overrideType);
+ InspectorInstrumentation::applyEmulatedMedia(frame(), overrideType);
if (!overrideType.isNull())
return overrideType;
return m_mediaType;
@@ -1451,7 +1650,7 @@ void FrameView::adjustMediaTypeForPrinting(bool printing)
if (printing) {
if (m_mediaTypeWhenNotPrinting.isNull())
m_mediaTypeWhenNotPrinting = mediaType();
- setMediaType("print");
+ setMediaType("print");
} else {
if (!m_mediaTypeWhenNotPrinting.isNull())
setMediaType(m_mediaTypeWhenNotPrinting);
@@ -1467,7 +1666,7 @@ bool FrameView::useSlowRepaints(bool considerOverlap) const
// m_contentIsOpaque, so don't take the fast path for composited layers
// if they are a platform widget in order to get painting correctness
// for transparent layers. See the comment in WidgetMac::paint.
- if (contentsInCompositedLayer() && !platformWidget())
+ if (usesCompositedScrolling() && !platformWidget())
return mustBeSlow;
bool isOverlapped = m_isOverlapped && considerOverlap;
@@ -1488,25 +1687,53 @@ bool FrameView::useSlowRepaintsIfNotOverlapped() const
void FrameView::updateCanBlitOnScrollRecursively()
{
- for (Frame* frame = m_frame.get(); frame; frame = frame->tree().traverseNext(m_frame.get())) {
+ for (auto* frame = m_frame.ptr(); frame; frame = frame->tree().traverseNext(m_frame.ptr())) {
if (FrameView* view = frame->view())
view->setCanBlitOnScroll(!view->useSlowRepaints());
}
}
-bool FrameView::contentsInCompositedLayer() const
+bool FrameView::usesCompositedScrolling() const
{
-#if USE(ACCELERATED_COMPOSITING)
RenderView* renderView = this->renderView();
if (renderView && renderView->isComposited()) {
GraphicsLayer* layer = renderView->layer()->backing()->graphicsLayer();
if (layer && layer->drawsContent())
return true;
}
+
+ return false;
+}
+
+bool FrameView::usesAsyncScrolling() const
+{
+#if ENABLE(ASYNC_SCROLLING)
+ if (Page* page = frame().page()) {
+ if (ScrollingCoordinator* scrollingCoordinator = page->scrollingCoordinator())
+ return scrollingCoordinator->coordinatesScrollingForFrameView(*this);
+ }
#endif
return false;
}
+bool FrameView::usesMockScrollAnimator() const
+{
+ return Settings::usesMockScrollAnimator();
+}
+
+void FrameView::logMockScrollAnimatorMessage(const String& message) const
+{
+ Document* document = frame().document();
+ if (!document)
+ return;
+ StringBuilder builder;
+ if (frame().isMainFrame())
+ builder.appendLiteral("Main");
+ builder.appendLiteral("FrameView: ");
+ builder.append(message);
+ document->addConsoleMessage(MessageSource::Other, MessageLevel::Debug, builder.toString());
+}
+
void FrameView::setCannotBlitToWindow()
{
m_cannotBlitToWindow = true;
@@ -1518,7 +1745,7 @@ void FrameView::addSlowRepaintObject(RenderElement* o)
bool hadSlowRepaintObjects = hasSlowRepaintObjects();
if (!m_slowRepaintObjects)
- m_slowRepaintObjects = adoptPtr(new HashSet<RenderElement*>);
+ m_slowRepaintObjects = std::make_unique<HashSet<const RenderElement*>>();
m_slowRepaintObjects->add(o);
@@ -1527,7 +1754,7 @@ void FrameView::addSlowRepaintObject(RenderElement* o)
if (Page* page = frame().page()) {
if (ScrollingCoordinator* scrollingCoordinator = page->scrollingCoordinator())
- scrollingCoordinator->frameViewHasSlowRepaintObjectsDidChange(this);
+ scrollingCoordinator->frameViewHasSlowRepaintObjectsDidChange(*this);
}
}
}
@@ -1544,7 +1771,7 @@ void FrameView::removeSlowRepaintObject(RenderElement* o)
if (Page* page = frame().page()) {
if (ScrollingCoordinator* scrollingCoordinator = page->scrollingCoordinator())
- scrollingCoordinator->frameViewHasSlowRepaintObjectsDidChange(this);
+ scrollingCoordinator->frameViewHasSlowRepaintObjectsDidChange(*this);
}
}
}
@@ -1552,7 +1779,7 @@ void FrameView::removeSlowRepaintObject(RenderElement* o)
void FrameView::addViewportConstrainedObject(RenderElement* object)
{
if (!m_viewportConstrainedObjects)
- m_viewportConstrainedObjects = adoptPtr(new ViewportConstrainedObjectSet);
+ m_viewportConstrainedObjects = std::make_unique<ViewportConstrainedObjectSet>();
if (!m_viewportConstrainedObjects->contains(object)) {
m_viewportConstrainedObjects->add(object);
@@ -1561,7 +1788,7 @@ void FrameView::addViewportConstrainedObject(RenderElement* object)
if (Page* page = frame().page()) {
if (ScrollingCoordinator* scrollingCoordinator = page->scrollingCoordinator())
- scrollingCoordinator->frameViewFixedObjectsDidChange(this);
+ scrollingCoordinator->frameViewFixedObjectsDidChange(*this);
}
}
}
@@ -1571,7 +1798,7 @@ void FrameView::removeViewportConstrainedObject(RenderElement* object)
if (m_viewportConstrainedObjects && m_viewportConstrainedObjects->remove(object)) {
if (Page* page = frame().page()) {
if (ScrollingCoordinator* scrollingCoordinator = page->scrollingCoordinator())
- scrollingCoordinator->frameViewFixedObjectsDidChange(this);
+ scrollingCoordinator->frameViewFixedObjectsDidChange(*this);
}
// FIXME: In addFixedObject() we only call this if there's a platform widget,
@@ -1580,20 +1807,214 @@ void FrameView::removeViewportConstrainedObject(RenderElement* object)
}
}
+// visualViewport and layoutViewport are both in content coordinates (unzoomed).
+LayoutPoint FrameView::computeLayoutViewportOrigin(const LayoutRect& visualViewport, const LayoutPoint& stableLayoutViewportOriginMin, const LayoutPoint& stableLayoutViewportOriginMax, const LayoutRect& layoutViewport, ScrollBehaviorForFixedElements fixedBehavior)
+{
+ LayoutPoint layoutViewportOrigin = layoutViewport.location();
+ bool allowRubberBanding = fixedBehavior == StickToViewportBounds;
+
+ if (visualViewport.width() > layoutViewport.width())
+ layoutViewportOrigin.setX(visualViewport.x());
+ else {
+ bool rubberbandingAtLeft = allowRubberBanding && visualViewport.x() < stableLayoutViewportOriginMin.x();
+ bool rubberbandingAtRight = allowRubberBanding && (visualViewport.maxX() - layoutViewport.width()) > stableLayoutViewportOriginMax.x();
+
+ if (visualViewport.x() < layoutViewport.x() || rubberbandingAtLeft)
+ layoutViewportOrigin.setX(visualViewport.x());
+
+ if (visualViewport.maxX() > layoutViewport.maxX() || rubberbandingAtRight)
+ layoutViewportOrigin.setX(visualViewport.maxX() - layoutViewport.width());
+
+ if (!rubberbandingAtLeft && layoutViewportOrigin.x() < stableLayoutViewportOriginMin.x())
+ layoutViewportOrigin.setX(stableLayoutViewportOriginMin.x());
+
+ if (!rubberbandingAtRight && layoutViewportOrigin.x() > stableLayoutViewportOriginMax.x())
+ layoutViewportOrigin.setX(stableLayoutViewportOriginMax.x());
+ }
+
+ if (visualViewport.height() > layoutViewport.height())
+ layoutViewportOrigin.setY(visualViewport.y());
+ else {
+ bool rubberbandingAtTop = allowRubberBanding && visualViewport.y() < stableLayoutViewportOriginMin.y();
+ bool rubberbandingAtBottom = allowRubberBanding && (visualViewport.maxY() - layoutViewport.height()) > stableLayoutViewportOriginMax.y();
+
+ if (visualViewport.y() < layoutViewport.y() || rubberbandingAtTop)
+ layoutViewportOrigin.setY(visualViewport.y());
+
+ if (visualViewport.maxY() > layoutViewport.maxY() || rubberbandingAtBottom)
+ layoutViewportOrigin.setY(visualViewport.maxY() - layoutViewport.height());
+
+ if (!rubberbandingAtTop && layoutViewportOrigin.y() < stableLayoutViewportOriginMin.y())
+ layoutViewportOrigin.setY(stableLayoutViewportOriginMin.y());
+
+ if (!rubberbandingAtBottom && layoutViewportOrigin.y() > stableLayoutViewportOriginMax.y())
+ layoutViewportOrigin.setY(stableLayoutViewportOriginMax.y());
+ }
+
+ return layoutViewportOrigin;
+}
+
+void FrameView::setBaseLayoutViewportOrigin(LayoutPoint origin, TriggerLayoutOrNot layoutTriggering)
+{
+ ASSERT(frame().settings().visualViewportEnabled());
+
+ if (origin == m_layoutViewportOrigin)
+ return;
+
+ m_layoutViewportOrigin = origin;
+ if (layoutTriggering == TriggerLayoutOrNot::Yes)
+ setViewportConstrainedObjectsNeedLayout();
+
+ if (TiledBacking* tiledBacking = this->tiledBacking()) {
+ FloatRect layoutViewport = layoutViewportRect();
+ layoutViewport.moveBy(unscaledScrollOrigin()); // tiledBacking deals in top-left relative coordinates.
+ tiledBacking->setLayoutViewportRect(layoutViewport);
+ }
+}
+
+void FrameView::setLayoutViewportOverrideRect(std::optional<LayoutRect> rect)
+{
+ if (rect == m_layoutViewportOverrideRect)
+ return;
+
+ LayoutRect oldRect = layoutViewportRect();
+ m_layoutViewportOverrideRect = rect;
+
+ LOG_WITH_STREAM(Scrolling, stream << "\nFrameView " << this << " setLayoutViewportOverrideRect() - changing layout viewport from " << oldRect << " to " << m_layoutViewportOverrideRect.value());
+
+ if (oldRect != layoutViewportRect())
+ setViewportConstrainedObjectsNeedLayout();
+}
+
+LayoutSize FrameView::baseLayoutViewportSize() const
+{
+ return renderView() ? renderView()->size() : size();
+}
+
+void FrameView::updateLayoutViewport()
+{
+ if (!frame().settings().visualViewportEnabled())
+ return;
+
+ // Don't update the layout viewport if we're in the middle of adjusting scrollbars. We'll get another call
+ // as a post-layout task.
+ if (m_layoutPhase == InViewSizeAdjust)
+ return;
+
+ if (m_layoutViewportOverrideRect) {
+ LOG_WITH_STREAM(Scrolling, stream << "\nFrameView " << this << " updateLayoutViewport() - has layoutViewportOverrideRect" << m_layoutViewportOverrideRect.value());
+ return;
+ }
+
+ LayoutRect layoutViewport = layoutViewportRect();
+
+ LOG_WITH_STREAM(Scrolling, stream << "\nFrameView " << this << " updateLayoutViewport() totalContentSize " << totalContentsSize() << " unscaledDocumentRect " << (renderView() ? renderView()->unscaledDocumentRect() : IntRect()) << " header height " << headerHeight() << " footer height " << footerHeight() << " fixed behavior " << scrollBehaviorForFixedElements());
+ LOG_WITH_STREAM(Scrolling, stream << "layoutViewport: " << layoutViewport);
+ LOG_WITH_STREAM(Scrolling, stream << "visualViewport: " << visualViewportRect());
+ LOG_WITH_STREAM(Scrolling, stream << "scroll positions: min: " << unscaledMinimumScrollPosition() << " max: "<< unscaledMaximumScrollPosition());
+
+ LayoutPoint newLayoutViewportOrigin = computeLayoutViewportOrigin(visualViewportRect(), minStableLayoutViewportOrigin(), maxStableLayoutViewportOrigin(), layoutViewport, scrollBehaviorForFixedElements());
+ if (newLayoutViewportOrigin != m_layoutViewportOrigin) {
+ setBaseLayoutViewportOrigin(newLayoutViewportOrigin);
+ LOG_WITH_STREAM(Scrolling, stream << "layoutViewport changed to " << layoutViewportRect());
+ }
+}
+
+LayoutPoint FrameView::minStableLayoutViewportOrigin() const
+{
+ return unscaledMinimumScrollPosition();
+}
+
+LayoutPoint FrameView::maxStableLayoutViewportOrigin() const
+{
+ LayoutPoint maxPosition = unscaledMaximumScrollPosition();
+ maxPosition = (maxPosition - LayoutSize(0, headerHeight() + footerHeight())).expandedTo({ });
+ return maxPosition;
+}
+
+IntPoint FrameView::unscaledScrollOrigin() const
+{
+ if (RenderView* renderView = this->renderView())
+ return -renderView->unscaledDocumentRect().location(); // Akin to code in adjustViewSize().
+
+ return { };
+}
+
+LayoutRect FrameView::layoutViewportRect() const
+{
+ if (m_layoutViewportOverrideRect)
+ return m_layoutViewportOverrideRect.value();
+
+ // Size of initial containing block, anchored at scroll position, in document coordinates (unchanged by scale factor).
+ return LayoutRect(m_layoutViewportOrigin, renderView() ? renderView()->size() : size());
+}
+
+// visibleContentRect is in the bounds of the scroll view content. That consists of an
+// optional header, the document, and an optional footer. Only the document is scaled,
+// so we have to compute the visible part of the document in unscaled document coordinates.
+// On iOS, pageScaleFactor is always 1 here, and we never have headers and footers.
+LayoutRect FrameView::visibleDocumentRect(const FloatRect& visibleContentRect, float headerHeight, float footerHeight, const FloatSize& totalContentsSize, float pageScaleFactor)
+{
+ float contentsHeight = totalContentsSize.height() - headerHeight - footerHeight;
+
+ float rubberBandTop = std::min<float>(visibleContentRect.y(), 0);
+ float visibleScaledDocumentTop = std::max<float>(visibleContentRect.y() - headerHeight, 0) + rubberBandTop;
+
+ float rubberBandBottom = std::min<float>((totalContentsSize.height() - visibleContentRect.y()) - visibleContentRect.height(), 0);
+ float visibleScaledDocumentBottom = std::min<float>(visibleContentRect.maxY() - headerHeight, contentsHeight) - rubberBandBottom;
+
+ FloatRect visibleDocumentRect = visibleContentRect;
+ visibleDocumentRect.setY(visibleScaledDocumentTop);
+ visibleDocumentRect.setHeight(std::max<float>(visibleScaledDocumentBottom - visibleScaledDocumentTop, 0));
+ visibleDocumentRect.scale(1 / pageScaleFactor);
+
+ return LayoutRect(visibleDocumentRect);
+}
+
+LayoutRect FrameView::visualViewportRect() const
+{
+ FloatRect visibleContentRect = this->visibleContentRect(LegacyIOSDocumentVisibleRect);
+ return visibleDocumentRect(visibleContentRect, headerHeight(), footerHeight(), totalContentsSize(), frameScaleFactor());
+}
+
LayoutRect FrameView::viewportConstrainedVisibleContentRect() const
{
+ ASSERT(!frame().settings().visualViewportEnabled());
+
#if PLATFORM(IOS)
if (useCustomFixedPositionLayoutRect())
return customFixedPositionLayoutRect();
#endif
LayoutRect viewportRect = visibleContentRect();
- viewportRect.setLocation(toPoint(scrollOffsetForFixedPosition()));
+
+ viewportRect.setLocation(scrollPositionForFixedPosition());
return viewportRect;
}
-IntSize FrameView::scrollOffsetForFixedPosition(const IntRect& visibleContentRect, const IntSize& totalContentsSize, const IntPoint& scrollPosition, const IntPoint& scrollOrigin, float frameScaleFactor, bool fixedElementsLayoutRelativeToFrame, ScrollBehaviorForFixedElements behaviorForFixed, int headerHeight, int footerHeight)
+LayoutRect FrameView::rectForFixedPositionLayout() const
{
- IntPoint position;
+ if (frame().settings().visualViewportEnabled())
+ return layoutViewportRect();
+
+ return viewportConstrainedVisibleContentRect();
+}
+
+float FrameView::frameScaleFactor() const
+{
+ return frame().frameScaleFactor();
+}
+
+LayoutPoint FrameView::scrollPositionForFixedPosition() const
+{
+ if (frame().settings().visualViewportEnabled())
+ return layoutViewportRect().location();
+
+ return scrollPositionForFixedPosition(visibleContentRect(), totalContentsSize(), scrollPosition(), scrollOrigin(), frameScaleFactor(), fixedElementsLayoutRelativeToFrame(), scrollBehaviorForFixedElements(), headerHeight(), footerHeight());
+}
+
+LayoutPoint FrameView::scrollPositionForFixedPosition(const LayoutRect& visibleContentRect, const LayoutSize& totalContentsSize, const LayoutPoint& scrollPosition, const LayoutPoint& scrollOrigin, float frameScaleFactor, bool fixedElementsLayoutRelativeToFrame, ScrollBehaviorForFixedElements behaviorForFixed, int headerHeight, int footerHeight)
+{
+ LayoutPoint position;
if (behaviorForFixed == StickToDocumentBounds)
position = ScrollableArea::constrainScrollPositionForOverhang(visibleContentRect, totalContentsSize, scrollPosition, scrollOrigin, headerHeight, footerHeight);
else {
@@ -1601,28 +2022,110 @@ IntSize FrameView::scrollOffsetForFixedPosition(const IntRect& visibleContentRec
position.setY(position.y() - headerHeight);
}
- IntSize maxSize = totalContentsSize - visibleContentRect.size();
+ LayoutSize maxSize = totalContentsSize - visibleContentRect.size();
float dragFactorX = (fixedElementsLayoutRelativeToFrame || !maxSize.width()) ? 1 : (totalContentsSize.width() - visibleContentRect.width() * frameScaleFactor) / maxSize.width();
float dragFactorY = (fixedElementsLayoutRelativeToFrame || !maxSize.height()) ? 1 : (totalContentsSize.height() - visibleContentRect.height() * frameScaleFactor) / maxSize.height();
- return IntSize(position.x() * dragFactorX / frameScaleFactor, position.y() * dragFactorY / frameScaleFactor);
+ return LayoutPoint(position.x() * dragFactorX / frameScaleFactor, position.y() * dragFactorY / frameScaleFactor);
+}
+
+float FrameView::yPositionForInsetClipLayer(const FloatPoint& scrollPosition, float topContentInset)
+{
+ if (!topContentInset)
+ return 0;
+
+ // The insetClipLayer should not move for negative scroll values.
+ float scrollY = std::max<float>(0, scrollPosition.y());
+
+ if (scrollY >= topContentInset)
+ return 0;
+
+ return topContentInset - scrollY;
}
-IntSize FrameView::scrollOffsetForFixedPosition() const
+float FrameView::yPositionForHeaderLayer(const FloatPoint& scrollPosition, float topContentInset)
+{
+ if (!topContentInset)
+ return 0;
+
+ float scrollY = std::max<float>(0, scrollPosition.y());
+
+ if (scrollY >= topContentInset)
+ return topContentInset;
+
+ return scrollY;
+}
+
+float FrameView::yPositionForFooterLayer(const FloatPoint& scrollPosition, float topContentInset, float totalContentsHeight, float footerHeight)
+{
+ return yPositionForHeaderLayer(scrollPosition, topContentInset) + totalContentsHeight - footerHeight;
+}
+
+FloatPoint FrameView::positionForRootContentLayer(const FloatPoint& scrollPosition, const FloatPoint& scrollOrigin, float topContentInset, float headerHeight)
+{
+ return FloatPoint(0, yPositionForHeaderLayer(scrollPosition, topContentInset) + headerHeight) - toFloatSize(scrollOrigin);
+}
+
+FloatPoint FrameView::positionForRootContentLayer() const
+{
+ return positionForRootContentLayer(scrollPosition(), scrollOrigin(), topContentInset(), headerHeight());
+}
+
+#if PLATFORM(IOS)
+LayoutRect FrameView::rectForViewportConstrainedObjects(const LayoutRect& visibleContentRect, const LayoutSize& totalContentsSize, float frameScaleFactor, bool fixedElementsLayoutRelativeToFrame, ScrollBehaviorForFixedElements scrollBehavior)
+{
+ if (fixedElementsLayoutRelativeToFrame)
+ return visibleContentRect;
+
+ if (totalContentsSize.isEmpty())
+ return visibleContentRect;
+
+ // We impose an lower limit on the size (so an upper limit on the scale) of
+ // the rect used to position fixed objects so that they don't crowd into the
+ // center of the screen at larger scales.
+ const LayoutUnit maxContentWidthForZoomThreshold = LayoutUnit::fromPixel(1024);
+ float zoomedOutScale = frameScaleFactor * visibleContentRect.width() / std::min(maxContentWidthForZoomThreshold, totalContentsSize.width());
+ float constraintThresholdScale = 1.5 * zoomedOutScale;
+ float maxPostionedObjectsRectScale = std::min(frameScaleFactor, constraintThresholdScale);
+
+ LayoutRect viewportConstrainedObjectsRect = visibleContentRect;
+
+ if (frameScaleFactor > constraintThresholdScale) {
+ FloatRect contentRect(FloatPoint(), totalContentsSize);
+ FloatRect viewportRect = visibleContentRect;
+
+ // Scale the rect up from a point that is relative to its position in the viewport.
+ FloatSize sizeDelta = contentRect.size() - viewportRect.size();
+
+ FloatPoint scaleOrigin;
+ scaleOrigin.setX(contentRect.x() + sizeDelta.width() > 0 ? contentRect.width() * (viewportRect.x() - contentRect.x()) / sizeDelta.width() : 0);
+ scaleOrigin.setY(contentRect.y() + sizeDelta.height() > 0 ? contentRect.height() * (viewportRect.y() - contentRect.y()) / sizeDelta.height() : 0);
+
+ AffineTransform rescaleTransform = AffineTransform::translation(scaleOrigin.x(), scaleOrigin.y());
+ rescaleTransform.scale(frameScaleFactor / maxPostionedObjectsRectScale, frameScaleFactor / maxPostionedObjectsRectScale);
+ rescaleTransform = CGAffineTransformTranslate(rescaleTransform, -scaleOrigin.x(), -scaleOrigin.y());
+
+ viewportConstrainedObjectsRect = enclosingLayoutRect(rescaleTransform.mapRect(visibleContentRect));
+ }
+
+ if (scrollBehavior == StickToDocumentBounds) {
+ LayoutRect documentBounds(LayoutPoint(), totalContentsSize);
+ viewportConstrainedObjectsRect.intersect(documentBounds);
+ }
+
+ return viewportConstrainedObjectsRect;
+}
+
+LayoutRect FrameView::viewportConstrainedObjectsRect() const
{
- IntRect visibleContentRect = this->visibleContentRect();
- IntSize totalContentsSize = this->totalContentsSize();
- IntPoint scrollPosition = this->scrollPosition();
- IntPoint scrollOrigin = this->scrollOrigin();
- float frameScaleFactor = frame().frameScaleFactor();
- ScrollBehaviorForFixedElements behaviorForFixed = scrollBehaviorForFixedElements();
- return scrollOffsetForFixedPosition(visibleContentRect, totalContentsSize, scrollPosition, scrollOrigin, frameScaleFactor, fixedElementsLayoutRelativeToFrame(), behaviorForFixed, headerHeight(), footerHeight());
+ return rectForViewportConstrainedObjects(visibleContentRect(), totalContentsSize(), frame().frameScaleFactor(), fixedElementsLayoutRelativeToFrame(), scrollBehaviorForFixedElements());
}
+#endif
-IntPoint FrameView::minimumScrollPosition() const
+ScrollPosition FrameView::minimumScrollPosition() const
{
- IntPoint minimumPosition(ScrollView::minimumScrollPosition());
+ ScrollPosition minimumPosition = ScrollView::minimumScrollPosition();
if (frame().isMainFrame() && m_scrollPinningBehavior == PinToBottom)
minimumPosition.setY(maximumScrollPosition().y());
@@ -1630,16 +2133,66 @@ IntPoint FrameView::minimumScrollPosition() const
return minimumPosition;
}
-IntPoint FrameView::maximumScrollPosition() const
+ScrollPosition FrameView::maximumScrollPosition() const
{
- IntPoint maximumOffset(contentsWidth() - visibleWidth() - scrollOrigin().x(), totalContentsSize().height() - visibleHeight() - scrollOrigin().y());
-
- maximumOffset.clampNegativeToZero();
+ ScrollPosition maximumPosition = ScrollView::maximumScrollPosition();
if (frame().isMainFrame() && m_scrollPinningBehavior == PinToTop)
- maximumOffset.setY(minimumScrollPosition().y());
+ maximumPosition.setY(minimumScrollPosition().y());
- return maximumOffset;
+ return maximumPosition;
+}
+
+ScrollPosition FrameView::unscaledMinimumScrollPosition() const
+{
+ if (RenderView* renderView = this->renderView()) {
+ IntRect unscaledDocumentRect = renderView->unscaledDocumentRect();
+ ScrollPosition minimumPosition = unscaledDocumentRect.location();
+
+ if (frame().isMainFrame() && m_scrollPinningBehavior == PinToBottom)
+ minimumPosition.setY(unscaledMaximumScrollPosition().y());
+
+ return minimumPosition;
+ }
+
+ return minimumScrollPosition();
+}
+
+ScrollPosition FrameView::unscaledMaximumScrollPosition() const
+{
+ if (RenderView* renderView = this->renderView()) {
+ IntRect unscaledDocumentRect = renderView->unscaledDocumentRect();
+ unscaledDocumentRect.expand(0, headerHeight() + footerHeight());
+ ScrollPosition maximumPosition = ScrollPosition(unscaledDocumentRect.maxXMaxYCorner() - visibleSize()).expandedTo({ 0, 0 });
+
+ if (frame().isMainFrame() && m_scrollPinningBehavior == PinToTop)
+ maximumPosition.setY(unscaledMinimumScrollPosition().y());
+
+ return maximumPosition;
+ }
+
+ return maximumScrollPosition();
+}
+
+void FrameView::viewportContentsChanged()
+{
+ if (!frame().view()) {
+ // The frame is being destroyed.
+ return;
+ }
+
+ if (auto* page = frame().page())
+ page->updateValidationBubbleStateIfNeeded();
+
+ // When the viewport contents changes (scroll, resize, style recalc, layout, ...),
+ // check if we should resume animated images or unthrottle DOM timers.
+ applyRecursivelyWithVisibleRect([] (FrameView& frameView, const IntRect& visibleRect) {
+ frameView.resumeVisibleImageAnimations(visibleRect);
+ frameView.updateScriptedAnimationsAndTimersThrottlingState(visibleRect);
+
+ if (auto* renderView = frameView.frame().contentRenderer())
+ renderView->updateVisibleViewportRect(visibleRect);
+ });
}
bool FrameView::fixedElementsLayoutRelativeToFrame() const
@@ -1666,42 +2219,38 @@ bool FrameView::shouldSetCursor() const
bool FrameView::scrollContentsFastPath(const IntSize& scrollDelta, const IntRect& rectToScroll, const IntRect& clipRect)
{
if (!m_viewportConstrainedObjects || m_viewportConstrainedObjects->isEmpty()) {
- hostWindow()->scroll(scrollDelta, rectToScroll, clipRect);
+ frame().page()->chrome().scroll(scrollDelta, rectToScroll, clipRect);
return true;
}
- const bool isCompositedContentLayer = contentsInCompositedLayer();
+ bool isCompositedContentLayer = usesCompositedScrolling();
// Get the rects of the fixed objects visible in the rectToScroll
Region regionToUpdate;
for (auto& renderer : *m_viewportConstrainedObjects) {
if (!renderer->style().hasViewportConstrainedPosition())
continue;
-#if USE(ACCELERATED_COMPOSITING)
if (renderer->isComposited())
continue;
-#endif
// Fixed items should always have layers.
ASSERT(renderer->hasLayer());
- RenderLayer* layer = toRenderBoxModelObject(renderer)->layer();
+ RenderLayer* layer = downcast<RenderBoxModelObject>(*renderer).layer();
-#if USE(ACCELERATED_COMPOSITING)
if (layer->viewportConstrainedNotCompositedReason() == RenderLayer::NotCompositedForBoundsOutOfView
|| layer->viewportConstrainedNotCompositedReason() == RenderLayer::NotCompositedForNoVisibleContent) {
// Don't invalidate for invisible fixed layers.
continue;
}
-#endif
-#if ENABLE(CSS_FILTERS)
if (layer->hasAncestorWithFilterOutsets()) {
// If the fixed layer has a blur/drop-shadow filter applied on at least one of its parents, we cannot
// scroll using the fast path, otherwise the outsets of the filter will be moved around the page.
return false;
}
-#endif
- IntRect updateRect = pixelSnappedIntRect(layer->repaintRectIncludingNonCompositingDescendants());
+
+ // FIXME: use pixel snapping instead of enclosing when ScrollView has finished transitioning from IntRect to Float/LayoutRect.
+ IntRect updateRect = enclosingIntRect(layer->repaintRectIncludingNonCompositingDescendants());
updateRect = contentsToRootView(updateRect);
if (!isCompositedContentLayer && clipsRepaints())
updateRect.intersect(rectToScroll);
@@ -1710,27 +2259,22 @@ bool FrameView::scrollContentsFastPath(const IntSize& scrollDelta, const IntRect
}
// 1) scroll
- hostWindow()->scroll(scrollDelta, rectToScroll, clipRect);
+ frame().page()->chrome().scroll(scrollDelta, rectToScroll, clipRect);
// 2) update the area of fixed objects that has been invalidated
- Vector<IntRect> subRectsToUpdate = regionToUpdate.rects();
- size_t viewportConstrainedObjectsCount = subRectsToUpdate.size();
- for (size_t i = 0; i < viewportConstrainedObjectsCount; ++i) {
- IntRect updateRect = subRectsToUpdate[i];
+ for (auto& updateRect : regionToUpdate.rects()) {
IntRect scrolledRect = updateRect;
scrolledRect.move(scrollDelta);
updateRect.unite(scrolledRect);
-#if USE(ACCELERATED_COMPOSITING)
if (isCompositedContentLayer) {
updateRect = rootViewToContents(updateRect);
ASSERT(renderView());
renderView()->layer()->setBackingNeedsRepaintInRect(updateRect);
continue;
}
-#endif
if (clipsRepaints())
updateRect.intersect(rectToScroll);
- hostWindow()->invalidateContentsAndRootView(updateRect, false);
+ frame().page()->chrome().invalidateContentsAndRootView(updateRect);
}
return true;
@@ -1738,31 +2282,16 @@ bool FrameView::scrollContentsFastPath(const IntSize& scrollDelta, const IntRect
void FrameView::scrollContentsSlowPath(const IntRect& updateRect)
{
-#if USE(ACCELERATED_COMPOSITING)
- if (contentsInCompositedLayer()) {
- // FIXME: respect paintsEntireContents()?
- IntRect updateRect = visibleContentRect(LegacyIOSDocumentVisibleRect);
-
- // Make sure to "apply" the scale factor here since we're converting from frame view
- // coordinates to layer backing coordinates.
- updateRect.scale(1 / frame().frameScaleFactor());
-
- ASSERT(renderView());
- renderView()->layer()->setBackingNeedsRepaintInRect(updateRect);
- }
-
repaintSlowRepaintObjects();
- if (RenderWidget* frameRenderer = frame().ownerRenderer()) {
- if (isEnclosedInCompositingLayer()) {
- LayoutRect rect(frameRenderer->borderLeft() + frameRenderer->paddingLeft(),
- frameRenderer->borderTop() + frameRenderer->paddingTop(),
- visibleWidth(), visibleHeight());
+ if (!usesCompositedScrolling() && isEnclosedInCompositingLayer()) {
+ if (RenderWidget* frameRenderer = frame().ownerRenderer()) {
+ LayoutRect rect(frameRenderer->borderLeft() + frameRenderer->paddingLeft(), frameRenderer->borderTop() + frameRenderer->paddingTop(),
+ visibleWidth(), visibleHeight());
frameRenderer->repaintRectangle(rect);
return;
}
}
-#endif
ScrollView::scrollContentsSlowPath(updateRect);
}
@@ -1775,7 +2304,7 @@ void FrameView::repaintSlowRepaintObjects()
// Renderers with fixed backgrounds may be in compositing layers, so we need to explicitly
// repaint them after scrolling.
for (auto& renderer : *m_slowRepaintObjects)
- renderer->repaint();
+ renderer->repaintSlowRepaintObject();
}
// Note that this gets called at painting time.
@@ -1786,45 +2315,6 @@ void FrameView::setIsOverlapped(bool isOverlapped)
m_isOverlapped = isOverlapped;
updateCanBlitOnScrollRecursively();
-
-#if USE(ACCELERATED_COMPOSITING)
- if (hasCompositedContentIncludingDescendants()) {
- // Overlap can affect compositing tests, so if it changes, we need to trigger
- // a layer update in the parent document.
- if (Frame* parentFrame = frame().tree().parent()) {
- if (RenderView* parentView = parentFrame->contentRenderer()) {
- RenderLayerCompositor& compositor = parentView->compositor();
- compositor.setCompositingLayersNeedRebuild();
- compositor.scheduleCompositingLayerUpdate();
- }
- }
-
- if (RenderLayerCompositor::allowsIndependentlyCompositedFrames(this)) {
- // We also need to trigger reevaluation for this and all descendant frames,
- // since a frame uses compositing if any ancestor is compositing.
- for (Frame* frame = m_frame.get(); frame; frame = frame->tree().traverseNext(m_frame.get())) {
- if (RenderView* view = frame->contentRenderer()) {
- RenderLayerCompositor& compositor = view->compositor();
- compositor.setCompositingLayersNeedRebuild();
- compositor.scheduleCompositingLayerUpdate();
- }
- }
- }
- }
-#endif
-}
-
-bool FrameView::isOverlappedIncludingAncestors() const
-{
- if (isOverlapped())
- return true;
-
- if (FrameView* parentView = parentFrameView()) {
- if (parentView->isOverlapped())
- return true;
- }
-
- return false;
}
void FrameView::setContentIsOpaque(bool contentIsOpaque)
@@ -1847,8 +2337,10 @@ bool FrameView::scrollToFragment(const URL& url)
// OTOH If CSS target was set previously, we want to set it to 0, recalc
// and possibly repaint because :target pseudo class may have been
// set (see bug 11321).
- if (!url.hasFragmentIdentifier() && !frame().document()->cssTarget())
+ if (!url.hasFragmentIdentifier()) {
+ frame().document()->setCSSTarget(nullptr);
return false;
+ }
String fragmentIdentifier = url.fragmentIdentifier();
if (scrollToAnchor(fragmentIdentifier))
@@ -1864,43 +2356,51 @@ bool FrameView::scrollToFragment(const URL& url)
bool FrameView::scrollToAnchor(const String& name)
{
ASSERT(frame().document());
+ auto& document = *frame().document();
- if (!frame().document()->haveStylesheetsLoaded()) {
- frame().document()->setGotoAnchorNeededAfterStylesheetsLoad(true);
+ if (!document.haveStylesheetsLoaded()) {
+ document.setGotoAnchorNeededAfterStylesheetsLoad(true);
return false;
}
- frame().document()->setGotoAnchorNeededAfterStylesheetsLoad(false);
+ document.setGotoAnchorNeededAfterStylesheetsLoad(false);
- Element* anchorElement = frame().document()->findAnchor(name);
+ Element* anchorElement = document.findAnchor(name);
// Setting to null will clear the current target.
- frame().document()->setCSSTarget(anchorElement);
+ document.setCSSTarget(anchorElement);
-#if ENABLE(SVG)
- if (frame().document()->isSVGDocument()) {
- if (SVGSVGElement* svg = toSVGDocument(frame().document())->rootElement()) {
- svg->setupInitialView(name, anchorElement);
+ if (is<SVGDocument>(document)) {
+ if (auto* rootElement = SVGDocument::rootElement(document)) {
+ rootElement->scrollToAnchor(name, anchorElement);
if (!anchorElement)
return true;
}
}
-#endif
-
+
// Implement the rule that "" and "top" both mean top of page as in other browsers.
- if (!anchorElement && !(name.isEmpty() || equalIgnoringCase(name, "top")))
+ if (!anchorElement && !(name.isEmpty() || equalLettersIgnoringASCIICase(name, "top")))
return false;
- maintainScrollPositionAtAnchor(anchorElement ? static_cast<Node*>(anchorElement) : frame().document());
+ ContainerNode* scrollPositionAnchor = anchorElement;
+ if (!scrollPositionAnchor)
+ scrollPositionAnchor = frame().document();
+ maintainScrollPositionAtAnchor(scrollPositionAnchor);
// If the anchor accepts keyboard focus, move focus there to aid users relying on keyboard navigation.
- if (anchorElement && anchorElement->isFocusable())
- frame().document()->setFocusedElement(anchorElement);
+ if (anchorElement) {
+ if (anchorElement->isFocusable())
+ document.setFocusedElement(anchorElement);
+ else {
+ document.setFocusedElement(nullptr);
+ document.setFocusNavigationStartingNode(anchorElement);
+ }
+ }
return true;
}
-void FrameView::maintainScrollPositionAtAnchor(Node* anchorNode)
+void FrameView::maintainScrollPositionAtAnchor(ContainerNode* anchorNode)
{
m_maintainScrollPositionAnchor = anchorNode;
if (!m_maintainScrollPositionAnchor)
@@ -1917,33 +2417,43 @@ void FrameView::maintainScrollPositionAtAnchor(Node* anchorNode)
scrollToAnchor();
}
-void FrameView::scrollElementToRect(Element* element, const IntRect& rect)
+void FrameView::scrollElementToRect(const Element& element, const IntRect& rect)
{
frame().document()->updateLayoutIgnorePendingStylesheets();
- LayoutRect bounds = element->boundingBox();
+ LayoutRect bounds;
+ if (RenderElement* renderer = element.renderer())
+ bounds = renderer->absoluteAnchorRect();
int centeringOffsetX = (rect.width() - bounds.width()) / 2;
int centeringOffsetY = (rect.height() - bounds.height()) / 2;
setScrollPosition(IntPoint(bounds.x() - centeringOffsetX - rect.x(), bounds.y() - centeringOffsetY - rect.y()));
}
-void FrameView::setScrollPosition(const IntPoint& scrollPoint)
+void FrameView::setScrollPosition(const ScrollPosition& scrollPosition)
{
- TemporaryChange<bool> changeInProgrammaticScroll(m_inProgrammaticScroll, true);
- m_maintainScrollPositionAnchor = 0;
- ScrollView::setScrollPosition(scrollPoint);
+ SetForScope<bool> changeInProgrammaticScroll(m_inProgrammaticScroll, true);
+ m_maintainScrollPositionAnchor = nullptr;
+ Page* page = frame().page();
+ if (page && page->expectsWheelEventTriggers())
+ scrollAnimator().setWheelEventTestTrigger(page->testTrigger());
+ ScrollView::setScrollPosition(scrollPosition);
+}
+
+void FrameView::contentsResized()
+{
+ // For non-delegated scrolling, updateTiledBackingAdaptiveSizing() is called via addedOrRemovedScrollbar() which occurs less often.
+ if (delegatesScrolling())
+ updateTiledBackingAdaptiveSizing();
}
void FrameView::delegatesScrollingDidChange()
{
-#if USE(ACCELERATED_COMPOSITING)
// When we switch to delgatesScrolling mode, we should destroy the scrolling/clipping layers in RenderLayerCompositor.
if (hasCompositedContent())
clearBackingStores();
-#endif
}
-#if USE(TILED_BACKING_STORE)
+#if USE(COORDINATED_GRAPHICS)
void FrameView::setFixedVisibleContentRect(const IntRect& visibleContentRect)
{
bool visibleContentSizeDidChange = false;
@@ -1954,20 +2464,21 @@ void FrameView::setFixedVisibleContentRect(const IntRect& visibleContentRect)
visibleContentSizeDidChange = true;
}
- IntSize offset = scrollOffset();
+ IntPoint oldPosition = scrollPosition();
ScrollView::setFixedVisibleContentRect(visibleContentRect);
- if (offset != scrollOffset()) {
+ IntPoint newPosition = scrollPosition();
+ if (oldPosition != newPosition) {
updateLayerPositionsAfterScrolling();
- if (frame().page()->settings().acceleratedCompositingForFixedPositionEnabled())
+ if (frame().settings().acceleratedCompositingForFixedPositionEnabled())
updateCompositingLayersAfterScrolling();
- scrollAnimator()->setCurrentPosition(scrollPosition());
- scrollPositionChanged();
+ scrollAnimator().setCurrentPosition(newPosition);
+ scrollPositionChanged(oldPosition, newPosition);
}
if (visibleContentSizeDidChange) {
// Update the scroll-bars to calculate new page-step size.
- updateScrollbars(scrollOffset());
+ updateScrollbars(scrollPosition());
}
- frame().loader().client().didChangeScrollOffset();
+ didChangeScrollOffset();
}
#endif
@@ -1980,25 +2491,92 @@ void FrameView::setViewportConstrainedObjectsNeedLayout()
renderer->setNeedsLayout();
}
-void FrameView::scrollPositionChangedViaPlatformWidget()
+void FrameView::didChangeScrollOffset()
+{
+ frame().mainFrame().pageOverlayController().didScrollFrame(frame());
+ frame().loader().client().didChangeScrollOffset();
+}
+
+void FrameView::scrollOffsetChangedViaPlatformWidgetImpl(const ScrollOffset& oldOffset, const ScrollOffset& newOffset)
{
updateLayerPositionsAfterScrolling();
updateCompositingLayersAfterScrolling();
repaintSlowRepaintObjects();
- scrollPositionChanged();
+ scrollPositionChanged(scrollPositionFromOffset(oldOffset), scrollPositionFromOffset(newOffset));
}
-void FrameView::scrollPositionChanged()
+// These scroll positions are affected by zooming.
+void FrameView::scrollPositionChanged(const ScrollPosition& oldPosition, const ScrollPosition& newPosition)
{
- frame().eventHandler().sendScrollEvent();
- frame().eventHandler().dispatchFakeMouseMoveEventSoon();
+ Page* page = frame().page();
+ Seconds throttlingDelay = page ? page->chrome().client().eventThrottlingDelay() : 0_s;
+
+ if (throttlingDelay == 0_s) {
+ m_delayedScrollEventTimer.stop();
+ sendScrollEvent();
+ } else if (!m_delayedScrollEventTimer.isActive())
+ m_delayedScrollEventTimer.startOneShot(throttlingDelay.value());
+
+ if (Document* document = frame().document())
+ document->sendWillRevealEdgeEventsIfNeeded(oldPosition, newPosition, visibleContentRect(), contentsSize());
-#if USE(ACCELERATED_COMPOSITING)
if (RenderView* renderView = this->renderView()) {
if (renderView->usesCompositing())
renderView->compositor().frameViewDidScroll();
}
-#endif
+
+ LOG_WITH_STREAM(Scrolling, stream << "FrameView " << this << " scrollPositionChanged from " << oldPosition << " to " << newPosition << " (scale " << frameScaleFactor() << " )");
+ updateLayoutViewport();
+ viewportContentsChanged();
+}
+
+void FrameView::applyRecursivelyWithVisibleRect(const std::function<void (FrameView& frameView, const IntRect& visibleRect)>& apply)
+{
+ IntRect windowClipRect = this->windowClipRect();
+ auto visibleRect = windowToContents(windowClipRect);
+ apply(*this, visibleRect);
+
+ // Recursive call for subframes. We cache the current FrameView's windowClipRect to avoid recomputing it for every subframe.
+ SetForScope<IntRect*> windowClipRectCache(m_cachedWindowClipRect, &windowClipRect);
+ for (Frame* childFrame = frame().tree().firstChild(); childFrame; childFrame = childFrame->tree().nextSibling()) {
+ if (auto* childView = childFrame->view())
+ childView->applyRecursivelyWithVisibleRect(apply);
+ }
+}
+
+void FrameView::resumeVisibleImageAnimations(const IntRect& visibleRect)
+{
+ if (visibleRect.isEmpty())
+ return;
+
+ if (auto* renderView = frame().contentRenderer())
+ renderView->resumePausedImageAnimationsIfNeeded(visibleRect);
+}
+
+void FrameView::updateScriptedAnimationsAndTimersThrottlingState(const IntRect& visibleRect)
+{
+ if (frame().isMainFrame())
+ return;
+
+ auto* document = frame().document();
+ if (!document)
+ return;
+
+ // We don't throttle zero-size or display:none frames because those are usually utility frames.
+ bool shouldThrottle = visibleRect.isEmpty() && !m_size.isEmpty() && frame().ownerRenderer();
+
+ if (auto* scriptedAnimationController = document->scriptedAnimationController())
+ scriptedAnimationController->setThrottled(shouldThrottle);
+
+ document->setTimerThrottlingEnabled(shouldThrottle);
+}
+
+
+void FrameView::resumeVisibleImageAnimationsIncludingSubframes()
+{
+ applyRecursivelyWithVisibleRect([] (FrameView& frameView, const IntRect& visibleRect) {
+ frameView.resumeVisibleImageAnimations(visibleRect);
+ });
}
void FrameView::updateLayerPositionsAfterScrolling()
@@ -2024,7 +2602,7 @@ bool FrameView::shouldUpdateCompositingLayersAfterScrolling() const
if (!page)
return true;
- if (&page->mainFrame() != &frame())
+ if (&page->mainFrame() != m_frame.ptr())
return true;
ScrollingCoordinator* scrollingCoordinator = page->scrollingCoordinator();
@@ -2034,7 +2612,7 @@ bool FrameView::shouldUpdateCompositingLayersAfterScrolling() const
if (!scrollingCoordinator->supportsFixedPositionLayers())
return true;
- if (scrollingCoordinator->shouldUpdateScrollLayerPositionSynchronously())
+ if (scrollingCoordinator->shouldUpdateScrollLayerPositionSynchronously(*this))
return true;
if (inProgrammaticScroll())
@@ -2047,7 +2625,8 @@ bool FrameView::shouldUpdateCompositingLayersAfterScrolling() const
void FrameView::updateCompositingLayersAfterScrolling()
{
-#if USE(ACCELERATED_COMPOSITING)
+ ASSERT(m_layoutPhase >= InPostLayout || m_layoutPhase == OutsideLayout);
+
if (!shouldUpdateCompositingLayersAfterScrolling())
return;
@@ -2055,7 +2634,6 @@ void FrameView::updateCompositingLayersAfterScrolling()
if (RenderView* renderView = this->renderView())
renderView->compositor().updateCompositingLayers(CompositingUpdateOnScroll);
}
-#endif
}
bool FrameView::isRubberBandInProgress() const
@@ -2067,7 +2645,7 @@ bool FrameView::isRubberBandInProgress() const
// ScrollingCoordinator::isRubberBandInProgress().
if (Page* page = frame().page()) {
if (ScrollingCoordinator* scrollingCoordinator = page->scrollingCoordinator()) {
- if (!scrollingCoordinator->shouldUpdateScrollLayerPositionSynchronously())
+ if (!scrollingCoordinator->shouldUpdateScrollLayerPositionSynchronously(*this))
return scrollingCoordinator->isRubberBandInProgress();
}
}
@@ -2080,18 +2658,19 @@ bool FrameView::isRubberBandInProgress() const
return false;
}
-bool FrameView::requestScrollPositionUpdate(const IntPoint& position)
+bool FrameView::requestScrollPositionUpdate(const ScrollPosition& position)
{
+ LOG_WITH_STREAM(Scrolling, stream << "FrameView::requestScrollPositionUpdate " << position);
+
#if ENABLE(ASYNC_SCROLLING)
- if (TiledBacking* tiledBacking = this->tiledBacking()) {
- IntRect visibleRect = visibleContentRect();
- visibleRect.setLocation(position);
- tiledBacking->prepopulateRect(visibleRect);
- }
+ if (TiledBacking* tiledBacking = this->tiledBacking())
+ tiledBacking->prepopulateRect(FloatRect(position, visibleContentRect().size()));
+#endif
+#if ENABLE(ASYNC_SCROLLING) || USE(COORDINATED_GRAPHICS)
if (Page* page = frame().page()) {
if (ScrollingCoordinator* scrollingCoordinator = page->scrollingCoordinator())
- return scrollingCoordinator->requestScrollPositionUpdate(this, position);
+ return scrollingCoordinator->requestScrollPositionUpdate(*this, position);
}
#else
UNUSED_PARAM(position);
@@ -2102,74 +2681,73 @@ bool FrameView::requestScrollPositionUpdate(const IntPoint& position)
HostWindow* FrameView::hostWindow() const
{
- if (Page* page = frame().page())
- return &page->chrome();
- return 0;
+ auto* page = frame().page();
+ if (!page)
+ return nullptr;
+ return &page->chrome();
}
-void FrameView::addTrackedRepaintRect(const IntRect& r)
+void FrameView::addTrackedRepaintRect(const FloatRect& r)
{
if (!m_isTrackingRepaints || r.isEmpty())
return;
- IntRect repaintRect = r;
- repaintRect.move(-scrollOffset());
+ FloatRect repaintRect = r;
+ repaintRect.moveBy(-scrollPosition());
m_trackedRepaintRects.append(repaintRect);
}
-void FrameView::repaintContentRectangle(const IntRect& r, bool immediate)
+void FrameView::repaintContentRectangle(const IntRect& r)
{
ASSERT(!frame().ownerElement());
- if (!shouldUpdate(immediate))
+ if (!shouldUpdate())
return;
-#if USE(TILED_BACKING_STORE)
- if (frame().tiledBackingStore()) {
- frame().tiledBackingStore()->invalidate(r);
- return;
- }
-#endif
- ScrollView::repaintContentRectangle(r, immediate);
+ ScrollView::repaintContentRectangle(r);
}
-static unsigned countRenderedCharactersInRenderObjectWithThreshold(const RenderObject& renderer, unsigned countSoFar, unsigned threshold)
+static unsigned countRenderedCharactersInRenderObjectWithThreshold(const RenderElement& renderer, unsigned threshold)
{
- // FIXME: Consider writing this using RenderObject::nextInPreOrder() instead of using recursion.
- if (renderer.isText())
- countSoFar += toRenderText(renderer).text()->length();
-
- for (RenderObject* child = renderer.firstChildSlow(); child; child = child->nextSibling()) {
- if (countSoFar >= threshold)
- break;
- countSoFar = countRenderedCharactersInRenderObjectWithThreshold(*child, countSoFar, threshold);
+ unsigned count = 0;
+ for (const RenderObject* descendant = &renderer; descendant; descendant = descendant->nextInPreOrder()) {
+ if (is<RenderText>(*descendant)) {
+ count += downcast<RenderText>(*descendant).text()->length();
+ if (count >= threshold)
+ break;
+ }
}
- return countSoFar;
+ return count;
}
bool FrameView::renderedCharactersExceed(unsigned threshold)
{
- if (!m_frame->contentRenderer())
+ if (!frame().contentRenderer())
return false;
- return countRenderedCharactersInRenderObjectWithThreshold(*m_frame->contentRenderer(), 0, threshold) >= threshold;
+ return countRenderedCharactersInRenderObjectWithThreshold(*frame().contentRenderer(), threshold) >= threshold;
}
-void FrameView::contentsResized()
+void FrameView::availableContentSizeChanged(AvailableSizeChangeReason reason)
{
- ScrollView::contentsResized();
+ if (Document* document = frame().document()) {
+ // FIXME: Merge this logic with m_setNeedsLayoutWasDeferred and find a more appropriate
+ // way of handling potential recursive layouts when the viewport is resized to accomodate
+ // the content but the content always overflows the viewport. See webkit.org/b/165781.
+ if (!(layoutPhase() == InViewSizeAdjust && useFixedLayout()))
+ document->updateViewportUnitsOnResize();
+ }
+
+ updateLayoutViewport();
setNeedsLayout();
+ ScrollView::availableContentSizeChanged(reason);
}
-void FrameView::fixedLayoutSizeChanged()
+bool FrameView::shouldLayoutAfterContentsResized() const
{
- // Can be triggered before the view is set, see comment in FrameView::visibleContentsResized().
- // An ASSERT is triggered when a view schedules a layout before being attached to a frame.
- if (!frame().view())
- return;
- ScrollView::fixedLayoutSizeChanged();
+ return !useFixedLayout() || useCustomFixedPositionLayoutRect();
}
-void FrameView::visibleContentsResized()
+void FrameView::updateContentsSize()
{
// We check to make sure the view is attached to a frame() as this method can
// be triggered before the view is attached by Frame::createView(...) setting
@@ -2190,44 +2768,134 @@ void FrameView::visibleContentsResized()
}
#endif
- if (!useFixedLayout() && needsLayout())
+ if (shouldLayoutAfterContentsResized() && needsLayout())
layout();
-#if USE(ACCELERATED_COMPOSITING)
if (RenderView* renderView = this->renderView()) {
if (renderView->usesCompositing())
renderView->compositor().frameViewDidChangeSize();
}
-#endif
}
void FrameView::addedOrRemovedScrollbar()
{
-#if USE(ACCELERATED_COMPOSITING)
if (RenderView* renderView = this->renderView()) {
if (renderView->usesCompositing())
renderView->compositor().frameViewDidAddOrRemoveScrollbars();
}
+
+ updateTiledBackingAdaptiveSizing();
+}
+
+TiledBacking::Scrollability FrameView::computeScrollability() const
+{
+ auto* page = frame().page();
+
+ // Use smaller square tiles if the Window is not active to facilitate app napping.
+ if (!page || !page->isWindowActive())
+ return TiledBacking::HorizontallyScrollable | TiledBacking::VerticallyScrollable;
+
+ bool horizontallyScrollable;
+ bool verticallyScrollable;
+ bool clippedByAncestorView = static_cast<bool>(m_viewExposedRect);
+
+#if PLATFORM(IOS)
+ if (page)
+ clippedByAncestorView |= page->enclosedInScrollableAncestorView();
+#endif
+
+ if (delegatesScrolling()) {
+ IntSize documentSize = contentsSize();
+ IntSize visibleSize = this->visibleSize();
+
+ horizontallyScrollable = clippedByAncestorView || documentSize.width() > visibleSize.width();
+ verticallyScrollable = clippedByAncestorView || documentSize.height() > visibleSize.height();
+ } else {
+ horizontallyScrollable = clippedByAncestorView || horizontalScrollbar();
+ verticallyScrollable = clippedByAncestorView || verticalScrollbar();
+ }
+
+ TiledBacking::Scrollability scrollability = TiledBacking::NotScrollable;
+ if (horizontallyScrollable)
+ scrollability = TiledBacking::HorizontallyScrollable;
+
+ if (verticallyScrollable)
+ scrollability |= TiledBacking::VerticallyScrollable;
+
+ return scrollability;
+}
+
+void FrameView::updateTiledBackingAdaptiveSizing()
+{
+ auto* tiledBacking = this->tiledBacking();
+ if (!tiledBacking)
+ return;
+
+ tiledBacking->setScrollability(computeScrollability());
+}
+
+#if PLATFORM(IOS)
+
+void FrameView::unobscuredContentSizeChanged()
+{
+ updateTiledBackingAdaptiveSizing();
+}
+
#endif
+
+static LayerFlushThrottleState::Flags determineLayerFlushThrottleState(Page& page)
+{
+ // We only throttle when constantly receiving new data during the inital page load.
+ if (!page.progress().isMainLoadProgressing())
+ return 0;
+ // Scrolling during page loading disables throttling.
+ if (page.mainFrame().view()->wasScrolledByUser())
+ return 0;
+ // Disable for image documents so large GIF animations don't get throttled during loading.
+ auto* document = page.mainFrame().document();
+ if (!document || is<ImageDocument>(*document))
+ return 0;
+ return LayerFlushThrottleState::Enabled;
}
void FrameView::disableLayerFlushThrottlingTemporarilyForInteraction()
{
-#if USE(ACCELERATED_COMPOSITING)
+ if (!frame().page())
+ return;
+ auto& page = *frame().page();
+
+ LayerFlushThrottleState::Flags flags = LayerFlushThrottleState::UserIsInteracting | determineLayerFlushThrottleState(page);
+ if (page.chrome().client().adjustLayerFlushThrottling(flags))
+ return;
+
if (RenderView* view = renderView())
view->compositor().disableLayerFlushThrottlingTemporarilyForInteraction();
-#endif
}
-void FrameView::updateLayerFlushThrottlingInAllFrames()
+void FrameView::loadProgressingStatusChanged()
{
-#if USE(ACCELERATED_COMPOSITING)
- bool isMainLoadProgressing = frame().page()->progress().isMainLoadProgressing();
- for (Frame* frame = m_frame.get(); frame; frame = frame->tree().traverseNext(m_frame.get())) {
+ updateLayerFlushThrottling();
+ adjustTiledBackingCoverage();
+}
+
+void FrameView::updateLayerFlushThrottling()
+{
+ Page* page = frame().page();
+ if (!page)
+ return;
+
+ ASSERT(frame().isMainFrame());
+
+ LayerFlushThrottleState::Flags flags = determineLayerFlushThrottleState(*page);
+
+ // See if the client is handling throttling.
+ if (page->chrome().client().adjustLayerFlushThrottling(flags))
+ return;
+
+ for (auto* frame = m_frame.ptr(); frame; frame = frame->tree().traverseNext(m_frame.ptr())) {
if (RenderView* renderView = frame->contentRenderer())
- renderView->compositor().setLayerFlushThrottlingEnabled(isMainLoadProgressing);
+ renderView->compositor().setLayerFlushThrottlingEnabled(flags & LayerFlushThrottleState::Enabled);
}
-#endif
}
void FrameView::adjustTiledBackingCoverage()
@@ -2235,20 +2903,19 @@ void FrameView::adjustTiledBackingCoverage()
if (!m_speculativeTilingEnabled)
enableSpeculativeTilingIfNeeded();
-#if USE(ACCELERATED_COMPOSITING)
RenderView* renderView = this->renderView();
if (renderView && renderView->layer()->backing())
renderView->layer()->backing()->adjustTiledBackingCoverage();
-#endif
#if PLATFORM(IOS)
- if (TileCache* tileCache = this->tileCache())
+ if (LegacyTileCache* tileCache = legacyTileCache())
tileCache->setSpeculativeTileCreationEnabled(m_speculativeTilingEnabled);
#endif
}
static bool shouldEnableSpeculativeTilingDuringLoading(const FrameView& view)
{
- return view.isVisuallyNonEmpty() && !view.frame().page()->progress().isMainLoadProgressing();
+ Page* page = view.frame().page();
+ return page && view.isVisuallyNonEmpty() && !page->progress().isMainLoadProgressing();
}
void FrameView::enableSpeculativeTilingIfNeeded()
@@ -2267,7 +2934,7 @@ void FrameView::enableSpeculativeTilingIfNeeded()
m_speculativeTilingEnableTimer.startOneShot(speculativeTilingEnableDelay);
}
-void FrameView::speculativeTilingEnableTimerFired(Timer<FrameView>&)
+void FrameView::speculativeTilingEnableTimerFired()
{
if (m_speculativeTilingEnabled)
return;
@@ -2275,11 +2942,30 @@ void FrameView::speculativeTilingEnableTimerFired(Timer<FrameView>&)
adjustTiledBackingCoverage();
}
-void FrameView::layoutTimerFired(Timer<FrameView>&)
+void FrameView::show()
{
-#ifdef INSTRUMENT_LAYOUT_SCHEDULING
+ ScrollView::show();
+
+ if (frame().isMainFrame()) {
+ // Turn off speculative tiling for a brief moment after a FrameView appears on screen.
+ // Note that adjustTiledBackingCoverage() kicks the (500ms) timer to re-enable it.
+ m_speculativeTilingEnabled = false;
+ m_wasScrolledByUser = false;
+ adjustTiledBackingCoverage();
+ }
+}
+void FrameView::convertSubtreeLayoutToFullLayout()
+{
+ ASSERT(m_layoutRoot);
+ m_layoutRoot->markContainingBlocksForLayout(ScheduleRelayout::No);
+ m_layoutRoot = nullptr;
+}
+
+void FrameView::layoutTimerFired()
+{
+#if !LOG_DISABLED
if (!frame().document()->ownerElement())
- printf("Layout timer fired at %lld\n", frame().document()->elapsedTime().count());
+ LOG(Layout, "FrameView %p layout timer fired at %.3fs", this, frame().document()->timeSinceDocumentCreation().value());
#endif
layout();
}
@@ -2290,33 +2976,32 @@ void FrameView::scheduleRelayout()
// too many false assertions. See <rdar://problem/7218118>.
ASSERT(frame().view() == this);
- if (m_layoutRoot) {
- m_layoutRoot->markContainingBlocksForLayout(false);
- m_layoutRoot = 0;
- }
+ if (m_layoutRoot)
+ convertSubtreeLayoutToFullLayout();
if (!m_layoutSchedulingEnabled)
return;
if (!needsLayout())
return;
if (!frame().document()->shouldScheduleLayout())
return;
- InspectorInstrumentation::didInvalidateLayout(&frame());
+ InspectorInstrumentation::didInvalidateLayout(frame());
// When frame flattening is enabled, the contents of the frame could affect the layout of the parent frames.
// Also invalidate parent frame starting from the owner element of this frame.
if (frame().ownerRenderer() && isInChildFrameWithFrameFlattening())
frame().ownerRenderer()->setNeedsLayout(MarkContainingBlockChain);
- std::chrono::milliseconds delay = frame().document()->minimumLayoutDelay();
- if (m_layoutTimer.isActive() && m_delayedLayout && !delay.count())
+ Seconds delay = frame().document()->minimumLayoutDelay();
+ if (m_layoutTimer.isActive() && m_delayedLayout && !delay)
unscheduleRelayout();
+
if (m_layoutTimer.isActive())
return;
- m_delayedLayout = delay.count();
+ m_delayedLayout = delay.value();
-#ifdef INSTRUMENT_LAYOUT_SCHEDULING
+#if !LOG_DISABLED
if (!frame().document()->ownerElement())
- printf("Scheduling layout for %d\n", delay);
+ LOG(Layout, "FrameView %p scheduling layout for %.3fs", this, delay.value());
#endif
m_layoutTimer.startOneShot(delay);
@@ -2334,23 +3019,26 @@ static bool isObjectAncestorContainerOf(RenderObject* ancestor, RenderObject* de
void FrameView::scheduleRelayoutOfSubtree(RenderElement& newRelayoutRoot)
{
ASSERT(renderView());
- RenderView& renderView = *this->renderView();
+ const RenderView& renderView = *this->renderView();
// Try to catch unnecessary work during render tree teardown.
- ASSERT(!renderView.documentBeingDestroyed());
+ ASSERT(!renderView.renderTreeBeingDestroyed());
ASSERT(frame().view() == this);
- if (renderView.needsLayout()) {
- newRelayoutRoot.markContainingBlocksForLayout(false);
+ // When m_layoutRoot is already set, ignore the renderView's needsLayout bit
+ // since we need to resolve the conflict between the m_layoutRoot and newRelayoutRoot layouts.
+ if (renderView.needsLayout() && !m_layoutRoot) {
+ m_layoutRoot = &newRelayoutRoot;
+ convertSubtreeLayoutToFullLayout();
return;
}
if (!layoutPending() && m_layoutSchedulingEnabled) {
- std::chrono::milliseconds delay = renderView.document().minimumLayoutDelay();
- ASSERT(!newRelayoutRoot.container() || !newRelayoutRoot.container()->needsLayout());
+ Seconds delay = renderView.document().minimumLayoutDelay();
+ ASSERT(!newRelayoutRoot.container() || is<RenderView>(newRelayoutRoot.container()) || !newRelayoutRoot.container()->needsLayout());
m_layoutRoot = &newRelayoutRoot;
- InspectorInstrumentation::didInvalidateLayout(&frame());
- m_delayedLayout = delay.count();
+ InspectorInstrumentation::didInvalidateLayout(frame());
+ m_delayedLayout = delay.value();
m_layoutTimer.startOneShot(delay);
return;
}
@@ -2359,33 +3047,31 @@ void FrameView::scheduleRelayoutOfSubtree(RenderElement& newRelayoutRoot)
return;
if (!m_layoutRoot) {
- // Just relayout the subtree.
- newRelayoutRoot.markContainingBlocksForLayout(false);
- InspectorInstrumentation::didInvalidateLayout(&frame());
+ // We already have a pending (full) layout. Just mark the subtree for layout.
+ newRelayoutRoot.markContainingBlocksForLayout(ScheduleRelayout::No);
+ InspectorInstrumentation::didInvalidateLayout(frame());
return;
}
if (isObjectAncestorContainerOf(m_layoutRoot, &newRelayoutRoot)) {
// Keep the current root.
- newRelayoutRoot.markContainingBlocksForLayout(false, m_layoutRoot);
- ASSERT(!m_layoutRoot->container() || !m_layoutRoot->container()->needsLayout());
+ newRelayoutRoot.markContainingBlocksForLayout(ScheduleRelayout::No, m_layoutRoot);
+ ASSERT(!m_layoutRoot->container() || is<RenderView>(m_layoutRoot->container()) || !m_layoutRoot->container()->needsLayout());
return;
}
if (isObjectAncestorContainerOf(&newRelayoutRoot, m_layoutRoot)) {
// Re-root at newRelayoutRoot.
- m_layoutRoot->markContainingBlocksForLayout(false, &newRelayoutRoot);
+ m_layoutRoot->markContainingBlocksForLayout(ScheduleRelayout::No, &newRelayoutRoot);
m_layoutRoot = &newRelayoutRoot;
- ASSERT(!m_layoutRoot->container() || !m_layoutRoot->container()->needsLayout());
- InspectorInstrumentation::didInvalidateLayout(&frame());
+ ASSERT(!m_layoutRoot->container() || is<RenderView>(m_layoutRoot->container()) || !m_layoutRoot->container()->needsLayout());
+ InspectorInstrumentation::didInvalidateLayout(frame());
return;
}
-
- // Just do a full relayout.
- m_layoutRoot->markContainingBlocksForLayout(false);
- m_layoutRoot = 0;
- newRelayoutRoot.markContainingBlocksForLayout(false);
- InspectorInstrumentation::didInvalidateLayout(&frame());
+ // Two disjoint subtrees need layout. Mark both of them and issue a full layout instead.
+ convertSubtreeLayoutToFullLayout();
+ newRelayoutRoot.markContainingBlocksForLayout(ScheduleRelayout::No);
+ InspectorInstrumentation::didInvalidateLayout(frame());
}
bool FrameView::layoutPending() const
@@ -2393,6 +3079,25 @@ bool FrameView::layoutPending() const
return m_layoutTimer.isActive();
}
+bool FrameView::needsStyleRecalcOrLayout(bool includeSubframes) const
+{
+ if (frame().document() && frame().document()->childNeedsStyleRecalc())
+ return true;
+
+ if (needsLayout())
+ return true;
+
+ if (!includeSubframes)
+ return false;
+
+ for (auto& frameView : renderedChildFrameViews()) {
+ if (frameView->needsStyleRecalcOrLayout())
+ return true;
+ }
+
+ return false;
+}
+
bool FrameView::needsLayout() const
{
// This can return true in cases where the document does not have a body yet.
@@ -2402,50 +3107,57 @@ bool FrameView::needsLayout() const
return layoutPending()
|| (renderView && renderView->needsLayout())
|| m_layoutRoot
- || (m_deferSetNeedsLayouts && m_setNeedsLayoutWasDeferred);
+ || (m_deferSetNeedsLayoutCount && m_setNeedsLayoutWasDeferred);
}
void FrameView::setNeedsLayout()
{
- if (m_deferSetNeedsLayouts) {
+ if (m_deferSetNeedsLayoutCount) {
m_setNeedsLayoutWasDeferred = true;
return;
}
- if (RenderView* renderView = this->renderView())
+ if (auto* renderView = this->renderView()) {
+ ASSERT(!renderView->inHitTesting());
renderView->setNeedsLayout();
+ }
}
void FrameView::unscheduleRelayout()
{
+ if (m_postLayoutTasksTimer.isActive())
+ m_postLayoutTasksTimer.stop();
+
if (!m_layoutTimer.isActive())
return;
-#ifdef INSTRUMENT_LAYOUT_SCHEDULING
+#if !LOG_DISABLED
if (!frame().document()->ownerElement())
- printf("Layout timer unscheduled at %d\n", frame().document()->elapsedTime());
+ LOG(Layout, "FrameView %p layout timer unscheduled at %.3fs", this, frame().document()->timeSinceDocumentCreation().value());
#endif
m_layoutTimer.stop();
m_delayedLayout = false;
}
-#if ENABLE(REQUEST_ANIMATION_FRAME)
-void FrameView::serviceScriptedAnimations(double monotonicAnimationStartTime)
+void FrameView::serviceScriptedAnimations()
{
- for (Frame* frame = m_frame.get(); frame; frame = frame->tree().traverseNext()) {
+ for (auto* frame = m_frame.ptr(); frame; frame = frame->tree().traverseNext()) {
frame->view()->serviceScrollAnimations();
frame->animation().serviceAnimations();
}
+ if (!frame().document() || !frame().document()->domWindow())
+ return;
+
Vector<RefPtr<Document>> documents;
- for (Frame* frame = m_frame.get(); frame; frame = frame->tree().traverseNext())
+ for (auto* frame = m_frame.ptr(); frame; frame = frame->tree().traverseNext())
documents.append(frame->document());
- for (size_t i = 0; i < documents.size(); ++i)
- documents[i]->serviceScriptedAnimations(monotonicAnimationStartTime);
+ double timestamp = frame().document()->domWindow()->nowTimestamp();
+ for (auto& document : documents)
+ document->serviceScriptedAnimations(timestamp);
}
-#endif
bool FrameView::isTransparent() const
{
@@ -2454,12 +3166,24 @@ bool FrameView::isTransparent() const
void FrameView::setTransparent(bool isTransparent)
{
+ if (m_isTransparent == isTransparent)
+ return;
+
m_isTransparent = isTransparent;
+
+ // setTransparent can be called in the window between FrameView initialization
+ // and switching in the new Document; this means that the RenderView that we
+ // retrieve is actually attached to the previous Document, which is going away,
+ // and must not update compositing layers.
+ if (!isViewForDocumentInFrame())
+ return;
+
+ renderView()->compositor().rootBackgroundTransparencyChanged();
}
bool FrameView::hasOpaqueBackground() const
{
- return !m_isTransparent && !m_baseBackgroundColor.hasAlpha();
+ return !m_isTransparent && m_baseBackgroundColor.isOpaque();
}
Color FrameView::baseBackgroundColor() const
@@ -2469,17 +3193,25 @@ Color FrameView::baseBackgroundColor() const
void FrameView::setBaseBackgroundColor(const Color& backgroundColor)
{
+ bool wasOpaque = m_baseBackgroundColor.isOpaque();
+
if (!backgroundColor.isValid())
m_baseBackgroundColor = Color::white;
else
m_baseBackgroundColor = backgroundColor;
+ if (!isViewForDocumentInFrame())
+ return;
+
recalculateScrollbarOverlayStyle();
+
+ if (m_baseBackgroundColor.isOpaque() != wasOpaque)
+ renderView()->compositor().rootBackgroundTransparencyChanged();
}
void FrameView::updateBackgroundRecursively(const Color& backgroundColor, bool transparent)
{
- for (Frame* frame = m_frame.get(); frame; frame = frame->tree().traverseNext(m_frame.get())) {
+ for (auto* frame = m_frame.ptr(); frame; frame = frame->tree().traverseNext(m_frame.ptr())) {
if (FrameView* view = frame->view()) {
view->setTransparent(transparent);
view->setBaseBackgroundColor(backgroundColor);
@@ -2487,9 +3219,8 @@ void FrameView::updateBackgroundRecursively(const Color& backgroundColor, bool t
}
}
-bool FrameView::hasExtendedBackground() const
+bool FrameView::hasExtendedBackgroundRectForPainting() const
{
-#if USE(ACCELERATED_COMPOSITING)
if (!frame().settings().backgroundShouldExtendBeyondPage())
return false;
@@ -2498,31 +3229,64 @@ bool FrameView::hasExtendedBackground() const
return false;
return tiledBacking->hasMargins();
-#else
- return false;
-#endif
}
-IntRect FrameView::extendedBackgroundRect() const
+void FrameView::updateExtendBackgroundIfNecessary()
{
-#if USE(ACCELERATED_COMPOSITING)
- TiledBacking* tiledBacking = this->tiledBacking();
- if (!tiledBacking)
- return IntRect();
+ ExtendedBackgroundMode mode = calculateExtendedBackgroundMode();
+ if (mode == ExtendedBackgroundModeNone)
+ return;
- return tiledBacking->bounds();
+ updateTilesForExtendedBackgroundMode(mode);
+}
+
+FrameView::ExtendedBackgroundMode FrameView::calculateExtendedBackgroundMode() const
+{
+ // Just because Settings::backgroundShouldExtendBeyondPage() is true does not necessarily mean
+ // that the background rect needs to be extended for painting. Simple backgrounds can be extended
+ // just with RenderLayerCompositor::setRootExtendedBackgroundColor(). More complicated backgrounds,
+ // such as images, require extending the background rect to continue painting into the extended
+ // region. This function finds out if it is necessary to extend the background rect for painting.
+
+#if PLATFORM(IOS)
+ // <rdar://problem/16201373>
+ return ExtendedBackgroundModeNone;
#else
- RenderView* renderView = this->renderView();
- if (!renderView)
- return IntRect();
+ if (!frame().settings().backgroundShouldExtendBeyondPage())
+ return ExtendedBackgroundModeNone;
- return renderView->unscaledDocumentRect();
+ if (!frame().isMainFrame())
+ return ExtendedBackgroundModeNone;
+
+ Document* document = frame().document();
+ if (!document)
+ return ExtendedBackgroundModeNone;
+
+ if (!renderView())
+ return ExtendedBackgroundModeNone;
+
+ auto* rootBackgroundRenderer = renderView()->rendererForRootBackground();
+ if (!rootBackgroundRenderer)
+ return ExtendedBackgroundModeNone;
+
+ if (!rootBackgroundRenderer->style().hasBackgroundImage())
+ return ExtendedBackgroundModeNone;
+
+ ExtendedBackgroundMode mode = ExtendedBackgroundModeNone;
+ if (rootBackgroundRenderer->style().backgroundRepeatX() == RepeatFill)
+ mode |= ExtendedBackgroundModeHorizontal;
+ if (rootBackgroundRenderer->style().backgroundRepeatY() == RepeatFill)
+ mode |= ExtendedBackgroundModeVertical;
+
+ return mode;
#endif
}
-#if USE(ACCELERATED_COMPOSITING)
-void FrameView::setBackgroundExtendsBeyondPage(bool extendBackground)
+void FrameView::updateTilesForExtendedBackgroundMode(ExtendedBackgroundMode mode)
{
+ if (!frame().settings().backgroundShouldExtendBeyondPage())
+ return;
+
RenderView* renderView = this->renderView();
if (!renderView)
return;
@@ -2531,9 +3295,41 @@ void FrameView::setBackgroundExtendsBeyondPage(bool extendBackground)
if (!backing)
return;
- backing->setTiledBackingHasMargins(extendBackground);
+ TiledBacking* tiledBacking = backing->graphicsLayer()->tiledBacking();
+ if (!tiledBacking)
+ return;
+
+ ExtendedBackgroundMode existingMode = ExtendedBackgroundModeNone;
+ if (tiledBacking->hasVerticalMargins())
+ existingMode |= ExtendedBackgroundModeVertical;
+ if (tiledBacking->hasHorizontalMargins())
+ existingMode |= ExtendedBackgroundModeHorizontal;
+
+ if (existingMode == mode)
+ return;
+
+ renderView->compositor().setRootExtendedBackgroundColor(mode == ExtendedBackgroundModeAll ? Color() : documentBackgroundColor());
+ backing->setTiledBackingHasMargins(mode & ExtendedBackgroundModeHorizontal, mode & ExtendedBackgroundModeVertical);
+}
+
+IntRect FrameView::extendedBackgroundRectForPainting() const
+{
+ TiledBacking* tiledBacking = this->tiledBacking();
+ if (!tiledBacking)
+ return IntRect();
+
+ RenderView* renderView = this->renderView();
+ if (!renderView)
+ return IntRect();
+
+ LayoutRect extendedRect = renderView->unextendedBackgroundRect();
+ if (!tiledBacking->hasMargins())
+ return snappedIntRect(extendedRect);
+
+ extendedRect.moveBy(LayoutPoint(-tiledBacking->leftMarginWidth(), -tiledBacking->topMarginHeight()));
+ extendedRect.expand(LayoutSize(tiledBacking->leftMarginWidth() + tiledBacking->rightMarginWidth(), tiledBacking->topMarginHeight() + tiledBacking->bottomMarginHeight()));
+ return snappedIntRect(extendedRect);
}
-#endif
bool FrameView::shouldUpdateWhileOffscreen() const
{
@@ -2545,16 +3341,16 @@ void FrameView::setShouldUpdateWhileOffscreen(bool shouldUpdateWhileOffscreen)
m_shouldUpdateWhileOffscreen = shouldUpdateWhileOffscreen;
}
-bool FrameView::shouldUpdate(bool immediateRequested) const
+bool FrameView::shouldUpdate() const
{
- if (!immediateRequested && isOffscreen() && !shouldUpdateWhileOffscreen())
+ if (isOffscreen() && !shouldUpdateWhileOffscreen())
return false;
return true;
}
void FrameView::scrollToAnchor()
{
- RefPtr<Node> anchorNode = m_maintainScrollPositionAnchor;
+ RefPtr<ContainerNode> anchorNode = m_maintainScrollPositionAnchor;
if (!anchorNode)
return;
@@ -2562,12 +3358,18 @@ void FrameView::scrollToAnchor()
return;
LayoutRect rect;
- if (anchorNode != frame().document())
- rect = anchorNode->boundingBox();
+ bool insideFixed = false;
+ if (anchorNode != frame().document() && anchorNode->renderer())
+ rect = anchorNode->renderer()->absoluteAnchorRect(&insideFixed);
// Scroll nested layers and frames to reveal the anchor.
// Align to the top and to the closest side (this matches other browsers).
- anchorNode->renderer()->scrollRectToVisible(rect, ScrollAlignment::alignToEdgeIfNeeded, ScrollAlignment::alignTopAlways);
+ if (anchorNode->renderer()->style().isHorizontalWritingMode())
+ anchorNode->renderer()->scrollRectToVisible(SelectionRevealMode::Reveal, rect, insideFixed, ScrollAlignment::alignToEdgeIfNeeded, ScrollAlignment::alignTopAlways);
+ else if (anchorNode->renderer()->style().isFlippedBlocksWritingMode())
+ anchorNode->renderer()->scrollRectToVisible(SelectionRevealMode::Reveal, rect, insideFixed, ScrollAlignment::alignRightAlways, ScrollAlignment::alignToEdgeIfNeeded);
+ else
+ anchorNode->renderer()->scrollRectToVisible(SelectionRevealMode::Reveal, rect, insideFixed, ScrollAlignment::alignLeftAlways, ScrollAlignment::alignToEdgeIfNeeded);
if (AXObjectCache* cache = frame().document()->existingAXObjectCache())
cache->handleScrolledToAnchor(anchorNode.get());
@@ -2585,8 +3387,8 @@ void FrameView::updateEmbeddedObject(RenderEmbeddedObject& embeddedObject)
HTMLFrameOwnerElement& element = embeddedObject.frameOwnerElement();
if (embeddedObject.isSnapshottedPlugIn()) {
- if (isHTMLObjectElement(element) || isHTMLEmbedElement(element)) {
- HTMLPlugInImageElement& pluginElement = toHTMLPlugInImageElement(element);
+ if (is<HTMLObjectElement>(element) || is<HTMLEmbedElement>(element)) {
+ HTMLPlugInImageElement& pluginElement = downcast<HTMLPlugInImageElement>(element);
pluginElement.checkSnapshotStatus();
}
return;
@@ -2596,29 +3398,23 @@ void FrameView::updateEmbeddedObject(RenderEmbeddedObject& embeddedObject)
// FIXME: This could turn into a real virtual dispatch if we defined
// updateWidget(PluginCreationOption) on HTMLElement.
- if (isHTMLObjectElement(element) || isHTMLEmbedElement(element) || isHTMLAppletElement(element)) {
- HTMLPlugInImageElement& pluginElement = toHTMLPlugInImageElement(element);
+ if (is<HTMLPlugInImageElement>(element)) {
+ HTMLPlugInImageElement& pluginElement = downcast<HTMLPlugInImageElement>(element);
if (pluginElement.needsCheckForSizeChange()) {
pluginElement.checkSnapshotStatus();
return;
}
if (pluginElement.needsWidgetUpdate())
- pluginElement.updateWidget(CreateAnyWidgetType);
- }
-
- // FIXME: It is not clear that Media elements need or want this updateWidget() call.
-#if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
- else if (element.isMediaElement())
- toHTMLMediaElement(element).updateWidget(CreateAnyWidgetType);
-#endif
- else
+ pluginElement.updateWidget(CreatePlugins::Yes);
+ } else
ASSERT_NOT_REACHED();
// It's possible the renderer was destroyed below updateWidget() since loading a plugin may execute arbitrary JavaScript.
if (!weakRenderer)
return;
- embeddedObject.updateWidgetPosition();
+ auto ignoreWidgetState = embeddedObject.updateWidgetPosition();
+ UNUSED_PARAM(ignoreWidgetState);
}
bool FrameView::updateEmbeddedObjects()
@@ -2642,9 +3438,9 @@ bool FrameView::updateEmbeddedObjects()
return m_embeddedObjectsToUpdate->isEmpty();
}
-void FrameView::updateEmbeddedObjectsTimerFired(Timer<FrameView>*)
+void FrameView::updateEmbeddedObjectsTimerFired()
{
- RefPtr<FrameView> protect(this);
+ RefPtr<FrameView> protectedThis(this);
m_updateEmbeddedObjectsTimer.stop();
for (unsigned i = 0; i < maxUpdateEmbeddedObjectsIterations; i++) {
if (updateEmbeddedObjects())
@@ -2657,57 +3453,55 @@ void FrameView::flushAnyPendingPostLayoutTasks()
if (m_postLayoutTasksTimer.isActive())
performPostLayoutTasks();
if (m_updateEmbeddedObjectsTimer.isActive())
- updateEmbeddedObjectsTimerFired(nullptr);
+ updateEmbeddedObjectsTimerFired();
+}
+
+void FrameView::queuePostLayoutCallback(Function<void()>&& callback)
+{
+ m_postLayoutCallbackQueue.append(WTFMove(callback));
+}
+
+void FrameView::flushPostLayoutTasksQueue()
+{
+ if (m_nestedLayoutCount > 1)
+ return;
+
+ if (!m_postLayoutCallbackQueue.size())
+ return;
+
+ Vector<Function<void()>> queue = WTFMove(m_postLayoutCallbackQueue);
+ for (auto& task : queue)
+ task();
}
void FrameView::performPostLayoutTasks()
{
+ LOG(Layout, "FrameView %p performPostLayoutTasks", this);
+
// FIXME: We should not run any JavaScript code in this function.
m_postLayoutTasksTimer.stop();
- frame().selection().setCaretRectNeedsUpdate();
- frame().selection().updateAppearance();
-
- LayoutMilestones requestedMilestones = 0;
- LayoutMilestones milestonesAchieved = 0;
- Page* page = frame().page();
- if (page)
- requestedMilestones = page->requestedLayoutMilestones();
+ frame().selection().updateAppearanceAfterLayout();
- if (m_nestedLayoutCount <= 1 && frame().document()->documentElement()) {
- if (m_firstLayoutCallbackPending) {
- m_firstLayoutCallbackPending = false;
- frame().loader().didFirstLayout();
- if (requestedMilestones & DidFirstLayout)
- milestonesAchieved |= DidFirstLayout;
- if (frame().isMainFrame())
- page->startCountingRelevantRepaintedObjects();
- }
- updateIsVisuallyNonEmpty();
+ flushPostLayoutTasksQueue();
- // If the layout was done with pending sheets, we are not in fact visually non-empty yet.
- if (m_isVisuallyNonEmpty && !frame().document()->didLayoutWithPendingStylesheets() && m_firstVisuallyNonEmptyLayoutCallbackPending) {
- m_firstVisuallyNonEmptyLayoutCallbackPending = false;
- if (requestedMilestones & DidFirstVisuallyNonEmptyLayout)
- milestonesAchieved |= DidFirstVisuallyNonEmptyLayout;
- }
- }
+ if (m_nestedLayoutCount <= 1 && frame().document()->documentElement())
+ fireLayoutRelatedMilestonesIfNeeded();
#if PLATFORM(IOS)
// Only send layout-related delegate callbacks synchronously for the main frame to
// avoid re-entering layout for the main frame while delivering a layout-related delegate
// callback for a subframe.
- if (frame().isMainFrame())
- page->chrome().client().didLayout();
+ if (frame().isMainFrame()) {
+ if (Page* page = frame().page())
+ page->chrome().client().didLayout();
+ }
#endif
- if (milestonesAchieved && frame().isMainFrame())
- frame().loader().didLayout(milestonesAchieved);
-
#if ENABLE(FONT_LOAD_EVENTS)
if (RuntimeEnabledFeatures::sharedFeatures().fontLoadEventsEnabled())
- frame().document()->fontloader()->didLayout();
+ frame().document()->fonts()->didLayout();
#endif
// FIXME: We should consider adding DidLayout as a LayoutMilestone. That would let us merge this
@@ -2715,51 +3509,73 @@ void FrameView::performPostLayoutTasks()
frame().loader().client().dispatchDidLayout();
updateWidgetPositions();
-
+
+#if ENABLE(CSS_SCROLL_SNAP)
+ updateSnapOffsets();
+#endif
+
// layout() protects FrameView, but it still can get destroyed when updateEmbeddedObjects()
// is called through the post layout timer.
- Ref<FrameView> protect(*this);
+ Ref<FrameView> protectedThis(*this);
m_updateEmbeddedObjectsTimer.startOneShot(0);
- if (page) {
- if (ScrollingCoordinator* scrollingCoordinator = page->scrollingCoordinator())
- scrollingCoordinator->frameViewLayoutUpdated(this);
+ if (auto* page = frame().page()) {
+ if (auto* scrollingCoordinator = page->scrollingCoordinator())
+ scrollingCoordinator->frameViewLayoutUpdated(*this);
}
-#if USE(ACCELERATED_COMPOSITING)
if (RenderView* renderView = this->renderView()) {
if (renderView->usesCompositing())
renderView->compositor().frameViewDidLayout();
}
-#endif
scrollToAnchor();
sendResizeEventIfNeeded();
+
+ updateLayoutViewport();
+ viewportContentsChanged();
+
+ updateScrollSnapState();
+
+ if (AXObjectCache* cache = frame().document()->existingAXObjectCache())
+ cache->performDeferredIsIgnoredChange();
+}
+
+IntSize FrameView::sizeForResizeEvent() const
+{
+#if PLATFORM(IOS)
+ if (m_useCustomSizeForResizeEvent)
+ return m_customSizeForResizeEvent;
+#endif
+ if (useFixedLayout() && !fixedLayoutSize().isEmpty() && delegatesScrolling())
+ return fixedLayoutSize();
+ return visibleContentRectIncludingScrollbars().size();
}
void FrameView::sendResizeEventIfNeeded()
{
+ if (isInRenderTreeLayout() || needsLayout())
+ return;
+
RenderView* renderView = this->renderView();
if (!renderView || renderView->printing())
return;
+
if (frame().page() && frame().page()->chrome().client().isSVGImageChromeClient())
return;
- IntSize currentSize;
- if (useFixedLayout() && !fixedLayoutSize().isEmpty() && delegatesScrolling())
- currentSize = fixedLayoutSize();
- else
- currentSize = visibleContentRectIncludingScrollbars().size();
-
+ IntSize currentSize = sizeForResizeEvent();
float currentZoomFactor = renderView->style().zoom();
- bool shouldSendResizeEvent = !m_firstLayout && (currentSize != m_lastViewportSize || currentZoomFactor != m_lastZoomFactor);
+
+ if (currentSize == m_lastViewportSize && currentZoomFactor == m_lastZoomFactor)
+ return;
m_lastViewportSize = currentSize;
m_lastZoomFactor = currentZoomFactor;
- if (!shouldSendResizeEvent)
+ if (m_firstLayout)
return;
#if PLATFORM(IOS)
@@ -2773,22 +3589,24 @@ void FrameView::sendResizeEventIfNeeded()
#endif
bool isMainFrame = frame().isMainFrame();
- bool canSendResizeEventSynchronously = !m_shouldAutoSize && isMainFrame && !isInLayout();
+ bool canSendResizeEventSynchronously = isMainFrame && !m_shouldAutoSize;
- // If we resized during layout, queue up a resize event for later, otherwise fire it right away.
- RefPtr<Event> resizeEvent = Event::create(eventNames().resizeEvent, false, false);
+ Ref<Event> resizeEvent = Event::create(eventNames().resizeEvent, false, false);
if (canSendResizeEventSynchronously)
- frame().document()->dispatchWindowEvent(resizeEvent.release(), frame().document()->domWindow());
- else
- frame().document()->enqueueWindowEvent(resizeEvent.release());
+ frame().document()->dispatchWindowEvent(resizeEvent);
+ else {
+ // FIXME: Queueing this event for an unpredictable time in the future seems
+ // intrinsically racy. By the time this resize event fires, the frame might
+ // be resized again, so we could end up with two resize events for the same size.
+ frame().document()->enqueueWindowEvent(WTFMove(resizeEvent));
+ }
-#if ENABLE(INSPECTOR)
- Page* page = frame().page();
if (InspectorInstrumentation::hasFrontends() && isMainFrame) {
- if (InspectorClient* inspectorClient = page ? page->inspectorController().inspectorClient() : nullptr)
- inspectorClient->didResizeMainFrame(&frame());
+ if (Page* page = frame().page()) {
+ if (InspectorClient* inspectorClient = page->inspectorController().inspectorClient())
+ inspectorClient->didResizeMainFrame(&frame());
+ }
}
-#endif
}
void FrameView::willStartLiveResize()
@@ -2803,11 +3621,6 @@ void FrameView::willEndLiveResize()
adjustTiledBackingCoverage();
}
-void FrameView::postLayoutTimerFired(Timer<FrameView>&)
-{
- performPostLayoutTasks();
-}
-
void FrameView::autoSizeIfEnabled()
{
if (!m_shouldAutoSize)
@@ -2816,7 +3629,9 @@ void FrameView::autoSizeIfEnabled()
if (m_inAutoSize)
return;
- TemporaryChange<bool> changeInAutoSize(m_inAutoSize, true);
+ LOG(Layout, "FrameView %p autoSizeIfEnabled", this);
+
+ SetForScope<bool> changeInAutoSize(m_inAutoSize, true);
Document* document = frame().document();
if (!document)
@@ -2827,6 +3642,8 @@ void FrameView::autoSizeIfEnabled()
if (!documentView || !documentElement)
return;
+ if (m_layoutRoot)
+ convertSubtreeLayoutToFullLayout();
// Start from the minimum size and allow it to grow.
resize(m_minAutoSize.width(), m_minAutoSize.height());
@@ -2849,8 +3666,7 @@ void FrameView::autoSizeIfEnabled()
RefPtr<Scrollbar> localHorizontalScrollbar = horizontalScrollbar();
if (!localHorizontalScrollbar)
localHorizontalScrollbar = createScrollbar(HorizontalScrollbar);
- if (!localHorizontalScrollbar->isOverlayScrollbar())
- newSize.setHeight(newSize.height() + localHorizontalScrollbar->height());
+ newSize.expand(0, localHorizontalScrollbar->occupiedHeight());
// Don't bother checking for a vertical scrollbar because the width is at
// already greater the maximum.
@@ -2858,8 +3674,7 @@ void FrameView::autoSizeIfEnabled()
RefPtr<Scrollbar> localVerticalScrollbar = verticalScrollbar();
if (!localVerticalScrollbar)
localVerticalScrollbar = createScrollbar(VerticalScrollbar);
- if (!localVerticalScrollbar->isOverlayScrollbar())
- newSize.setWidth(newSize.width() + localVerticalScrollbar->width());
+ newSize.expand(localVerticalScrollbar->occupiedWidth(), 0);
// Don't bother checking for a horizontal scrollbar because the height is
// already greater the maximum.
@@ -2889,7 +3704,10 @@ void FrameView::autoSizeIfEnabled()
&& !frame().loader().isComplete() && (newSize.height() < size.height() || newSize.width() < size.width()))
break;
- resize(newSize.width(), newSize.height());
+ // The first time around, resize to the minimum height again; otherwise,
+ // on pages (e.g. quirks mode) where the body/document resize to the view size,
+ // we'll end up not shrinking back down after resizing to the computed preferred width.
+ resize(newSize.width(), i ? newSize.height() : m_minAutoSize.height());
// Force the scrollbar state to avoid the scrollbar code adding them and causing them to be needed. For example,
// a vertical scrollbar may cause text to wrap and thus increase the height (which is the only reason the scollbar is needed).
setVerticalScrollbarLock(false);
@@ -2897,6 +3715,9 @@ void FrameView::autoSizeIfEnabled()
setScrollbarModes(horizonalScrollbarMode, verticalScrollbarMode, true, true);
}
+ // All the resizing above may have invalidated style (for example if viewport units are being used).
+ document->updateStyleIfNeeded();
+
m_autoSizeContentSize = contentsSize();
if (m_autoSizeFixedMinimumHeight) {
@@ -2917,9 +3738,37 @@ void FrameView::setAutoSizeFixedMinimumHeight(int fixedMinimumHeight)
setNeedsLayout();
}
+RenderElement* FrameView::viewportRenderer() const
+{
+ if (m_viewportRendererType == ViewportRendererType::None)
+ return nullptr;
+
+ auto* document = frame().document();
+ if (!document)
+ return nullptr;
+
+ if (m_viewportRendererType == ViewportRendererType::Document) {
+ auto* documentElement = document->documentElement();
+ if (!documentElement)
+ return nullptr;
+ return documentElement->renderer();
+ }
+
+ if (m_viewportRendererType == ViewportRendererType::Body) {
+ auto* body = document->body();
+ if (!body)
+ return nullptr;
+ return body->renderer();
+ }
+
+ ASSERT_NOT_REACHED();
+ return nullptr;
+}
+
void FrameView::updateOverflowStatus(bool horizontalOverflow, bool verticalOverflow)
{
- if (!m_viewportRenderer)
+ auto* viewportRenderer = this->viewportRenderer();
+ if (!viewportRenderer)
return;
if (m_overflowStatusDirty) {
@@ -2936,13 +3785,12 @@ void FrameView::updateOverflowStatus(bool horizontalOverflow, bool verticalOverf
m_horizontalOverflow = horizontalOverflow;
m_verticalOverflow = verticalOverflow;
- RefPtr<OverflowEvent> overflowEvent = OverflowEvent::create(horizontalOverflowChanged, horizontalOverflow,
+ Ref<OverflowEvent> overflowEvent = OverflowEvent::create(horizontalOverflowChanged, horizontalOverflow,
verticalOverflowChanged, verticalOverflow);
- overflowEvent->setTarget(m_viewportRenderer->element());
+ overflowEvent->setTarget(viewportRenderer->element());
- frame().document()->enqueueOverflowEvent(overflowEvent.release());
+ frame().document()->enqueueOverflowEvent(WTFMove(overflowEvent));
}
-
}
const Pagination& FrameView::pagination() const
@@ -2950,8 +3798,10 @@ const Pagination& FrameView::pagination() const
if (m_pagination != Pagination())
return m_pagination;
- if (frame().isMainFrame())
- return frame().page()->pagination();
+ if (frame().isMainFrame()) {
+ if (Page* page = frame().page())
+ return page->pagination();
+ }
return m_pagination;
}
@@ -2963,22 +3813,22 @@ void FrameView::setPagination(const Pagination& pagination)
m_pagination = pagination;
- frame().document()->styleResolverChanged(DeferRecalcStyle);
+ frame().document()->styleScope().didChangeStyleSheetEnvironment();
}
-IntRect FrameView::windowClipRect(bool clipToContents) const
+IntRect FrameView::windowClipRect() const
{
ASSERT(frame().view() == this);
+ if (m_cachedWindowClipRect)
+ return *m_cachedWindowClipRect;
+
if (paintsEntireContents())
- return IntRect(IntPoint(), totalContentsSize());
+ return contentsToWindow(IntRect(IntPoint(), totalContentsSize()));
// Set our clip rect to be our contents.
- IntRect clipRect;
- if (clipToContents)
- clipRect = contentsToWindow(visibleContentRect(LegacyIOSDocumentVisibleRect));
- else
- clipRect = contentsToWindow(visibleContentRectIncludingScrollbars(LegacyIOSDocumentVisibleRect));
+ IntRect clipRect = contentsToWindow(visibleContentRect(LegacyIOSDocumentVisibleRect));
+
if (!frame().ownerElement())
return clipRect;
@@ -3004,9 +3854,9 @@ IntRect FrameView::windowClipRectForFrameOwner(const HTMLFrameOwnerElement* owne
// Apply the clip from the layer.
IntRect clipRect;
if (clipToLayerContents)
- clipRect = pixelSnappedIntRect(enclosingLayer->childrenClipRect());
+ clipRect = snappedIntRect(enclosingLayer->childrenClipRect());
else
- clipRect = pixelSnappedIntRect(enclosingLayer->selfClipRect());
+ clipRect = snappedIntRect(enclosingLayer->selfClipRect());
clipRect = contentsToWindow(clipRect);
return intersection(clipRect, windowClipRect());
}
@@ -3017,46 +3867,79 @@ bool FrameView::isActive() const
return page && page->focusController().isActive();
}
-bool FrameView::updatesScrollLayerPositionOnMainThread() const
+bool FrameView::forceUpdateScrollbarsOnMainThreadForPerformanceTesting() const
{
- if (Page* page = frame().page()) {
- if (ScrollingCoordinator* scrollingCoordinator = page->scrollingCoordinator())
- return scrollingCoordinator->shouldUpdateScrollLayerPositionSynchronously();
- }
+ Page* page = frame().page();
+ return page && page->settings().forceUpdateScrollbarsOnMainThreadForPerformanceTesting();
+}
- return true;
+void FrameView::scrollTo(const ScrollPosition& newPosition)
+{
+ IntPoint oldPosition = scrollPosition();
+ ScrollView::scrollTo(newPosition);
+ if (oldPosition != scrollPosition())
+ scrollPositionChanged(oldPosition, scrollPosition());
+
+ didChangeScrollOffset();
}
-void FrameView::scrollTo(const IntSize& newOffset)
+float FrameView::adjustScrollStepForFixedContent(float step, ScrollbarOrientation orientation, ScrollGranularity granularity)
{
- LayoutSize offset = scrollOffset();
- ScrollView::scrollTo(newOffset);
- if (offset != scrollOffset())
- scrollPositionChanged();
- frame().loader().client().didChangeScrollOffset();
+ if (granularity != ScrollByPage || orientation == HorizontalScrollbar)
+ return step;
+
+ TrackedRendererListHashSet* positionedObjects = nullptr;
+ if (RenderView* root = frame().contentRenderer()) {
+ if (!root->hasPositionedObjects())
+ return step;
+ positionedObjects = root->positionedObjects();
+ }
+
+ FloatRect unobscuredContentRect = this->unobscuredContentRect();
+ float topObscuredArea = 0;
+ float bottomObscuredArea = 0;
+ for (const auto& positionedObject : *positionedObjects) {
+ const RenderStyle& style = positionedObject->style();
+ if (style.position() != FixedPosition || style.visibility() == HIDDEN || !style.opacity())
+ continue;
+
+ FloatQuad contentQuad = positionedObject->absoluteContentQuad();
+ if (!contentQuad.isRectilinear())
+ continue;
+
+ FloatRect contentBoundingBox = contentQuad.boundingBox();
+ FloatRect fixedRectInView = intersection(unobscuredContentRect, contentBoundingBox);
+
+ if (fixedRectInView.width() < unobscuredContentRect.width())
+ continue;
+
+ if (fixedRectInView.y() == unobscuredContentRect.y())
+ topObscuredArea = std::max(topObscuredArea, fixedRectInView.height());
+ else if (fixedRectInView.maxY() == unobscuredContentRect.maxY())
+ bottomObscuredArea = std::max(bottomObscuredArea, fixedRectInView.height());
+ }
+
+ return Scrollbar::pageStep(unobscuredContentRect.height(), unobscuredContentRect.height() - topObscuredArea - bottomObscuredArea);
}
-void FrameView::invalidateScrollbarRect(Scrollbar* scrollbar, const IntRect& rect)
+void FrameView::invalidateScrollbarRect(Scrollbar& scrollbar, const IntRect& rect)
{
// Add in our offset within the FrameView.
IntRect dirtyRect = rect;
- dirtyRect.moveBy(scrollbar->location());
+ dirtyRect.moveBy(scrollbar.location());
invalidateRect(dirtyRect);
}
-IntRect FrameView::windowResizerRect() const
-{
- if (Page* page = frame().page())
- return page->chrome().windowResizerRect();
- return IntRect();
-}
-
float FrameView::visibleContentScaleFactor() const
{
if (!frame().isMainFrame() || !frame().settings().delegatesPageScaling())
return 1;
- return frame().page()->pageScaleFactor();
+ Page* page = frame().page();
+ if (!page)
+ return 1;
+
+ return page->pageScaleFactor();
}
void FrameView::setVisibleScrollerThumbRect(const IntRect& scrollerThumb)
@@ -3064,16 +3947,17 @@ void FrameView::setVisibleScrollerThumbRect(const IntRect& scrollerThumb)
if (!frame().isMainFrame())
return;
- frame().page()->chrome().client().notifyScrollerThumbIsVisibleInRect(scrollerThumb);
+ if (Page* page = frame().page())
+ page->chrome().client().notifyScrollerThumbIsVisibleInRect(scrollerThumb);
}
ScrollableArea* FrameView::enclosingScrollableArea() const
{
// FIXME: Walk up the frame tree and look for a scrollable parent frame or RenderLayer.
- return 0;
+ return nullptr;
}
-IntRect FrameView::scrollableAreaBoundingBox() const
+IntRect FrameView::scrollableAreaBoundingBox(bool*) const
{
RenderWidget* ownerRenderer = frame().ownerRenderer();
if (!ownerRenderer)
@@ -3082,7 +3966,7 @@ IntRect FrameView::scrollableAreaBoundingBox() const
return ownerRenderer->absoluteContentQuad().enclosingBoundingBox();
}
-bool FrameView::isScrollable()
+bool FrameView::isScrollable(Scrollability definitionOfScrollable)
{
// Check for:
// 1) If there an actual overflow.
@@ -3090,11 +3974,18 @@ bool FrameView::isScrollable()
// 3) overflow{-x,-y}: hidden;
// 4) scrolling: no;
+ bool requiresActualOverflowToBeConsideredScrollable = !frame().isMainFrame() || definitionOfScrollable != Scrollability::ScrollableOrRubberbandable;
+#if !ENABLE(RUBBER_BANDING)
+ requiresActualOverflowToBeConsideredScrollable = true;
+#endif
+
// Covers #1
- IntSize totalContentsSize = this->totalContentsSize();
- IntSize visibleContentSize = visibleContentRect(LegacyIOSDocumentVisibleRect).size();
- if ((totalContentsSize.height() <= visibleContentSize.height() && totalContentsSize.width() <= visibleContentSize.width()))
- return false;
+ if (requiresActualOverflowToBeConsideredScrollable) {
+ IntSize totalContentsSize = this->totalContentsSize();
+ IntSize visibleContentSize = visibleContentRect(LegacyIOSDocumentVisibleRect).size();
+ if (totalContentsSize.height() <= visibleContentSize.height() && totalContentsSize.width() <= visibleContentSize.width())
+ return false;
+ }
// Covers #2.
HTMLFrameOwnerElement* owner = frame().ownerElement();
@@ -3111,6 +4002,25 @@ bool FrameView::isScrollable()
return true;
}
+bool FrameView::isScrollableOrRubberbandable()
+{
+ return isScrollable(Scrollability::ScrollableOrRubberbandable);
+}
+
+bool FrameView::hasScrollableOrRubberbandableAncestor()
+{
+ if (frame().isMainFrame())
+ return isScrollableOrRubberbandable();
+
+ for (FrameView* parent = this->parentFrameView(); parent; parent = parent->parentFrameView()) {
+ Scrollability frameScrollability = parent->frame().isMainFrame() ? Scrollability::ScrollableOrRubberbandable : Scrollability::Scrollable;
+ if (parent->isScrollable(frameScrollability))
+ return true;
+ }
+
+ return false;
+}
+
void FrameView::updateScrollableAreaSet()
{
// That ensures that only inner frames are cached.
@@ -3131,15 +4041,15 @@ bool FrameView::shouldSuspendScrollAnimations() const
return frame().loader().state() != FrameStateComplete;
}
-void FrameView::scrollbarStyleChanged(int newStyle, bool forceUpdate)
+void FrameView::scrollbarStyleChanged(ScrollbarStyle newStyle, bool forceUpdate)
{
if (!frame().isMainFrame())
return;
- frame().page()->chrome().client().recommendedScrollbarStyleDidChange(newStyle);
+ if (Page* page = frame().page())
+ page->chrome().client().recommendedScrollbarStyleDidChange(newStyle);
- if (forceUpdate)
- ScrollView::scrollbarStyleChanged(newStyle, forceUpdate);
+ ScrollView::scrollbarStyleChanged(newStyle, forceUpdate);
}
void FrameView::notifyPageThatContentAreaWillPaint() const
@@ -3187,14 +4097,14 @@ void FrameView::updateAnnotatedRegions()
void FrameView::updateScrollCorner()
{
- RenderElement* renderer = 0;
- RefPtr<RenderStyle> cornerStyle;
+ RenderElement* renderer = nullptr;
+ std::unique_ptr<RenderStyle> cornerStyle;
IntRect cornerRect = scrollCornerRect();
if (!cornerRect.isEmpty()) {
// Try the <body> element first as a scroll corner source.
Document* doc = frame().document();
- Element* body = doc ? doc->body() : 0;
+ Element* body = doc ? doc->bodyOrFrameset() : nullptr;
if (body && body->renderer()) {
renderer = body->renderer();
cornerStyle = renderer->getUncachedPseudoStyle(PseudoStyleRequest(SCROLLBAR_CORNER), &renderer->style());
@@ -3202,7 +4112,7 @@ void FrameView::updateScrollCorner()
if (!cornerStyle) {
// If the <body> didn't have a custom style, then the root element might.
- Element* docElement = doc ? doc->documentElement() : 0;
+ Element* docElement = doc ? doc->documentElement() : nullptr;
if (docElement && docElement->renderer()) {
renderer = docElement->renderer();
cornerStyle = renderer->getUncachedPseudoStyle(PseudoStyleRequest(SCROLLBAR_CORNER), &renderer->style());
@@ -3220,26 +4130,24 @@ void FrameView::updateScrollCorner()
m_scrollCorner = nullptr;
else {
if (!m_scrollCorner) {
- m_scrollCorner = createRenderer<RenderScrollbarPart>(renderer->document(), cornerStyle.releaseNonNull());
+ m_scrollCorner = createRenderer<RenderScrollbarPart>(renderer->document(), WTFMove(*cornerStyle));
m_scrollCorner->initializeStyle();
} else
- m_scrollCorner->setStyle(cornerStyle.releaseNonNull());
+ m_scrollCorner->setStyle(WTFMove(*cornerStyle));
invalidateScrollCorner(cornerRect);
}
-
- ScrollView::updateScrollCorner();
}
-void FrameView::paintScrollCorner(GraphicsContext* context, const IntRect& cornerRect)
+void FrameView::paintScrollCorner(GraphicsContext& context, const IntRect& cornerRect)
{
- if (context->updatingControlTints()) {
+ if (context.updatingControlTints()) {
updateScrollCorner();
return;
}
if (m_scrollCorner) {
if (frame().isMainFrame())
- context->fillRect(cornerRect, baseBackgroundColor(), ColorSpaceDeviceRGB);
+ context.fillRect(cornerRect, baseBackgroundColor());
m_scrollCorner->paintIntoRect(context, cornerRect.location(), cornerRect);
return;
}
@@ -3247,12 +4155,12 @@ void FrameView::paintScrollCorner(GraphicsContext* context, const IntRect& corne
ScrollView::paintScrollCorner(context, cornerRect);
}
-void FrameView::paintScrollbar(GraphicsContext* context, Scrollbar* bar, const IntRect& rect)
+void FrameView::paintScrollbar(GraphicsContext& context, Scrollbar& bar, const IntRect& rect)
{
- if (bar->isCustomScrollbar() && frame().isMainFrame()) {
- IntRect toFill = bar->frameRect();
+ if (bar.isCustomScrollbar() && frame().isMainFrame()) {
+ IntRect toFill = bar.frameRect();
toFill.intersect(rect);
- context->fillRect(toFill, baseBackgroundColor(), ColorSpaceDeviceRGB);
+ context.fillRect(toFill, baseBackgroundColor());
}
ScrollView::paintScrollbar(context, bar, rect);
@@ -3268,8 +4176,8 @@ Color FrameView::documentBackgroundColor() const
if (!frame().document())
return Color();
- Element* htmlElement = frame().document()->documentElement();
- Element* bodyElement = frame().document()->body();
+ auto* htmlElement = frame().document()->documentElement();
+ auto* bodyElement = frame().document()->bodyOrFrameset();
// Start with invalid colors.
Color htmlBackgroundColor;
@@ -3301,46 +4209,48 @@ Color FrameView::documentBackgroundColor() const
bool FrameView::hasCustomScrollbars() const
{
for (auto& widget : children()) {
- if (widget->isFrameView()) {
- if (toFrameView(*widget).hasCustomScrollbars())
+ if (is<FrameView>(widget.get())) {
+ if (downcast<FrameView>(widget.get()).hasCustomScrollbars())
return true;
- } else if (widget->isScrollbar()) {
- if (toScrollbar(*widget).isCustomScrollbar())
+ } else if (is<Scrollbar>(widget.get())) {
+ if (downcast<Scrollbar>(widget.get()).isCustomScrollbar())
return true;
}
}
-
return false;
}
FrameView* FrameView::parentFrameView() const
{
if (!parent())
- return 0;
-
- if (Frame* parentFrame = frame().tree().parent())
- return parentFrame->view();
-
- return 0;
+ return nullptr;
+ auto* parentFrame = frame().tree().parent();
+ if (!parentFrame)
+ return nullptr;
+ return parentFrame->view();
}
bool FrameView::isInChildFrameWithFrameFlattening() const
{
- if (!parent() || !frame().ownerElement())
+ if (!frameFlatteningEnabled())
return false;
- // Frame flattening applies when the owner element is either in a frameset or
- // an iframe with flattening parameters.
- if (frame().ownerElement()->hasTagName(iframeTag)) {
- RenderIFrame* iframeRenderer = toRenderIFrame(frame().ownerElement()->renderWidget());
- if (iframeRenderer->flattenFrame() || iframeRenderer->isSeamless())
- return true;
- }
+ if (!parent())
+ return false;
- if (!frameFlatteningEnabled())
+ HTMLFrameOwnerElement* ownerElement = frame().ownerElement();
+ if (!ownerElement)
+ return false;
+
+ if (!ownerElement->renderWidget())
return false;
- if (frame().ownerElement()->hasTagName(frameTag))
+ // Frame flattening applies when the owner element is either in a frameset or
+ // an iframe with flattening parameters.
+ if (is<HTMLIFrameElement>(*ownerElement))
+ return downcast<RenderIFrame>(*ownerElement->renderWidget()).flattenFrame();
+
+ if (is<HTMLFrameElement>(*ownerElement))
return true;
return false;
@@ -3366,10 +4276,8 @@ void FrameView::startLayoutAtMainFrameViewIfNeeded(bool allowSubtree)
while (parentView->parentFrameView())
parentView = parentView->parentFrameView();
+ LOG(Layout, " frame flattening, starting from root");
parentView->layout(allowSubtree);
-
- RenderElement* root = m_layoutRoot ? m_layoutRoot : frame().document()->renderView();
- ASSERT_UNUSED(root, !root->needsLayout());
}
void FrameView::updateControlTints()
@@ -3383,23 +4291,33 @@ void FrameView::updateControlTints()
if (frame().document()->url().isEmpty())
return;
+ // As noted above, this is a "fake" paint, so we should pause counting relevant repainted objects.
+ Page* page = frame().page();
+ bool isCurrentlyCountingRelevantRepaintedObject = false;
+ if (page) {
+ isCurrentlyCountingRelevantRepaintedObject = page->isCountingRelevantRepaintedObjects();
+ page->setIsCountingRelevantRepaintedObjects(false);
+ }
+
RenderView* renderView = this->renderView();
if ((renderView && renderView->theme().supportsControlTints()) || hasCustomScrollbars())
paintControlTints();
+
+ if (page)
+ page->setIsCountingRelevantRepaintedObjects(isCurrentlyCountingRelevantRepaintedObject);
}
void FrameView::paintControlTints()
{
if (needsLayout())
layout();
- PlatformGraphicsContext* const noContext = 0;
- GraphicsContext context(noContext);
- context.setUpdatingControlTints(true);
+
+ GraphicsContext context(GraphicsContext::NonPaintingReasons::UpdatingControlTints);
if (platformWidget()) {
// FIXME: consult paintsEntireContents().
- paintContents(&context, visibleContentRect(LegacyIOSDocumentVisibleRect));
+ paintContents(context, visibleContentRect(LegacyIOSDocumentVisibleRect));
} else
- paint(&context, frameRect());
+ paint(context, frameRect());
}
bool FrameView::wasScrolledByUser() const
@@ -3411,134 +4329,141 @@ void FrameView::setWasScrolledByUser(bool wasScrolledByUser)
{
if (m_inProgrammaticScroll)
return;
- m_maintainScrollPositionAnchor = 0;
+ m_maintainScrollPositionAnchor = nullptr;
if (m_wasScrolledByUser == wasScrolledByUser)
return;
m_wasScrolledByUser = wasScrolledByUser;
+ if (frame().isMainFrame())
+ updateLayerFlushThrottling();
adjustTiledBackingCoverage();
}
-void FrameView::paintContents(GraphicsContext* p, const IntRect& rect)
+void FrameView::willPaintContents(GraphicsContext& context, const IntRect&, PaintingState& paintingState)
{
Document* document = frame().document();
-#ifndef NDEBUG
- bool fillWithRed;
- if (document->printing())
- fillWithRed = false; // Printing, don't fill with red (can't remember why).
- else if (frame().ownerElement())
- fillWithRed = false; // Subframe, don't fill with red.
- else if (isTransparent())
- fillWithRed = false; // Transparent, don't fill with red.
- else if (m_paintBehavior & PaintBehaviorSelectionOnly)
- fillWithRed = false; // Selections are transparent, don't fill with red.
- else if (m_nodeToDraw)
- fillWithRed = false; // Element images are transparent, don't fill with red.
- else
- fillWithRed = true;
-
- if (fillWithRed)
- p->fillRect(rect, Color(0xFF, 0, 0), ColorSpaceDeviceRGB);
-#endif
-
- RenderView* renderView = this->renderView();
- if (!renderView) {
- LOG_ERROR("called FrameView::paint with nil renderer");
- return;
- }
-
- ASSERT(!needsLayout());
- if (needsLayout())
- return;
+ if (!context.paintingDisabled())
+ InspectorInstrumentation::willPaint(*renderView());
- if (!p->paintingDisabled())
- InspectorInstrumentation::willPaint(renderView);
+ paintingState.isTopLevelPainter = !sCurrentPaintTimeStamp;
- bool isTopLevelPainter = !sCurrentPaintTimeStamp;
-#if PLATFORM(IOS)
- // FIXME: Remove PLATFORM(IOS)-guard once we upstream the iOS changes to MemoryPressureHandler.h.
- if (isTopLevelPainter && memoryPressureHandler().hasReceivedMemoryPressure()) {
- LOG(MemoryPressure, "Under memory pressure: %s", __PRETTY_FUNCTION__);
+ if (paintingState.isTopLevelPainter && MemoryPressureHandler::singleton().isUnderMemoryPressure()) {
+ LOG(MemoryPressure, "Under memory pressure: %s", WTF_PRETTY_FUNCTION);
// To avoid unnecessary image decoding, we don't prune recently-decoded live resources here since
// we might need some live bitmaps on painting.
- memoryCache()->prune();
+ MemoryCache::singleton().prune();
}
-#endif
- if (isTopLevelPainter)
- sCurrentPaintTimeStamp = monotonicallyIncreasingTime();
-
- FontCachePurgePreventer fontCachePurgePreventer;
-#if USE(ACCELERATED_COMPOSITING)
- if (!p->paintingDisabled() && !document->printing())
- flushCompositingStateForThisFrame(&frame());
-#endif
+ if (paintingState.isTopLevelPainter)
+ sCurrentPaintTimeStamp = monotonicallyIncreasingTime();
- PaintBehavior oldPaintBehavior = m_paintBehavior;
+ paintingState.paintBehavior = m_paintBehavior;
if (FrameView* parentView = parentFrameView()) {
if (parentView->paintBehavior() & PaintBehaviorFlattenCompositingLayers)
m_paintBehavior |= PaintBehaviorFlattenCompositingLayers;
}
-
- if (m_paintBehavior == PaintBehaviorNormal)
- document->markers().invalidateRenderedRectsForMarkersInRect(rect);
if (document->printing())
m_paintBehavior |= PaintBehaviorFlattenCompositingLayers;
- bool flatteningPaint = m_paintBehavior & PaintBehaviorFlattenCompositingLayers;
- bool isRootFrame = !frame().ownerElement();
- if (flatteningPaint && isRootFrame)
+ paintingState.isFlatteningPaintOfRootFrame = (m_paintBehavior & PaintBehaviorFlattenCompositingLayers) && !frame().ownerElement();
+ if (paintingState.isFlatteningPaintOfRootFrame)
notifyWidgetsInAllFrames(WillPaintFlattened);
ASSERT(!m_isPainting);
m_isPainting = true;
+}
- // m_nodeToDraw is used to draw only one element (and its descendants)
- RenderObject* eltRenderer = m_nodeToDraw ? m_nodeToDraw->renderer() : 0;
- RenderLayer* rootLayer = renderView->layer();
-
-#ifndef NDEBUG
- RenderElement::SetLayoutNeededForbiddenScope forbidSetNeedsLayout(&rootLayer->renderer());
-#endif
-
- rootLayer->paint(p, rect, m_paintBehavior, eltRenderer);
-
- if (rootLayer->containsDirtyOverlayScrollbars())
- rootLayer->paintOverlayScrollbars(p, rect, m_paintBehavior, eltRenderer);
-
+void FrameView::didPaintContents(GraphicsContext& context, const IntRect& dirtyRect, PaintingState& paintingState)
+{
m_isPainting = false;
- if (flatteningPaint && isRootFrame)
+ if (paintingState.isFlatteningPaintOfRootFrame)
notifyWidgetsInAllFrames(DidPaintFlattened);
- m_paintBehavior = oldPaintBehavior;
+ m_paintBehavior = paintingState.paintBehavior;
m_lastPaintTime = monotonicallyIncreasingTime();
-#if PLATFORM(IOS)
// Painting can lead to decoding of large amounts of bitmaps
// If we are low on memory, wipe them out after the paint.
- // FIXME: Remove PLATFORM(IOS)-guard once we upstream the iOS changes to MemoryPressureHandler.h.
- if (isTopLevelPainter && memoryPressureHandler().hasReceivedMemoryPressure())
- memoryCache()->pruneLiveResources(true);
-#endif
+ if (paintingState.isTopLevelPainter && MemoryPressureHandler::singleton().isUnderMemoryPressure())
+ MemoryCache::singleton().pruneLiveResources(true);
// Regions may have changed as a result of the visibility/z-index of element changing.
#if ENABLE(DASHBOARD_SUPPORT)
- if (document->annotatedRegionsDirty())
+ if (frame().document()->annotatedRegionsDirty())
updateAnnotatedRegions();
#endif
- if (isTopLevelPainter)
+ if (paintingState.isTopLevelPainter)
sCurrentPaintTimeStamp = 0;
- if (!p->paintingDisabled()) {
- InspectorInstrumentation::didPaint(renderView, p, rect);
+ if (!context.paintingDisabled()) {
+ InspectorInstrumentation::didPaint(*renderView(), dirtyRect);
// FIXME: should probably not fire milestones for snapshot painting. https://bugs.webkit.org/show_bug.cgi?id=117623
- firePaintRelatedMilestones();
+ firePaintRelatedMilestonesIfNeeded();
+ }
+}
+
+void FrameView::paintContents(GraphicsContext& context, const IntRect& dirtyRect, SecurityOriginPaintPolicy securityOriginPaintPolicy)
+{
+#ifndef NDEBUG
+ bool fillWithRed;
+ if (frame().document()->printing())
+ fillWithRed = false; // Printing, don't fill with red (can't remember why).
+ else if (frame().ownerElement())
+ fillWithRed = false; // Subframe, don't fill with red.
+ else if (isTransparent())
+ fillWithRed = false; // Transparent, don't fill with red.
+ else if (m_paintBehavior & PaintBehaviorSelectionOnly)
+ fillWithRed = false; // Selections are transparent, don't fill with red.
+ else if (m_nodeToDraw)
+ fillWithRed = false; // Element images are transparent, don't fill with red.
+ else
+ fillWithRed = true;
+
+ if (fillWithRed)
+ context.fillRect(dirtyRect, Color(0xFF, 0, 0));
+#endif
+
+ RenderView* renderView = this->renderView();
+ if (!renderView) {
+ LOG_ERROR("called FrameView::paint with nil renderer");
+ return;
}
+
+ if (!inPaintableState())
+ return;
+
+ TraceScope tracingScope(PaintViewStart, PaintViewEnd);
+
+ ASSERT(!needsLayout());
+ if (needsLayout())
+ return;
+
+ PaintingState paintingState;
+ willPaintContents(context, dirtyRect, paintingState);
+
+ // m_nodeToDraw is used to draw only one element (and its descendants)
+ RenderObject* renderer = m_nodeToDraw ? m_nodeToDraw->renderer() : nullptr;
+ RenderLayer* rootLayer = renderView->layer();
+
+#ifndef NDEBUG
+ RenderElement::SetLayoutNeededForbiddenScope forbidSetNeedsLayout(&rootLayer->renderer());
+#endif
+
+ // To work around http://webkit.org/b/135106, ensure that the paint root isn't an inline with culled line boxes.
+ // FIXME: This can cause additional content to be included in the snapshot, so remove this once that bug is fixed.
+ while (is<RenderInline>(renderer) && !downcast<RenderInline>(*renderer).firstLineBox())
+ renderer = renderer->parent();
+
+ rootLayer->paint(context, dirtyRect, LayoutSize(), m_paintBehavior, renderer, 0, securityOriginPaintPolicy == SecurityOriginPaintPolicy::AnyOrigin ? RenderLayer::SecurityOriginPaintPolicy::AnyOrigin : RenderLayer::SecurityOriginPaintPolicy::AccessibleOriginOnly);
+ if (rootLayer->containsDirtyOverlayScrollbars())
+ rootLayer->paintOverlayScrollbars(context, dirtyRect, m_paintBehavior, renderer);
+
+ didPaintContents(context, dirtyRect, paintingState);
}
void FrameView::setPaintBehavior(PaintBehavior behavior)
@@ -3562,7 +4487,7 @@ void FrameView::setNodeToDraw(Node* node)
m_nodeToDraw = node;
}
-void FrameView::paintContentsForSnapshot(GraphicsContext* context, const IntRect& imageRect, SelectionInSnapshot shouldPaintSelection, CoordinateSpaceForSnapshot coordinateSpace)
+void FrameView::paintContentsForSnapshot(GraphicsContext& context, const IntRect& imageRect, SelectionInSnapshot shouldPaintSelection, CoordinateSpaceForSnapshot coordinateSpace)
{
updateLayoutAndStyleIfNeededRecursive();
@@ -3574,7 +4499,7 @@ void FrameView::paintContentsForSnapshot(GraphicsContext* context, const IntRect
// in the render tree only. This will allow us to restore the selection from the DOM
// after we paint the snapshot.
if (shouldPaintSelection == ExcludeSelection) {
- for (Frame* frame = m_frame.get(); frame; frame = frame->tree().traverseNext(m_frame.get())) {
+ for (auto* frame = m_frame.ptr(); frame; frame = frame->tree().traverseNext(m_frame.ptr())) {
if (RenderView* root = frame->contentRenderer())
root->clearSelection();
}
@@ -3590,7 +4515,7 @@ void FrameView::paintContentsForSnapshot(GraphicsContext* context, const IntRect
// Restore selection.
if (shouldPaintSelection == ExcludeSelection) {
- for (Frame* frame = m_frame.get(); frame; frame = frame->tree().traverseNext(m_frame.get()))
+ for (auto* frame = m_frame.ptr(); frame; frame = frame->tree().traverseNext(m_frame.ptr()))
frame->selection().updateAppearance();
}
@@ -3598,9 +4523,9 @@ void FrameView::paintContentsForSnapshot(GraphicsContext* context, const IntRect
setPaintBehavior(oldBehavior);
}
-void FrameView::paintOverhangAreas(GraphicsContext* context, const IntRect& horizontalOverhangArea, const IntRect& verticalOverhangArea, const IntRect& dirtyRect)
+void FrameView::paintOverhangAreas(GraphicsContext& context, const IntRect& horizontalOverhangArea, const IntRect& verticalOverhangArea, const IntRect& dirtyRect)
{
- if (context->paintingDisabled())
+ if (context.paintingDisabled())
return;
if (frame().document()->printing())
@@ -3609,6 +4534,17 @@ void FrameView::paintOverhangAreas(GraphicsContext* context, const IntRect& hori
ScrollView::paintOverhangAreas(context, horizontalOverhangArea, verticalOverhangArea, dirtyRect);
}
+FrameView::FrameViewList FrameView::renderedChildFrameViews() const
+{
+ FrameViewList childViews;
+ for (Frame* frame = m_frame->tree().firstRenderedChild(); frame; frame = frame->tree().nextRenderedSibling()) {
+ if (frame->view())
+ childViews.append(*frame->view());
+ }
+
+ return childViews;
+}
+
void FrameView::updateLayoutAndStyleIfNeededRecursive()
{
// We have to crawl our entire tree looking for any FrameViews that need
@@ -3620,28 +4556,24 @@ void FrameView::updateLayoutAndStyleIfNeededRecursive()
// region but then become included later by the second frame adding rects to the dirty region
// when it lays out.
+ AnimationUpdateBlock animationUpdateBlock(&frame().animation());
+
frame().document()->updateStyleIfNeeded();
if (needsLayout())
layout();
- // Grab a copy of the children() set, as it may be mutated by the following updateLayoutAndStyleIfNeededRecursive
- // calls, as they can potentially re-enter a layout of the parent frame view, which may add/remove scrollbars
- // and thus mutates the children() set.
- Vector<Ref<FrameView>, 16> childViews;
- childViews.reserveInitialCapacity(children().size());
- for (auto& widget : children()) {
- if (widget->isFrameView())
- childViews.uncheckedAppend(toFrameView(*widget));
- }
+ // Grab a copy of the child views, as the list may be mutated by the following updateLayoutAndStyleIfNeededRecursive
+ // calls, as they can potentially re-enter a layout of the parent frame view.
+ for (auto& frameView : renderedChildFrameViews())
+ frameView->updateLayoutAndStyleIfNeededRecursive();
- for (unsigned i = 0; i < childViews.size(); ++i)
- childViews[i]->updateLayoutAndStyleIfNeededRecursive();
+ // A child frame may have dirtied us during its layout.
+ frame().document()->updateStyleIfNeeded();
+ if (needsLayout())
+ layout();
- // When frame flattening is on, child frame can mark parent frame dirty. In such case, child frame
- // needs to call layout on parent frame recursively.
- // This assert ensures that parent frames are clean, when child frames finished updating layout and style.
- ASSERT(!needsLayout());
+ ASSERT(!frame().isMainFrame() || !needsStyleRecalcOrLayout());
}
bool FrameView::qualifiesAsVisuallyNonEmpty() const
@@ -3652,12 +4584,14 @@ bool FrameView::qualifiesAsVisuallyNonEmpty() const
return false;
// Ensure that we always get marked visually non-empty eventually.
- if (!frame().document()->parsing() && frame().loader().stateMachine()->committedFirstRealDocumentLoad())
+ if (!frame().document()->parsing() && frame().loader().stateMachine().committedFirstRealDocumentLoad())
return true;
// Require the document to grow a bit.
- static const int documentHeightThreshold = 200;
- if (documentElement->renderBox()->layoutOverflowRect().pixelSnappedHeight() < documentHeightThreshold)
+ // Using a value of 48 allows the header on Google's search page to render immediately before search results populate later.
+ static const int documentHeightThreshold = 48;
+ LayoutRect overflowRect = documentElement->renderBox()->layoutOverflowRect();
+ if (snappedIntRect(overflowRect).height() < documentHeightThreshold)
return false;
// The first few hundred characters rarely contain the interesting content of the page.
@@ -3679,6 +4613,15 @@ void FrameView::updateIsVisuallyNonEmpty()
adjustTiledBackingCoverage();
}
+bool FrameView::isViewForDocumentInFrame() const
+{
+ RenderView* renderView = this->renderView();
+ if (!renderView)
+ return false;
+
+ return &renderView->frameView() == this;
+}
+
void FrameView::enableAutoSizeMode(bool enable, const IntSize& minSize, const IntSize& maxSize)
{
ASSERT(!enable || !minSize.isEmpty());
@@ -3717,10 +4660,8 @@ void FrameView::forceLayoutForPagination(const FloatSize& pageSize, const FloatS
float pageLogicalWidth = renderView->style().isHorizontalWritingMode() ? pageSize.width() : pageSize.height();
float pageLogicalHeight = renderView->style().isHorizontalWritingMode() ? pageSize.height() : pageSize.width();
- LayoutUnit flooredPageLogicalWidth = static_cast<LayoutUnit>(pageLogicalWidth);
- LayoutUnit flooredPageLogicalHeight = static_cast<LayoutUnit>(pageLogicalHeight);
- renderView->setLogicalWidth(flooredPageLogicalWidth);
- renderView->setPageLogicalHeight(flooredPageLogicalHeight);
+ renderView->setLogicalWidth(floor(pageLogicalWidth));
+ renderView->setPageLogicalHeight(floor(pageLogicalHeight));
renderView->setNeedsLayoutAndPrefWidthsRecalc();
forceLayout();
@@ -3738,10 +4679,8 @@ void FrameView::forceLayoutForPagination(const FloatSize& pageSize, const FloatS
pageLogicalWidth = horizontalWritingMode ? maxPageSize.width() : maxPageSize.height();
pageLogicalHeight = horizontalWritingMode ? maxPageSize.height() : maxPageSize.width();
- flooredPageLogicalWidth = static_cast<LayoutUnit>(pageLogicalWidth);
- flooredPageLogicalHeight = static_cast<LayoutUnit>(pageLogicalHeight);
- renderView->setLogicalWidth(flooredPageLogicalWidth);
- renderView->setPageLogicalHeight(flooredPageLogicalHeight);
+ renderView->setLogicalWidth(floor(pageLogicalWidth));
+ renderView->setPageLogicalHeight(floor(pageLogicalHeight));
renderView->setNeedsLayoutAndPrefWidthsRecalc();
forceLayout();
@@ -3774,35 +4713,34 @@ void FrameView::adjustPageHeightDeprecated(float *newBottom, float oldTop, float
}
// Use a context with painting disabled.
- GraphicsContext context((PlatformGraphicsContext*)0);
+ GraphicsContext context((PlatformGraphicsContext*)nullptr);
renderView->setTruncatedAt(static_cast<int>(floorf(oldBottom)));
IntRect dirtyRect(0, static_cast<int>(floorf(oldTop)), renderView->layoutOverflowRect().maxX(), static_cast<int>(ceilf(oldBottom - oldTop)));
renderView->setPrintRect(dirtyRect);
- renderView->layer()->paint(&context, dirtyRect);
+ renderView->layer()->paint(context, dirtyRect);
*newBottom = renderView->bestTruncatedAt();
if (!*newBottom)
*newBottom = oldBottom;
renderView->setPrintRect(IntRect());
}
-IntRect FrameView::convertFromRenderer(const RenderElement* renderer, const IntRect& rendererRect) const
+IntRect FrameView::convertFromRendererToContainingView(const RenderElement* renderer, const IntRect& rendererRect) const
{
- IntRect rect = pixelSnappedIntRect(enclosingLayoutRect(renderer->localToAbsoluteQuad(FloatRect(rendererRect)).boundingBox()));
+ IntRect rect = snappedIntRect(enclosingLayoutRect(renderer->localToAbsoluteQuad(FloatRect(rendererRect)).boundingBox()));
- // Convert from page ("absolute") to FrameView coordinates.
if (!delegatesScrolling())
- rect.moveBy(-scrollPosition() + IntPoint(0, headerHeight()));
+ rect = contentsToView(rect);
return rect;
}
-IntRect FrameView::convertToRenderer(const RenderElement* renderer, const IntRect& viewRect) const
+IntRect FrameView::convertFromContainingViewToRenderer(const RenderElement* renderer, const IntRect& viewRect) const
{
IntRect rect = viewRect;
// Convert from FrameView coords into page ("absolute") coordinates.
if (!delegatesScrolling())
- rect.moveBy(scrollPositionRelativeToDocument());
+ rect = viewToContents(rect);
// FIXME: we don't have a way to map an absolute rect down to a local quad, so just
// move the rect for now.
@@ -3810,23 +4748,24 @@ IntRect FrameView::convertToRenderer(const RenderElement* renderer, const IntRec
return rect;
}
-IntPoint FrameView::convertFromRenderer(const RenderElement* renderer, const IntPoint& rendererPoint) const
+IntPoint FrameView::convertFromRendererToContainingView(const RenderElement* renderer, const IntPoint& rendererPoint) const
{
IntPoint point = roundedIntPoint(renderer->localToAbsolute(rendererPoint, UseTransforms));
// Convert from page ("absolute") to FrameView coordinates.
if (!delegatesScrolling())
- point.moveBy(-scrollPosition() + IntPoint(0, headerHeight()));
+ point = contentsToView(point);
+
return point;
}
-IntPoint FrameView::convertToRenderer(const RenderElement* renderer, const IntPoint& viewPoint) const
+IntPoint FrameView::convertFromContainingViewToRenderer(const RenderElement* renderer, const IntPoint& viewPoint) const
{
IntPoint point = viewPoint;
// Convert from FrameView coords into page ("absolute") coordinates.
if (!delegatesScrolling())
- point = point + scrollPositionRelativeToDocument();
+ point = viewToContents(point);
return roundedIntPoint(renderer->absoluteToLocal(point, UseTransforms));
}
@@ -3834,8 +4773,8 @@ IntPoint FrameView::convertToRenderer(const RenderElement* renderer, const IntPo
IntRect FrameView::convertToContainingView(const IntRect& localRect) const
{
if (const ScrollView* parentScrollView = parent()) {
- if (parentScrollView->isFrameView()) {
- const FrameView* parentView = toFrameView(parentScrollView);
+ if (is<FrameView>(*parentScrollView)) {
+ const FrameView& parentView = downcast<FrameView>(*parentScrollView);
// Get our renderer in the parent view
RenderWidget* renderer = frame().ownerRenderer();
if (!renderer)
@@ -3845,7 +4784,7 @@ IntRect FrameView::convertToContainingView(const IntRect& localRect) const
// Add borders and padding??
rect.move(renderer->borderLeft() + renderer->paddingLeft(),
renderer->borderTop() + renderer->paddingTop());
- return parentView->convertFromRenderer(renderer, rect);
+ return parentView.convertFromRendererToContainingView(renderer, rect);
}
return Widget::convertToContainingView(localRect);
@@ -3857,15 +4796,15 @@ IntRect FrameView::convertToContainingView(const IntRect& localRect) const
IntRect FrameView::convertFromContainingView(const IntRect& parentRect) const
{
if (const ScrollView* parentScrollView = parent()) {
- if (parentScrollView->isFrameView()) {
- const FrameView* parentView = toFrameView(parentScrollView);
+ if (is<FrameView>(*parentScrollView)) {
+ const FrameView& parentView = downcast<FrameView>(*parentScrollView);
// Get our renderer in the parent view
RenderWidget* renderer = frame().ownerRenderer();
if (!renderer)
return parentRect;
- IntRect rect = parentView->convertToRenderer(renderer, parentRect);
+ IntRect rect = parentView.convertFromContainingViewToRenderer(renderer, parentRect);
// Subtract borders and padding
rect.move(-renderer->borderLeft() - renderer->paddingLeft(),
-renderer->borderTop() - renderer->paddingTop());
@@ -3881,8 +4820,8 @@ IntRect FrameView::convertFromContainingView(const IntRect& parentRect) const
IntPoint FrameView::convertToContainingView(const IntPoint& localPoint) const
{
if (const ScrollView* parentScrollView = parent()) {
- if (parentScrollView->isFrameView()) {
- const FrameView* parentView = toFrameView(parentScrollView);
+ if (is<FrameView>(*parentScrollView)) {
+ const FrameView& parentView = downcast<FrameView>(*parentScrollView);
// Get our renderer in the parent view
RenderWidget* renderer = frame().ownerRenderer();
@@ -3894,7 +4833,7 @@ IntPoint FrameView::convertToContainingView(const IntPoint& localPoint) const
// Add borders and padding
point.move(renderer->borderLeft() + renderer->paddingLeft(),
renderer->borderTop() + renderer->paddingTop());
- return parentView->convertFromRenderer(renderer, point);
+ return parentView.convertFromRendererToContainingView(renderer, point);
}
return Widget::convertToContainingView(localPoint);
@@ -3906,15 +4845,15 @@ IntPoint FrameView::convertToContainingView(const IntPoint& localPoint) const
IntPoint FrameView::convertFromContainingView(const IntPoint& parentPoint) const
{
if (const ScrollView* parentScrollView = parent()) {
- if (parentScrollView->isFrameView()) {
- const FrameView* parentView = toFrameView(parentScrollView);
+ if (is<FrameView>(*parentScrollView)) {
+ const FrameView& parentView = downcast<FrameView>(*parentScrollView);
// Get our renderer in the parent view
RenderWidget* renderer = frame().ownerRenderer();
if (!renderer)
return parentPoint;
- IntPoint point = parentView->convertToRenderer(renderer, parentPoint);
+ IntPoint point = parentView.convertFromContainingViewToRenderer(renderer, parentPoint);
// Subtract borders and padding
point.move(-renderer->borderLeft() - renderer->paddingLeft(),
-renderer->borderTop() - renderer->paddingTop());
@@ -3938,12 +4877,10 @@ void FrameView::setTracksRepaints(bool trackRepaints)
frame().document()->updateLayout();
}
-#if USE(ACCELERATED_COMPOSITING)
for (Frame* frame = &m_frame->tree().top(); frame; frame = frame->tree().traverseNext()) {
if (RenderView* renderView = frame->contentRenderer())
renderView->compositor().setTracksRepaints(trackRepaints);
}
-#endif
resetTrackedRepaints();
m_isTrackingRepaints = trackRepaints;
@@ -3952,10 +4889,8 @@ void FrameView::setTracksRepaints(bool trackRepaints)
void FrameView::resetTrackedRepaints()
{
m_trackedRepaintRects.clear();
-#if USE(ACCELERATED_COMPOSITING)
if (RenderView* renderView = this->renderView())
renderView->compositor().resetTrackedRepaintRects();
-#endif
}
String FrameView::trackedRepaintRectsAsText() const
@@ -3966,8 +4901,8 @@ String FrameView::trackedRepaintRectsAsText() const
TextStream ts;
if (!m_trackedRepaintRects.isEmpty()) {
ts << "(repaint rects\n";
- for (size_t i = 0; i < m_trackedRepaintRects.size(); ++i)
- ts << " (rect " << m_trackedRepaintRects[i].x() << " " << m_trackedRepaintRects[i].y() << " " << m_trackedRepaintRects[i].width() << " " << m_trackedRepaintRects[i].height() << ")\n";
+ for (auto& rect : m_trackedRepaintRects)
+ ts << " (rect " << LayoutUnit(rect.x()) << " " << LayoutUnit(rect.y()) << " " << LayoutUnit(rect.width()) << " " << LayoutUnit(rect.height()) << ")\n";
ts << ")\n";
}
return ts.release();
@@ -3976,13 +4911,23 @@ String FrameView::trackedRepaintRectsAsText() const
bool FrameView::addScrollableArea(ScrollableArea* scrollableArea)
{
if (!m_scrollableAreas)
- m_scrollableAreas = adoptPtr(new ScrollableAreaSet);
- return m_scrollableAreas->add(scrollableArea).isNewEntry;
+ m_scrollableAreas = std::make_unique<ScrollableAreaSet>();
+
+ if (m_scrollableAreas->add(scrollableArea).isNewEntry) {
+ scrollableAreaSetChanged();
+ return true;
+ }
+
+ return false;
}
bool FrameView::removeScrollableArea(ScrollableArea* scrollableArea)
{
- return m_scrollableAreas && m_scrollableAreas->remove(scrollableArea);
+ if (m_scrollableAreas && m_scrollableAreas->remove(scrollableArea)) {
+ scrollableAreaSetChanged();
+ return true;
+ }
+ return false;
}
bool FrameView::containsScrollableArea(ScrollableArea* scrollableArea) const
@@ -3990,10 +4935,27 @@ bool FrameView::containsScrollableArea(ScrollableArea* scrollableArea) const
return m_scrollableAreas && m_scrollableAreas->contains(scrollableArea);
}
-void FrameView::removeChild(Widget* widget)
+void FrameView::scrollableAreaSetChanged()
+{
+ if (auto* page = frame().page()) {
+ if (auto* scrollingCoordinator = page->scrollingCoordinator())
+ scrollingCoordinator->frameViewEventTrackingRegionsChanged(*this);
+ }
+}
+
+void FrameView::sendScrollEvent()
+{
+ frame().eventHandler().sendScrollEvent();
+ frame().eventHandler().dispatchFakeMouseMoveEventSoon();
+#if ENABLE(CSS_ANIMATIONS_LEVEL_2)
+ frame().animation().scrollWasUpdated();
+#endif
+}
+
+void FrameView::removeChild(Widget& widget)
{
- if (widget->isFrameView())
- removeScrollableArea(toFrameView(widget));
+ if (is<FrameView>(widget))
+ removeScrollableArea(&downcast<FrameView>(widget));
ScrollView::removeChild(widget);
}
@@ -4008,12 +4970,12 @@ bool FrameView::wheelEvent(const PlatformWheelEvent& wheelEvent)
#endif
if (delegatesScrolling()) {
- IntSize offset = scrollOffset();
- IntSize newOffset = IntSize(offset.width() - wheelEvent.deltaX(), offset.height() - wheelEvent.deltaY());
- if (offset != newOffset) {
- ScrollView::scrollTo(newOffset);
- scrollPositionChanged();
- frame().loader().client().didChangeScrollOffset();
+ ScrollPosition oldPosition = scrollPosition();
+ ScrollPosition newPosition = oldPosition - IntSize(wheelEvent.deltaX(), wheelEvent.deltaY());
+ if (oldPosition != newPosition) {
+ ScrollView::scrollTo(newPosition);
+ scrollPositionChanged(oldPosition, scrollPosition());
+ didChangeScrollOffset();
}
return true;
}
@@ -4028,8 +4990,8 @@ bool FrameView::wheelEvent(const PlatformWheelEvent& wheelEvent)
#if ENABLE(ASYNC_SCROLLING)
if (Page* page = frame().page()) {
if (ScrollingCoordinator* scrollingCoordinator = page->scrollingCoordinator()) {
- if (scrollingCoordinator->coordinatesScrollingForFrameView(this))
- return scrollingCoordinator->handleWheelEvent(this, wheelEvent);
+ if (scrollingCoordinator->coordinatesScrollingForFrameView(*this))
+ return scrollingCoordinator->handleWheelEvent(*this, wheelEvent);
}
}
#endif
@@ -4058,7 +5020,7 @@ bool FrameView::isFlippedDocument() const
void FrameView::notifyWidgetsInAllFrames(WidgetNotification notification)
{
- for (Frame* frame = m_frame.get(); frame; frame = frame->tree().traverseNext(m_frame.get())) {
+ for (auto* frame = m_frame.ptr(); frame; frame = frame->tree().traverseNext(m_frame.ptr())) {
if (FrameView* view = frame->view())
view->notifyWidgets(notification);
}
@@ -4068,16 +5030,13 @@ AXObjectCache* FrameView::axObjectCache() const
{
if (frame().document())
return frame().document()->existingAXObjectCache();
- return 0;
+ return nullptr;
}
-
+
#if PLATFORM(IOS)
-void FrameView::setUseCustomFixedPositionLayoutRect(bool useCustomFixedPositionLayoutRect)
+bool FrameView::useCustomFixedPositionLayoutRect() const
{
- if (m_useCustomFixedPositionLayoutRect == useCustomFixedPositionLayoutRect)
- return;
- m_useCustomFixedPositionLayoutRect = useCustomFixedPositionLayoutRect;
- visibleContentsResized();
+ return !frame().settings().visualViewportEnabled() && m_useCustomFixedPositionLayoutRect;
}
void FrameView::setCustomFixedPositionLayoutRect(const IntRect& rect)
@@ -4086,7 +5045,7 @@ void FrameView::setCustomFixedPositionLayoutRect(const IntRect& rect)
return;
m_useCustomFixedPositionLayoutRect = true;
m_customFixedPositionLayoutRect = rect;
- visibleContentsResized();
+ updateContentsSize();
}
bool FrameView::updateFixedPositionLayoutRect()
@@ -4106,21 +5065,33 @@ bool FrameView::updateFixedPositionLayoutRect()
}
return false;
}
+
+void FrameView::setCustomSizeForResizeEvent(IntSize customSize)
+{
+ m_useCustomSizeForResizeEvent = true;
+ m_customSizeForResizeEvent = customSize;
+ sendResizeEventIfNeeded();
+}
+
+void FrameView::setScrollVelocity(double horizontalVelocity, double verticalVelocity, double scaleChangeRate, MonotonicTime timestamp)
+{
+ if (TiledBacking* tiledBacking = this->tiledBacking())
+ tiledBacking->setVelocity(VelocityData(horizontalVelocity, verticalVelocity, scaleChangeRate, timestamp));
+}
#endif // PLATFORM(IOS)
void FrameView::setScrollingPerformanceLoggingEnabled(bool flag)
{
-#if USE(ACCELERATED_COMPOSITING)
if (TiledBacking* tiledBacking = this->tiledBacking())
tiledBacking->setScrollingPerformanceLoggingEnabled(flag);
-#else
- UNUSED_PARAM(flag);
-#endif
}
void FrameView::didAddScrollbar(Scrollbar* scrollbar, ScrollbarOrientation orientation)
{
ScrollableArea::didAddScrollbar(scrollbar, orientation);
+ Page* page = frame().page();
+ if (page && page->expectsWheelEventTriggers())
+ scrollAnimator().setWheelEventTestTrigger(page->testTrigger());
if (AXObjectCache* cache = axObjectCache())
cache->handleScrollbarUpdate(this);
}
@@ -4139,7 +5110,36 @@ void FrameView::addPaintPendingMilestones(LayoutMilestones milestones)
m_milestonesPendingPaint |= milestones;
}
-void FrameView::firePaintRelatedMilestones()
+void FrameView::fireLayoutRelatedMilestonesIfNeeded()
+{
+ LayoutMilestones requestedMilestones = 0;
+ LayoutMilestones milestonesAchieved = 0;
+ Page* page = frame().page();
+ if (page)
+ requestedMilestones = page->requestedLayoutMilestones();
+
+ if (m_firstLayoutCallbackPending) {
+ m_firstLayoutCallbackPending = false;
+ frame().loader().didFirstLayout();
+ if (requestedMilestones & DidFirstLayout)
+ milestonesAchieved |= DidFirstLayout;
+ if (frame().isMainFrame())
+ page->startCountingRelevantRepaintedObjects();
+ }
+ updateIsVisuallyNonEmpty();
+
+ // If the layout was done with pending sheets, we are not in fact visually non-empty yet.
+ if (m_isVisuallyNonEmpty && !frame().document()->didLayoutWithPendingStylesheets() && m_firstVisuallyNonEmptyLayoutCallbackPending) {
+ m_firstVisuallyNonEmptyLayoutCallbackPending = false;
+ if (requestedMilestones & DidFirstVisuallyNonEmptyLayout)
+ milestonesAchieved |= DidFirstVisuallyNonEmptyLayout;
+ }
+
+ if (milestonesAchieved && frame().isMainFrame())
+ frame().loader().didReachLayoutMilestone(milestonesAchieved);
+}
+
+void FrameView::firePaintRelatedMilestonesIfNeeded()
{
Page* page = frame().page();
if (!page)
@@ -4161,7 +5161,7 @@ void FrameView::firePaintRelatedMilestones()
m_milestonesPendingPaint = 0;
if (milestonesAchieved)
- page->mainFrame().loader().didLayout(milestonesAchieved);
+ page->mainFrame().loader().didReachLayoutMilestone(milestonesAchieved);
}
void FrameView::setVisualUpdatesAllowedByClient(bool visualUpdatesAllowed)
@@ -4178,10 +5178,12 @@ void FrameView::setScrollPinningBehavior(ScrollPinningBehavior pinning)
{
m_scrollPinningBehavior = pinning;
- if (ScrollingCoordinator* scrollingCoordinator = frame().page()->scrollingCoordinator())
- scrollingCoordinator->setScrollPinningBehavior(pinning);
+ if (Page* page = frame().page()) {
+ if (auto* scrollingCoordinator = page->scrollingCoordinator())
+ scrollingCoordinator->setScrollPinningBehavior(pinning);
+ }
- updateScrollbars(scrollOffset());
+ updateScrollbars(scrollPosition());
}
ScrollBehaviorForFixedElements FrameView::scrollBehaviorForFixedElements() const
@@ -4228,36 +5230,75 @@ void FrameView::updateWidgetPositions()
// updateWidgetPosition() can possibly cause layout to be re-entered (via plug-ins running
// scripts in response to NPP_SetWindow, for example), so we need to keep the Widgets
// alive during enumeration.
- auto protectedWidgets = collectAndProtectWidgets(m_widgetsInRenderTree);
-
- for (unsigned i = 0, size = protectedWidgets.size(); i < size; ++i) {
- if (RenderWidget* renderWidget = RenderWidget::find(protectedWidgets[i].get()))
- renderWidget->updateWidgetPosition();
+ for (auto& widget : collectAndProtectWidgets(m_widgetsInRenderTree)) {
+ if (auto* renderer = RenderWidget::find(*widget)) {
+ auto ignoreWidgetState = renderer->updateWidgetPosition();
+ UNUSED_PARAM(ignoreWidgetState);
+ }
}
}
void FrameView::notifyWidgets(WidgetNotification notification)
{
- auto protectedWidgets = collectAndProtectWidgets(m_widgetsInRenderTree);
-
- for (unsigned i = 0, size = protectedWidgets.size(); i < size; ++i)
- protectedWidgets[i]->notifyWidget(notification);
+ for (auto& widget : collectAndProtectWidgets(m_widgetsInRenderTree))
+ widget->notifyWidget(notification);
}
-void FrameView::setExposedRect(FloatRect exposedRect)
+void FrameView::setViewExposedRect(std::optional<FloatRect> viewExposedRect)
{
- if (m_exposedRect == exposedRect)
+ if (m_viewExposedRect == viewExposedRect)
return;
- m_exposedRect = exposedRect;
+ LOG_WITH_STREAM(Scrolling, stream << "FrameView " << this << " setViewExposedRect " << (viewExposedRect ? viewExposedRect.value() : FloatRect()));
+
+ bool hasRectChanged = !m_viewExposedRect == !viewExposedRect;
+ m_viewExposedRect = viewExposedRect;
-#if USE(ACCELERATED_COMPOSITING)
// FIXME: We should support clipping to the exposed rect for subframes as well.
- if (m_frame->isMainFrame()) {
- if (TiledBacking* tiledBacking = this->tiledBacking())
- tiledBacking->setExposedRect(exposedRect);
+ if (!frame().isMainFrame())
+ return;
+
+ if (TiledBacking* tiledBacking = this->tiledBacking()) {
+ if (hasRectChanged)
+ updateTiledBackingAdaptiveSizing();
+ adjustTiledBackingCoverage();
+ tiledBacking->setTiledScrollingIndicatorPosition(m_viewExposedRect ? m_viewExposedRect.value().location() : FloatPoint());
}
-#endif
+
+ if (auto* view = renderView())
+ view->compositor().scheduleLayerFlush(false /* canThrottle */);
+
+ frame().mainFrame().pageOverlayController().didChangeViewExposedRect();
+}
+
+void FrameView::setViewportSizeForCSSViewportUnits(IntSize size)
+{
+ if (m_hasOverrideViewportSize && m_overrideViewportSize == size)
+ return;
+
+ m_overrideViewportSize = size;
+ m_hasOverrideViewportSize = true;
+
+ if (Document* document = frame().document())
+ document->styleScope().didChangeStyleSheetEnvironment();
+}
+
+IntSize FrameView::viewportSizeForCSSViewportUnits() const
+{
+ if (m_hasOverrideViewportSize)
+ return m_overrideViewportSize;
+
+ if (useFixedLayout())
+ return fixedLayoutSize();
+
+ // FIXME: the value returned should take into account the value of the overflow
+ // property on the root element.
+ return visibleContentRectIncludingScrollbars().size();
}
+bool FrameView::shouldPlaceBlockDirectionScrollbarOnLeft() const
+{
+ return renderView() && renderView()->shouldPlaceBlockDirectionScrollbarOnLeft();
+}
+
} // namespace WebCore