Home | History | Annotate | Download | only in rendering
      1 /*
      2  * Copyright (C) 1999 Lars Knoll (knoll (at) kde.org)
      3  * Copyright (C) 2000 Dirk Mueller (mueller (at) kde.org)
      4  * Copyright (C) 2004, 2006, 2009, 2010 Apple Inc. All rights reserved.
      5  * Copyright (C) 2013 Google Inc. All rights reserved.
      6  *
      7  * This library is free software; you can redistribute it and/or
      8  * modify it under the terms of the GNU Library General Public
      9  * License as published by the Free Software Foundation; either
     10  * version 2 of the License, or (at your option) any later version.
     11  *
     12  * This library is distributed in the hope that it will be useful,
     13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     15  * Library General Public License for more details.
     16  *
     17  * You should have received a copy of the GNU Library General Public License
     18  * along with this library; see the file COPYING.LIB.  If not, write to
     19  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     20  * Boston, MA 02110-1301, USA.
     21  *
     22  */
     23 
     24 #include "config.h"
     25 #include "core/rendering/RenderWidget.h"
     26 
     27 #include "core/accessibility/AXObjectCache.h"
     28 #include "core/frame/Frame.h"
     29 #include "core/rendering/CompositedLayerMapping.h"
     30 #include "core/rendering/GraphicsContextAnnotator.h"
     31 #include "core/rendering/HitTestResult.h"
     32 #include "core/rendering/LayoutRectRecorder.h"
     33 #include "core/rendering/RenderLayer.h"
     34 #include "core/rendering/RenderView.h"
     35 #include "platform/graphics/GraphicsContext.h"
     36 #include "wtf/HashMap.h"
     37 
     38 namespace WebCore {
     39 
     40 typedef HashMap<RefPtr<Widget>, FrameView*> WidgetToParentMap;
     41 static WidgetToParentMap& widgetNewParentMap()
     42 {
     43     DEFINE_STATIC_LOCAL(WidgetToParentMap, map, ());
     44     return map;
     45 }
     46 
     47 static unsigned s_updateSuspendCount = 0;
     48 
     49 RenderWidget::UpdateSuspendScope::UpdateSuspendScope()
     50 {
     51     ++s_updateSuspendCount;
     52 }
     53 
     54 RenderWidget::UpdateSuspendScope::~UpdateSuspendScope()
     55 {
     56     ASSERT(s_updateSuspendCount > 0);
     57     if (s_updateSuspendCount == 1) {
     58         WidgetToParentMap map;
     59         widgetNewParentMap().swap(map);
     60         WidgetToParentMap::iterator end = map.end();
     61         for (WidgetToParentMap::iterator it = map.begin(); it != end; ++it) {
     62             Widget* child = it->key.get();
     63             ScrollView* currentParent = toScrollView(child->parent());
     64             FrameView* newParent = it->value;
     65             if (newParent != currentParent) {
     66                 if (currentParent)
     67                     currentParent->removeChild(child);
     68                 if (newParent)
     69                     newParent->addChild(child);
     70             }
     71         }
     72     }
     73     --s_updateSuspendCount;
     74 }
     75 
     76 static void moveWidgetToParentSoon(Widget* child, FrameView* parent)
     77 {
     78     if (!s_updateSuspendCount) {
     79         if (parent)
     80             parent->addChild(child);
     81         else
     82             toScrollView(child->parent())->removeChild(child);
     83         return;
     84     }
     85     widgetNewParentMap().set(child, parent);
     86 }
     87 
     88 RenderWidget::RenderWidget(Element* element)
     89     : RenderReplaced(element)
     90     , m_widget(0)
     91     , m_frameView(element->document().view())
     92     // Reference counting is used to prevent the widget from being
     93     // destroyed while inside the Widget code, which might not be
     94     // able to handle that.
     95     , m_refCount(1)
     96 {
     97     view()->addWidget(this);
     98 }
     99 
    100 void RenderWidget::willBeDestroyed()
    101 {
    102     if (RenderView* v = view())
    103         v->removeWidget(this);
    104 
    105     if (AXObjectCache* cache = document().existingAXObjectCache()) {
    106         cache->childrenChanged(this->parent());
    107         cache->remove(this);
    108     }
    109 
    110     setWidget(0);
    111 
    112     RenderReplaced::willBeDestroyed();
    113 }
    114 
    115 void RenderWidget::destroy()
    116 {
    117     willBeDestroyed();
    118     clearNode();
    119     deref();
    120 }
    121 
    122 RenderWidget::~RenderWidget()
    123 {
    124     ASSERT(m_refCount <= 0);
    125     clearWidget();
    126 }
    127 
    128 // Widgets are always placed on integer boundaries, so rounding the size is actually
    129 // the desired behavior. This function is here because it's otherwise seldom what we
    130 // want to do with a LayoutRect.
    131 static inline IntRect roundedIntRect(const LayoutRect& rect)
    132 {
    133     return IntRect(roundedIntPoint(rect.location()), roundedIntSize(rect.size()));
    134 }
    135 
    136 bool RenderWidget::setWidgetGeometry(const LayoutRect& frame)
    137 {
    138     if (!node())
    139         return false;
    140 
    141     IntRect clipRect = roundedIntRect(enclosingLayer()->childrenClipRect());
    142     IntRect newFrame = roundedIntRect(frame);
    143     bool clipChanged = m_clipRect != clipRect;
    144     bool frameRectChanged = m_widget->frameRect() != newFrame;
    145 
    146     if (!frameRectChanged && !clipChanged)
    147         return false;
    148 
    149     m_clipRect = clipRect;
    150 
    151     RefPtr<RenderWidget> protector(this);
    152     RefPtr<Node> protectedNode(node());
    153     m_widget->setFrameRect(newFrame);
    154 
    155     if (clipChanged && !frameRectChanged)
    156         m_widget->clipRectChanged();
    157 
    158     if (hasLayer() && layer()->compositingState() == PaintsIntoOwnBacking)
    159         layer()->compositedLayerMapping()->updateAfterWidgetResize();
    160 
    161     bool boundsChanged = m_widget->frameRect().size() != newFrame.size();
    162     return boundsChanged;
    163 }
    164 
    165 bool RenderWidget::updateWidgetGeometry()
    166 {
    167     LayoutRect contentBox = contentBoxRect();
    168     LayoutRect absoluteContentBox(localToAbsoluteQuad(FloatQuad(contentBox)).boundingBox());
    169     if (m_widget->isFrameView()) {
    170         contentBox.setLocation(absoluteContentBox.location());
    171         return setWidgetGeometry(contentBox);
    172     }
    173 
    174     return setWidgetGeometry(absoluteContentBox);
    175 }
    176 
    177 void RenderWidget::setWidget(PassRefPtr<Widget> widget)
    178 {
    179     if (widget == m_widget)
    180         return;
    181 
    182     if (m_widget) {
    183         moveWidgetToParentSoon(m_widget.get(), 0);
    184         clearWidget();
    185     }
    186     m_widget = widget;
    187     if (m_widget) {
    188         // If we've already received a layout, apply the calculated space to the
    189         // widget immediately, but we have to have really been fully constructed (with a non-null
    190         // style pointer).
    191         if (style()) {
    192             if (!needsLayout())
    193                 updateWidgetGeometry();
    194 
    195             if (style()->visibility() != VISIBLE)
    196                 m_widget->hide();
    197             else {
    198                 m_widget->show();
    199                 repaint();
    200             }
    201         }
    202         moveWidgetToParentSoon(m_widget.get(), m_frameView);
    203     }
    204 
    205     if (AXObjectCache* cache = document().existingAXObjectCache())
    206         cache->childrenChanged(this);
    207 }
    208 
    209 void RenderWidget::layout()
    210 {
    211     ASSERT(needsLayout());
    212 
    213     LayoutRectRecorder recorder(*this);
    214     clearNeedsLayout();
    215 }
    216 
    217 void RenderWidget::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
    218 {
    219     RenderReplaced::styleDidChange(diff, oldStyle);
    220     if (m_widget) {
    221         if (style()->visibility() != VISIBLE)
    222             m_widget->hide();
    223         else
    224             m_widget->show();
    225     }
    226 }
    227 
    228 void RenderWidget::paintContents(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
    229 {
    230     LayoutPoint adjustedPaintOffset = paintOffset + location();
    231 
    232     // Tell the widget to paint now. This is the only time the widget is allowed
    233     // to paint itself. That way it will composite properly with z-indexed layers.
    234     IntPoint widgetLocation = m_widget->frameRect().location();
    235     IntPoint paintLocation(roundToInt(adjustedPaintOffset.x() + borderLeft() + paddingLeft()),
    236         roundToInt(adjustedPaintOffset.y() + borderTop() + paddingTop()));
    237     IntRect paintRect = paintInfo.rect;
    238 
    239     IntSize widgetPaintOffset = paintLocation - widgetLocation;
    240     // When painting widgets into compositing layers, tx and ty are relative to the enclosing compositing layer,
    241     // not the root. In this case, shift the CTM and adjust the paintRect to be root-relative to fix plug-in drawing.
    242     if (!widgetPaintOffset.isZero()) {
    243         paintInfo.context->translate(widgetPaintOffset);
    244         paintRect.move(-widgetPaintOffset);
    245     }
    246     m_widget->paint(paintInfo.context, paintRect);
    247 
    248     if (!widgetPaintOffset.isZero())
    249         paintInfo.context->translate(-widgetPaintOffset);
    250 
    251     if (m_widget->isFrameView()) {
    252         FrameView* frameView = toFrameView(m_widget.get());
    253         bool runOverlapTests = !frameView->useSlowRepaintsIfNotOverlapped() || frameView->hasCompositedContent();
    254         if (paintInfo.overlapTestRequests && runOverlapTests) {
    255             ASSERT(!paintInfo.overlapTestRequests->contains(this));
    256             paintInfo.overlapTestRequests->set(this, m_widget->frameRect());
    257         }
    258     }
    259 }
    260 
    261 void RenderWidget::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
    262 {
    263     ANNOTATE_GRAPHICS_CONTEXT(paintInfo, this);
    264 
    265     if (!shouldPaint(paintInfo, paintOffset))
    266         return;
    267 
    268     LayoutPoint adjustedPaintOffset = paintOffset + location();
    269 
    270     if (hasBoxDecorations() && (paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseSelection))
    271         paintBoxDecorations(paintInfo, adjustedPaintOffset);
    272 
    273     if (paintInfo.phase == PaintPhaseMask) {
    274         paintMask(paintInfo, adjustedPaintOffset);
    275         return;
    276     }
    277 
    278     if ((paintInfo.phase == PaintPhaseOutline || paintInfo.phase == PaintPhaseSelfOutline) && hasOutline())
    279         paintOutline(paintInfo, LayoutRect(adjustedPaintOffset, size()));
    280 
    281     if (!m_frameView || paintInfo.phase != PaintPhaseForeground)
    282         return;
    283 
    284     if (style()->hasBorderRadius()) {
    285         LayoutRect borderRect = LayoutRect(adjustedPaintOffset, size());
    286 
    287         if (borderRect.isEmpty())
    288             return;
    289 
    290         // Push a clip if we have a border radius, since we want to round the foreground content that gets painted.
    291         paintInfo.context->save();
    292         RoundedRect roundedInnerRect = style()->getRoundedInnerBorderFor(borderRect,
    293             paddingTop() + borderTop(), paddingBottom() + borderBottom(), paddingLeft() + borderLeft(), paddingRight() + borderRight(), true, true);
    294         clipRoundedInnerRect(paintInfo.context, borderRect, roundedInnerRect);
    295     }
    296 
    297     if (m_widget)
    298         paintContents(paintInfo, paintOffset);
    299 
    300     if (style()->hasBorderRadius())
    301         paintInfo.context->restore();
    302 
    303     // Paint a partially transparent wash over selected widgets.
    304     if (isSelected() && !document().printing()) {
    305         // FIXME: selectionRect() is in absolute, not painting coordinates.
    306         paintInfo.context->fillRect(pixelSnappedIntRect(selectionRect()), selectionBackgroundColor());
    307     }
    308 
    309     if (canResize())
    310         layer()->scrollableArea()->paintResizer(paintInfo.context, roundedIntPoint(adjustedPaintOffset), paintInfo.rect);
    311 }
    312 
    313 void RenderWidget::setIsOverlapped(bool isOverlapped)
    314 {
    315     ASSERT(m_widget);
    316     ASSERT(m_widget->isFrameView());
    317     toFrameView(m_widget.get())->setIsOverlapped(isOverlapped);
    318 }
    319 
    320 void RenderWidget::deref()
    321 {
    322     if (--m_refCount <= 0)
    323         postDestroy();
    324 }
    325 
    326 void RenderWidget::updateWidgetPosition()
    327 {
    328     if (!m_widget || !node()) // Check the node in case destroy() has been called.
    329         return;
    330 
    331     bool boundsChanged = updateWidgetGeometry();
    332 
    333     // if the frame bounds got changed, or if view needs layout (possibly indicating
    334     // content size is wrong) we have to do a layout to set the right widget size
    335     if (m_widget && m_widget->isFrameView()) {
    336         FrameView* frameView = toFrameView(m_widget.get());
    337         // Check the frame's page to make sure that the frame isn't in the process of being destroyed.
    338         if ((boundsChanged || frameView->needsLayout()) && frameView->frame().page())
    339             frameView->layout();
    340     }
    341 }
    342 
    343 void RenderWidget::widgetPositionsUpdated()
    344 {
    345     if (!m_widget)
    346         return;
    347     m_widget->widgetPositionsUpdated();
    348 }
    349 
    350 void RenderWidget::clearWidget()
    351 {
    352     m_widget = 0;
    353 }
    354 
    355 bool RenderWidget::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction action)
    356 {
    357     bool hadResult = result.innerNode();
    358     bool inside = RenderReplaced::nodeAtPoint(request, result, locationInContainer, accumulatedOffset, action);
    359 
    360     // Check to see if we are really over the widget itself (and not just in the border/padding area).
    361     if ((inside || result.isRectBasedTest()) && !hadResult && result.innerNode() == node())
    362         result.setIsOverWidget(contentBoxRect().contains(result.localPoint()));
    363     return inside;
    364 }
    365 
    366 CursorDirective RenderWidget::getCursor(const LayoutPoint& point, Cursor& cursor) const
    367 {
    368     if (widget() && widget()->isPluginView()) {
    369         // A plug-in is responsible for setting the cursor when the pointer is over it.
    370         return DoNotSetCursor;
    371     }
    372     return RenderReplaced::getCursor(point, cursor);
    373 }
    374 
    375 } // namespace WebCore
    376