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 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 "RenderView.h" 31 #include "RenderWidgetProtector.h" 32 33 using namespace std; 34 35 namespace WebCore { 36 37 static HashMap<const Widget*, RenderWidget*>& widgetRendererMap() 38 { 39 static HashMap<const Widget*, RenderWidget*>* staticWidgetRendererMap = new HashMap<const Widget*, RenderWidget*>; 40 return *staticWidgetRendererMap; 41 } 42 43 static size_t widgetHierarchyUpdateSuspendCount; 44 45 typedef HashMap<RefPtr<Widget>, FrameView*> WidgetToParentMap; 46 47 static WidgetToParentMap& widgetNewParentMap() 48 { 49 DEFINE_STATIC_LOCAL(WidgetToParentMap, map, ()); 50 return map; 51 } 52 53 void RenderWidget::suspendWidgetHierarchyUpdates() 54 { 55 widgetHierarchyUpdateSuspendCount++; 56 } 57 58 void RenderWidget::resumeWidgetHierarchyUpdates() 59 { 60 ASSERT(widgetHierarchyUpdateSuspendCount); 61 if (widgetHierarchyUpdateSuspendCount == 1) { 62 WidgetToParentMap map = widgetNewParentMap(); 63 widgetNewParentMap().clear(); 64 WidgetToParentMap::iterator end = map.end(); 65 for (WidgetToParentMap::iterator it = map.begin(); it != end; ++it) { 66 Widget* child = it->first.get(); 67 ScrollView* currentParent = child->parent(); 68 FrameView* newParent = it->second; 69 if (newParent != currentParent) { 70 if (currentParent) 71 currentParent->removeChild(child); 72 if (newParent) 73 newParent->addChild(child); 74 } 75 } 76 } 77 widgetHierarchyUpdateSuspendCount--; 78 } 79 80 static void moveWidgetToParentSoon(Widget* child, FrameView* parent) 81 { 82 if (!widgetHierarchyUpdateSuspendCount) { 83 if (parent) 84 parent->addChild(child); 85 else 86 child->removeFromParent(); 87 return; 88 } 89 widgetNewParentMap().set(child, parent); 90 } 91 92 RenderWidget::RenderWidget(Node* node) 93 : RenderReplaced(node) 94 , m_widget(0) 95 , m_frameView(node->document()->view()) 96 // Reference counting is used to prevent the widget from being 97 // destroyed while inside the Widget code, which might not be 98 // able to handle that. 99 , m_refCount(1) 100 { 101 view()->addWidget(this); 102 } 103 104 void RenderWidget::destroy() 105 { 106 // We can't call the base class's destroy because we don't 107 // want to unconditionally delete ourselves (we're ref-counted). 108 // So the code below includes copied and pasted contents of 109 // both RenderBox::destroy() and RenderObject::destroy(). 110 // Fix originally made for <rdar://problem/4228818>. 111 112 animation()->cancelAnimations(this); 113 114 if (RenderView* v = view()) 115 v->removeWidget(this); 116 117 if (AXObjectCache::accessibilityEnabled()) { 118 document()->axObjectCache()->childrenChanged(this->parent()); 119 document()->axObjectCache()->remove(this); 120 } 121 remove(); 122 123 setWidget(0); 124 125 // removes from override size map 126 if (hasOverrideSize()) 127 setOverrideSize(-1); 128 129 if (style() && (style()->height().isPercent() || style()->minHeight().isPercent() || style()->maxHeight().isPercent())) 130 RenderBlock::removePercentHeightDescendant(this); 131 132 if (hasLayer()) { 133 layer()->clearClipRects(); 134 setHasLayer(false); 135 destroyLayer(); 136 } 137 138 // Grab the arena from node()->document()->renderArena() before clearing the node pointer. 139 // Clear the node before deref-ing, as this may be deleted when deref is called. 140 RenderArena* arena = renderArena(); 141 setNode(0); 142 deref(arena); 143 } 144 145 RenderWidget::~RenderWidget() 146 { 147 ASSERT(m_refCount <= 0); 148 clearWidget(); 149 } 150 151 bool RenderWidget::setWidgetGeometry(const IntRect& frame) 152 { 153 ASSERT(!widgetHierarchyUpdateSuspendCount); 154 if (!node() || m_widget->frameRect() == frame) 155 return false; 156 157 RenderWidgetProtector protector(this); 158 RefPtr<Node> protectedNode(node()); 159 m_widget->setFrameRect(frame); 160 return true; 161 } 162 163 void RenderWidget::setWidget(PassRefPtr<Widget> widget) 164 { 165 if (widget == m_widget) 166 return; 167 168 if (m_widget) { 169 moveWidgetToParentSoon(m_widget.get(), 0); 170 widgetRendererMap().remove(m_widget.get()); 171 clearWidget(); 172 } 173 m_widget = widget; 174 if (m_widget) { 175 widgetRendererMap().add(m_widget.get(), this); 176 // If we've already received a layout, apply the calculated space to the 177 // widget immediately, but we have to have really been fully constructed (with a non-null 178 // style pointer). 179 if (style()) { 180 if (!needsLayout()) 181 setWidgetGeometry(absoluteContentBox()); 182 if (style()->visibility() != VISIBLE) 183 m_widget->hide(); 184 else 185 m_widget->show(); 186 } 187 moveWidgetToParentSoon(m_widget.get(), m_frameView); 188 } 189 } 190 191 void RenderWidget::layout() 192 { 193 ASSERT(needsLayout()); 194 195 setNeedsLayout(false); 196 } 197 198 void RenderWidget::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) 199 { 200 RenderReplaced::styleDidChange(diff, oldStyle); 201 if (m_widget) { 202 if (style()->visibility() != VISIBLE) 203 m_widget->hide(); 204 else 205 m_widget->show(); 206 } 207 } 208 209 void RenderWidget::showSubstituteImage(PassRefPtr<Image> prpImage) 210 { 211 m_substituteImage = prpImage; 212 repaint(); 213 } 214 215 void RenderWidget::paint(PaintInfo& paintInfo, int tx, int ty) 216 { 217 if (!shouldPaint(paintInfo, tx, ty)) 218 return; 219 220 tx += x(); 221 ty += y(); 222 223 if (hasBoxDecorations() && (paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseSelection)) 224 paintBoxDecorations(paintInfo, tx, ty); 225 226 if (paintInfo.phase == PaintPhaseMask) { 227 paintMask(paintInfo, tx, ty); 228 return; 229 } 230 231 if (!m_frameView || paintInfo.phase != PaintPhaseForeground || style()->visibility() != VISIBLE) 232 return; 233 234 #if PLATFORM(MAC) 235 if (style()->highlight() != nullAtom && !paintInfo.context->paintingDisabled()) 236 paintCustomHighlight(tx - x(), ty - y(), style()->highlight(), true); 237 #endif 238 239 if (style()->hasBorderRadius()) { 240 IntRect borderRect = IntRect(tx, ty, width(), height()); 241 242 if (borderRect.isEmpty()) 243 return; 244 245 // Push a clip if we have a border radius, since we want to round the foreground content that gets painted. 246 paintInfo.context->save(); 247 248 IntSize topLeft, topRight, bottomLeft, bottomRight; 249 style()->getBorderRadiiForRect(borderRect, topLeft, topRight, bottomLeft, bottomRight); 250 251 paintInfo.context->addRoundedRectClip(borderRect, topLeft, topRight, bottomLeft, bottomRight); 252 } 253 254 if (m_widget) { 255 // Tell the widget to paint now. This is the only time the widget is allowed 256 // to paint itself. That way it will composite properly with z-indexed layers. 257 if (m_substituteImage) 258 paintInfo.context->drawImage(m_substituteImage.get(), style()->colorSpace(), m_widget->frameRect()); 259 else { 260 IntPoint widgetLocation = m_widget->frameRect().location(); 261 IntPoint paintLocation(tx + borderLeft() + paddingLeft(), ty + borderTop() + paddingTop()); 262 IntRect paintRect = paintInfo.rect; 263 264 IntSize paintOffset = paintLocation - widgetLocation; 265 // When painting widgets into compositing layers, tx and ty are relative to the enclosing compositing layer, 266 // not the root. In this case, shift the CTM and adjust the paintRect to be root-relative to fix plug-in drawing. 267 if (!paintOffset.isZero()) { 268 paintInfo.context->translate(paintOffset); 269 paintRect.move(-paintOffset); 270 } 271 m_widget->paint(paintInfo.context, paintRect); 272 273 if (!paintOffset.isZero()) 274 paintInfo.context->translate(-paintOffset); 275 } 276 if (m_widget->isFrameView() && paintInfo.overlapTestRequests && !static_cast<FrameView*>(m_widget.get())->useSlowRepaintsIfNotOverlapped()) { 277 ASSERT(!paintInfo.overlapTestRequests->contains(this)); 278 paintInfo.overlapTestRequests->set(this, m_widget->frameRect()); 279 } 280 } 281 282 if (style()->hasBorderRadius()) 283 paintInfo.context->restore(); 284 285 // Paint a partially transparent wash over selected widgets. 286 if (isSelected() && !document()->printing()) { 287 // FIXME: selectionRect() is in absolute, not painting coordinates. 288 paintInfo.context->fillRect(selectionRect(), selectionBackgroundColor(), style()->colorSpace()); 289 } 290 } 291 292 void RenderWidget::setOverlapTestResult(bool isOverlapped) 293 { 294 ASSERT(m_widget); 295 ASSERT(m_widget->isFrameView()); 296 static_cast<FrameView*>(m_widget.get())->setIsOverlapped(isOverlapped); 297 } 298 299 void RenderWidget::deref(RenderArena *arena) 300 { 301 if (--m_refCount <= 0) 302 arenaDelete(arena, this); 303 } 304 305 void RenderWidget::updateWidgetPosition() 306 { 307 if (!m_widget) 308 return; 309 310 // FIXME: This doesn't work correctly with transforms. 311 FloatPoint absPos = localToAbsolute(); 312 absPos.move(borderLeft() + paddingLeft(), borderTop() + paddingTop()); 313 314 int w = width() - borderLeft() - borderRight() - paddingLeft() - paddingRight(); 315 int h = height() - borderTop() - borderBottom() - paddingTop() - paddingBottom(); 316 317 bool boundsChanged = setWidgetGeometry(IntRect(absPos.x(), absPos.y(), w, h)); 318 319 #ifndef FLATTEN_IFRAME 320 // if the frame bounds got changed, or if view needs layout (possibly indicating 321 // content size is wrong) we have to do a layout to set the right widget size 322 if (m_widget->isFrameView()) { 323 FrameView* frameView = static_cast<FrameView*>(m_widget.get()); 324 if (boundsChanged || frameView->needsLayout()) 325 frameView->layout(); 326 } 327 #endif 328 } 329 330 void RenderWidget::setSelectionState(SelectionState state) 331 { 332 if (selectionState() != state) { 333 RenderReplaced::setSelectionState(state); 334 if (m_widget) 335 m_widget->setIsSelected(isSelected()); 336 } 337 } 338 339 void RenderWidget::clearWidget() 340 { 341 m_widget = 0; 342 } 343 344 RenderWidget* RenderWidget::find(const Widget* widget) 345 { 346 return widgetRendererMap().get(widget); 347 } 348 349 bool RenderWidget::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int x, int y, int tx, int ty, HitTestAction action) 350 { 351 bool hadResult = result.innerNode(); 352 bool inside = RenderReplaced::nodeAtPoint(request, result, x, y, tx, ty, action); 353 354 // Check to see if we are really over the widget itself (and not just in the border/padding area). 355 if (inside && !hadResult && result.innerNode() == node()) 356 result.setIsOverWidget(contentBoxRect().contains(result.localPoint())); 357 return inside; 358 } 359 360 } // namespace WebCore 361