/* * Copyright (C) 1998, 1999 Torben Weis * 1999 Lars Knoll * 1999 Antti Koivisto * 2000 Dirk Mueller * Copyright (C) 2004, 2005, 2006, 2007, 2008 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. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "sky/engine/config.h" #include "sky/engine/core/frame/FrameView.h" #include "gen/sky/platform/RuntimeEnabledFeatures.h" #include "sky/engine/core/animation/DocumentAnimations.h" #include "sky/engine/core/css/FontFaceSet.h" #include "sky/engine/core/css/resolver/StyleResolver.h" #include "sky/engine/core/dom/DocumentMarkerController.h" #include "sky/engine/core/editing/FrameSelection.h" #include "sky/engine/core/fetch/ResourceFetcher.h" #include "sky/engine/core/frame/FrameHost.h" #include "sky/engine/core/frame/LocalFrame.h" #include "sky/engine/core/frame/Settings.h" #include "sky/engine/core/html/parser/TextResourceDecoder.h" #include "sky/engine/core/inspector/InspectorTraceEvents.h" #include "sky/engine/core/loader/FrameLoaderClient.h" #include "sky/engine/core/page/Chrome.h" #include "sky/engine/core/page/ChromeClient.h" #include "sky/engine/core/page/EventHandler.h" #include "sky/engine/core/page/FocusController.h" #include "sky/engine/core/page/Page.h" #include "sky/engine/core/rendering/RenderLayer.h" #include "sky/engine/core/rendering/RenderView.h" #include "sky/engine/core/rendering/style/RenderStyle.h" #include "sky/engine/platform/ScriptForbiddenScope.h" #include "sky/engine/platform/TraceEvent.h" #include "sky/engine/platform/fonts/FontCache.h" #include "sky/engine/platform/geometry/FloatRect.h" #include "sky/engine/platform/graphics/GraphicsContext.h" #include "sky/engine/platform/scroll/ScrollAnimator.h" #include "sky/engine/platform/scroll/Scrollbar.h" #include "sky/engine/platform/text/TextStream.h" #include "sky/engine/wtf/CurrentTime.h" #include "sky/engine/wtf/TemporaryChange.h" namespace blink { double FrameView::s_currentFrameTimeStamp = 0.0; bool FrameView::s_inPaintContents = false; FrameView::FrameView(LocalFrame* frame) : m_frame(frame) , m_hasPendingLayout(false) , m_layoutSubtreeRoot(0) , m_inSynchronousPostLayout(false) , m_postLayoutTasksTimer(this, &FrameView::postLayoutTimerFired) , m_isTransparent(false) , m_baseBackgroundColor(Color::white) , m_mediaType("screen") , m_overflowStatusDirty(true) , m_viewportRenderer(0) , m_isTrackingPaintInvalidations(false) , m_hasSoftwareFilters(false) , m_visibleContentScaleFactor(1) , m_inputEventsScaleFactorForEmulation(1) , m_layoutSizeFixedToFrameSize(true) { ASSERT(m_frame); init(); } PassRefPtr FrameView::create(LocalFrame* frame) { RefPtr view = adoptRef(new FrameView(frame)); return view.release(); } PassRefPtr FrameView::create(LocalFrame* frame, const IntSize& initialSize) { RefPtr view = adoptRef(new FrameView(frame)); view->Widget::setFrameRect(IntRect(view->location(), initialSize)); view->setLayoutSizeInternal(initialSize); return view.release(); } FrameView::~FrameView() { if (m_postLayoutTasksTimer.isActive()) m_postLayoutTasksTimer.stop(); ASSERT(m_frame); ASSERT(m_frame->view() != this || !m_frame->contentRenderer()); } void FrameView::reset() { m_hasPendingLayout = false; m_layoutSubtreeRoot = 0; m_doFullPaintInvalidation = false; m_layoutSchedulingEnabled = true; m_inPerformLayout = false; m_canInvalidatePaintDuringPerformLayout = false; m_inSynchronousPostLayout = false; m_layoutCount = 0; m_nestedLayoutCount = 0; m_postLayoutTasksTimer.stop(); m_firstLayout = true; m_firstLayoutCallbackPending = false; m_lastViewportSize = IntSize(); m_isTrackingPaintInvalidations = false; m_trackedPaintInvalidationRects.clear(); m_lastPaintTime = 0; m_isPainting = false; } void FrameView::init() { reset(); m_size = LayoutSize(); } void FrameView::prepareForDetach() { // FIXME(sky): Remove } void FrameView::clear() { reset(); } bool FrameView::didFirstLayout() const { return !m_firstLayout; } void FrameView::invalidateRect(const IntRect& rect) { // FIXME(sky): Parent is always null in sky? if (!parent()) { if (HostWindow* window = hostWindow()) window->invalidateContentsAndRootView(rect); } } void FrameView::setFrameRect(const IntRect& newRect) { IntRect oldRect = frameRect(); if (newRect == oldRect) return; Widget::setFrameRect(newRect); } Page* FrameView::page() const { return frame().page(); } RenderView* FrameView::renderView() const { return frame().contentRenderer(); } IntPoint FrameView::clampOffsetAtScale(const IntPoint& offset, float scale) const { FloatSize scaledSize = unscaledVisibleContentSize(); if (scale) scaledSize.scale(1 / scale); IntPoint clampedOffset = offset; clampedOffset = clampedOffset.shrunkTo( IntPoint(size()) - expandedIntSize(scaledSize)); return clampedOffset; } void FrameView::recalcOverflowAfterStyleChange() { RenderView* renderView = this->renderView(); RELEASE_ASSERT(renderView); if (!renderView->needsOverflowRecalcAfterStyleChange()) return; renderView->recalcOverflowAfterStyleChange(); } bool FrameView::scheduleAnimation() { if (HostWindow* window = hostWindow()) { window->scheduleAnimation(); return true; } return false; } bool FrameView::isEnclosedInCompositingLayer() const { return false; } RenderObject* FrameView::layoutRoot(bool onlyDuringLayout) const { return onlyDuringLayout && layoutPending() ? 0 : m_layoutSubtreeRoot; } void FrameView::performPreLayoutTasks() { TRACE_EVENT0("blink", "FrameView::performPreLayoutTasks"); lifecycle().advanceTo(DocumentLifecycle::InPreLayout); // Don't schedule more layouts, we're in one. TemporaryChange changeSchedulingEnabled(m_layoutSchedulingEnabled, false); if (!m_nestedLayoutCount && !m_inSynchronousPostLayout && m_postLayoutTasksTimer.isActive()) { // This is a new top-level layout. If there are any remaining tasks from the previous layout, finish them now. m_inSynchronousPostLayout = true; performPostLayoutTasks(); m_inSynchronousPostLayout = false; } Document* document = m_frame->document(); if (wasViewportResized()) document->notifyResizeForViewportUnits(); // Viewport-dependent media queries may cause us to need completely different style information. if (!document->styleResolver() || document->styleResolver()->mediaQueryAffectedByViewportChange()) { document->styleResolverChanged(); document->mediaQueryAffectingValueChanged(); } else { document->evaluateMediaQueryList(); } document->updateRenderTreeIfNeeded(); lifecycle().advanceTo(DocumentLifecycle::StyleClean); } void FrameView::performLayout(RenderObject* rootForThisLayout, bool inSubtreeLayout) { TRACE_EVENT0("blink", "FrameView::performLayout"); ScriptForbiddenScope forbidScript; ASSERT(!isInPerformLayout()); lifecycle().advanceTo(DocumentLifecycle::InPerformLayout); TemporaryChange changeInPerformLayout(m_inPerformLayout, true); // performLayout is the actual guts of layout(). // FIXME: The 300 other lines in layout() probably belong in other helper functions // so that a single human could understand what layout() is actually doing. LayoutState layoutState(*rootForThisLayout); // FIXME (crbug.com/256657): Do not do two layouts for text autosizing. rootForThisLayout->layout(); lifecycle().advanceTo(DocumentLifecycle::AfterPerformLayout); } void FrameView::scheduleOrPerformPostLayoutTasks() { if (m_postLayoutTasksTimer.isActive()) return; if (!m_inSynchronousPostLayout) { m_inSynchronousPostLayout = true; // Calls resumeScheduledEvents() performPostLayoutTasks(); m_inSynchronousPostLayout = false; } if (!m_postLayoutTasksTimer.isActive() && (needsLayout() || m_inSynchronousPostLayout)) { // If we need layout or are already in a synchronous call to postLayoutTasks(), // defer widget updates and event dispatch until after we return. postLayoutTasks() // 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, FROM_HERE); if (needsLayout()) layout(); } } void FrameView::layout(bool allowSubtree) { // We should never layout a Document which is not in a LocalFrame. ASSERT(m_frame); ASSERT(m_frame->view() == this); ASSERT(m_frame->page()); ScriptForbiddenScope forbidScript; if (isInPerformLayout() || !m_frame->document()->isActive()) return; TRACE_EVENT0("blink", "FrameView::layout"); TRACE_EVENT_SCOPED_SAMPLING_STATE("blink", "Layout"); // Protect the view from being deleted during layout (in recalcStyle) RefPtr protector(this); m_hasPendingLayout = false; DocumentLifecycle::Scope lifecycleScope(lifecycle(), DocumentLifecycle::LayoutClean); RELEASE_ASSERT(!isPainting()); TRACE_EVENT_BEGIN1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "Layout", "beginData", InspectorLayoutEvent::beginData(this)); TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline.stack"), "CallStack", TRACE_EVENT_SCOPE_PROCESS, "stack", InspectorCallStackEvent::currentCallStack()); if (!allowSubtree && isSubtreeLayout()) { m_layoutSubtreeRoot->markContainingBlocksForLayout(false); m_layoutSubtreeRoot = 0; } performPreLayoutTasks(); // 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 (protector->hasOneRef()) return; Document* document = m_frame->document(); bool inSubtreeLayout = isSubtreeLayout(); RenderObject* rootForThisLayout = inSubtreeLayout ? m_layoutSubtreeRoot : document->renderView(); if (!rootForThisLayout) { // FIXME: Do we need to set m_size here? ASSERT_NOT_REACHED(); return; } FontCachePurgePreventer fontCachePurgePreventer; RenderLayer* layer; { TemporaryChange changeSchedulingEnabled(m_layoutSchedulingEnabled, false); m_nestedLayoutCount++; if (!inSubtreeLayout) { if (m_firstLayout) { m_doFullPaintInvalidation = true; m_firstLayout = false; m_firstLayoutCallbackPending = true; m_lastViewportSize = layoutSize(IncludeScrollbars); } m_size = LayoutSize(layoutSize().width(), layoutSize().height()); // We need to set m_doFullPaintInvalidation before triggering layout as RenderObject::checkForPaintInvalidation // checks the boolean to disable local paint invalidations. m_doFullPaintInvalidation |= renderView()->shouldDoFullPaintInvalidationForNextLayout(); } layer = rootForThisLayout->enclosingLayer(); performLayout(rootForThisLayout, inSubtreeLayout); m_layoutSubtreeRoot = 0; // We need to ensure that we mark up all renderers up to the RenderView // for paint invalidation. This simplifies our code as we just always // do a full tree walk. if (RenderObject* container = rootForThisLayout->container()) container->setMayNeedPaintInvalidation(true); } // Reset m_layoutSchedulingEnabled to its previous value. layer->updateLayerPositionsAfterLayout(); m_layoutCount++; ASSERT(!rootForThisLayout->needsLayout()); scheduleOrPerformPostLayoutTasks(); TRACE_EVENT_END1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "Layout", "endData", InspectorLayoutEvent::endData(rootForThisLayout)); m_nestedLayoutCount--; if (m_nestedLayoutCount) return; #if ENABLE(ASSERT) // Post-layout assert that nobody was re-marked as needing layout during layout. document->renderView()->assertSubtreeIsLaidOut(); #endif // FIXME: It should be not possible to remove the FrameView from the frame/page during layout // however m_inPerformLayout is not set for most of this function, so none of our RELEASE_ASSERTS // in LocalFrame/Page will fire. One of the post-layout tasks is disconnecting the LocalFrame from // the page in fast/frames/crash-remove-iframe-during-object-beforeload-2.html // necessitating this check here. // ASSERT(frame()->page()); if (frame().page()) frame().page()->chrome().client().layoutUpdated(m_frame.get()); } void FrameView::invalidateTreeIfNeeded() { lifecycle().advanceTo(DocumentLifecycle::InPaintInvalidation); ASSERT(renderView()); RenderView& rootForPaintInvalidation = *renderView(); ASSERT(!rootForPaintInvalidation.needsLayout()); TRACE_EVENT1("blink", "FrameView::invalidateTree", "root", rootForPaintInvalidation.debugName().ascii().data()); PaintInvalidationState rootPaintInvalidationState(rootForPaintInvalidation); rootForPaintInvalidation.invalidateTreeIfNeeded(rootPaintInvalidationState); m_doFullPaintInvalidation = false; #ifndef NDEBUG renderView()->assertSubtreeClearedPaintInvalidationState(); #endif if (m_frame->selection().isCaretBoundsDirty()) m_frame->selection().invalidateCaretRect(); lifecycle().advanceTo(DocumentLifecycle::PaintInvalidationClean); } DocumentLifecycle& FrameView::lifecycle() const { return m_frame->document()->lifecycle(); } void FrameView::setMediaType(const AtomicString& mediaType) { ASSERT(m_frame->document()); m_frame->document()->mediaQueryAffectingValueChanged(); m_mediaType = mediaType; } AtomicString FrameView::mediaType() const { // See if we have an override type. String overrideType; if (!overrideType.isNull()) return AtomicString(overrideType); return m_mediaType; } bool FrameView::shouldSetCursor() const { Page* page = frame().page(); return page && page->visibilityState() != PageVisibilityStateHidden && page->focusController().isActive() && page->settings().deviceSupportsMouse(); } // FIXME(sky): remove IntSize FrameView::layoutSize(IncludeScrollbarsInRect) const { return m_layoutSize; } void FrameView::setLayoutSize(const IntSize& size) { ASSERT(!layoutSizeFixedToFrameSize()); setLayoutSizeInternal(size); } HostWindow* FrameView::hostWindow() const { Page* page = frame().page(); if (!page) return 0; return &page->chrome(); } void FrameView::contentRectangleForPaintInvalidation(const IntRect& r) { ASSERT(paintInvalidationIsAllowed()); if (m_isTrackingPaintInvalidations) { m_trackedPaintInvalidationRects.append(r); // FIXME: http://crbug.com/368518. Eventually, invalidateContentRectangleForPaint // is going away entirely once all layout tests are FCM. In the short // term, no code should be tracking non-composited FrameView paint invalidations. RELEASE_ASSERT_NOT_REACHED(); } IntRect paintRect = r; if (clipsPaintInvalidations() && !paintsEntireContents()) paintRect.intersect(visibleContentRect()); if (paintRect.isEmpty()) return; if (HostWindow* window = hostWindow()) window->invalidateContentsAndRootView(contentsToWindow(paintRect)); } void FrameView::contentsResized() { setNeedsLayout(); } void FrameView::scheduleRelayout() { ASSERT(m_frame->view() == this); if (isSubtreeLayout()) { m_layoutSubtreeRoot->markContainingBlocksForLayout(false); m_layoutSubtreeRoot = 0; } if (!m_layoutSchedulingEnabled) return; if (!needsLayout()) return; if (!m_frame->document()->shouldScheduleLayout()) return; TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "InvalidateLayout", TRACE_EVENT_SCOPE_PROCESS, "frame", m_frame.get()); TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline.stack"), "CallStack", TRACE_EVENT_SCOPE_PROCESS, "stack", InspectorCallStackEvent::currentCallStack()); if (m_hasPendingLayout) return; m_hasPendingLayout = true; page()->animator().scheduleVisualUpdate(); lifecycle().ensureStateAtMost(DocumentLifecycle::StyleClean); } static bool isObjectAncestorContainerOf(RenderObject* ancestor, RenderObject* descendant) { for (RenderObject* r = descendant; r; r = r->container()) { if (r == ancestor) return true; } return false; } void FrameView::scheduleRelayoutOfSubtree(RenderObject* relayoutRoot) { ASSERT(m_frame->view() == this); // FIXME: Should this call shouldScheduleLayout instead? if (!m_frame->document()->isActive()) return; RenderView* renderView = this->renderView(); if (renderView && renderView->needsLayout()) { if (relayoutRoot) relayoutRoot->markContainingBlocksForLayout(false); return; } if (layoutPending() || !m_layoutSchedulingEnabled) { if (m_layoutSubtreeRoot != relayoutRoot) { if (isObjectAncestorContainerOf(m_layoutSubtreeRoot, relayoutRoot)) { // Keep the current root relayoutRoot->markContainingBlocksForLayout(false, m_layoutSubtreeRoot); ASSERT(!m_layoutSubtreeRoot->container() || !m_layoutSubtreeRoot->container()->needsLayout()); } else if (isSubtreeLayout() && isObjectAncestorContainerOf(relayoutRoot, m_layoutSubtreeRoot)) { // Re-root at relayoutRoot m_layoutSubtreeRoot->markContainingBlocksForLayout(false, relayoutRoot); m_layoutSubtreeRoot = relayoutRoot; ASSERT(!m_layoutSubtreeRoot->container() || !m_layoutSubtreeRoot->container()->needsLayout()); } else { // Just do a full relayout if (isSubtreeLayout()) m_layoutSubtreeRoot->markContainingBlocksForLayout(false); m_layoutSubtreeRoot = 0; relayoutRoot->markContainingBlocksForLayout(false); } } } else if (m_layoutSchedulingEnabled) { m_layoutSubtreeRoot = relayoutRoot; ASSERT(!m_layoutSubtreeRoot->container() || !m_layoutSubtreeRoot->container()->needsLayout()); m_hasPendingLayout = true; page()->animator().scheduleVisualUpdate(); lifecycle().ensureStateAtMost(DocumentLifecycle::StyleClean); } TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "InvalidateLayout", TRACE_EVENT_SCOPE_PROCESS, "frame", m_frame.get()); TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline.stack"), "CallStack", TRACE_EVENT_SCOPE_PROCESS, "stack", InspectorCallStackEvent::currentCallStack()); } bool FrameView::layoutPending() const { // FIXME: This should check Document::lifecycle instead. return m_hasPendingLayout; } bool FrameView::isInPerformLayout() const { ASSERT(m_inPerformLayout == (lifecycle().state() == DocumentLifecycle::InPerformLayout)); return m_inPerformLayout; } bool FrameView::needsLayout() const { // This can return true in cases where the document does not have a body yet. // Document::shouldScheduleLayout takes care of preventing us from scheduling // layout in that case. RenderView* renderView = this->renderView(); return layoutPending() || (renderView && renderView->needsLayout()) || isSubtreeLayout(); } void FrameView::setNeedsLayout() { if (RenderView* renderView = this->renderView()) renderView->setNeedsLayout(); } bool FrameView::isTransparent() const { return m_isTransparent; } void FrameView::setTransparent(bool isTransparent) { m_isTransparent = isTransparent; } bool FrameView::hasOpaqueBackground() const { return !m_isTransparent && !m_baseBackgroundColor.hasAlpha(); } Color FrameView::baseBackgroundColor() const { return m_baseBackgroundColor; } void FrameView::setBaseBackgroundColor(const Color& backgroundColor) { m_baseBackgroundColor = backgroundColor; } void FrameView::updateBackgroundRecursively(const Color& backgroundColor, bool transparent) { // FIXME(sky): simplify setTransparent(transparent); setBaseBackgroundColor(backgroundColor); } void FrameView::flushAnyPendingPostLayoutTasks() { ASSERT(!isInPerformLayout()); if (m_postLayoutTasksTimer.isActive()) performPostLayoutTasks(); } void FrameView::performPostLayoutTasks() { // FIXME: We can reach here, even when the page is not active! // http/tests/inspector/elements/html-link-import.html and many other // tests hit that case. // We should ASSERT(isActive()); or at least return early if we can! ASSERT(!isInPerformLayout()); // Always before or after performLayout(), part of the highest-level layout() call. TRACE_EVENT0("blink", "FrameView::performPostLayoutTasks"); RefPtr protect(this); m_postLayoutTasksTimer.stop(); m_frame->selection().setCaretRectNeedsUpdate(); m_frame->selection().updateAppearance(); ASSERT(m_frame->document()); if (m_nestedLayoutCount <= 1) { if (m_firstLayoutCallbackPending) m_firstLayoutCallbackPending = false; } FontFaceSet::didLayout(*m_frame->document()); sendResizeEventIfNeeded(); } bool FrameView::wasViewportResized() { return layoutSize(IncludeScrollbars) != m_lastViewportSize; } void FrameView::sendResizeEventIfNeeded() { if (!wasViewportResized()) return; m_lastViewportSize = layoutSize(IncludeScrollbars); m_frame->document()->enqueueResizeEvent(); } void FrameView::postLayoutTimerFired(Timer*) { performPostLayoutTasks(); } IntRect FrameView::windowClipRect(IncludeScrollbarsInRect scrollbarInclusion) const { ASSERT(m_frame->view() == this); if (paintsEntireContents()) return IntRect(IntPoint(), size()); // Set our clip rect to be our contents. IntRect clipRect = contentsToWindow(visibleContentRect(scrollbarInclusion)); return clipRect; } bool FrameView::isActive() const { Page* page = frame().page(); return page && page->focusController().isActive(); } void FrameView::setVisibleContentScaleFactor(float visibleContentScaleFactor) { if (m_visibleContentScaleFactor == visibleContentScaleFactor) return; m_visibleContentScaleFactor = visibleContentScaleFactor; } void FrameView::setInputEventsTransformForEmulation(const IntSize& offset, float contentScaleFactor) { m_inputEventsOffsetForEmulation = offset; m_inputEventsScaleFactorForEmulation = contentScaleFactor; } IntSize FrameView::inputEventsOffsetForEmulation() const { return m_inputEventsOffsetForEmulation; } float FrameView::inputEventsScaleFactor() const { float pageScale = visibleContentScaleFactor(); return pageScale * m_inputEventsScaleFactorForEmulation; } Color FrameView::documentBackgroundColor() const { // We blend the background color of // the document and the body against the base background color of the frame view. // Background images are unfortunately impractical to include. Color result = baseBackgroundColor(); if (!frame().document()) return result; Element* htmlElement = frame().document()->documentElement(); // We take the aggregate of the base background color // the background color, and the // background color to find the document color. The // addition of the base background color is not // technically part of the document background, but it // otherwise poses problems when the aggregate is not // fully opaque. if (htmlElement && htmlElement->renderer()) result = result.blend(htmlElement->renderer()->style()->colorIncludingFallback(CSSPropertyBackgroundColor)); return result; } void FrameView::paint(GraphicsContext* context, const IntRect& rect) { paintContents(context, rect); } void FrameView::paintContents(GraphicsContext* p, const IntRect& rect) { Document* document = m_frame->document(); #ifndef NDEBUG bool fillWithRed; if (isTransparent()) fillWithRed = false; // 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)); #endif RenderView* renderView = this->renderView(); if (!renderView) { WTF_LOG_ERROR("called FrameView::paint with nil renderer"); return; } RELEASE_ASSERT(!needsLayout()); ASSERT(document->lifecycle().state() >= DocumentLifecycle::PaintInvalidationClean); TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "Paint", "data", InspectorPaintEvent::data(renderView, rect)); TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline.stack"), "CallStack", TRACE_EVENT_SCOPE_PROCESS, "stack", InspectorCallStackEvent::currentCallStack()); bool isTopLevelPainter = !s_inPaintContents; s_inPaintContents = true; FontCachePurgePreventer fontCachePurgePreventer; document->markers().invalidateRenderedRectsForMarkersInRect(rect); ASSERT(!m_isPainting); m_isPainting = true; // m_nodeToDraw is used to draw only one element (and its descendants) RenderObject* renderer = m_nodeToDraw ? m_nodeToDraw->renderer() : 0; RenderLayer* rootLayer = renderView->layer(); #if ENABLE(ASSERT) renderView->assertSubtreeIsLaidOut(); RenderObject::SetLayoutNeededForbiddenScope forbidSetNeedsLayout(*rootLayer->renderer()); #endif rootLayer->paint(p, rect, renderer); if (rootLayer->containsDirtyOverlayScrollbars()) rootLayer->paintOverlayScrollbars(p, rect, renderer); m_isPainting = false; m_lastPaintTime = currentTime(); if (isTopLevelPainter) { // Everything that happens after paintContents completions is considered // to be part of the next frame. s_currentFrameTimeStamp = currentTime(); s_inPaintContents = false; } } bool FrameView::isPainting() const { return m_isPainting; } void FrameView::setNodeToDraw(Node* node) { m_nodeToDraw = node; } void FrameView::updateLayoutAndStyleForPainting() { // Updating layout can run script, which can tear down the FrameView. RefPtr protector(this); updateLayoutAndStyleIfNeededRecursive(); if (RenderView* view = renderView()) { TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "InvalidateTreeAndUpdateIframes", TRACE_EVENT_SCOPE_PROCESS, "frame", m_frame.get()); invalidateTreeIfNeeded(); view->updateIFramesAfterLayout(); } ASSERT(lifecycle().state() == DocumentLifecycle::PaintInvalidationClean); DocumentAnimations::startPendingAnimations(*m_frame->document()); } void FrameView::updateLayoutAndStyleIfNeededRecursive() { // We have to crawl our entire tree looking for any FrameViews that need // layout and make sure they are up to date. // Mac actually tests for intersection with the dirty region and tries not to // update layout for frames that are outside the dirty region. Not only does this seem // pointless (since those frames will have set a zero timer to layout anyway), but // it is also incorrect, since if two frames overlap, the first could be excluded from the dirty // region but then become included later by the second frame adding rects to the dirty region // when it lays out. m_frame->document()->updateRenderTreeIfNeeded(); if (needsLayout()) layout(); // These asserts ensure that parent frames are clean, when child frames finished updating layout and style. ASSERT(!needsLayout()); #if ENABLE(ASSERT) m_frame->document()->renderView()->assertRendererLaidOut(); #endif } void FrameView::forceLayout(bool allowSubtree) { layout(allowSubtree); } IntRect FrameView::convertFromRenderer(const RenderObject& renderer, const IntRect& rendererRect) const { return pixelSnappedIntRect(enclosingLayoutRect(renderer.localToAbsoluteQuad(FloatRect(rendererRect)).boundingBox())); } IntRect FrameView::convertToRenderer(const RenderObject& renderer, const IntRect& viewRect) const { IntRect rect = viewRect; // FIXME: we don't have a way to map an absolute rect down to a local quad, so just // move the rect for now. rect.setLocation(roundedIntPoint(renderer.absoluteToLocal(rect.location(), UseTransforms))); return rect; } IntPoint FrameView::convertFromRenderer(const RenderObject& renderer, const IntPoint& rendererPoint) const { return roundedIntPoint(renderer.localToAbsolute(rendererPoint, UseTransforms)); } IntPoint FrameView::convertToRenderer(const RenderObject& renderer, const IntPoint& viewPoint) const { return roundedIntPoint(renderer.absoluteToLocal(viewPoint, UseTransforms)); } void FrameView::setTracksPaintInvalidations(bool trackPaintInvalidations) { // FIXME(sky): Does this code work anymore now that we don't have the compositor? if (trackPaintInvalidations == m_isTrackingPaintInvalidations) return; TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("blink.invalidation"), "FrameView::setTracksPaintInvalidations", TRACE_EVENT_SCOPE_PROCESS, "enabled", trackPaintInvalidations); resetTrackedPaintInvalidations(); m_isTrackingPaintInvalidations = trackPaintInvalidations; } void FrameView::resetTrackedPaintInvalidations() { m_trackedPaintInvalidationRects.clear(); } String FrameView::trackedPaintInvalidationRectsAsText() const { TextStream ts; if (!m_trackedPaintInvalidationRects.isEmpty()) { ts << "(repaint rects\n"; for (size_t i = 0; i < m_trackedPaintInvalidationRects.size(); ++i) ts << " (rect " << m_trackedPaintInvalidationRects[i].x() << " " << m_trackedPaintInvalidationRects[i].y() << " " << m_trackedPaintInvalidationRects[i].width() << " " << m_trackedPaintInvalidationRects[i].height() << ")\n"; ts << ")\n"; } return ts.release(); } void FrameView::addScrollableArea(ScrollableArea* scrollableArea) { ASSERT(scrollableArea); if (!m_scrollableAreas) m_scrollableAreas = adoptPtr(new ScrollableAreaSet); m_scrollableAreas->add(scrollableArea); } void FrameView::removeScrollableArea(ScrollableArea* scrollableArea) { if (!m_scrollableAreas) return; m_scrollableAreas->remove(scrollableArea); } bool FrameView::wheelEvent(const PlatformWheelEvent& wheelEvent) { // FIXME(sky): Remove return false; } bool FrameView::isVerticalDocument() const { // FIXME(sky): Remove return true; } bool FrameView::isFlippedDocument() const { // FIXME(sky): Remove return false; } void FrameView::setCursor(const Cursor& cursor) { Page* page = frame().page(); if (!page || !page->settings().deviceSupportsMouse()) return; page->chrome().setCursor(cursor); } void FrameView::setLayoutSizeInternal(const IntSize& size) { if (m_layoutSize == size) return; m_layoutSize = size; contentsResized(); } void FrameView::countObjectsNeedingLayout(unsigned& needsLayoutObjects, unsigned& totalObjects, bool& isPartial) { RenderObject* root = layoutRoot(); isPartial = true; if (!root) { isPartial = false; root = m_frame->contentRenderer(); } needsLayoutObjects = 0; totalObjects = 0; for (RenderObject* o = root; o; o = o->nextInPreOrder(root)) { ++totalObjects; if (o->needsLayout()) ++needsLayoutObjects; } } } // namespace blink