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/LocalFrame.h" 29 #include "core/html/HTMLFrameOwnerElement.h" 30 #include "core/html/HTMLPlugInElement.h" 31 #include "core/paint/BoxPainter.h" 32 #include "core/rendering/GraphicsContextAnnotator.h" 33 #include "core/rendering/HitTestResult.h" 34 #include "core/rendering/RenderLayer.h" 35 #include "core/rendering/RenderView.h" 36 #include "core/rendering/compositing/CompositedLayerMapping.h" 37 #include "core/rendering/compositing/RenderLayerCompositor.h" 38 #include "wtf/HashMap.h" 39 40 namespace blink { 41 42 RenderWidget::RenderWidget(Element* element) 43 : RenderReplaced(element) 44 #if !ENABLE(OILPAN) 45 // Reference counting is used to prevent the widget from being 46 // destroyed while inside the Widget code, which might not be 47 // able to handle that. 48 , m_refCount(1) 49 #endif 50 { 51 ASSERT(element); 52 frameView()->addWidget(this); 53 } 54 55 void RenderWidget::willBeDestroyed() 56 { 57 frameView()->removeWidget(this); 58 59 if (AXObjectCache* cache = document().existingAXObjectCache()) { 60 cache->childrenChanged(this->parent()); 61 cache->remove(this); 62 } 63 64 Element* element = toElement(node()); 65 if (element && element->isFrameOwnerElement()) 66 toHTMLFrameOwnerElement(element)->setWidget(nullptr); 67 68 RenderReplaced::willBeDestroyed(); 69 } 70 71 void RenderWidget::destroy() 72 { 73 #if ENABLE(ASSERT) && ENABLE(OILPAN) 74 ASSERT(!m_didCallDestroy); 75 m_didCallDestroy = true; 76 #endif 77 willBeDestroyed(); 78 clearNode(); 79 #if ENABLE(OILPAN) 80 // In Oilpan, postDestroy doesn't delete |this|. So calling it here is safe 81 // though |this| will be referred in FrameView. 82 postDestroy(); 83 #else 84 deref(); 85 #endif 86 } 87 88 RenderWidget::~RenderWidget() 89 { 90 #if !ENABLE(OILPAN) 91 ASSERT(m_refCount <= 0); 92 #endif 93 } 94 95 Widget* RenderWidget::widget() const 96 { 97 // Plugin widgets are stored in their DOM node. This includes HTMLAppletElement. 98 Element* element = toElement(node()); 99 100 if (element && element->isFrameOwnerElement()) 101 return toHTMLFrameOwnerElement(element)->ownedWidget(); 102 103 return 0; 104 } 105 106 // Widgets are always placed on integer boundaries, so rounding the size is actually 107 // the desired behavior. This function is here because it's otherwise seldom what we 108 // want to do with a LayoutRect. 109 static inline IntRect roundedIntRect(const LayoutRect& rect) 110 { 111 return IntRect(roundedIntPoint(rect.location()), roundedIntSize(rect.size())); 112 } 113 114 bool RenderWidget::setWidgetGeometry(const LayoutRect& frame) 115 { 116 if (!node()) 117 return false; 118 119 Widget* widget = this->widget(); 120 ASSERT(widget); 121 122 IntRect newFrame = roundedIntRect(frame); 123 124 if (widget->frameRect() == newFrame) 125 return false; 126 127 RefPtrWillBeRawPtr<RenderWidget> protector(this); 128 RefPtrWillBeRawPtr<Node> protectedNode(node()); 129 widget->setFrameRect(newFrame); 130 return widget->frameRect().size() != newFrame.size(); 131 } 132 133 bool RenderWidget::updateWidgetGeometry() 134 { 135 Widget* widget = this->widget(); 136 ASSERT(widget); 137 138 LayoutRect contentBox = contentBoxRect(); 139 LayoutRect absoluteContentBox(localToAbsoluteQuad(FloatQuad(contentBox)).boundingBox()); 140 if (widget->isFrameView()) { 141 contentBox.setLocation(absoluteContentBox.location()); 142 return setWidgetGeometry(contentBox); 143 } 144 145 return setWidgetGeometry(absoluteContentBox); 146 } 147 148 void RenderWidget::layout() 149 { 150 ASSERT(needsLayout()); 151 152 clearNeedsLayout(); 153 } 154 155 void RenderWidget::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) 156 { 157 RenderReplaced::styleDidChange(diff, oldStyle); 158 Widget* widget = this->widget(); 159 160 if (widget) { 161 if (style()->visibility() != VISIBLE) { 162 widget->hide(); 163 } else { 164 widget->show(); 165 } 166 } 167 } 168 169 void RenderWidget::paintContents(PaintInfo& paintInfo, const LayoutPoint& paintOffset) 170 { 171 LayoutPoint adjustedPaintOffset = paintOffset + location(); 172 173 Widget* widget = this->widget(); 174 RELEASE_ASSERT(widget); 175 176 // Tell the widget to paint now. This is the only time the widget is allowed 177 // to paint itself. That way it will composite properly with z-indexed layers. 178 IntPoint widgetLocation = widget->frameRect().location(); 179 IntPoint paintLocation(roundToInt(adjustedPaintOffset.x() + borderLeft() + paddingLeft()), 180 roundToInt(adjustedPaintOffset.y() + borderTop() + paddingTop())); 181 IntRect paintRect = paintInfo.rect; 182 183 IntSize widgetPaintOffset = paintLocation - widgetLocation; 184 // When painting widgets into compositing layers, tx and ty are relative to the enclosing compositing layer, 185 // not the root. In this case, shift the CTM and adjust the paintRect to be root-relative to fix plug-in drawing. 186 if (!widgetPaintOffset.isZero()) { 187 paintInfo.context->translate(widgetPaintOffset.width(), widgetPaintOffset.height()); 188 paintRect.move(-widgetPaintOffset); 189 } 190 widget->paint(paintInfo.context, paintRect); 191 192 if (!widgetPaintOffset.isZero()) 193 paintInfo.context->translate(-widgetPaintOffset.width(), -widgetPaintOffset.height()); 194 } 195 196 void RenderWidget::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset) 197 { 198 ANNOTATE_GRAPHICS_CONTEXT(paintInfo, this); 199 200 if (!shouldPaint(paintInfo, paintOffset)) 201 return; 202 203 LayoutPoint adjustedPaintOffset = paintOffset + location(); 204 205 if (hasBoxDecorationBackground() && (paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseSelection)) 206 paintBoxDecorationBackground(paintInfo, adjustedPaintOffset); 207 208 if (paintInfo.phase == PaintPhaseMask) { 209 paintMask(paintInfo, adjustedPaintOffset); 210 return; 211 } 212 213 if ((paintInfo.phase == PaintPhaseOutline || paintInfo.phase == PaintPhaseSelfOutline) && style()->hasOutline()) 214 paintOutline(paintInfo, LayoutRect(adjustedPaintOffset, size())); 215 216 if (paintInfo.phase != PaintPhaseForeground) 217 return; 218 219 if (style()->hasBorderRadius()) { 220 LayoutRect borderRect = LayoutRect(adjustedPaintOffset, size()); 221 222 if (borderRect.isEmpty()) 223 return; 224 225 // Push a clip if we have a border radius, since we want to round the foreground content that gets painted. 226 paintInfo.context->save(); 227 RoundedRect roundedInnerRect = style()->getRoundedInnerBorderFor(borderRect, 228 paddingTop() + borderTop(), paddingBottom() + borderBottom(), paddingLeft() + borderLeft(), paddingRight() + borderRight(), true, true); 229 BoxPainter::clipRoundedInnerRect(paintInfo.context, borderRect, roundedInnerRect); 230 } 231 232 Widget* widget = this->widget(); 233 if (widget) 234 paintContents(paintInfo, paintOffset); 235 236 if (style()->hasBorderRadius()) 237 paintInfo.context->restore(); 238 239 // Paint a partially transparent wash over selected widgets. 240 if (isSelected() && !document().printing()) { 241 LayoutRect rect = localSelectionRect(); 242 rect.moveBy(adjustedPaintOffset); 243 paintInfo.context->fillRect(pixelSnappedIntRect(rect), selectionBackgroundColor()); 244 } 245 246 if (canResize()) 247 layer()->scrollableArea()->paintResizer(paintInfo.context, roundedIntPoint(adjustedPaintOffset), paintInfo.rect); 248 } 249 250 #if !ENABLE(OILPAN) 251 void RenderWidget::deref() 252 { 253 if (--m_refCount <= 0) 254 postDestroy(); 255 } 256 #endif 257 258 void RenderWidget::updateOnWidgetChange() 259 { 260 Widget* widget = this->widget(); 261 if (!widget) 262 return; 263 264 if (!style()) 265 return; 266 267 if (!needsLayout()) 268 updateWidgetGeometry(); 269 270 if (style()->visibility() != VISIBLE) { 271 widget->hide(); 272 } else { 273 widget->show(); 274 // FIXME: Why do we issue a full paint invalidation in this case, but not the other? 275 setShouldDoFullPaintInvalidation(true); 276 } 277 } 278 279 void RenderWidget::updateWidgetPosition() 280 { 281 Widget* widget = this->widget(); 282 if (!widget || !node()) // Check the node in case destroy() has been called. 283 return; 284 285 bool boundsChanged = updateWidgetGeometry(); 286 287 // if the frame bounds got changed, or if view needs layout (possibly indicating 288 // content size is wrong) we have to do a layout to set the right widget size 289 if (widget && widget->isFrameView()) { 290 FrameView* frameView = toFrameView(widget); 291 // Check the frame's page to make sure that the frame isn't in the process of being destroyed. 292 if ((boundsChanged || frameView->needsLayout()) && frameView->frame().page()) 293 frameView->layout(); 294 } 295 } 296 297 void RenderWidget::widgetPositionsUpdated() 298 { 299 Widget* widget = this->widget(); 300 if (!widget) 301 return; 302 widget->widgetPositionsUpdated(); 303 } 304 305 bool RenderWidget::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction action) 306 { 307 bool hadResult = result.innerNode(); 308 bool inside = RenderReplaced::nodeAtPoint(request, result, locationInContainer, accumulatedOffset, action); 309 310 // Check to see if we are really over the widget itself (and not just in the border/padding area). 311 if ((inside || result.isRectBasedTest()) && !hadResult && result.innerNode() == node()) 312 result.setIsOverWidget(contentBoxRect().contains(result.localPoint())); 313 return inside; 314 } 315 316 CursorDirective RenderWidget::getCursor(const LayoutPoint& point, Cursor& cursor) const 317 { 318 if (widget() && widget()->isPluginView()) { 319 // A plug-in is responsible for setting the cursor when the pointer is over it. 320 return DoNotSetCursor; 321 } 322 return RenderReplaced::getCursor(point, cursor); 323 } 324 325 } // namespace blink 326