#include "config.h"
#include "TextIndicator.h"
#include "Document.h"
#include "Editor.h"
#include "Frame.h"
#include "FrameSelection.h"
#include "FrameSnapshotting.h"
#include "FrameView.h"
#include "GeometryUtilities.h"
#include "GraphicsContext.h"
#include "ImageBuffer.h"
#include "IntRect.h"
#include "Page.h"
#include "Range.h"
using namespace WebCore;
namespace WebCore {
Ref<TextIndicator> TextIndicator::create(const TextIndicatorData& data)
{
return adoptRef(*new TextIndicator(data));
}
RefPtr<TextIndicator> TextIndicator::createWithRange(const Range& range, TextIndicatorPresentationTransition presentationTransition, unsigned margin)
{
Frame* frame = range.startContainer()->document().frame();
if (!frame)
return nullptr;
#if PLATFORM(IOS)
frame->editor().setIgnoreCompositionSelectionChange(true);
frame->selection().setUpdateAppearanceEnabled(true);
#endif
VisibleSelection oldSelection = frame->selection().selection();
frame->selection().setSelection(range);
RefPtr<TextIndicator> indicator = TextIndicator::createWithSelectionInFrame(*frame, presentationTransition, margin);
frame->selection().setSelection(oldSelection);
if (indicator)
indicator->setWantsMargin(!areRangesEqual(&range, oldSelection.toNormalizedRange().get()));
#if PLATFORM(IOS)
frame->editor().setIgnoreCompositionSelectionChange(false, Editor::RevealSelection::No);
frame->selection().setUpdateAppearanceEnabled(false);
#endif
return indicator.release();
}
static RefPtr<Image> snapshotSelectionWithHighlight(Frame& frame)
{
auto& selection = frame.selection();
if (!selection.isRange())
return nullptr;
FloatRect selectionBounds = selection.selectionBounds();
if (selectionBounds.isEmpty())
return nullptr;
std::unique_ptr<ImageBuffer> snapshot = snapshotFrameRect(frame, enclosingIntRect(selectionBounds), 0);
if (!snapshot)
return nullptr;
return snapshot->copyImage(CopyBackingStore, Unscaled);
}
RefPtr<TextIndicator> TextIndicator::createWithSelectionInFrame(Frame& frame, TextIndicatorPresentationTransition presentationTransition, unsigned margin)
{
Vector<FloatRect> textRects;
#if PLATFORM(IOS)
FrameSelection::TextRectangleHeight textRectHeight = FrameSelection::TextRectangleHeight::TextHeight;
#else
FrameSelection::TextRectangleHeight textRectHeight = FrameSelection::TextRectangleHeight::SelectionHeight;
#endif
frame.selection().getClippedVisibleTextRectangles(textRects, textRectHeight);
FloatRect textBoundingRectInRootViewCoordinates;
FloatRect textBoundingRectInDocumentCoordinates;
Vector<FloatRect> textRectsInRootViewCoordinates;
for (const FloatRect& textRect : textRects) {
FloatRect textRectInDocumentCoordinatesIncludingMargin = textRect;
textRectInDocumentCoordinatesIncludingMargin.inflate(margin);
textBoundingRectInDocumentCoordinates.unite(textRectInDocumentCoordinatesIncludingMargin);
FloatRect textRectInRootViewCoordinates = frame.view()->contentsToRootView(enclosingIntRect(textRectInDocumentCoordinatesIncludingMargin));
textRectsInRootViewCoordinates.append(textRectInRootViewCoordinates);
textBoundingRectInRootViewCoordinates.unite(textRectInRootViewCoordinates);
}
Vector<FloatRect> textRectsInBoundingRectCoordinates;
for (auto rect : textRectsInRootViewCoordinates) {
rect.moveBy(-textBoundingRectInRootViewCoordinates.location());
textRectsInBoundingRectCoordinates.append(rect);
}
#if PLATFORM(IOS)
SnapshotOptions snapshotOptions = SnapshotOptionsPaintSelectionAndBackgroundsOnly;
#else
SnapshotOptions snapshotOptions = SnapshotOptionsForceBlackText | SnapshotOptionsPaintSelectionOnly;
#endif
std::unique_ptr<ImageBuffer> indicatorBuffer = snapshotFrameRect(frame, enclosingIntRect(textBoundingRectInDocumentCoordinates), snapshotOptions);
if (!indicatorBuffer)
return nullptr;
RefPtr<Image> indicatorBitmap = indicatorBuffer->copyImage(CopyBackingStore, Unscaled);
if (!indicatorBitmap)
return nullptr;
RefPtr<Image> indicatorBitmapWithHighlight;
if (presentationTransition == TextIndicatorPresentationTransition::BounceAndCrossfade)
indicatorBitmapWithHighlight = snapshotSelectionWithHighlight(frame);
TextIndicatorData data;
data.selectionRectInRootViewCoordinates = frame.view()->contentsToRootView(enclosingIntRect(frame.selection().selectionBounds()));
data.textBoundingRectInRootViewCoordinates = textBoundingRectInRootViewCoordinates;
data.textRectsInBoundingRectCoordinates = textRectsInBoundingRectCoordinates;
data.contentImageScaleFactor = indicatorBuffer->resolutionScale();
data.contentImage = indicatorBitmap;
data.contentImageWithHighlight = indicatorBitmapWithHighlight;
data.presentationTransition = presentationTransition;
data.wantsMargin = true;
return TextIndicator::create(data);
}
TextIndicator::TextIndicator(const TextIndicatorData& data)
: m_data(data)
{
}
TextIndicator::~TextIndicator()
{
}
bool TextIndicator::wantsBounce() const
{
switch (m_data.presentationTransition) {
case TextIndicatorPresentationTransition::BounceAndCrossfade:
case TextIndicatorPresentationTransition::Bounce:
return true;
case TextIndicatorPresentationTransition::FadeIn:
case TextIndicatorPresentationTransition::None:
return false;
}
ASSERT_NOT_REACHED();
return false;
}
bool TextIndicator::wantsContentCrossfade() const
{
if (!m_data.contentImageWithHighlight)
return false;
switch (m_data.presentationTransition) {
case TextIndicatorPresentationTransition::BounceAndCrossfade:
return true;
case TextIndicatorPresentationTransition::Bounce:
case TextIndicatorPresentationTransition::FadeIn:
case TextIndicatorPresentationTransition::None:
return false;
}
ASSERT_NOT_REACHED();
return false;
}
bool TextIndicator::wantsFadeIn() const
{
switch (m_data.presentationTransition) {
case TextIndicatorPresentationTransition::FadeIn:
return true;
case TextIndicatorPresentationTransition::Bounce:
case TextIndicatorPresentationTransition::BounceAndCrossfade:
case TextIndicatorPresentationTransition::None:
return false;
}
ASSERT_NOT_REACHED();
return false;
}
bool TextIndicator::wantsManualAnimation() const
{
switch (m_data.presentationTransition) {
case TextIndicatorPresentationTransition::FadeIn:
return true;
case TextIndicatorPresentationTransition::Bounce:
case TextIndicatorPresentationTransition::BounceAndCrossfade:
case TextIndicatorPresentationTransition::None:
return false;
}
ASSERT_NOT_REACHED();
return false;
}
}