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 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Library General Public 8 * License as published by the Free Software Foundation; either 9 * version 2 of the License, or (at your option) any later version. 10 * 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Library General Public License for more details. 15 * 16 * You should have received a copy of the GNU Library General Public License 17 * along with this library; see the file COPYING.LIB. If not, write to 18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 19 * Boston, MA 02110-1301, USA. 20 * 21 */ 22 23 #include "config.h" 24 #include "RenderWidget.h" 25 26 #include "AXObjectCache.h" 27 #include "AnimationController.h" 28 #include "GraphicsContext.h" 29 #include "HitTestResult.h" 30 #include "RenderCounter.h" 31 #include "RenderLayer.h" 32 #include "RenderView.h" 33 #include "RenderWidgetProtector.h" 34 35 #if USE(ACCELERATED_COMPOSITING) 36 #include "RenderLayerBacking.h" 37 #endif 38 39 using namespace std; 40 41 namespace WebCore { 42 43 static HashMap<const Widget*, RenderWidget*>& widgetRendererMap() 44 { 45 static HashMap<const Widget*, RenderWidget*>* staticWidgetRendererMap = new HashMap<const Widget*, RenderWidget*>; 46 return *staticWidgetRendererMap; 47 } 48 49 static size_t widgetHierarchyUpdateSuspendCount; 50 51 typedef HashMap<RefPtr<Widget>, FrameView*> WidgetToParentMap; 52 53 static WidgetToParentMap& widgetNewParentMap() 54 { 55 DEFINE_STATIC_LOCAL(WidgetToParentMap, map, ()); 56 return map; 57 } 58 59 void RenderWidget::suspendWidgetHierarchyUpdates() 60 { 61 widgetHierarchyUpdateSuspendCount++; 62 } 63 64 void RenderWidget::resumeWidgetHierarchyUpdates() 65 { 66 ASSERT(widgetHierarchyUpdateSuspendCount); 67 if (widgetHierarchyUpdateSuspendCount == 1) { 68 WidgetToParentMap map = widgetNewParentMap(); 69 widgetNewParentMap().clear(); 70 WidgetToParentMap::iterator end = map.end(); 71 for (WidgetToParentMap::iterator it = map.begin(); it != end; ++it) { 72 Widget* child = it->first.get(); 73 ScrollView* currentParent = child->parent(); 74 FrameView* newParent = it->second; 75 if (newParent != currentParent) { 76 if (currentParent) 77 currentParent->removeChild(child); 78 if (newParent) 79 newParent->addChild(child); 80 } 81 } 82 } 83 widgetHierarchyUpdateSuspendCount--; 84 } 85 86 static void moveWidgetToParentSoon(Widget* child, FrameView* parent) 87 { 88 if (!widgetHierarchyUpdateSuspendCount) { 89 if (parent) 90 parent->addChild(child); 91 else 92 child->removeFromParent(); 93 return; 94 } 95 widgetNewParentMap().set(child, parent); 96 } 97 98 RenderWidget::RenderWidget(Node* node) 99 : RenderReplaced(node) 100 , m_widget(0) 101 , m_frameView(node->document()->view()) 102 // Reference counting is used to prevent the widget from being 103 // destroyed while inside the Widget code, which might not be 104 // able to handle that. 105 , m_refCount(1) 106 { 107 view()->addWidget(this); 108 } 109 110 void RenderWidget::destroy() 111 { 112 // We can't call the base class's destroy because we don't 113 // want to unconditionally delete ourselves (we're ref-counted). 114 // So the code below includes copied and pasted contents of 115 // both RenderBox::destroy() and RenderObject::destroy(). 116 // Fix originally made for <rdar://problem/4228818>. 117 118 animation()->cancelAnimations(this); 119 120 if (RenderView* v = view()) 121 v->removeWidget(this); 122 123 124 if (AXObjectCache::accessibilityEnabled()) { 125 document()->axObjectCache()->childrenChanged(this->parent()); 126 document()->axObjectCache()->remove(this); 127 } 128 remove(); 129 130 if (m_hasCounterNodeMap) 131 RenderCounter::destroyCounterNodes(this); 132 133 setWidget(0); 134 135 // removes from override size map 136 if (hasOverrideSize()) 137 setOverrideSize(-1); 138 139 if (style() && (style()->logicalHeight().isPercent() || style()->logicalMinHeight().isPercent() || style()->logicalMaxHeight().isPercent())) 140 RenderBlock::removePercentHeightDescendant(this); 141 142 if (hasLayer()) { 143 layer()->clearClipRects(); 144 setHasLayer(false); 145 destroyLayer(); 146 } 147 148 // Grab the arena from node()->document()->renderArena() before clearing the node pointer. 149 // Clear the node before deref-ing, as this may be deleted when deref is called. 150 RenderArena* arena = renderArena(); 151 setNode(0); 152 deref(arena); 153 } 154 155 RenderWidget::~RenderWidget() 156 { 157 ASSERT(m_refCount <= 0); 158 clearWidget(); 159 } 160 161 bool RenderWidget::setWidgetGeometry(const IntRect& frame, const IntSize& boundsSize) 162 { 163 if (!node()) 164 return false; 165 166 IntRect clipRect = enclosingLayer()->childrenClipRect(); 167 bool clipChanged = m_clipRect != clipRect; 168 bool boundsChanged = m_widget->frameRect() != frame; 169 170 if (!boundsChanged && !clipChanged) 171 return false; 172 173 m_clipRect = clipRect; 174 175 RenderWidgetProtector protector(this); 176 RefPtr<Node> protectedNode(node()); 177 m_widget->setFrameRect(frame); 178 if (m_widget) // setFrameRect can run arbitrary script, which might clear m_widget. 179 m_widget->setBoundsSize(boundsSize); 180 181 #if USE(ACCELERATED_COMPOSITING) 182 if (hasLayer() && layer()->isComposited()) 183 layer()->backing()->updateAfterWidgetResize(); 184 #endif 185 186 return boundsChanged; 187 } 188 189 void RenderWidget::setWidget(PassRefPtr<Widget> widget) 190 { 191 if (widget == m_widget) 192 return; 193 194 if (m_widget) { 195 moveWidgetToParentSoon(m_widget.get(), 0); 196 widgetRendererMap().remove(m_widget.get()); 197 clearWidget(); 198 } 199 m_widget = widget; 200 if (m_widget) { 201 widgetRendererMap().add(m_widget.get(), this); 202 // If we've already received a layout, apply the calculated space to the 203 // widget immediately, but we have to have really been fully constructed (with a non-null 204 // style pointer). 205 if (style()) { 206 if (!needsLayout()) 207 setWidgetGeometry(IntRect(localToAbsoluteQuad(FloatQuad(contentBoxRect())).boundingBox()), contentBoxRect().size()); 208 if (style()->visibility() != VISIBLE) 209 m_widget->hide(); 210 else { 211 m_widget->show(); 212 repaint(); 213 } 214 } 215 moveWidgetToParentSoon(m_widget.get(), m_frameView); 216 } 217 } 218 219 void RenderWidget::layout() 220 { 221 ASSERT(needsLayout()); 222 223 setNeedsLayout(false); 224 } 225 226 void RenderWidget::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) 227 { 228 RenderReplaced::styleDidChange(diff, oldStyle); 229 if (m_widget) { 230 if (style()->visibility() != VISIBLE) 231 m_widget->hide(); 232 else 233 m_widget->show(); 234 } 235 } 236 237 void RenderWidget::showSubstituteImage(PassRefPtr<Image> prpImage) 238 { 239 m_substituteImage = prpImage; 240 repaint(); 241 } 242 243 void RenderWidget::notifyWidget(WidgetNotification notification) 244 { 245 if (m_widget) 246 m_widget->notifyWidget(notification); 247 } 248 249 void RenderWidget::paint(PaintInfo& paintInfo, int tx, int ty) 250 { 251 if (!shouldPaint(paintInfo, tx, ty)) 252 return; 253 254 tx += x(); 255 ty += y(); 256 257 if (hasBoxDecorations() && (paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseSelection)) 258 paintBoxDecorations(paintInfo, tx, ty); 259 260 if (paintInfo.phase == PaintPhaseMask) { 261 paintMask(paintInfo, tx, ty); 262 return; 263 } 264 265 if (!m_frameView || paintInfo.phase != PaintPhaseForeground || style()->visibility() != VISIBLE) 266 return; 267 268 #if PLATFORM(MAC) 269 if (style()->highlight() != nullAtom && !paintInfo.context->paintingDisabled()) 270 paintCustomHighlight(tx - x(), ty - y(), style()->highlight(), true); 271 #endif 272 273 if (style()->hasBorderRadius()) { 274 IntRect borderRect = IntRect(tx, ty, width(), height()); 275 276 if (borderRect.isEmpty()) 277 return; 278 279 // Push a clip if we have a border radius, since we want to round the foreground content that gets painted. 280 paintInfo.context->save(); 281 paintInfo.context->addRoundedRectClip(style()->getRoundedBorderFor(borderRect)); 282 } 283 284 if (m_widget) { 285 // Tell the widget to paint now. This is the only time the widget is allowed 286 // to paint itself. That way it will composite properly with z-indexed layers. 287 if (m_substituteImage) 288 paintInfo.context->drawImage(m_substituteImage.get(), style()->colorSpace(), m_widget->frameRect()); 289 else { 290 IntPoint widgetLocation = m_widget->frameRect().location(); 291 IntPoint paintLocation(tx + borderLeft() + paddingLeft(), ty + borderTop() + paddingTop()); 292 IntRect paintRect = paintInfo.rect; 293 294 IntSize paintOffset = paintLocation - widgetLocation; 295 // When painting widgets into compositing layers, tx and ty are relative to the enclosing compositing layer, 296 // not the root. In this case, shift the CTM and adjust the paintRect to be root-relative to fix plug-in drawing. 297 if (!paintOffset.isZero()) { 298 paintInfo.context->translate(paintOffset); 299 paintRect.move(-paintOffset); 300 } 301 m_widget->paint(paintInfo.context, paintRect); 302 303 if (!paintOffset.isZero()) 304 paintInfo.context->translate(-paintOffset); 305 } 306 307 if (m_widget->isFrameView()) { 308 FrameView* frameView = static_cast<FrameView*>(m_widget.get()); 309 bool runOverlapTests = !frameView->useSlowRepaintsIfNotOverlapped() || frameView->hasCompositedContentIncludingDescendants(); 310 if (paintInfo.overlapTestRequests && runOverlapTests) { 311 ASSERT(!paintInfo.overlapTestRequests->contains(this)); 312 paintInfo.overlapTestRequests->set(this, m_widget->frameRect()); 313 } 314 } 315 } 316 317 if (style()->hasBorderRadius()) 318 paintInfo.context->restore(); 319 320 // Paint a partially transparent wash over selected widgets. 321 if (isSelected() && !document()->printing()) { 322 // FIXME: selectionRect() is in absolute, not painting coordinates. 323 paintInfo.context->fillRect(selectionRect(), selectionBackgroundColor(), style()->colorSpace()); 324 } 325 } 326 327 void RenderWidget::setOverlapTestResult(bool isOverlapped) 328 { 329 ASSERT(m_widget); 330 ASSERT(m_widget->isFrameView()); 331 static_cast<FrameView*>(m_widget.get())->setIsOverlapped(isOverlapped); 332 } 333 334 void RenderWidget::deref(RenderArena *arena) 335 { 336 if (--m_refCount <= 0) 337 arenaDelete(arena, this); 338 } 339 340 void RenderWidget::updateWidgetPosition() 341 { 342 if (!m_widget || !node()) // Check the node in case destroy() has been called. 343 return; 344 345 IntRect contentBox = contentBoxRect(); 346 IntRect absoluteContentBox = IntRect(localToAbsoluteQuad(FloatQuad(contentBox)).boundingBox()); 347 bool boundsChanged = setWidgetGeometry(absoluteContentBox, contentBox.size()); 348 349 // if the frame bounds got changed, or if view needs layout (possibly indicating 350 // content size is wrong) we have to do a layout to set the right widget size 351 if (m_widget && m_widget->isFrameView()) { 352 FrameView* frameView = static_cast<FrameView*>(m_widget.get()); 353 // Check the frame's page to make sure that the frame isn't in the process of being destroyed. 354 if ((boundsChanged || frameView->needsLayout()) && frameView->frame()->page()) 355 frameView->layout(); 356 } 357 } 358 359 void RenderWidget::widgetPositionsUpdated() 360 { 361 if (!m_widget) 362 return; 363 m_widget->widgetPositionsUpdated(); 364 } 365 366 IntRect RenderWidget::windowClipRect() const 367 { 368 if (!m_frameView) 369 return IntRect(); 370 371 return intersection(m_frameView->contentsToWindow(m_clipRect), m_frameView->windowClipRect()); 372 } 373 374 void RenderWidget::setSelectionState(SelectionState state) 375 { 376 if (selectionState() != state) { 377 RenderReplaced::setSelectionState(state); 378 if (m_widget) 379 m_widget->setIsSelected(isSelected()); 380 } 381 } 382 383 void RenderWidget::clearWidget() 384 { 385 m_widget = 0; 386 } 387 388 RenderWidget* RenderWidget::find(const Widget* widget) 389 { 390 return widgetRendererMap().get(widget); 391 } 392 393 bool RenderWidget::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int x, int y, int tx, int ty, HitTestAction action) 394 { 395 bool hadResult = result.innerNode(); 396 bool inside = RenderReplaced::nodeAtPoint(request, result, x, y, tx, ty, action); 397 398 // Check to see if we are really over the widget itself (and not just in the border/padding area). 399 if ((inside || result.isRectBasedTest()) && !hadResult && result.innerNode() == node()) 400 result.setIsOverWidget(contentBoxRect().contains(result.localPoint())); 401 return inside; 402 } 403 404 } // namespace WebCore 405