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 "CSSValueKeywords.h"
28#include "Chrome.h"
29#include "ChromeClient.h"
30#include "Cursor.h"
31#include "EventHandler.h"
32#include "Font.h"
33#include "FontSelector.h"
34#include "Frame.h"
35#include "FrameLoaderClient.h"
36#include "GraphicsContext.h"
37#include "HTMLAppletElement.h"
38#include "HTMLEmbedElement.h"
39#include "HTMLIFrameElement.h"
40#include "HTMLNames.h"
41#include "HTMLObjectElement.h"
42#include "HTMLParamElement.h"
43#include "HTMLPlugInElement.h"
44#include "HitTestResult.h"
45#include "LocalizedStrings.h"
46#include "MIMETypeRegistry.h"
47#include "MouseEvent.h"
48#include "Page.h"
49#include "PaintInfo.h"
50#include "Path.h"
51#include "PlatformMouseEvent.h"
52#include "PluginViewBase.h"
53#include "RenderLayer.h"
54#include "RenderTheme.h"
55#include "RenderView.h"
56#include "Settings.h"
57#include "Text.h"
58#include "TextRun.h"
59#include <wtf/StackStats.h>
60
61namespace WebCore {
62
63using namespace HTMLNames;
64
65static const float replacementTextRoundedRectHeight = 22;
66static const float replacementTextRoundedRectLeftTextMargin = 10;
67static const float replacementTextRoundedRectRightTextMargin = 10;
68static const float replacementTextRoundedRectRightTextMarginWithArrow = 5;
69static const float replacementTextRoundedRectTopTextMargin = -1;
70static const float replacementTextRoundedRectRadius = 11;
71static const float replacementArrowLeftMargin = -4;
72static const float replacementArrowPadding = 4;
73static const float replacementArrowCirclePadding = 3;
74
75static const Color& replacementTextRoundedRectPressedColor()
76{
77    static const Color pressed(105, 105, 105, 242);
78    return pressed;
79}
80
81static const Color& replacementTextRoundedRectColor()
82{
83    static const Color standard(125, 125, 125, 242);
84    return standard;
85}
86
87static const Color& replacementTextColor()
88{
89    static const Color standard(240, 240, 240, 255);
90    return standard;
91}
92
93static const Color& unavailablePluginBorderColor()
94{
95    static const Color standard(255, 255, 255, 216);
96    return standard;
97}
98
99RenderEmbeddedObject::RenderEmbeddedObject(HTMLFrameOwnerElement& element, PassRef<RenderStyle> style)
100    : RenderWidget(element, WTF::move(style))
101    , m_isPluginUnavailable(false)
102    , m_isUnavailablePluginIndicatorHidden(false)
103    , m_unavailablePluginIndicatorIsPressed(false)
104    , m_mouseDownWasInUnavailablePluginIndicator(false)
105{
106    // Actual size is not known yet, report the default intrinsic size.
107    view().frameView().incrementVisuallyNonEmptyPixelCount(roundedIntSize(intrinsicSize()));
108}
109
110RenderEmbeddedObject::~RenderEmbeddedObject()
111{
112    view().frameView().removeEmbeddedObjectToUpdate(*this);
113}
114
115RenderPtr<RenderEmbeddedObject> RenderEmbeddedObject::createForApplet(HTMLAppletElement& applet, PassRef<RenderStyle> style)
116{
117    auto renderer = createRenderer<RenderEmbeddedObject>(applet, WTF::move(style));
118    renderer->setInline(true);
119    return renderer;
120}
121
122bool RenderEmbeddedObject::requiresLayer() const
123{
124    if (RenderWidget::requiresLayer())
125        return true;
126
127    return allowsAcceleratedCompositing();
128}
129
130bool RenderEmbeddedObject::allowsAcceleratedCompositing() const
131{
132#if PLATFORM(IOS)
133    // The timing of layer creation is different on the phone, since the plugin can only be manipulated from the main thread.
134    return widget() && widget()->isPluginViewBase() && toPluginViewBase(widget())->willProvidePluginLayer();
135#else
136    return widget() && widget()->isPluginViewBase() && toPluginViewBase(widget())->platformLayer();
137#endif
138}
139
140#if !PLATFORM(IOS)
141static String unavailablePluginReplacementText(RenderEmbeddedObject::PluginUnavailabilityReason pluginUnavailabilityReason)
142{
143    switch (pluginUnavailabilityReason) {
144    case RenderEmbeddedObject::PluginMissing:
145        return missingPluginText();
146    case RenderEmbeddedObject::PluginCrashed:
147        return crashedPluginText();
148    case RenderEmbeddedObject::PluginBlockedByContentSecurityPolicy:
149        return blockedPluginByContentSecurityPolicyText();
150    case RenderEmbeddedObject::InsecurePluginVersion:
151        return insecurePluginVersionText();
152    }
153
154    ASSERT_NOT_REACHED();
155    return String();
156}
157#endif
158
159static bool shouldUnavailablePluginMessageBeButton(Document& document, RenderEmbeddedObject::PluginUnavailabilityReason pluginUnavailabilityReason)
160{
161    Page* page = document.page();
162    return page && page->chrome().client().shouldUnavailablePluginMessageBeButton(pluginUnavailabilityReason);
163}
164
165void RenderEmbeddedObject::setPluginUnavailabilityReason(PluginUnavailabilityReason pluginUnavailabilityReason)
166{
167#if PLATFORM(IOS)
168    UNUSED_PARAM(pluginUnavailabilityReason);
169#else
170    setPluginUnavailabilityReasonWithDescription(pluginUnavailabilityReason, unavailablePluginReplacementText(pluginUnavailabilityReason));
171#endif
172}
173
174void RenderEmbeddedObject::setPluginUnavailabilityReasonWithDescription(PluginUnavailabilityReason pluginUnavailabilityReason, const String& description)
175{
176#if PLATFORM(IOS)
177    UNUSED_PARAM(pluginUnavailabilityReason);
178    UNUSED_PARAM(description);
179#else
180    ASSERT(!m_isPluginUnavailable);
181    m_isPluginUnavailable = true;
182    m_pluginUnavailabilityReason = pluginUnavailabilityReason;
183
184    if (description.isEmpty())
185        m_unavailablePluginReplacementText = unavailablePluginReplacementText(pluginUnavailabilityReason);
186    else
187        m_unavailablePluginReplacementText = description;
188#endif
189}
190
191void RenderEmbeddedObject::setUnavailablePluginIndicatorIsPressed(bool pressed)
192{
193    if (m_unavailablePluginIndicatorIsPressed == pressed)
194        return;
195
196    m_unavailablePluginIndicatorIsPressed = pressed;
197    repaint();
198}
199
200void RenderEmbeddedObject::paintSnapshotImage(PaintInfo& paintInfo, const LayoutPoint& paintOffset, Image* image)
201{
202    LayoutUnit cWidth = contentWidth();
203    LayoutUnit cHeight = contentHeight();
204    if (!cWidth || !cHeight)
205        return;
206
207    GraphicsContext* context = paintInfo.context;
208    LayoutSize contentSize(cWidth, cHeight);
209    LayoutPoint contentLocation = location() + paintOffset;
210    contentLocation.move(borderLeft() + paddingLeft(), borderTop() + paddingTop());
211
212    LayoutRect rect(contentLocation, contentSize);
213    IntRect alignedRect = pixelSnappedIntRect(rect);
214    if (alignedRect.width() <= 0 || alignedRect.height() <= 0)
215        return;
216
217    bool useLowQualityScaling = shouldPaintAtLowQuality(context, image, image, alignedRect.size());
218    ImageOrientationDescription orientationDescription(shouldRespectImageOrientation());
219#if ENABLE(CSS_IMAGE_ORIENTATION)
220    orientationDescription.setImageOrientationEnum(style().imageOrientation());
221#endif
222    context->drawImage(image, style().colorSpace(), alignedRect, ImagePaintingOptions(orientationDescription, useLowQualityScaling));
223}
224
225void RenderEmbeddedObject::paintContents(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
226{
227    if (!frameOwnerElement().isPluginElement())
228        return;
229
230    HTMLPlugInElement& plugInElement = toHTMLPlugInElement(frameOwnerElement());
231
232    if (plugInElement.displayState() > HTMLPlugInElement::DisplayingSnapshot) {
233        RenderWidget::paintContents(paintInfo, paintOffset);
234        if (!plugInElement.isRestartedPlugin())
235            return;
236    }
237
238    if (!plugInElement.isPlugInImageElement())
239        return;
240
241    Image* snapshot = toHTMLPlugInImageElement(plugInElement).snapshotImage();
242    if (snapshot)
243        paintSnapshotImage(paintInfo, paintOffset, snapshot);
244}
245
246void RenderEmbeddedObject::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
247{
248    Page* page = frame().page();
249
250    if (isPluginUnavailable()) {
251        if (page && paintInfo.phase == PaintPhaseForeground)
252            page->addRelevantUnpaintedObject(this, visualOverflowRect());
253        RenderReplaced::paint(paintInfo, paintOffset);
254        return;
255    }
256
257    if (page && paintInfo.phase == PaintPhaseForeground)
258        page->addRelevantRepaintedObject(this, visualOverflowRect());
259
260    RenderWidget::paint(paintInfo, paintOffset);
261}
262
263static void drawReplacementArrow(GraphicsContext* context, const FloatRect& insideRect)
264{
265    GraphicsContextStateSaver stateSaver(*context);
266
267    FloatRect rect(insideRect);
268    rect.inflate(-replacementArrowPadding);
269
270    FloatPoint center(rect.center());
271    FloatPoint arrowTip(rect.maxX(), center.y());
272
273    context->setStrokeThickness(2);
274    context->setLineCap(RoundCap);
275    context->setLineJoin(RoundJoin);
276
277    Path path;
278    path.moveTo(FloatPoint(rect.x(), center.y()));
279    path.addLineTo(arrowTip);
280    path.addLineTo(FloatPoint(center.x(), rect.y()));
281    path.moveTo(arrowTip);
282    path.addLineTo(FloatPoint(center.x(), rect.maxY()));
283    context->strokePath(path);
284}
285
286void RenderEmbeddedObject::paintReplaced(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
287{
288    if (!showsUnavailablePluginIndicator())
289        return;
290
291    if (paintInfo.phase == PaintPhaseSelection)
292        return;
293
294    GraphicsContext* context = paintInfo.context;
295    if (context->paintingDisabled())
296        return;
297
298    FloatRect contentRect;
299    FloatRect indicatorRect;
300    FloatRect replacementTextRect;
301    FloatRect arrowRect;
302    Font font;
303    TextRun run("");
304    float textWidth;
305    if (!getReplacementTextGeometry(paintOffset, contentRect, indicatorRect, replacementTextRect, arrowRect, font, run, textWidth))
306        return;
307
308    Path background;
309    background.addRoundedRect(indicatorRect, FloatSize(replacementTextRoundedRectRadius, replacementTextRoundedRectRadius));
310
311    GraphicsContextStateSaver stateSaver(*context);
312    context->clip(contentRect);
313    context->setFillColor(m_unavailablePluginIndicatorIsPressed ? replacementTextRoundedRectPressedColor() : replacementTextRoundedRectColor(), style().colorSpace());
314    context->fillPath(background);
315
316    Path strokePath;
317    FloatRect strokeRect(indicatorRect);
318    strokeRect.inflate(1);
319    strokePath.addRoundedRect(strokeRect, FloatSize(replacementTextRoundedRectRadius + 1, replacementTextRoundedRectRadius + 1));
320
321    context->setStrokeColor(unavailablePluginBorderColor(), style().colorSpace());
322    context->setStrokeThickness(2);
323    context->strokePath(strokePath);
324
325    const FontMetrics& fontMetrics = font.fontMetrics();
326    float labelX = roundf(replacementTextRect.location().x() + replacementTextRoundedRectLeftTextMargin);
327    float labelY = roundf(replacementTextRect.location().y() + (replacementTextRect.size().height() - fontMetrics.height()) / 2 + fontMetrics.ascent() + replacementTextRoundedRectTopTextMargin);
328    context->setFillColor(replacementTextColor(), style().colorSpace());
329    context->drawBidiText(font, run, FloatPoint(labelX, labelY));
330
331    if (shouldUnavailablePluginMessageBeButton(document(), m_pluginUnavailabilityReason)) {
332        arrowRect.inflate(-replacementArrowCirclePadding);
333
334        context->beginTransparencyLayer(1.0);
335        context->setFillColor(replacementTextColor(), style().colorSpace());
336        context->fillEllipse(arrowRect);
337
338        context->setCompositeOperation(CompositeClear);
339        drawReplacementArrow(context, arrowRect);
340        context->endTransparencyLayer();
341    }
342}
343
344void RenderEmbeddedObject::setUnavailablePluginIndicatorIsHidden(bool hidden)
345{
346    m_isUnavailablePluginIndicatorHidden = hidden;
347
348    repaint();
349}
350
351bool RenderEmbeddedObject::getReplacementTextGeometry(const LayoutPoint& accumulatedOffset, FloatRect& contentRect, FloatRect& indicatorRect, FloatRect& replacementTextRect, FloatRect& arrowRect, Font& font, TextRun& run, float& textWidth) const
352{
353    bool includesArrow = shouldUnavailablePluginMessageBeButton(document(), m_pluginUnavailabilityReason);
354
355    contentRect = contentBoxRect();
356    contentRect.moveBy(roundedIntPoint(accumulatedOffset));
357
358    FontDescription fontDescription;
359    RenderTheme::defaultTheme()->systemFont(CSSValueWebkitSmallControl, fontDescription);
360    fontDescription.setWeight(FontWeightBold);
361    fontDescription.setRenderingMode(frame().settings().fontRenderingMode());
362    fontDescription.setComputedSize(12);
363    font = Font(fontDescription, 0, 0);
364    font.update(0);
365
366    run = TextRun(m_unavailablePluginReplacementText);
367    textWidth = font.width(run);
368
369    replacementTextRect.setSize(FloatSize(textWidth + replacementTextRoundedRectLeftTextMargin + (includesArrow ? replacementTextRoundedRectRightTextMarginWithArrow : replacementTextRoundedRectRightTextMargin), replacementTextRoundedRectHeight));
370    float x = (contentRect.size().width() / 2 - replacementTextRect.size().width() / 2) + contentRect.location().x();
371    float y = (contentRect.size().height() / 2 - replacementTextRect.size().height() / 2) + contentRect.location().y();
372    replacementTextRect.setLocation(FloatPoint(x, y));
373
374    indicatorRect = replacementTextRect;
375
376    // Expand the background rect to include the arrow, if it will be used.
377    if (includesArrow) {
378        arrowRect = indicatorRect;
379        arrowRect.setX(ceilf(arrowRect.maxX() + replacementArrowLeftMargin));
380        arrowRect.setWidth(arrowRect.height());
381        indicatorRect.unite(arrowRect);
382    }
383
384    return true;
385}
386
387LayoutRect RenderEmbeddedObject::unavailablePluginIndicatorBounds(const LayoutPoint& accumulatedOffset) const
388{
389    FloatRect contentRect;
390    FloatRect indicatorRect;
391    FloatRect replacementTextRect;
392    FloatRect arrowRect;
393    Font font;
394    TextRun run("", 0);
395    float textWidth;
396    if (getReplacementTextGeometry(accumulatedOffset, contentRect, indicatorRect, replacementTextRect, arrowRect, font, run, textWidth))
397        return LayoutRect(indicatorRect);
398
399    return LayoutRect();
400}
401
402bool RenderEmbeddedObject::isReplacementObscured() const
403{
404    // Return whether or not the replacement content for blocked plugins is accessible to the user.
405
406    // Check the opacity of each layer containing the element or its ancestors.
407    float opacity = 1.0;
408    for (RenderLayer* layer = enclosingLayer(); layer; layer = layer->parent()) {
409        opacity *= layer->renderer().style().opacity();
410        if (opacity < 0.1)
411            return true;
412    }
413
414    // Calculate the absolute rect for the blocked plugin replacement text.
415    IntRect absoluteBoundingBox = absoluteBoundingBoxRect();
416    LayoutPoint absoluteLocation(absoluteBoundingBox.location());
417    LayoutRect rect = unavailablePluginIndicatorBounds(absoluteLocation);
418    if (rect.isEmpty())
419        return true;
420
421    RenderView* rootRenderView = document().topDocument().renderView();
422    ASSERT(rootRenderView);
423    if (!rootRenderView)
424        return true;
425
426    IntRect rootViewRect = view().frameView().convertToRootView(pixelSnappedIntRect(rect));
427
428    HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::Active | HitTestRequest::IgnoreClipping | HitTestRequest::DisallowShadowContent | HitTestRequest::AllowChildFrameContent);
429    HitTestResult result;
430    HitTestLocation location;
431
432    LayoutUnit x = rootViewRect.x();
433    LayoutUnit y = rootViewRect.y();
434    LayoutUnit width = rootViewRect.width();
435    LayoutUnit height = rootViewRect.height();
436
437    // Hit test the center and near the corners of the replacement text to ensure
438    // it is visible and is not masked by other elements.
439    bool hit = false;
440    location = LayoutPoint(x + width / 2, y + height / 2);
441    hit = rootRenderView->hitTest(request, location, result);
442    if (!hit || result.innerNode() != &frameOwnerElement())
443        return true;
444
445    location = LayoutPoint(x, y);
446    hit = rootRenderView->hitTest(request, location, result);
447    if (!hit || result.innerNode() != &frameOwnerElement())
448        return true;
449
450    location = LayoutPoint(x + width, y);
451    hit = rootRenderView->hitTest(request, location, result);
452    if (!hit || result.innerNode() != &frameOwnerElement())
453        return true;
454
455    location = LayoutPoint(x + width, y + height);
456    hit = rootRenderView->hitTest(request, location, result);
457    if (!hit || result.innerNode() != &frameOwnerElement())
458        return true;
459
460    location = LayoutPoint(x, y + height);
461    hit = rootRenderView->hitTest(request, location, result);
462    if (!hit || result.innerNode() != &frameOwnerElement())
463        return true;
464
465    return false;
466}
467
468void RenderEmbeddedObject::layout()
469{
470    StackStats::LayoutCheckPoint layoutCheckPoint;
471    ASSERT(needsLayout());
472
473    LayoutSize oldSize = contentBoxRect().size();
474
475    updateLogicalWidth();
476    updateLogicalHeight();
477
478    RenderWidget::layout();
479
480    clearOverflow();
481    addVisualEffectOverflow();
482
483    updateLayerTransform();
484
485    bool wasMissingWidget = false;
486    if (!widget() && canHaveWidget()) {
487        wasMissingWidget = true;
488        view().frameView().addEmbeddedObjectToUpdate(*this);
489    }
490
491    clearNeedsLayout();
492
493    LayoutSize newSize = contentBoxRect().size();
494
495    if (!wasMissingWidget && newSize.width() >= oldSize.width() && newSize.height() >= oldSize.height()) {
496        HTMLFrameOwnerElement& element = frameOwnerElement();
497        if (element.isPluginElement() && toHTMLPlugInElement(element).isPlugInImageElement()) {
498            HTMLPlugInImageElement& plugInImageElement = toHTMLPlugInImageElement(element);
499            if (plugInImageElement.displayState() > HTMLPlugInElement::DisplayingSnapshot && plugInImageElement.snapshotDecision() == HTMLPlugInImageElement::MaySnapshotWhenResized) {
500                plugInImageElement.setNeedsCheckForSizeChange();
501                view().frameView().addEmbeddedObjectToUpdate(*this);
502            }
503        }
504    }
505
506    if (!canHaveChildren())
507        return;
508
509    // This code copied from RenderMedia::layout().
510    RenderObject* child = firstChild();
511
512    if (!child)
513        return;
514
515    RenderBox* childBox = toRenderBox(child);
516
517    if (!childBox)
518        return;
519
520    if (newSize == oldSize && !childBox->needsLayout())
521        return;
522
523    // When calling layout() on a child node, a parent must either push a LayoutStateMaintainter, or
524    // instantiate LayoutStateDisabler. Since using a LayoutStateMaintainer is slightly more efficient,
525    // and this method will be called many times per second during playback, use a LayoutStateMaintainer:
526    LayoutStateMaintainer statePusher(view(), *this, locationOffset(), hasTransform() || hasReflection() || style().isFlippedBlocksWritingMode());
527
528    childBox->setLocation(LayoutPoint(borderLeft(), borderTop()) + LayoutSize(paddingLeft(), paddingTop()));
529    childBox->style().setHeight(Length(newSize.height(), Fixed));
530    childBox->style().setWidth(Length(newSize.width(), Fixed));
531    childBox->setNeedsLayout(MarkOnlyThis);
532    childBox->layout();
533    clearChildNeedsLayout();
534
535    statePusher.pop();
536}
537
538bool RenderEmbeddedObject::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction hitTestAction)
539{
540    if (!RenderWidget::nodeAtPoint(request, result, locationInContainer, accumulatedOffset, hitTestAction))
541        return false;
542
543    if (!widget() || !widget()->isPluginViewBase())
544        return true;
545
546    PluginViewBase* view = toPluginViewBase(widget());
547    IntPoint roundedPoint = locationInContainer.roundedPoint();
548
549    if (Scrollbar* horizontalScrollbar = view->horizontalScrollbar()) {
550        if (horizontalScrollbar->shouldParticipateInHitTesting() && horizontalScrollbar->frameRect().contains(roundedPoint)) {
551            result.setScrollbar(horizontalScrollbar);
552            return true;
553        }
554    }
555
556    if (Scrollbar* verticalScrollbar = view->verticalScrollbar()) {
557        if (verticalScrollbar->shouldParticipateInHitTesting() && verticalScrollbar->frameRect().contains(roundedPoint)) {
558            result.setScrollbar(verticalScrollbar);
559            return true;
560        }
561    }
562
563    return true;
564}
565
566bool RenderEmbeddedObject::scroll(ScrollDirection direction, ScrollGranularity granularity, float, Element**, RenderBox*, const IntPoint&)
567{
568    if (!widget() || !widget()->isPluginViewBase())
569        return false;
570
571    return toPluginViewBase(widget())->scroll(direction, granularity);
572}
573
574bool RenderEmbeddedObject::logicalScroll(ScrollLogicalDirection direction, ScrollGranularity granularity, float multiplier, Element** stopElement)
575{
576    // Plugins don't expose a writing direction, so assuming horizontal LTR.
577    return scroll(logicalToPhysical(direction, true, false), granularity, multiplier, stopElement);
578}
579
580
581bool RenderEmbeddedObject::isInUnavailablePluginIndicator(const LayoutPoint& point) const
582{
583    FloatRect contentRect;
584    FloatRect indicatorRect;
585    FloatRect replacementTextRect;
586    FloatRect arrowRect;
587    Font font;
588    TextRun run("");
589    float textWidth;
590    return getReplacementTextGeometry(IntPoint(), contentRect, indicatorRect, replacementTextRect, arrowRect, font, run, textWidth)
591        && indicatorRect.contains(point);
592}
593
594bool RenderEmbeddedObject::isInUnavailablePluginIndicator(MouseEvent* event) const
595{
596    return isInUnavailablePluginIndicator(roundedLayoutPoint(absoluteToLocal(event->absoluteLocation(), UseTransforms)));
597}
598
599void RenderEmbeddedObject::handleUnavailablePluginIndicatorEvent(Event* event)
600{
601    if (!shouldUnavailablePluginMessageBeButton(document(), m_pluginUnavailabilityReason))
602        return;
603
604    if (!event->isMouseEvent())
605        return;
606
607    MouseEvent* mouseEvent = toMouseEvent(event);
608    HTMLPlugInElement& element = toHTMLPlugInElement(frameOwnerElement());
609    if (event->type() == eventNames().mousedownEvent && toMouseEvent(event)->button() == LeftButton) {
610        m_mouseDownWasInUnavailablePluginIndicator = isInUnavailablePluginIndicator(mouseEvent);
611        if (m_mouseDownWasInUnavailablePluginIndicator) {
612            frame().eventHandler().setCapturingMouseEventsElement(&element);
613            element.setIsCapturingMouseEvents(true);
614            setUnavailablePluginIndicatorIsPressed(true);
615        }
616        event->setDefaultHandled();
617    }
618    if (event->type() == eventNames().mouseupEvent && toMouseEvent(event)->button() == LeftButton) {
619        if (m_unavailablePluginIndicatorIsPressed) {
620            frame().eventHandler().setCapturingMouseEventsElement(nullptr);
621            element.setIsCapturingMouseEvents(false);
622            setUnavailablePluginIndicatorIsPressed(false);
623        }
624        if (m_mouseDownWasInUnavailablePluginIndicator && isInUnavailablePluginIndicator(mouseEvent)) {
625            if (Page* page = document().page())
626                page->chrome().client().unavailablePluginButtonClicked(&element, m_pluginUnavailabilityReason);
627        }
628        m_mouseDownWasInUnavailablePluginIndicator = false;
629        event->setDefaultHandled();
630    }
631    if (event->type() == eventNames().mousemoveEvent) {
632        setUnavailablePluginIndicatorIsPressed(m_mouseDownWasInUnavailablePluginIndicator && isInUnavailablePluginIndicator(mouseEvent));
633        event->setDefaultHandled();
634    }
635}
636
637CursorDirective RenderEmbeddedObject::getCursor(const LayoutPoint& point, Cursor& cursor) const
638{
639    if (showsUnavailablePluginIndicator() && shouldUnavailablePluginMessageBeButton(document(), m_pluginUnavailabilityReason) && isInUnavailablePluginIndicator(point)) {
640        cursor = handCursor();
641        return SetCursor;
642    }
643    if (widget() && widget()->isPluginViewBase()) {
644        // A plug-in is responsible for setting the cursor when the pointer is over it.
645        return DoNotSetCursor;
646    }
647    return RenderWidget::getCursor(point, cursor);
648}
649
650bool RenderEmbeddedObject::canHaveChildren() const
651{
652    if (isSnapshottedPlugIn())
653        return true;
654
655    return false;
656}
657
658}
659