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