diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
commit | 1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch) | |
tree | 46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WebCore/page/TextIndicator.cpp | |
parent | 32761a6cee1d0dee366b885b7b9c777e67885688 (diff) | |
download | WebKitGtk-tarball-master.tar.gz |
webkitgtk-2.16.5HEADwebkitgtk-2.16.5master
Diffstat (limited to 'Source/WebCore/page/TextIndicator.cpp')
-rw-r--r-- | Source/WebCore/page/TextIndicator.cpp | 245 |
1 files changed, 245 insertions, 0 deletions
diff --git a/Source/WebCore/page/TextIndicator.cpp b/Source/WebCore/page/TextIndicator.cpp new file mode 100644 index 000000000..ea90bf74b --- /dev/null +++ b/Source/WebCore/page/TextIndicator.cpp @@ -0,0 +1,245 @@ +/* + * Copyright (C) 2010, 2015-2016 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 "TextIndicator.h" + +#include "Document.h" +#include "Editor.h" +#include "Element.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 "NodeTraversal.h" +#include "Range.h" +#include "RenderObject.h" + +using namespace WebCore; + +namespace WebCore { + +static bool initializeIndicator(TextIndicatorData&, Frame&, const Range&, FloatSize margin, bool indicatesCurrentSelection); + +TextIndicator::TextIndicator(const TextIndicatorData& data) + : m_data(data) +{ +} + +TextIndicator::~TextIndicator() +{ +} + +Ref<TextIndicator> TextIndicator::create(const TextIndicatorData& data) +{ + return adoptRef(*new TextIndicator(data)); +} + +RefPtr<TextIndicator> TextIndicator::createWithRange(const Range& range, TextIndicatorOptions options, TextIndicatorPresentationTransition presentationTransition, FloatSize margin) +{ + Frame* frame = range.startContainer().document().frame(); + + if (!frame) + return nullptr; + + Ref<Frame> protector(*frame); + +#if PLATFORM(IOS) + frame->editor().setIgnoreCompositionSelectionChange(true); + frame->selection().setUpdateAppearanceEnabled(true); +#endif + + VisibleSelection oldSelection = frame->selection().selection(); + frame->selection().setSelection(range); + + TextIndicatorData data; + + data.presentationTransition = presentationTransition; + data.options = options; + + bool indicatesCurrentSelection = areRangesEqual(&range, oldSelection.toNormalizedRange().get()); + + if (!initializeIndicator(data, *frame, range, margin, indicatesCurrentSelection)) + return nullptr; + + RefPtr<TextIndicator> indicator = TextIndicator::create(data); + + frame->selection().setSelection(oldSelection); + +#if PLATFORM(IOS) + frame->editor().setIgnoreCompositionSelectionChange(false, Editor::RevealSelection::No); + frame->selection().setUpdateAppearanceEnabled(false); +#endif + + return indicator; +} + +RefPtr<TextIndicator> TextIndicator::createWithSelectionInFrame(Frame& frame, TextIndicatorOptions options, TextIndicatorPresentationTransition presentationTransition, FloatSize margin) +{ + RefPtr<Range> range = frame.selection().toNormalizedRange(); + if (!range) + return nullptr; + + TextIndicatorData data; + + data.presentationTransition = presentationTransition; + data.options = options; + + if (!initializeIndicator(data, frame, *range, margin, true)) + return nullptr; + + return TextIndicator::create(data); +} + +static bool hasNonInlineOrReplacedElements(const Range& range) +{ + Node* stopNode = range.pastLastNode(); + for (Node* node = range.firstNode(); node != stopNode; node = NodeTraversal::next(*node)) { + if (!node) + continue; + RenderObject* renderer = node->renderer(); + if (!renderer) + continue; + if ((!renderer->isInline() || renderer->isReplaced()) && range.intersectsNode(*node).releaseReturnValue()) + return true; + } + + return false; +} + +static SnapshotOptions snapshotOptionsForTextIndicatorOptions(TextIndicatorOptions options) +{ + SnapshotOptions snapshotOptions = SnapshotOptionsNone; + if (!(options & TextIndicatorOptionRespectTextColor)) + snapshotOptions |= SnapshotOptionsForceBlackText; + + if (!(options & TextIndicatorOptionPaintAllContent)) { + if (options & TextIndicatorOptionPaintBackgrounds) + snapshotOptions |= SnapshotOptionsPaintSelectionAndBackgroundsOnly; + else + snapshotOptions |= SnapshotOptionsPaintSelectionOnly; + } else + snapshotOptions |= SnapshotOptionsExcludeSelectionHighlighting; + + return snapshotOptions; +} + +static RefPtr<Image> takeSnapshot(Frame& frame, IntRect rect, SnapshotOptions options, float& scaleFactor, Vector<FloatRect>& clipRectsInDocumentCoordinates) +{ + std::unique_ptr<ImageBuffer> buffer = snapshotFrameRectWithClip(frame, rect, clipRectsInDocumentCoordinates, options); + if (!buffer) + return nullptr; + scaleFactor = buffer->resolutionScale(); + return ImageBuffer::sinkIntoImage(WTFMove(buffer), Unscaled); +} + +static bool takeSnapshots(TextIndicatorData& data, Frame& frame, IntRect snapshotRect, Vector<FloatRect>& clipRectsInDocumentCoordinates) +{ + SnapshotOptions snapshotOptions = snapshotOptionsForTextIndicatorOptions(data.options); + + data.contentImage = takeSnapshot(frame, snapshotRect, snapshotOptions, data.contentImageScaleFactor, clipRectsInDocumentCoordinates); + if (!data.contentImage) + return false; + + if (data.options & TextIndicatorOptionIncludeSnapshotWithSelectionHighlight) { + float snapshotScaleFactor; + data.contentImageWithHighlight = takeSnapshot(frame, snapshotRect, SnapshotOptionsNone, snapshotScaleFactor, clipRectsInDocumentCoordinates); + ASSERT(!data.contentImageWithHighlight || data.contentImageScaleFactor == snapshotScaleFactor); + } + + return true; +} + +static bool initializeIndicator(TextIndicatorData& data, Frame& frame, const Range& range, FloatSize margin, bool indicatesCurrentSelection) +{ + Vector<FloatRect> textRects; + + // FIXME (138888): Ideally we wouldn't remove the margin in this case, but we need to + // ensure that the indicator and indicator-with-highlight overlap precisely, and + // we can't add a margin to the indicator-with-highlight. + if (indicatesCurrentSelection && !(data.options & TextIndicatorOptionIncludeMarginIfRangeMatchesSelection)) + margin = FloatSize(); + + FrameSelection::TextRectangleHeight textRectHeight = (data.options & TextIndicatorOptionTightlyFitContent) ? FrameSelection::TextRectangleHeight::TextHeight : FrameSelection::TextRectangleHeight::SelectionHeight; + + if ((data.options & TextIndicatorOptionUseBoundingRectAndPaintAllContentForComplexRanges) && hasNonInlineOrReplacedElements(range)) + data.options |= TextIndicatorOptionPaintAllContent; + else { + if (data.options & TextIndicatorOptionDoNotClipToVisibleRect) + frame.selection().getTextRectangles(textRects, textRectHeight); + else + frame.selection().getClippedVisibleTextRectangles(textRects, textRectHeight); + } + + if (textRects.isEmpty()) { + RenderView* renderView = frame.contentRenderer(); + if (!renderView) + return false; + FloatRect boundingRect = range.absoluteBoundingRect(); + if (data.options & TextIndicatorOptionDoNotClipToVisibleRect) + textRects.append(boundingRect); + else { + // Clip to the visible rect, just like getClippedVisibleTextRectangles does. + // FIXME: We really want to clip to the unobscured rect in both cases, I think. + // (this seems to work on Mac, but maybe not iOS?) + FloatRect visibleContentRect = frame.view()->visibleContentRect(ScrollableArea::LegacyIOSDocumentVisibleRect); + textRects.append(intersection(visibleContentRect, boundingRect)); + } + } + + FloatRect textBoundingRectInRootViewCoordinates; + FloatRect textBoundingRectInDocumentCoordinates; + Vector<FloatRect> textRectsInRootViewCoordinates; + for (const FloatRect& textRect : textRects) { + FloatRect textRectInDocumentCoordinatesIncludingMargin = textRect; + textRectInDocumentCoordinatesIncludingMargin.inflateX(margin.width()); + textRectInDocumentCoordinatesIncludingMargin.inflateY(margin.height()); + 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); + } + + // Store the selection rect in window coordinates, to be used subsequently + // to determine if the indicator and selection still precisely overlap. + data.selectionRectInRootViewCoordinates = frame.view()->contentsToRootView(enclosingIntRect(frame.selection().selectionBounds())); + data.textBoundingRectInRootViewCoordinates = textBoundingRectInRootViewCoordinates; + data.textRectsInBoundingRectCoordinates = textRectsInBoundingRectCoordinates; + + return takeSnapshots(data, frame, enclosingIntRect(textBoundingRectInDocumentCoordinates), textRects); +} + +} // namespace WebCore |