Diff
Modified: trunk/Source/WebCore/CMakeLists.txt (222297 => 222298)
--- trunk/Source/WebCore/CMakeLists.txt 2017-09-20 23:08:50 UTC (rev 222297)
+++ trunk/Source/WebCore/CMakeLists.txt 2017-09-20 23:13:27 UTC (rev 222298)
@@ -2657,6 +2657,7 @@
rendering/LayoutDisallowedScope.cpp
rendering/LayoutRepainter.cpp
rendering/LayoutState.cpp
+ rendering/MarkerSubrange.cpp
rendering/OrderIterator.cpp
rendering/PointerEventsHitRules.cpp
rendering/RenderAttachment.cpp
Modified: trunk/Source/WebCore/ChangeLog (222297 => 222298)
--- trunk/Source/WebCore/ChangeLog 2017-09-20 23:08:50 UTC (rev 222297)
+++ trunk/Source/WebCore/ChangeLog 2017-09-20 23:13:27 UTC (rev 222298)
@@ -1,3 +1,42 @@
+2017-09-20 Daniel Bates <[email protected]>
+
+ Spelling and grammar dots should not overlap
+ https://bugs.webkit.org/show_bug.cgi?id=177265
+ <rdar://problem/34556424>
+
+ Reviewed by David Hyatt.
+
+ A line may contain both spelling and grammar errors such that these errors overlap.
+ For example, "to mooof or not to mooof.". It is more pleasing aesthetically to
+ paint spelling and grammar dots such that they do not overlap. This also matches
+ AppKit's behavior.
+
+ A side benefit of this change is that it adds support infrastructure towards
+ implementing the CSS Pseudo-Elements Module Level 4 pseudo elements ::spelling-error
+ and ::grammar-error (see <https://bugs.webkit.org/show_bug.cgi?id=175784>).
+ It will also make it straightforward to add ::inactive-selection and allow us
+ to make ::selection conform to CSS Pseudo-Elements Module Level 4.
+
+ * CMakeLists.txt: Add file MarkerSubrange.cpp.
+ * WebCore.xcodeproj/project.pbxproj: Add files MarkerSubrange.{cpp, h}.
+ * rendering/InlineTextBox.cpp:
+ (WebCore::InlineTextBox::paintDocumentMarker): Modified to take a const MarkerSubrange&
+ instead of a RenderedDocumentMarker&.
+ (WebCore::InlineTextBox::paintTextMatchMarker): Modified to take a const MarkerSubrange&
+ instead of a RenderedDocumentMarker& and take a boolean as to whether the text match is active.
+ (WebCore::InlineTextBox::paintDocumentMarkers): Collect the subranges that need to be
+ painted, subdivide them preserving only the frontmost subrange when two or more subranges
+ overlap and paint the resulting subranges.
+ (WebCore::lineStyleForMarkerType): Deleted; converted to a lambda function inlined
+ in paintDocumentMarker() as this is the only place we made use of this function.
+ * rendering/InlineTextBox.h:
+ * rendering/MarkerSubrange.cpp: Added.
+ (WebCore::subdivide): Subdivides the specified list of subranges and returns a list of non-overlapping
+ subranges in paint order. The implementation of subdivide() is derived from an algorithm that
+ Said Abou-Hallawa came up with.
+ * rendering/MarkerSubrange.h: Added.
+ (WebCore::MarkerSubrange::MarkerSubrange):
+
2017-09-20 Alex Christensen <[email protected]>
Remove ActionType::CSSDisplayNoneStyleSheet
Modified: trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj (222297 => 222298)
--- trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj 2017-09-20 23:08:50 UTC (rev 222297)
+++ trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj 2017-09-20 23:13:27 UTC (rev 222298)
@@ -6485,6 +6485,8 @@
CE057FA61220731100A476D5 /* DocumentMarkerController.h in Headers */ = {isa = PBXBuildFile; fileRef = CE057FA41220731100A476D5 /* DocumentMarkerController.h */; settings = {ATTRIBUTES = (Private, ); }; };
CE08C3D1152B599A0021B8C2 /* AlternativeTextController.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CE08C3CF152B599A0021B8C2 /* AlternativeTextController.cpp */; };
CE08C3D2152B599A0021B8C2 /* AlternativeTextController.h in Headers */ = {isa = PBXBuildFile; fileRef = CE08C3D0152B599A0021B8C2 /* AlternativeTextController.h */; settings = {ATTRIBUTES = (); }; };
+ CE1866441F72E5B400A0CAB6 /* MarkerSubrange.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CE1866421F72E5B400A0CAB6 /* MarkerSubrange.cpp */; };
+ CE1866451F72E5B400A0CAB6 /* MarkerSubrange.h in Headers */ = {isa = PBXBuildFile; fileRef = CE1866431F72E5B400A0CAB6 /* MarkerSubrange.h */; settings = {ATTRIBUTES = (Private, ); }; };
CE2849871CA360DF00B4A57F /* ContentSecurityPolicyDirectiveNames.h in Headers */ = {isa = PBXBuildFile; fileRef = CE2849861CA360DF00B4A57F /* ContentSecurityPolicyDirectiveNames.h */; };
CE2849891CA3614600B4A57F /* ContentSecurityPolicyDirectiveNames.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CE2849881CA3614600B4A57F /* ContentSecurityPolicyDirectiveNames.cpp */; };
CE6DADF91C591E6A003F6A88 /* ContentSecurityPolicyResponseHeaders.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CE6DADF71C591E6A003F6A88 /* ContentSecurityPolicyResponseHeaders.cpp */; };
@@ -15256,6 +15258,8 @@
CE057FA41220731100A476D5 /* DocumentMarkerController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DocumentMarkerController.h; sourceTree = "<group>"; };
CE08C3CF152B599A0021B8C2 /* AlternativeTextController.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AlternativeTextController.cpp; sourceTree = "<group>"; };
CE08C3D0152B599A0021B8C2 /* AlternativeTextController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AlternativeTextController.h; sourceTree = "<group>"; };
+ CE1866421F72E5B400A0CAB6 /* MarkerSubrange.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = MarkerSubrange.cpp; sourceTree = "<group>"; };
+ CE1866431F72E5B400A0CAB6 /* MarkerSubrange.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MarkerSubrange.h; sourceTree = "<group>"; };
CE2849861CA360DF00B4A57F /* ContentSecurityPolicyDirectiveNames.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ContentSecurityPolicyDirectiveNames.h; path = csp/ContentSecurityPolicyDirectiveNames.h; sourceTree = "<group>"; };
CE2849881CA3614600B4A57F /* ContentSecurityPolicyDirectiveNames.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ContentSecurityPolicyDirectiveNames.cpp; path = csp/ContentSecurityPolicyDirectiveNames.cpp; sourceTree = "<group>"; };
CE5CB1B314EDAB6F00BB2795 /* EventSender.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EventSender.h; sourceTree = "<group>"; };
@@ -25885,6 +25889,8 @@
2D9066040BE141D400956998 /* LayoutState.cpp */,
2D9066050BE141D400956998 /* LayoutState.h */,
9BA273F3172206BB0097CE47 /* LogicalSelectionOffsetCaches.h */,
+ CE1866421F72E5B400A0CAB6 /* MarkerSubrange.cpp */,
+ CE1866431F72E5B400A0CAB6 /* MarkerSubrange.h */,
CDE7FC42181904B1002BBB77 /* OrderIterator.cpp */,
CDE7FC43181904B1002BBB77 /* OrderIterator.h */,
3774ABA30FA21EB400AD7DE9 /* OverlapTestRequestClient.h */,
@@ -29197,6 +29203,7 @@
7AE6C93C1BE0C60100E19E03 /* MainThreadSharedTimer.h in Headers */,
1A8F6BC60DB55CDC001DB794 /* ManifestParser.h in Headers */,
93309DF8099E64920056E581 /* markup.h in Headers */,
+ CE1866451F72E5B400A0CAB6 /* MarkerSubrange.h in Headers */,
9728C3141268E4390041E89B /* MarkupAccumulator.h in Headers */,
00C60E3F13D76D7E0092A275 /* MarkupTokenizerInlines.h in Headers */,
FABE72F51059C1EB00D888CC /* MathMLAnnotationElement.h in Headers */,
@@ -33270,6 +33277,7 @@
7AE6C9381BE0C5C800E19E03 /* MainThreadSharedTimerCF.cpp in Sources */,
1A8F6BC50DB55CDC001DB794 /* ManifestParser.cpp in Sources */,
93309DF7099E64920056E581 /* markup.cpp in Sources */,
+ CE1866441F72E5B400A0CAB6 /* MarkerSubrange.cpp in Sources */,
9728C3131268E4390041E89B /* MarkupAccumulator.cpp in Sources */,
FABE72F41059C1EB00D888CC /* MathMLAnnotationElement.cpp in Sources */,
FABE72F41059C1EB00D999DD /* MathMLElement.cpp in Sources */,
Modified: trunk/Source/WebCore/rendering/InlineTextBox.cpp (222297 => 222298)
--- trunk/Source/WebCore/rendering/InlineTextBox.cpp 2017-09-20 23:08:50 UTC (rev 222297)
+++ trunk/Source/WebCore/rendering/InlineTextBox.cpp 2017-09-20 23:13:27 UTC (rev 222298)
@@ -1,7 +1,7 @@
/*
* (C) 1999 Lars Knoll ([email protected])
* (C) 2000 Dirk Mueller ([email protected])
- * Copyright (C) 2004-2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2004-2017 Apple Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
@@ -34,6 +34,7 @@
#include "HitTestResult.h"
#include "ImageBuffer.h"
#include "InlineTextBoxStyle.h"
+#include "MarkerSubrange.h"
#include "Page.h"
#include "PaintInfo.h"
#include "RenderBlock.h"
@@ -782,30 +783,8 @@
context.concatCTM(rotation(boxRect, Counterclockwise));
}
-static GraphicsContext::DocumentMarkerLineStyle lineStyleForMarkerType(DocumentMarker::MarkerType markerType)
+void InlineTextBox::paintDocumentMarker(GraphicsContext& context, const FloatPoint& boxOrigin, const MarkerSubrange& subrange, const RenderStyle& style, const FontCascade& font)
{
- switch (markerType) {
- case DocumentMarker::Spelling:
- return GraphicsContext::DocumentMarkerSpellingLineStyle;
- case DocumentMarker::Grammar:
- return GraphicsContext::DocumentMarkerGrammarLineStyle;
- case DocumentMarker::CorrectionIndicator:
- return GraphicsContext::DocumentMarkerAutocorrectionReplacementLineStyle;
- case DocumentMarker::DictationAlternatives:
- return GraphicsContext::DocumentMarkerDictationAlternativesLineStyle;
-#if PLATFORM(IOS)
- case DocumentMarker::DictationPhraseWithAlternatives:
- // FIXME: Rename TextCheckingDictationPhraseWithAlternativesLineStyle and remove the PLATFORM(IOS)-guard.
- return GraphicsContext::TextCheckingDictationPhraseWithAlternativesLineStyle;
-#endif
- default:
- ASSERT_NOT_REACHED();
- return GraphicsContext::DocumentMarkerSpellingLineStyle;
- }
-}
-
-void InlineTextBox::paintDocumentMarker(GraphicsContext& context, const FloatPoint& boxOrigin, RenderedDocumentMarker& marker, const RenderStyle& style, const FontCascade& font)
-{
// Never print spelling/grammar markers (5327887)
if (renderer().document().printing())
return;
@@ -818,16 +797,16 @@
// Determine whether we need to measure text
bool markerSpansWholeBox = true;
- if (m_start <= marker.startOffset())
+ if (m_start <= subrange.startOffset)
markerSpansWholeBox = false;
- if ((end() + 1) != marker.endOffset()) // end points at the last char, not past it
+ if ((end() + 1) != subrange.endOffset) // End points at the last char, not past it
markerSpansWholeBox = false;
if (m_truncation != cNoTruncation)
markerSpansWholeBox = false;
if (!markerSpansWholeBox) {
- unsigned startPosition = clampedOffset(marker.startOffset());
- unsigned endPosition = clampedOffset(marker.endOffset());
+ unsigned startPosition = clampedOffset(subrange.startOffset);
+ unsigned endPosition = clampedOffset(subrange.endOffset);
if (m_truncation != cNoTruncation)
endPosition = std::min(endPosition, static_cast<unsigned>(m_truncation));
@@ -845,6 +824,27 @@
width = markerRect.width();
}
+ auto lineStyleForSubrangeType = [] (MarkerSubrange::Type type) {
+ switch (type) {
+ case MarkerSubrange::SpellingError:
+ return GraphicsContext::DocumentMarkerSpellingLineStyle;
+ case MarkerSubrange::GrammarError:
+ return GraphicsContext::DocumentMarkerGrammarLineStyle;
+ case MarkerSubrange::Correction:
+ return GraphicsContext::DocumentMarkerAutocorrectionReplacementLineStyle;
+ case MarkerSubrange::DictationAlternatives:
+ return GraphicsContext::DocumentMarkerDictationAlternativesLineStyle;
+#if PLATFORM(IOS)
+ case MarkerSubrange::DictationPhraseWithAlternatives:
+ // FIXME: Rename TextCheckingDictationPhraseWithAlternativesLineStyle and remove the PLATFORM(IOS)-guard.
+ return GraphicsContext::TextCheckingDictationPhraseWithAlternativesLineStyle;
+#endif
+ default:
+ ASSERT_NOT_REACHED();
+ return GraphicsContext::DocumentMarkerSpellingLineStyle;
+ }
+ };
+
// IMPORTANT: The misspelling underline is not considered when calculating the text bounds, so we have to
// make sure to fit within those bounds. This means the top pixel(s) of the underline will overlap the
// bottom pixel(s) of the glyphs in smaller font sizes. The alternatives are to increase the line spacing (bad!!)
@@ -862,15 +862,15 @@
// In larger fonts, though, place the underline up near the baseline to prevent a big gap.
underlineOffset = baseline + 2;
}
- context.drawLineForDocumentMarker(FloatPoint(boxOrigin.x() + start, boxOrigin.y() + underlineOffset), width, lineStyleForMarkerType(marker.type()));
+ context.drawLineForDocumentMarker(FloatPoint(boxOrigin.x() + start, boxOrigin.y() + underlineOffset), width, lineStyleForSubrangeType(subrange.type));
}
-void InlineTextBox::paintTextMatchMarker(GraphicsContext& context, const FloatPoint& boxOrigin, RenderedDocumentMarker& marker, const RenderStyle& style, const FontCascade& font)
+void InlineTextBox::paintTextMatchMarker(GraphicsContext& context, const FloatPoint& boxOrigin, const MarkerSubrange& subrange, const RenderStyle& style, const FontCascade& font, bool isActiveMatch)
{
if (!renderer().frame().editor().markedTextMatchesAreHighlighted())
return;
- Color color = marker.isActiveMatch() ? renderer().theme().platformActiveTextSearchHighlightColor() : renderer().theme().platformInactiveTextSearchHighlightColor();
+ auto color = isActiveMatch ? renderer().theme().platformActiveTextSearchHighlightColor() : renderer().theme().platformInactiveTextSearchHighlightColor();
GraphicsContextStateSaver stateSaver(context);
updateGraphicsContext(context, TextPaintStyle(color)); // Don't draw text at all!
@@ -879,8 +879,8 @@
LayoutUnit deltaY = renderer().style().isFlippedLinesWritingMode() ? selectionBottom() - logicalBottom() : logicalTop() - selectionTop();
LayoutRect selectionRect = LayoutRect(boxOrigin.x(), boxOrigin.y() - deltaY, 0, this->selectionHeight());
- unsigned sPos = clampedOffset(marker.startOffset());
- unsigned ePos = clampedOffset(marker.endOffset());
+ unsigned sPos = clampedOffset(subrange.startOffset);
+ unsigned ePos = clampedOffset(subrange.endOffset);
TextRun run = constructTextRun(style);
font.adjustSelectionRectForText(run, selectionRect, sPos, ePos);
@@ -889,7 +889,7 @@
context.fillRect(snapRectToDevicePixelsWithWritingDirection(selectionRect, renderer().document().deviceScaleFactor(), run.ltr()), color);
}
-
+
void InlineTextBox::paintDocumentMarkers(GraphicsContext& context, const FloatPoint& boxOrigin, const RenderStyle& style, const FontCascade& font, bool background)
{
if (!renderer().textNode())
@@ -897,6 +897,30 @@
Vector<RenderedDocumentMarker*> markers = renderer().document().markers().markersFor(renderer().textNode());
+ auto markerTypeForSubrangeType = [] (DocumentMarker::MarkerType type) {
+ switch (type) {
+ case DocumentMarker::Spelling:
+ return MarkerSubrange::SpellingError;
+ case DocumentMarker::Grammar:
+ return MarkerSubrange::GrammarError;
+ case DocumentMarker::CorrectionIndicator:
+ return MarkerSubrange::Correction;
+ case DocumentMarker::TextMatch:
+ return MarkerSubrange::TextMatch;
+ case DocumentMarker::DictationAlternatives:
+ return MarkerSubrange::DictationAlternatives;
+#if PLATFORM(IOS)
+ case DocumentMarker::DictationPhraseWithAlternatives:
+ return MarkerSubrange::DictationPhraseWithAlternatives;
+#endif
+ default:
+ return MarkerSubrange::Unmarked;
+ }
+ };
+
+ Vector<MarkerSubrange> subranges;
+ subranges.reserveInitialCapacity(markers.size());
+
// Give any document markers that touch this run a chance to draw before the text has been drawn.
// Note end() points at the last char, not one past it like endOffset and ranges do.
for (auto* marker : markers) {
@@ -939,19 +963,13 @@
case DocumentMarker::Spelling:
case DocumentMarker::CorrectionIndicator:
case DocumentMarker::DictationAlternatives:
- paintDocumentMarker(context, boxOrigin, *marker, style, font);
- break;
case DocumentMarker::Grammar:
- paintDocumentMarker(context, boxOrigin, *marker, style, font);
- break;
#if PLATFORM(IOS)
// FIXME: See <rdar://problem/8933352>. Also, remove the PLATFORM(IOS)-guard.
case DocumentMarker::DictationPhraseWithAlternatives:
- paintDocumentMarker(context, boxOrigin, *marker, style, font);
- break;
#endif
case DocumentMarker::TextMatch:
- paintTextMatchMarker(context, boxOrigin, *marker, style, font);
+ subranges.uncheckedAppend({ marker->startOffset(), marker->endOffset(), markerTypeForSubrangeType(marker->type()), marker });
break;
case DocumentMarker::Replacement:
break;
@@ -962,7 +980,13 @@
default:
ASSERT_NOT_REACHED();
}
+ }
+ for (auto& subrange : subdivide(subranges, OverlapStrategy::Frontmost)) {
+ if (subrange.type == MarkerSubrange::TextMatch)
+ paintTextMatchMarker(context, boxOrigin, subrange, style, font, subrange.marker->isActiveMatch());
+ else
+ paintDocumentMarker(context, boxOrigin, subrange, style, font);
}
}
Modified: trunk/Source/WebCore/rendering/InlineTextBox.h (222297 => 222298)
--- trunk/Source/WebCore/rendering/InlineTextBox.h 2017-09-20 23:08:50 UTC (rev 222297)
+++ trunk/Source/WebCore/rendering/InlineTextBox.h 2017-09-20 23:13:27 UTC (rev 222298)
@@ -1,7 +1,7 @@
/*
* (C) 1999 Lars Knoll ([email protected])
* (C) 2000 Dirk Mueller ([email protected])
- * Copyright (C) 2004, 2005, 2006, 2009, 2010, 2011, 2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2004-2017 Apple Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
@@ -28,10 +28,11 @@
namespace WebCore {
-struct CompositionUnderline;
class RenderCombineText;
class RenderedDocumentMarker;
class TextPainter;
+struct CompositionUnderline;
+struct MarkerSubrange;
struct TextPaintStyle;
const unsigned short cNoTruncation = USHRT_MAX;
@@ -161,8 +162,8 @@
void paintDecoration(GraphicsContext&, const FontCascade&, RenderCombineText*, const TextRun&, const FloatPoint& textOrigin, const FloatRect& boxRect,
TextDecoration, TextPaintStyle, const ShadowData*, const FloatRect& clipOutRect);
void paintSelection(GraphicsContext&, const FloatPoint& boxOrigin, const RenderStyle&, const FontCascade&, const Color& textColor);
- void paintDocumentMarker(GraphicsContext&, const FloatPoint& boxOrigin, RenderedDocumentMarker&, const RenderStyle&, const FontCascade&);
- void paintTextMatchMarker(GraphicsContext&, const FloatPoint& boxOrigin, RenderedDocumentMarker&, const RenderStyle&, const FontCascade&);
+ void paintDocumentMarker(GraphicsContext&, const FloatPoint& boxOrigin, const MarkerSubrange&, const RenderStyle&, const FontCascade&);
+ void paintTextMatchMarker(GraphicsContext&, const FloatPoint& boxOrigin, const MarkerSubrange&, const RenderStyle&, const FontCascade&, bool isActiveMatch);
ExpansionBehavior expansionBehavior() const;
Added: trunk/Source/WebCore/rendering/MarkerSubrange.cpp (0 => 222298)
--- trunk/Source/WebCore/rendering/MarkerSubrange.cpp (rev 0)
+++ trunk/Source/WebCore/rendering/MarkerSubrange.cpp 2017-09-20 23:13:27 UTC (rev 222298)
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2017 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 "MarkerSubrange.h"
+
+#include <wtf/HashSet.h>
+#include <algorithm>
+
+namespace WebCore {
+
+Vector<MarkerSubrange> subdivide(const Vector<MarkerSubrange>& subranges, OverlapStrategy overlapStrategy)
+{
+ if (subranges.isEmpty())
+ return { };
+
+ struct Offset {
+ enum Kind { Begin, End };
+ Kind kind;
+ unsigned value; // Copy of subrange.startOffset/endOffset to avoid the need to branch based on kind.
+ const MarkerSubrange* subrange;
+ };
+
+ // 1. Build table of all offsets.
+ Vector<Offset> offsets;
+ ASSERT(subranges.size() < std::numeric_limits<unsigned>::max() / 2);
+ unsigned numberOfSubranges = subranges.size();
+ unsigned numberOfOffsets = 2 * numberOfSubranges;
+ offsets.reserveInitialCapacity(numberOfOffsets);
+ for (auto& subrange : subranges) {
+ offsets.uncheckedAppend({ Offset::Begin, subrange.startOffset, &subrange });
+ offsets.uncheckedAppend({ Offset::End, subrange.endOffset, &subrange });
+ }
+
+ // 2. Sort offsets such that begin offsets are in paint order and end offsets are in reverse paint order.
+ std::sort(offsets.begin(), offsets.end(), [] (const Offset& a, const Offset& b) {
+ return a.value < b.value || (a.value == b.value && a.kind == b.kind && a.kind == Offset::Begin && a.subrange->type < b.subrange->type)
+ || (a.value == b.value && a.kind == b.kind && a.kind == Offset::End && a.subrange->type > b.subrange->type);
+ });
+
+ // 3. Compute intersection.
+ Vector<MarkerSubrange> result;
+ result.reserveInitialCapacity(numberOfSubranges);
+ HashSet<const MarkerSubrange*> processedSubranges;
+ unsigned offsetSoFar = offsets[0].value;
+ for (unsigned i = 1; i < numberOfOffsets; ++i) {
+ if (offsets[i].value > offsets[i - 1].value) {
+ if (overlapStrategy == OverlapStrategy::Frontmost) {
+ std::optional<unsigned> frontmost;
+ for (unsigned j = 0; j < i; ++j) {
+ if (!processedSubranges.contains(offsets[j].subrange))
+ frontmost = j;
+ }
+ if (frontmost)
+ result.append({ offsetSoFar, offsets[i].value, offsets[frontmost.value()].subrange->type, offsets[frontmost.value()].subrange->marker });
+ } else {
+ for (unsigned j = 0; j < i; ++j) {
+ if (!processedSubranges.contains(offsets[j].subrange))
+ result.append({ offsetSoFar, offsets[i].value, offsets[j].subrange->type, offsets[j].subrange->marker });
+ }
+ }
+ offsetSoFar = offsets[i].value;
+ }
+ if (offsets[i].kind == Offset::End)
+ processedSubranges.add(offsets[i].subrange);
+ }
+ return result;
+}
+
+}
+
+
Added: trunk/Source/WebCore/rendering/MarkerSubrange.h (0 => 222298)
--- trunk/Source/WebCore/rendering/MarkerSubrange.h (rev 0)
+++ trunk/Source/WebCore/rendering/MarkerSubrange.h 2017-09-20 23:13:27 UTC (rev 222298)
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#pragma once
+
+#include <wtf/Vector.h>
+
+namespace WebCore {
+
+class RenderedDocumentMarker;
+
+struct MarkerSubrange {
+ // Sorted by paint order
+ enum Type {
+ Unmarked,
+ GrammarError,
+ Correction,
+ SpellingError,
+ TextMatch,
+ DictationAlternatives,
+#if PLATFORM(IOS)
+ // FIXME: See <rdar://problem/8933352>. Also, remove the PLATFORM(IOS)-guard.
+ DictationPhraseWithAlternatives,
+#endif
+ };
+#if !COMPILER_SUPPORTS(NSDMI_FOR_AGGREGATES)
+ MarkerSubrange() = default;
+ MarkerSubrange(unsigned startOffset, unsigned endOffset, Type type, const RenderedDocumentMarker* marker = nullptr)
+ : startOffset { startOffset }
+ , endOffset { endOffset }
+ , type { type }
+ , marker { marker }
+ {
+ }
+#endif
+ unsigned startOffset;
+ unsigned endOffset;
+ Type type;
+ const RenderedDocumentMarker* marker { nullptr };
+};
+
+enum class OverlapStrategy { None, Frontmost };
+WEBCORE_EXPORT Vector<MarkerSubrange> subdivide(const Vector<MarkerSubrange>&, OverlapStrategy = OverlapStrategy::None);
+
+}
+
Modified: trunk/Tools/ChangeLog (222297 => 222298)
--- trunk/Tools/ChangeLog 2017-09-20 23:08:50 UTC (rev 222297)
+++ trunk/Tools/ChangeLog 2017-09-20 23:13:27 UTC (rev 222298)
@@ -1,3 +1,19 @@
+2017-09-20 Daniel Bates <[email protected]>
+
+ Spelling and grammar dots should not overlap
+ https://bugs.webkit.org/show_bug.cgi?id=177265
+ <rdar://problem/34556424>
+
+ Reviewed by David Hyatt.
+
+ Add unit tests for the subdivision algorithm.
+
+ * TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
+ * TestWebKitAPI/Tests/WebCore/MarkerSubrange.cpp: Added.
+ (WebCore::operator<<):
+ (WebCore::operator==):
+ (TestWebKitAPI::TEST):
+
2017-09-20 Filip Pizlo <[email protected]>
WSL should not type-check functions in the standard library that it does not use
Modified: trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj (222297 => 222298)
--- trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj 2017-09-20 23:08:50 UTC (rev 222297)
+++ trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj 2017-09-20 23:13:27 UTC (rev 222298)
@@ -642,6 +642,7 @@
CDE195B51CFE0B880053D256 /* FullscreenTopContentInset.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = CDE195B21CFE0ADE0053D256 /* FullscreenTopContentInset.html */; };
CE06DF9B1E1851F200E570C9 /* SecurityOrigin.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CE06DF9A1E1851F200E570C9 /* SecurityOrigin.cpp */; };
CE14F1A4181873B0001C2705 /* WillPerformClientRedirectToURLCrash.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = CE14F1A2181873B0001C2705 /* WillPerformClientRedirectToURLCrash.html */; };
+ CE1866491F72E8F100A0CAB6 /* MarkerSubrange.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CE1866471F72E8F100A0CAB6 /* MarkerSubrange.cpp */; };
CE3524F81B1431F60028A7C5 /* TextFieldDidBeginAndEndEditing_Bundle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CE3524F21B142B8D0028A7C5 /* TextFieldDidBeginAndEndEditing_Bundle.cpp */; };
CE3524F91B1441C40028A7C5 /* TextFieldDidBeginAndEndEditing.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CE3524F11B142B8D0028A7C5 /* TextFieldDidBeginAndEndEditing.cpp */; };
CE3524FA1B1443890028A7C5 /* input-focus-blur.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = CE3524F51B142BBB0028A7C5 /* input-focus-blur.html */; };
@@ -1670,6 +1671,7 @@
CDE195B31CFE0ADE0053D256 /* FullscreenTopContentInset.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FullscreenTopContentInset.mm; sourceTree = "<group>"; };
CE06DF9A1E1851F200E570C9 /* SecurityOrigin.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SecurityOrigin.cpp; sourceTree = "<group>"; };
CE14F1A2181873B0001C2705 /* WillPerformClientRedirectToURLCrash.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = WillPerformClientRedirectToURLCrash.html; sourceTree = "<group>"; };
+ CE1866471F72E8F100A0CAB6 /* MarkerSubrange.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = MarkerSubrange.cpp; sourceTree = "<group>"; };
CE32C7C718184C4900CD8C28 /* WillPerformClientRedirectToURLCrash.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WillPerformClientRedirectToURLCrash.mm; sourceTree = "<group>"; };
CE3524F11B142B8D0028A7C5 /* TextFieldDidBeginAndEndEditing.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TextFieldDidBeginAndEndEditing.cpp; sourceTree = "<group>"; };
CE3524F21B142B8D0028A7C5 /* TextFieldDidBeginAndEndEditing_Bundle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TextFieldDidBeginAndEndEditing_Bundle.cpp; sourceTree = "<group>"; };
@@ -2085,6 +2087,7 @@
7A909A751D877475007E10F8 /* IntSize.cpp */,
14464012167A8305000BD218 /* LayoutUnit.cpp */,
076E507E1F45031E006E9F5A /* Logging.cpp */,
+ CE1866471F72E8F100A0CAB6 /* MarkerSubrange.cpp */,
A5B149DD1F5A19DC00C6DAFF /* MIMETypeRegistry.cpp */,
CD225C071C45A69200140761 /* ParsedContentRange.cpp */,
F418BE141F71B7DC001970E6 /* RoundedRectTests.cpp */,
@@ -3369,6 +3372,7 @@
7CCE7ED11A411A7E00447C4C /* StringTruncator.mm in Sources */,
ECA680CE1E68CC0900731D20 /* StringUtilities.mm in Sources */,
CE4D5DE71F6743BA0072CFC6 /* StringWithDirection.cpp in Sources */,
+ CE1866491F72E8F100A0CAB6 /* MarkerSubrange.cpp in Sources */,
7CCE7ED21A411A7E00447C4C /* SubresourceErrorCrash.mm in Sources */,
7CCE7EA81A411A1900447C4C /* SyntheticBackingScaleFactorWindow.m in Sources */,
7CCE7F161A411AE600447C4C /* TerminateTwice.cpp in Sources */,
Added: trunk/Tools/TestWebKitAPI/Tests/WebCore/MarkerSubrange.cpp (0 => 222298)
--- trunk/Tools/TestWebKitAPI/Tests/WebCore/MarkerSubrange.cpp (rev 0)
+++ trunk/Tools/TestWebKitAPI/Tests/WebCore/MarkerSubrange.cpp 2017-09-20 23:13:27 UTC (rev 222298)
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2017 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 "Test.h"
+#include <WebCore/MarkerSubrange.h>
+#include <WebCore/RenderedDocumentMarker.h>
+
+using namespace WebCore;
+
+namespace WebCore {
+
+std::ostream& operator<<(std::ostream& os, MarkerSubrange::Type type)
+{
+ switch (type) {
+ case MarkerSubrange::Unmarked:
+ return os << "Unmarked";
+ case MarkerSubrange::GrammarError:
+ return os << "GrammarError";
+ case MarkerSubrange::Correction:
+ return os << "Correction";
+ case MarkerSubrange::SpellingError:
+ return os << "SpellingError";
+ case MarkerSubrange::TextMatch:
+ return os << "TextMatch";
+ case MarkerSubrange::DictationAlternatives:
+ return os << "DictationAlternatives";
+#if PLATFORM(IOS)
+ // FIXME: See <rdar://problem/8933352>. Also, remove the PLATFORM(IOS)-guard.
+ case MarkerSubrange::DictationPhraseWithAlternatives:
+ return os << "DictationPhraseWithAlternatives";
+#endif
+ }
+}
+
+std::ostream& operator<<(std::ostream& os, const MarkerSubrange& subrange)
+{
+ os << "(" << subrange.startOffset << ", " << subrange.endOffset << ", " << subrange.type;
+ if (subrange.marker)
+ os << static_cast<const void*>(subrange.marker);
+ return os << ")";
+}
+
+bool operator==(const MarkerSubrange& a, const MarkerSubrange& b)
+{
+ return a.startOffset == b.startOffset && a.endOffset == b.endOffset && a.type == b.type && a.marker == b.marker;
+}
+
+}
+
+namespace TestWebKitAPI {
+
+TEST(MarkerSubrange, SubdivideEmpty)
+{
+ EXPECT_EQ(0U, subdivide({ }).size());
+ EXPECT_EQ(0U, subdivide({ }, OverlapStrategy::Frontmost).size());
+}
+
+TEST(MarkerSubrange, SubdivideSimple)
+{
+ MarkerSubrange subrange { 0, 9, MarkerSubrange::SpellingError };
+ auto results = subdivide({ subrange });
+ ASSERT_EQ(1U, results.size());
+ EXPECT_EQ(subrange, results[0]);
+}
+
+TEST(MarkerSubrange, SubdivideSpellingAndGrammarSimple)
+{
+ RenderedDocumentMarker grammarErrorMarker { DocumentMarker { DocumentMarker::Grammar, 7, 8 } };
+ Vector<MarkerSubrange> expectedSubranges {
+ MarkerSubrange { grammarErrorMarker.startOffset(), grammarErrorMarker.endOffset(), MarkerSubrange::GrammarError, &grammarErrorMarker },
+ MarkerSubrange { 22, 32, MarkerSubrange::SpellingError },
+ };
+ auto results = subdivide(expectedSubranges);
+ ASSERT_EQ(expectedSubranges.size(), results.size());
+ for (size_t i = 0; i < expectedSubranges.size(); ++i)
+ EXPECT_EQ(expectedSubranges[i], results[i]);
+}
+
+TEST(MarkerSubrange, SubdivideSpellingAndGrammarOverlap)
+{
+ Vector<MarkerSubrange> subranges {
+ MarkerSubrange { 0, 40, MarkerSubrange::GrammarError },
+ MarkerSubrange { 2, 17, MarkerSubrange::SpellingError },
+ MarkerSubrange { 20, 40, MarkerSubrange::SpellingError },
+ MarkerSubrange { 41, 45, MarkerSubrange::SpellingError },
+ };
+ Vector<MarkerSubrange> expectedSubranges {
+ MarkerSubrange { 0, 2, MarkerSubrange::GrammarError },
+ MarkerSubrange { 2, 17, MarkerSubrange::GrammarError },
+ MarkerSubrange { 2, 17, MarkerSubrange::SpellingError },
+ MarkerSubrange { 17, 20, MarkerSubrange::GrammarError },
+ MarkerSubrange { 20, 40, MarkerSubrange::GrammarError },
+ MarkerSubrange { 20, 40, MarkerSubrange::SpellingError },
+ MarkerSubrange { 41, 45, MarkerSubrange::SpellingError },
+ };
+ auto results = subdivide(subranges);
+ ASSERT_EQ(expectedSubranges.size(), results.size());
+ for (size_t i = 0; i < expectedSubranges.size(); ++i)
+ EXPECT_EQ(expectedSubranges[i], results[i]);
+}
+
+TEST(MarkerSubrange, SubdivideSpellingAndGrammarOverlapFrontmost)
+{
+ Vector<MarkerSubrange> subranges {
+ MarkerSubrange { 0, 40, MarkerSubrange::GrammarError },
+ MarkerSubrange { 2, 17, MarkerSubrange::SpellingError },
+ MarkerSubrange { 20, 40, MarkerSubrange::SpellingError },
+ MarkerSubrange { 41, 45, MarkerSubrange::SpellingError },
+ };
+ Vector<MarkerSubrange> expectedSubranges {
+ MarkerSubrange { 0, 2, MarkerSubrange::GrammarError },
+ MarkerSubrange { 2, 17, MarkerSubrange::SpellingError },
+ MarkerSubrange { 17, 20, MarkerSubrange::GrammarError },
+ MarkerSubrange { 20, 40, MarkerSubrange::SpellingError },
+ MarkerSubrange { 41, 45, MarkerSubrange::SpellingError },
+ };
+ auto results = subdivide(subranges, OverlapStrategy::Frontmost);
+ ASSERT_EQ(expectedSubranges.size(), results.size());
+ for (size_t i = 0; i < expectedSubranges.size(); ++i)
+ EXPECT_EQ(expectedSubranges[i], results[i]);
+}
+
+TEST(MarkerSubrange, SubdivideSpellingAndGrammarComplicatedFrontmost)
+{
+ Vector<MarkerSubrange> subranges {
+ MarkerSubrange { 0, 6, MarkerSubrange::SpellingError },
+ MarkerSubrange { 0, 46, MarkerSubrange::GrammarError },
+ MarkerSubrange { 7, 16, MarkerSubrange::SpellingError },
+ MarkerSubrange { 22, 27, MarkerSubrange::SpellingError },
+ MarkerSubrange { 34, 44, MarkerSubrange::SpellingError },
+ MarkerSubrange { 46, 50, MarkerSubrange::SpellingError },
+ MarkerSubrange { 51, 58, MarkerSubrange::SpellingError },
+ MarkerSubrange { 59, 63, MarkerSubrange::GrammarError },
+ };
+ Vector<MarkerSubrange> expectedSubranges {
+ MarkerSubrange { 0, 6, MarkerSubrange::SpellingError },
+ MarkerSubrange { 6, 7, MarkerSubrange::GrammarError },
+ MarkerSubrange { 7, 16, MarkerSubrange::SpellingError },
+ MarkerSubrange { 16, 22, MarkerSubrange::GrammarError },
+ MarkerSubrange { 22, 27, MarkerSubrange::SpellingError },
+ MarkerSubrange { 27, 34, MarkerSubrange::GrammarError },
+ MarkerSubrange { 34, 44, MarkerSubrange::SpellingError },
+ MarkerSubrange { 44, 46, MarkerSubrange::GrammarError },
+ MarkerSubrange { 46, 50, MarkerSubrange::SpellingError },
+ MarkerSubrange { 51, 58, MarkerSubrange::SpellingError },
+ MarkerSubrange { 59, 63, MarkerSubrange::GrammarError },
+ };
+ auto results = subdivide(subranges, OverlapStrategy::Frontmost);
+ ASSERT_EQ(expectedSubranges.size(), results.size());
+ for (size_t i = 0; i < expectedSubranges.size(); ++i)
+ EXPECT_EQ(expectedSubranges[i], results[i]);
+}
+
+}