blob: 695e56183dec876a54aa951a5408b5fe801d231f [file] [log] [blame]
/*
* Copyright (C) 2012 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "RenderSnapshottedPlugIn.h"
#include "Chrome.h"
#include "ChromeClient.h"
#include "Cursor.h"
#include "FrameLoaderClient.h"
#include "FrameView.h"
#include "Gradient.h"
#include "HTMLPlugInImageElement.h"
#include "MouseEvent.h"
#include "Page.h"
#include "PaintInfo.h"
#include "Path.h"
namespace WebCore {
static const int autoStartPlugInSizeThresholdWidth = 1;
static const int autoStartPlugInSizeThresholdHeight = 1;
static const int startLabelPadding = 10;
static const double hoverDelay = 1;
RenderSnapshottedPlugIn::RenderSnapshottedPlugIn(HTMLPlugInImageElement* element)
: RenderEmbeddedObject(element)
, m_snapshotResource(RenderImageResource::create())
, m_shouldShowLabel(false)
, m_hoverDelayTimer(this, &RenderSnapshottedPlugIn::hoverDelayTimerFired, hoverDelay)
{
m_snapshotResource->initialize(this);
}
RenderSnapshottedPlugIn::~RenderSnapshottedPlugIn()
{
ASSERT(m_snapshotResource);
m_snapshotResource->shutdown();
}
HTMLPlugInImageElement* RenderSnapshottedPlugIn::plugInImageElement() const
{
return static_cast<HTMLPlugInImageElement*>(node());
}
void RenderSnapshottedPlugIn::updateSnapshot(PassRefPtr<Image> image)
{
// Zero-size plugins will have no image.
if (!image)
return;
m_snapshotResource->setCachedImage(new CachedImage(image.get()));
repaint();
}
void RenderSnapshottedPlugIn::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
{
if (plugInImageElement()->displayState() < HTMLPlugInElement::PlayingWithPendingMouseClick) {
RenderReplaced::paint(paintInfo, paintOffset);
return;
}
RenderEmbeddedObject::paint(paintInfo, paintOffset);
}
void RenderSnapshottedPlugIn::paintReplaced(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
{
if (plugInImageElement()->displayState() < HTMLPlugInElement::PlayingWithPendingMouseClick) {
paintReplacedSnapshot(paintInfo, paintOffset);
if (m_shouldShowLabel)
paintLabel(paintInfo, paintOffset);
return;
}
RenderEmbeddedObject::paintReplaced(paintInfo, paintOffset);
}
void RenderSnapshottedPlugIn::paintReplacedSnapshot(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
{
// This code should be similar to RenderImage::paintReplaced() and RenderImage::paintIntoRect().
LayoutUnit cWidth = contentWidth();
LayoutUnit cHeight = contentHeight();
if (!cWidth || !cHeight)
return;
RefPtr<Image> image = m_snapshotResource->image();
if (!image || image->isNull())
return;
GraphicsContext* context = paintInfo.context;
#if PLATFORM(MAC)
if (style()->highlight() != nullAtom && !context->paintingDisabled())
paintCustomHighlight(toPoint(paintOffset - location()), style()->highlight(), true);
#endif
LayoutSize contentSize(cWidth, cHeight);
LayoutPoint contentLocation = paintOffset;
contentLocation.move(borderLeft() + paddingLeft(), borderTop() + paddingTop());
LayoutRect rect(contentLocation, contentSize);
IntRect alignedRect = pixelSnappedIntRect(rect);
if (alignedRect.width() <= 0 || alignedRect.height() <= 0)
return;
bool useLowQualityScaling = shouldPaintAtLowQuality(context, image.get(), image.get(), alignedRect.size());
context->drawImage(image.get(), style()->colorSpace(), alignedRect, CompositeSourceOver, shouldRespectImageOrientation(), useLowQualityScaling);
}
Image* RenderSnapshottedPlugIn::startLabelImage(LabelSize size) const
{
static Image* labelImages[2] = { 0, 0 };
static bool initializedImages[2] = { false, false };
int arrayIndex = static_cast<int>(size);
if (labelImages[arrayIndex])
return labelImages[arrayIndex];
if (initializedImages[arrayIndex])
return 0;
if (document()->page()) {
labelImages[arrayIndex] = document()->page()->chrome()->client()->plugInStartLabelImage(size).leakRef();
initializedImages[arrayIndex] = true;
}
return labelImages[arrayIndex];
}
void RenderSnapshottedPlugIn::paintLabel(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
{
if (contentBoxRect().isEmpty())
return;
if (!plugInImageElement()->hovered())
return;
LayoutRect rect = contentBoxRect();
LayoutRect labelRect = tryToFitStartLabel(LabelSizeLarge, rect);
LabelSize size = NoLabel;
if (!labelRect.isEmpty())
size = LabelSizeLarge;
else {
labelRect = tryToFitStartLabel(LabelSizeSmall, rect);
if (!labelRect.isEmpty())
size = LabelSizeSmall;
else
return;
}
Image* labelImage = startLabelImage(size);
if (!labelImage)
return;
paintInfo.context->drawImage(labelImage, ColorSpaceDeviceRGB, roundedIntPoint(paintOffset + labelRect.location()), labelImage->rect());
}
void RenderSnapshottedPlugIn::repaintLabel()
{
// FIXME: This is unfortunate. We should just repaint the label.
repaint();
}
void RenderSnapshottedPlugIn::hoverDelayTimerFired(DeferrableOneShotTimer<RenderSnapshottedPlugIn>*)
{
m_shouldShowLabel = true;
repaintLabel();
}
CursorDirective RenderSnapshottedPlugIn::getCursor(const LayoutPoint& point, Cursor& overrideCursor) const
{
if (plugInImageElement()->displayState() < HTMLPlugInElement::PlayingWithPendingMouseClick) {
overrideCursor = handCursor();
return SetCursor;
}
return RenderEmbeddedObject::getCursor(point, overrideCursor);
}
void RenderSnapshottedPlugIn::handleEvent(Event* event)
{
if (!event->isMouseEvent())
return;
MouseEvent* mouseEvent = static_cast<MouseEvent*>(event);
if (event->type() == eventNames().clickEvent) {
if (mouseEvent->button() != LeftButton)
return;
plugInImageElement()->setDisplayState(HTMLPlugInElement::PlayingWithPendingMouseClick);
plugInImageElement()->userDidClickSnapshot(mouseEvent);
if (widget()) {
if (Frame* frame = document()->frame())
frame->loader()->client()->recreatePlugin(widget());
repaint();
}
event->setDefaultHandled();
} else if (event->type() == eventNames().mousedownEvent) {
if (mouseEvent->button() != LeftButton)
return;
if (m_hoverDelayTimer.isActive())
m_hoverDelayTimer.stop();
event->setDefaultHandled();
} else if (event->type() == eventNames().mouseoverEvent) {
m_hoverDelayTimer.restart();
event->setDefaultHandled();
} else if (event->type() == eventNames().mouseoutEvent) {
if (m_hoverDelayTimer.isActive())
m_hoverDelayTimer.stop();
m_shouldShowLabel = false;
repaintLabel();
event->setDefaultHandled();
}
}
LayoutRect RenderSnapshottedPlugIn::tryToFitStartLabel(LabelSize size, const LayoutRect& contentBox) const
{
Image* labelImage = startLabelImage(size);
if (!labelImage)
return LayoutRect();
LayoutSize labelSize = labelImage->size();
LayoutRect candidateRect(contentBox.maxXMinYCorner() + LayoutSize(-startLabelPadding, startLabelPadding) + LayoutSize(-labelSize.width(), 0), labelSize);
// The minimum allowed content box size is the label image placed in the center of the box, surrounded by startLabelPadding.
if (candidateRect.x() < startLabelPadding || candidateRect.maxY() > contentBox.height() - startLabelPadding)
return LayoutRect();
return candidateRect;
}
} // namespace WebCore