Push pixel snapping logic into TransformState
[WebKit-https.git] / Source / WebCore / rendering / RenderEmbeddedObject.cpp
1 /*
2  * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3  *           (C) 2000 Simon Hausmann <hausmann@kde.org>
4  *           (C) 2000 Stefan Schimanski (1Stein@gmx.de)
5  * Copyright (C) 2004, 2005, 2006, 2008, 2009, 2010 Apple 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 "RenderEmbeddedObject.h"
26
27 #include "Chrome.h"
28 #include "ChromeClient.h"
29 #include "Cursor.h"
30 #include "CSSValueKeywords.h"
31 #include "Font.h"
32 #include "FontSelector.h"
33 #include "Frame.h"
34 #include "FrameLoaderClient.h"
35 #include "GraphicsContext.h"
36 #include "HTMLEmbedElement.h"
37 #include "HTMLIFrameElement.h"
38 #include "HTMLNames.h"
39 #include "HTMLObjectElement.h"
40 #include "HTMLParamElement.h"
41 #include "HitTestResult.h"
42 #include "LocalizedStrings.h"
43 #include "MIMETypeRegistry.h"
44 #include "MouseEvent.h"
45 #include "Page.h"
46 #include "PaintInfo.h"
47 #include "Path.h"
48 #include "PluginViewBase.h"
49 #include "RenderTheme.h"
50 #include "RenderView.h"
51 #include "RenderWidgetProtector.h"
52 #include "Settings.h"
53 #include "Text.h"
54 #include "TextRun.h"
55
56 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
57 #include "HTMLMediaElement.h"
58 #endif
59
60 namespace WebCore {
61
62 using namespace HTMLNames;
63     
64 static const float replacementTextRoundedRectHeight = 18;
65 static const float replacementTextRoundedRectLeftRightTextMargin = 6;
66 static const float replacementTextRoundedRectOpacity = 0.20f;
67 static const float replacementTextPressedRoundedRectOpacity = 0.65f;
68 static const float replacementTextRoundedRectRadius = 5;
69 static const float replacementTextTextOpacity = 0.55f;
70 static const float replacementTextPressedTextOpacity = 0.65f;
71
72 static const Color& replacementTextRoundedRectPressedColor()
73 {
74     static const Color lightGray(205, 205, 205);
75     return lightGray;
76 }
77     
78 RenderEmbeddedObject::RenderEmbeddedObject(Element* element)
79     : RenderPart(element)
80     , m_hasFallbackContent(false)
81     , m_showsUnavailablePluginIndicator(false)
82     , m_unavailablePluginIndicatorIsPressed(false)
83     , m_mouseDownWasInUnavailablePluginIndicator(false)
84 {
85     view()->frameView()->setIsVisuallyNonEmpty();
86 }
87
88 RenderEmbeddedObject::~RenderEmbeddedObject()
89 {
90     if (frameView())
91         frameView()->removeWidgetToUpdate(this);
92 }
93
94 #if USE(ACCELERATED_COMPOSITING)
95 bool RenderEmbeddedObject::requiresLayer() const
96 {
97     if (RenderPart::requiresLayer())
98         return true;
99     
100     return allowsAcceleratedCompositing();
101 }
102
103 bool RenderEmbeddedObject::allowsAcceleratedCompositing() const
104 {
105     return widget() && widget()->isPluginViewBase() && static_cast<PluginViewBase*>(widget())->platformLayer();
106 }
107 #endif
108
109 static String unavailablePluginReplacementText(RenderEmbeddedObject::PluginUnavailabilityReason pluginUnavailabilityReason)
110 {
111     switch (pluginUnavailabilityReason) {
112     case RenderEmbeddedObject::PluginMissing:
113         return missingPluginText();
114     case RenderEmbeddedObject::PluginCrashed:
115         return crashedPluginText();
116     case RenderEmbeddedObject::PluginBlockedByContentSecurityPolicy:
117         return blockedPluginByContentSecurityPolicyText();
118     case RenderEmbeddedObject::InsecurePluginVersion:
119         return insecurePluginVersionText();
120     case RenderEmbeddedObject::PluginInactive:
121         return inactivePluginText();
122     }
123
124     ASSERT_NOT_REACHED();
125     return String();
126 }
127
128 void RenderEmbeddedObject::setPluginUnavailabilityReason(PluginUnavailabilityReason pluginUnavailabilityReason)
129 {
130     ASSERT(!m_showsUnavailablePluginIndicator);
131     m_showsUnavailablePluginIndicator = true;
132     m_pluginUnavailabilityReason = pluginUnavailabilityReason;
133
134     m_unavailablePluginReplacementText = unavailablePluginReplacementText(pluginUnavailabilityReason);
135 }
136
137 bool RenderEmbeddedObject::showsUnavailablePluginIndicator() const
138 {
139     return m_showsUnavailablePluginIndicator;
140 }
141
142 void RenderEmbeddedObject::setUnavailablePluginIndicatorIsPressed(bool pressed)
143 {
144     if (m_unavailablePluginIndicatorIsPressed == pressed)
145         return;
146     
147     m_unavailablePluginIndicatorIsPressed = pressed;
148     repaint();
149 }
150
151 void RenderEmbeddedObject::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
152 {
153     Page* page = 0;
154     if (Frame* frame = this->frame())
155         page = frame->page();
156
157     if (showsUnavailablePluginIndicator()) {
158         if (page && paintInfo.phase == PaintPhaseForeground)
159             page->addRelevantUnpaintedObject(this, visualOverflowRect());
160         RenderReplaced::paint(paintInfo, paintOffset);
161         return;
162     }
163
164     if (page && paintInfo.phase == PaintPhaseForeground)
165         page->addRelevantRepaintedObject(this, visualOverflowRect());
166
167     RenderPart::paint(paintInfo, paintOffset);
168 }
169
170 void RenderEmbeddedObject::paintReplaced(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
171 {
172     if (!showsUnavailablePluginIndicator())
173         return;
174
175     if (paintInfo.phase == PaintPhaseSelection)
176         return;
177     
178     GraphicsContext* context = paintInfo.context;
179     if (context->paintingDisabled())
180         return;
181     
182     FloatRect contentRect;
183     Path path;
184     FloatRect replacementTextRect;
185     Font font;
186     TextRun run("");
187     float textWidth;
188     if (!getReplacementTextGeometry(paintOffset, contentRect, path, replacementTextRect, font, run, textWidth))
189         return;
190     
191     GraphicsContextStateSaver stateSaver(*context);
192     context->clip(contentRect);
193     context->setAlpha(m_unavailablePluginIndicatorIsPressed ? replacementTextPressedRoundedRectOpacity : replacementTextRoundedRectOpacity);
194     context->setFillColor(m_unavailablePluginIndicatorIsPressed ? replacementTextRoundedRectPressedColor() : Color::white, style()->colorSpace());
195     context->fillPath(path);
196
197     const FontMetrics& fontMetrics = font.fontMetrics();
198     float labelX = roundf(replacementTextRect.location().x() + (replacementTextRect.size().width() - textWidth) / 2);
199     float labelY = roundf(replacementTextRect.location().y() + (replacementTextRect.size().height() - fontMetrics.height()) / 2 + fontMetrics.ascent());
200     context->setAlpha(m_unavailablePluginIndicatorIsPressed ? replacementTextPressedTextOpacity : replacementTextTextOpacity);
201     context->setFillColor(Color::black, style()->colorSpace());
202     context->drawBidiText(font, run, FloatPoint(labelX, labelY));
203 }
204
205 bool RenderEmbeddedObject::getReplacementTextGeometry(const LayoutPoint& accumulatedOffset, FloatRect& contentRect, Path& path, FloatRect& replacementTextRect, Font& font, TextRun& run, float& textWidth) const
206 {
207     contentRect = contentBoxRect();
208     contentRect.moveBy(roundedIntPoint(accumulatedOffset));
209     
210     FontDescription fontDescription;
211     RenderTheme::defaultTheme()->systemFont(CSSValueWebkitSmallControl, fontDescription);
212     fontDescription.setWeight(FontWeightBold);
213     Settings* settings = document()->settings();
214     ASSERT(settings);
215     if (!settings)
216         return false;
217     fontDescription.setRenderingMode(settings->fontRenderingMode());
218     fontDescription.setComputedSize(fontDescription.specifiedSize());
219     font = Font(fontDescription, 0, 0);
220     font.update(0);
221
222     run = TextRun(m_unavailablePluginReplacementText);
223     textWidth = font.width(run);
224     
225     replacementTextRect.setSize(FloatSize(textWidth + replacementTextRoundedRectLeftRightTextMargin * 2, replacementTextRoundedRectHeight));
226     float x = (contentRect.size().width() / 2 - replacementTextRect.size().width() / 2) + contentRect.location().x();
227     float y = (contentRect.size().height() / 2 - replacementTextRect.size().height() / 2) + contentRect.location().y();
228     replacementTextRect.setLocation(FloatPoint(x, y));
229     
230     path.addRoundedRect(replacementTextRect, FloatSize(replacementTextRoundedRectRadius, replacementTextRoundedRectRadius));
231
232     return true;
233 }
234
235 void RenderEmbeddedObject::layout()
236 {
237     StackStats::LayoutCheckPoint layoutCheckPoint;
238     ASSERT(needsLayout());
239
240 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
241     LayoutSize oldSize = contentBoxRect().size();
242 #endif
243
244     updateLogicalWidth();
245     updateLogicalHeight();
246
247     RenderPart::layout();
248
249     m_overflow.clear();
250     addVisualEffectOverflow();
251
252     updateLayerTransform();
253
254     if (!widget() && frameView())
255         frameView()->addWidgetToUpdate(this);
256
257     setNeedsLayout(false);
258
259 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
260     // This code copied from RenderMedia::layout().
261     RenderBox* controlsRenderer = toRenderBox(m_children.firstChild());
262     if (!controlsRenderer)
263         return;
264     
265     LayoutSize newSize = contentBoxRect().size();
266     if (newSize == oldSize && !controlsRenderer->needsLayout())
267         return;
268     
269     // When calling layout() on a child node, a parent must either push a LayoutStateMaintainter, or
270     // instantiate LayoutStateDisabler. Since using a LayoutStateMaintainer is slightly more efficient,
271     // and this method will be called many times per second during playback, use a LayoutStateMaintainer:
272     LayoutStateMaintainer statePusher(view(), this, locationOffset(), hasTransform() || hasReflection() || style()->isFlippedBlocksWritingMode());
273     
274     controlsRenderer->setLocation(LayoutPoint(borderLeft(), borderTop()) + LayoutSize(paddingLeft(), paddingTop()));
275     controlsRenderer->style()->setHeight(Length(newSize.height(), Fixed));
276     controlsRenderer->style()->setWidth(Length(newSize.width(), Fixed));
277     controlsRenderer->setNeedsLayout(true, MarkOnlyThis);
278     controlsRenderer->layout();
279     setChildNeedsLayout(false);
280     
281     statePusher.pop();
282 #endif
283 }
284
285 void RenderEmbeddedObject::viewCleared()
286 {
287     // This is required for <object> elements whose contents are rendered by WebCore (e.g. src="foo.html").
288     if (node() && widget() && widget()->isFrameView()) {
289         FrameView* view = static_cast<FrameView*>(widget());
290         int marginWidth = -1;
291         int marginHeight = -1;
292         if (node()->hasTagName(iframeTag)) {
293             HTMLIFrameElement* frame = static_cast<HTMLIFrameElement*>(node());
294             marginWidth = frame->marginWidth();
295             marginHeight = frame->marginHeight();
296         }
297         if (marginWidth != -1)
298             view->setMarginWidth(marginWidth);
299         if (marginHeight != -1)
300             view->setMarginHeight(marginHeight);
301     }
302 }
303
304 bool RenderEmbeddedObject::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction hitTestAction)
305 {
306     if (!RenderPart::nodeAtPoint(request, result, locationInContainer, accumulatedOffset, hitTestAction))
307         return false;
308
309     if (!widget() || !widget()->isPluginViewBase())
310         return true;
311
312     PluginViewBase* view = static_cast<PluginViewBase*>(widget());
313     IntPoint roundedPoint = locationInContainer.roundedPoint();
314
315     if (Scrollbar* horizontalScrollbar = view->horizontalScrollbar()) {
316         if (horizontalScrollbar->shouldParticipateInHitTesting() && horizontalScrollbar->frameRect().contains(roundedPoint)) {
317             result.setScrollbar(horizontalScrollbar);
318             return true;
319         }
320     }
321
322     if (Scrollbar* verticalScrollbar = view->verticalScrollbar()) {
323         if (verticalScrollbar->shouldParticipateInHitTesting() && verticalScrollbar->frameRect().contains(roundedPoint)) {
324             result.setScrollbar(verticalScrollbar);
325             return true;
326         }
327     }
328
329     return true;
330 }
331
332 bool RenderEmbeddedObject::scroll(ScrollDirection direction, ScrollGranularity granularity, float, Node**)
333 {
334     if (!widget() || !widget()->isPluginViewBase())
335         return false;
336
337     return static_cast<PluginViewBase*>(widget())->scroll(direction, granularity);
338 }
339
340 bool RenderEmbeddedObject::logicalScroll(ScrollLogicalDirection direction, ScrollGranularity granularity, float multiplier, Node** stopNode)
341 {
342     // Plugins don't expose a writing direction, so assuming horizontal LTR.
343     return scroll(logicalToPhysical(direction, true, false), granularity, multiplier, stopNode);
344 }
345
346
347 bool RenderEmbeddedObject::isInUnavailablePluginIndicator(const LayoutPoint& point) const
348 {
349     FloatRect contentRect;
350     Path path;
351     FloatRect replacementTextRect;
352     Font font;
353     TextRun run("");
354     float textWidth;
355     return getReplacementTextGeometry(IntPoint(), contentRect, path, replacementTextRect, font, run, textWidth)
356         && path.contains(point);
357 }
358
359 bool RenderEmbeddedObject::isInUnavailablePluginIndicator(MouseEvent* event) const
360 {
361     return isInUnavailablePluginIndicator(roundedLayoutPoint(absoluteToLocal(event->absoluteLocation(), UseTransforms)));
362 }
363
364 static bool shouldUnavailablePluginMessageBeButton(Document* document, RenderEmbeddedObject::PluginUnavailabilityReason pluginUnavailabilityReason)
365 {
366     Page* page = document->page();
367     return page && page->chrome()->client()->shouldUnavailablePluginMessageBeButton(pluginUnavailabilityReason);
368 }
369
370 void RenderEmbeddedObject::handleUnavailablePluginIndicatorEvent(Event* event)
371 {
372     if (!shouldUnavailablePluginMessageBeButton(document(), m_pluginUnavailabilityReason))
373         return;
374     
375     if (!event->isMouseEvent())
376         return;
377     
378     MouseEvent* mouseEvent = static_cast<MouseEvent*>(event);
379     HTMLPlugInElement* element = static_cast<HTMLPlugInElement*>(node());
380     if (event->type() == eventNames().mousedownEvent && static_cast<MouseEvent*>(event)->button() == LeftButton) {
381         m_mouseDownWasInUnavailablePluginIndicator = isInUnavailablePluginIndicator(mouseEvent);
382         if (m_mouseDownWasInUnavailablePluginIndicator) {
383             if (Frame* frame = document()->frame()) {
384                 frame->eventHandler()->setCapturingMouseEventsNode(element);
385                 element->setIsCapturingMouseEvents(true);
386             }
387             setUnavailablePluginIndicatorIsPressed(true);
388         }
389         event->setDefaultHandled();
390     }        
391     if (event->type() == eventNames().mouseupEvent && static_cast<MouseEvent*>(event)->button() == LeftButton) {
392         if (m_unavailablePluginIndicatorIsPressed) {
393             if (Frame* frame = document()->frame()) {
394                 frame->eventHandler()->setCapturingMouseEventsNode(0);
395                 element->setIsCapturingMouseEvents(false);
396             }
397             setUnavailablePluginIndicatorIsPressed(false);
398         }
399         if (m_mouseDownWasInUnavailablePluginIndicator && isInUnavailablePluginIndicator(mouseEvent)) {
400             if (Page* page = document()->page())
401                 page->chrome()->client()->unavailablePluginButtonClicked(element, m_pluginUnavailabilityReason);
402         }
403         m_mouseDownWasInUnavailablePluginIndicator = false;
404         event->setDefaultHandled();
405     }
406     if (event->type() == eventNames().mousemoveEvent) {
407         setUnavailablePluginIndicatorIsPressed(m_mouseDownWasInUnavailablePluginIndicator && isInUnavailablePluginIndicator(mouseEvent));
408         event->setDefaultHandled();
409     }
410 }
411
412 CursorDirective RenderEmbeddedObject::getCursor(const LayoutPoint& point, Cursor& cursor) const
413 {
414     if (showsUnavailablePluginIndicator() && shouldUnavailablePluginMessageBeButton(document(), m_pluginUnavailabilityReason) && isInUnavailablePluginIndicator(point)) {
415         cursor = handCursor();
416         return SetCursor;
417     }
418     return RenderPart::getCursor(point, cursor);
419 }
420
421 }