| diff --git a/Source/WTF/wtf/Platform.h b/Source/WTF/wtf/Platform.h |
| index 290f6de219b..f7411e2dfb4 100644 |
| --- a/Source/WTF/wtf/Platform.h |
| +++ b/Source/WTF/wtf/Platform.h |
| @@ -1120,7 +1120,7 @@ |
| #if !defined(NDEBUG) |
| #define ENABLE_TREE_DEBUGGING 1 |
| #else |
| -#define ENABLE_TREE_DEBUGGING 0 |
| +#define ENABLE_TREE_DEBUGGING 1 |
| #endif |
| #endif |
| |
| diff --git a/Source/WebCore/DerivedSources.make b/Source/WebCore/DerivedSources.make |
| index f5d0e5d2c3e..3945090acf3 100644 |
| --- a/Source/WebCore/DerivedSources.make |
| +++ b/Source/WebCore/DerivedSources.make |
| @@ -532,6 +532,7 @@ JS_BINDING_IDLS = \ |
| $(WebCore)/dom/TextDecoder.idl \ |
| $(WebCore)/dom/TextEncoder.idl \ |
| $(WebCore)/dom/TextEvent.idl \ |
| + $(WebCore)/dom/InlineTextRun.idl \ |
| $(WebCore)/dom/TransitionEvent.idl \ |
| $(WebCore)/dom/TreeWalker.idl \ |
| $(WebCore)/dom/UIEvent.idl \ |
| diff --git a/Source/WebCore/Sources.txt b/Source/WebCore/Sources.txt |
| index a461719e2dd..2d48c60758b 100644 |
| --- a/Source/WebCore/Sources.txt |
| +++ b/Source/WebCore/Sources.txt |
| @@ -1802,7 +1802,9 @@ rendering/InlineBox.cpp |
| rendering/InlineElementBox.cpp |
| rendering/InlineFlowBox.cpp |
| rendering/InlineIterator.cpp |
| +rendering/InlineTextBreaker.cpp |
| rendering/InlineTextBox.cpp |
| +rendering/InlineTextRunIterator.cpp |
| rendering/LayoutDisallowedScope.cpp |
| rendering/LayoutRepainter.cpp |
| rendering/LayoutState.cpp |
| diff --git a/Source/WebCore/WebCore.xcodeproj/project.pbxproj b/Source/WebCore/WebCore.xcodeproj/project.pbxproj |
| index a6a138060f6..91fb8a5cd04 100644 |
| --- a/Source/WebCore/WebCore.xcodeproj/project.pbxproj |
| +++ b/Source/WebCore/WebCore.xcodeproj/project.pbxproj |
| @@ -396,6 +396,10 @@ |
| 1193408A1FEB355D00935F1E /* RenderTreeBuilderRuby.h in Headers */ = {isa = PBXBuildFile; fileRef = 119340881FEB355D00935F1E /* RenderTreeBuilderRuby.h */; }; |
| 119340971FED715500935F1E /* RenderTreeBuilderFormControls.h in Headers */ = {isa = PBXBuildFile; fileRef = 119340951FED715500935F1E /* RenderTreeBuilderFormControls.h */; }; |
| 119340A31FEE024000935F1E /* RenderTreeBuilderBlock.h in Headers */ = {isa = PBXBuildFile; fileRef = 119340A11FEE024000935F1E /* RenderTreeBuilderBlock.h */; }; |
| + 11C0C79E20694AE100BE3A84 /* InlineTextRunIterator.h in Headers */ = {isa = PBXBuildFile; fileRef = 11C0C79920694ADE00BE3A84 /* InlineTextRunIterator.h */; }; |
| + 11C0C7A020694AE100BE3A84 /* InlineTextBreaker.h in Headers */ = {isa = PBXBuildFile; fileRef = 11C0C79C20694AE000BE3A84 /* InlineTextBreaker.h */; }; |
| + 11C0C7A520694B0A00BE3A84 /* InlineTextRun.h in Headers */ = {isa = PBXBuildFile; fileRef = 11C0C7A320694B0900BE3A84 /* InlineTextRun.h */; }; |
| + 11C0C7A7206953F700BE3A84 /* JSInlineTextRun.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 11C0C7A6206953F600BE3A84 /* JSInlineTextRun.cpp */; }; |
| 11CB2789203BA570004A1DC9 /* RenderTreeBuilderFullScreen.h in Headers */ = {isa = PBXBuildFile; fileRef = 11CB2787203BA570004A1DC9 /* RenderTreeBuilderFullScreen.h */; }; |
| 11E067EE1E6246E500162D16 /* SimpleLineLayoutCoverage.h in Headers */ = {isa = PBXBuildFile; fileRef = 11E067ED1E6246E500162D16 /* SimpleLineLayoutCoverage.h */; settings = {ATTRIBUTES = (Private, ); }; }; |
| 1400D7A817136EA70077CE05 /* ScriptWrappableInlines.h in Headers */ = {isa = PBXBuildFile; fileRef = 1400D7A717136EA70077CE05 /* ScriptWrappableInlines.h */; settings = {ATTRIBUTES = (Private, ); }; }; |
| @@ -5706,6 +5710,13 @@ |
| 119340951FED715500935F1E /* RenderTreeBuilderFormControls.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RenderTreeBuilderFormControls.h; sourceTree = "<group>"; }; |
| 119340A01FEE024000935F1E /* RenderTreeBuilderBlock.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = RenderTreeBuilderBlock.cpp; sourceTree = "<group>"; }; |
| 119340A11FEE024000935F1E /* RenderTreeBuilderBlock.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RenderTreeBuilderBlock.h; sourceTree = "<group>"; }; |
| + 11C0C79920694ADE00BE3A84 /* InlineTextRunIterator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InlineTextRunIterator.h; sourceTree = "<group>"; }; |
| + 11C0C79B20694ADF00BE3A84 /* InlineTextBreaker.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = InlineTextBreaker.cpp; sourceTree = "<group>"; }; |
| + 11C0C79C20694AE000BE3A84 /* InlineTextBreaker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InlineTextBreaker.h; sourceTree = "<group>"; }; |
| + 11C0C79D20694AE000BE3A84 /* InlineTextRunIterator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = InlineTextRunIterator.cpp; sourceTree = "<group>"; }; |
| + 11C0C7A220694B0800BE3A84 /* InlineTextRun.idl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = InlineTextRun.idl; sourceTree = "<group>"; }; |
| + 11C0C7A320694B0900BE3A84 /* InlineTextRun.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InlineTextRun.h; sourceTree = "<group>"; }; |
| + 11C0C7A6206953F600BE3A84 /* JSInlineTextRun.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = JSInlineTextRun.cpp; path = JSInlineTextRun.cpp; sourceTree = "<group>"; }; |
| 11C5F1162003E7750001AE60 /* RenderTreeBuilderInline.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RenderTreeBuilderInline.cpp; sourceTree = "<group>"; }; |
| 11C5F1182003E7760001AE60 /* RenderTreeBuilderInline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RenderTreeBuilderInline.h; sourceTree = "<group>"; }; |
| 11C5F11D2003F69E0001AE60 /* RenderTreeBuilderBlockFlow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RenderTreeBuilderBlockFlow.h; sourceTree = "<group>"; }; |
| @@ -22145,6 +22156,7 @@ |
| A83B79100CCB001B000B0825 /* Core */ = { |
| isa = PBXGroup; |
| children = ( |
| + 11C0C7A6206953F600BE3A84 /* JSInlineTextRun.cpp */, |
| 7C30D97E1F815AC000268356 /* JSAbortController.cpp */, |
| 7C30D9801F815AC100268356 /* JSAbortController.h */, |
| 7C30D9821F815AC200268356 /* JSAbortSignal.cpp */, |
| @@ -25479,6 +25491,10 @@ |
| F523D2F302DE443B018635CA /* rendering */ = { |
| isa = PBXGroup; |
| children = ( |
| + 11C0C79B20694ADF00BE3A84 /* InlineTextBreaker.cpp */, |
| + 11C0C79C20694AE000BE3A84 /* InlineTextBreaker.h */, |
| + 11C0C79D20694AE000BE3A84 /* InlineTextRunIterator.cpp */, |
| + 11C0C79920694ADE00BE3A84 /* InlineTextRunIterator.h */, |
| FFB698C81832F10B00158A31 /* line */, |
| 439046C212DA25CE00AF80A2 /* mathml */, |
| FD08A879175D3926002CD360 /* shapes */, |
| @@ -25775,6 +25791,8 @@ |
| F523D32402DE4478018635CA /* dom */ = { |
| isa = PBXGroup; |
| children = ( |
| + 11C0C7A320694B0900BE3A84 /* InlineTextRun.h */, |
| + 11C0C7A220694B0800BE3A84 /* InlineTextRun.idl */, |
| CE2616A4187E65C1007955F3 /* ios */, |
| 2D5036661BCDDDC400E20BB3 /* mac */, |
| 51ECC3E42005831F00483EAE /* messageports */, |
| @@ -26771,6 +26789,7 @@ |
| 31DCD29D1AB4FBDE0072E817 /* AnimationTrigger.h in Headers */, |
| 0F580FAF149800D400FB5BD8 /* AnimationUtilities.h in Headers */, |
| 93309DD7099E64920056E581 /* AppendNodeCommand.h in Headers */, |
| + 11C0C7A520694B0A00BE3A84 /* InlineTextRun.h in Headers */, |
| A1DF5A941F7EC4320058A477 /* ApplePayContactField.h in Headers */, |
| A12C59EE2035FC9B0012236B /* ApplePayError.h in Headers */, |
| 7C6579E31E00827000E3A27A /* ApplePayLineItem.h in Headers */, |
| @@ -30369,6 +30388,7 @@ |
| 4945BFD413CF809000CC3B38 /* TransformState.h in Headers */, |
| E17B491616A9B094001C8839 /* TransitionEvent.h in Headers */, |
| 49E911D20EF86D47009D0CAF /* TranslateTransformOperation.h in Headers */, |
| + 11C0C7A020694AE100BE3A84 /* InlineTextBreaker.h in Headers */, |
| 854FE7370A2297BE0058D7AD /* Traversal.h in Headers */, |
| 37FD4298118368460093C029 /* TreeDepthLimit.h in Headers */, |
| 14D64B5D134A5B6B00E58FDA /* TreeScope.h in Headers */, |
| @@ -30583,6 +30603,7 @@ |
| 97AABD1E14FA09D5007457AE /* WebSocketChannel.h in Headers */, |
| 97AABD1F14FA09D5007457AE /* WebSocketChannelClient.h in Headers */, |
| 4A38BF5114FE1C0900612512 /* WebSocketDeflateFramer.h in Headers */, |
| + 11C0C79E20694AE100BE3A84 /* InlineTextRunIterator.h in Headers */, |
| 97AABD2114FA09D5007457AE /* WebSocketDeflater.h in Headers */, |
| 97AABD2314FA09D5007457AE /* WebSocketExtensionDispatcher.h in Headers */, |
| 4A5A2ADC161E7E00005889DD /* WebSocketExtensionParser.h in Headers */, |
| @@ -31067,6 +31088,7 @@ |
| 0719427F1D088F21002AA51D /* AVFoundationMIMETypeCache.mm in Sources */, |
| 070363E5181A1CDC00C074A5 /* AVMediaCaptureSource.mm in Sources */, |
| CD336F6117F9F64700DDDCD0 /* AVTrackPrivateAVFObjCImpl.mm in Sources */, |
| + 11C0C7A7206953F700BE3A84 /* JSInlineTextRun.cpp in Sources */, |
| 070363E7181A1CDC00C074A5 /* AVVideoCaptureSource.mm in Sources */, |
| 7A45032F18DB717200377B34 /* BufferedLineReader.cpp in Sources */, |
| 0753860214489E9800B78452 /* CachedTextTrack.cpp in Sources */, |
| diff --git a/Source/WebCore/css/CSSComputedStyleDeclaration.cpp b/Source/WebCore/css/CSSComputedStyleDeclaration.cpp |
| index 923ab54d4f2..12004b5afbe 100644 |
| --- a/Source/WebCore/css/CSSComputedStyleDeclaration.cpp |
| +++ b/Source/WebCore/css/CSSComputedStyleDeclaration.cpp |
| @@ -4113,6 +4113,36 @@ String CSSComputedStyleDeclaration::getPropertyValue(CSSPropertyID propertyID) c |
| return value->cssText(); |
| } |
| |
| +bool CSSComputedStyleDeclaration::isPropertyValueInitial(const String& propertyName) |
| +{ |
| + m_element->document().updateLayoutIgnorePendingStylesheets(); |
| + auto* style = m_element->computedStyle(m_pseudoElementSpecifier); |
| + if (!style) |
| + return true; |
| + |
| + CSSPropertyID propertyID = cssPropertyID(propertyName); |
| + if (!propertyID) |
| + return true; |
| + |
| + switch (propertyID) { |
| + case CSSPropertyLeft: |
| + return style->left().isAuto(); |
| + case CSSPropertyRight: |
| + return style->right().isAuto(); |
| + case CSSPropertyTop: |
| + return style->top().isAuto(); |
| + case CSSPropertyBottom: |
| + return style->bottom().isAuto(); |
| + case CSSPropertyWidth: |
| + return style->width().isAuto(); |
| + case CSSPropertyHeight: |
| + return style->height().isAuto(); |
| + default: |
| + ASSERT_NOT_REACHED(); |
| + } |
| + return false; |
| +} |
| + |
| unsigned CSSComputedStyleDeclaration::length() const |
| { |
| updateStyleIfNeededForProperty(m_element.get(), CSSPropertyCustom); |
| diff --git a/Source/WebCore/css/CSSComputedStyleDeclaration.h b/Source/WebCore/css/CSSComputedStyleDeclaration.h |
| index 39ae6c30d7e..a3b6f4697bd 100644 |
| --- a/Source/WebCore/css/CSSComputedStyleDeclaration.h |
| +++ b/Source/WebCore/css/CSSComputedStyleDeclaration.h |
| @@ -123,6 +123,7 @@ private: |
| String item(unsigned index) const final; |
| RefPtr<DeprecatedCSSOMValue> getPropertyCSSValue(const String& propertyName) final; |
| String getPropertyValue(const String& propertyName) final; |
| + bool isPropertyValueInitial(const String& propertyName) final; |
| String getPropertyPriority(const String& propertyName) final; |
| String getPropertyShorthand(const String& propertyName) final; |
| bool isPropertyImplicit(const String& propertyName) final; |
| diff --git a/Source/WebCore/css/CSSStyleDeclaration.h b/Source/WebCore/css/CSSStyleDeclaration.h |
| index b18c9ce3274..4dfb6b936f8 100644 |
| --- a/Source/WebCore/css/CSSStyleDeclaration.h |
| +++ b/Source/WebCore/css/CSSStyleDeclaration.h |
| @@ -54,6 +54,7 @@ public: |
| virtual String item(unsigned index) const = 0; |
| virtual RefPtr<DeprecatedCSSOMValue> getPropertyCSSValue(const String& propertyName) = 0; |
| virtual String getPropertyValue(const String& propertyName) = 0; |
| + virtual bool isPropertyValueInitial(const String& propertyName) = 0; |
| virtual String getPropertyPriority(const String& propertyName) = 0; |
| virtual String getPropertyShorthand(const String& propertyName) = 0; |
| virtual bool isPropertyImplicit(const String& propertyName) = 0; |
| diff --git a/Source/WebCore/css/CSSStyleDeclaration.idl b/Source/WebCore/css/CSSStyleDeclaration.idl |
| index 035cb6245d9..c622313db3b 100644 |
| --- a/Source/WebCore/css/CSSStyleDeclaration.idl |
| +++ b/Source/WebCore/css/CSSStyleDeclaration.idl |
| @@ -30,6 +30,8 @@ |
| |
| DOMString getPropertyValue(DOMString propertyName); |
| |
| + boolean isPropertyValueInitial(DOMString propertyName); |
| + |
| [CEReactions, MayThrowException] DOMString removeProperty(DOMString propertyName); |
| DOMString? getPropertyPriority(DOMString propertyName); |
| |
| diff --git a/Source/WebCore/css/PropertySetCSSStyleDeclaration.cpp b/Source/WebCore/css/PropertySetCSSStyleDeclaration.cpp |
| index 084cbca04ec..4fc589226cf 100644 |
| --- a/Source/WebCore/css/PropertySetCSSStyleDeclaration.cpp |
| +++ b/Source/WebCore/css/PropertySetCSSStyleDeclaration.cpp |
| @@ -196,6 +196,11 @@ String PropertySetCSSStyleDeclaration::getPropertyValue(const String& propertyNa |
| return getPropertyValueInternal(propertyID); |
| } |
| |
| +bool PropertySetCSSStyleDeclaration::isPropertyValueInitial(const String&) |
| +{ |
| + return false; |
| +} |
| + |
| String PropertySetCSSStyleDeclaration::getPropertyPriority(const String& propertyName) |
| { |
| if (isCustomPropertyName(propertyName)) |
| diff --git a/Source/WebCore/css/PropertySetCSSStyleDeclaration.h b/Source/WebCore/css/PropertySetCSSStyleDeclaration.h |
| index 04196405ddf..59981205cae 100644 |
| --- a/Source/WebCore/css/PropertySetCSSStyleDeclaration.h |
| +++ b/Source/WebCore/css/PropertySetCSSStyleDeclaration.h |
| @@ -68,6 +68,7 @@ private: |
| String item(unsigned index) const final; |
| RefPtr<DeprecatedCSSOMValue> getPropertyCSSValue(const String& propertyName) final; |
| String getPropertyValue(const String& propertyName) final; |
| + bool isPropertyValueInitial(const String& propertyName) final; |
| String getPropertyPriority(const String& propertyName) final; |
| String getPropertyShorthand(const String& propertyName) final; |
| bool isPropertyImplicit(const String& propertyName) final; |
| diff --git a/Source/WebCore/dom/InlineTextRun.h b/Source/WebCore/dom/InlineTextRun.h |
| new file mode 100644 |
| index 00000000000..7ae48649fbd |
| --- /dev/null |
| +++ b/Source/WebCore/dom/InlineTextRun.h |
| @@ -0,0 +1,79 @@ |
| +/* |
| + * Copyright (C) 2018 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 "InlineTextRunIterator.h" |
| +#include <wtf/Ref.h> |
| +#include <wtf/RefCounted.h> |
| + |
| +namespace WebCore { |
| + |
| +class InlineTextRun : public RefCounted<InlineTextRun> { |
| + WTF_MAKE_FAST_ALLOCATED; |
| +public: |
| + static Ref<InlineTextRun> create(unsigned startPosition, unsigned endPosition, double width, unsigned type, bool isCollapsed, bool isCollapsible) |
| + { return adoptRef(*new InlineTextRun(startPosition, endPosition, width, type, isCollapsed, isCollapsible)); } |
| + |
| + static Ref<InlineTextRun> create(const LayoutReloaded::InlineTextRunIterator::Run& run) |
| + { return adoptRef(*new InlineTextRun(run.start(), run.end(), run.width(), run.type(), run.isCollapsed(), run.isCollapsible())); } |
| + |
| + enum Type { Invalid, ContentEnd, SoftLineBreak, Whitespace, NonWhitespace, Mixed }; |
| + void setStartPosition(unsigned position) { m_startPosition = position; } |
| + void setEndPosition(unsigned position) { m_endPosition = position; } |
| + void setWidth(double width) { m_width = width; } |
| + void setTypeMixed() { m_type = InlineTextRun::Mixed; } |
| + void setIsCollapsed() { m_isCollapsed = true; } |
| + |
| + unsigned startPosition() const { return m_startPosition; } |
| + unsigned endPosition() const { return m_endPosition; } |
| + double width() const { return m_width; } |
| + unsigned type() const { return m_type; } |
| + unsigned length() const { return endPosition() - startPosition(); } |
| + bool isCollapsed() const { return m_isCollapsed; } |
| + bool isCollapsible() const { return m_isCollapsible; } |
| + |
| +protected: |
| + InlineTextRun(unsigned startPosition, unsigned endPosition, double width, unsigned type, bool isCollapsed, bool isCollapsible) |
| + : m_startPosition(startPosition) |
| + , m_endPosition(endPosition) |
| + , m_width(width) |
| + , m_type(type) |
| + , m_isCollapsed(isCollapsed) |
| + , m_isCollapsible(isCollapsible) |
| + { |
| + } |
| + |
| + InlineTextRun() = default; |
| + |
| + unsigned m_startPosition { 0 }; |
| + unsigned m_endPosition { 0 }; |
| + double m_width { 0 }; |
| + unsigned m_type { 0 }; |
| + bool m_isCollapsed { false }; |
| + bool m_isCollapsible { false }; |
| +}; |
| + |
| +} |
| diff --git a/Source/WebCore/dom/InlineTextRun.idl b/Source/WebCore/dom/InlineTextRun.idl |
| new file mode 100644 |
| index 00000000000..20c01b8cff5 |
| --- /dev/null |
| +++ b/Source/WebCore/dom/InlineTextRun.idl |
| @@ -0,0 +1,36 @@ |
| +/* |
| + * Copyright (C) 2018 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. |
| + */ |
| + |
| +[ |
| + NoInterfaceObject, |
| + ImplementationLacksVTable, |
| +] interface InlineTextRun { |
| + readonly attribute long startPosition; |
| + readonly attribute long endPosition; |
| + readonly attribute double width; |
| + readonly attribute long type; |
| + readonly attribute boolean isCollapsed; |
| + readonly attribute boolean isCollapsible; |
| +}; |
| diff --git a/Source/WebCore/dom/Node.cpp b/Source/WebCore/dom/Node.cpp |
| index 4b1e8725795..cae6e476cad 100644 |
| --- a/Source/WebCore/dom/Node.cpp |
| +++ b/Source/WebCore/dom/Node.cpp |
| @@ -517,6 +517,27 @@ ExceptionOr<void> Node::before(Vector<NodeOrString>&& nodeOrStringVector) |
| return parent->insertBefore(*node, viablePreviousSibling.get()); |
| } |
| |
| +int Node::rendererId() |
| +{ |
| + if (!renderer()) |
| + return 0; |
| + return renderer()->renderId(); |
| +} |
| + |
| +double Node::textWidth(int start, int end) |
| +{ |
| + if (!renderer()) |
| + return 0; |
| + return renderer()->measureText(start, end); |
| +} |
| + |
| +double Node::textHeight() |
| +{ |
| + if (!renderer()) |
| + return 0; |
| + return renderer()->style().computedFontPixelSize(); |
| +} |
| + |
| ExceptionOr<void> Node::after(Vector<NodeOrString>&& nodeOrStringVector) |
| { |
| RefPtr<ContainerNode> parent = parentNode(); |
| diff --git a/Source/WebCore/dom/Node.h b/Source/WebCore/dom/Node.h |
| index 7f176126c7a..1b7a19e07f0 100644 |
| --- a/Source/WebCore/dom/Node.h |
| +++ b/Source/WebCore/dom/Node.h |
| @@ -179,7 +179,11 @@ public: |
| |
| WEBCORE_EXPORT String textContent(bool convertBRsToNewlines = false) const; |
| WEBCORE_EXPORT ExceptionOr<void> setTextContent(const String&); |
| - |
| + |
| + int rendererId(); |
| + double textWidth(int start, int end); |
| + double textHeight(); |
| + |
| Node* lastDescendant() const; |
| Node* firstDescendant() const; |
| |
| diff --git a/Source/WebCore/dom/Node.idl b/Source/WebCore/dom/Node.idl |
| index 25177a8cd43..294307dc815 100644 |
| --- a/Source/WebCore/dom/Node.idl |
| +++ b/Source/WebCore/dom/Node.idl |
| @@ -73,6 +73,10 @@ |
| unsigned short compareDocumentPosition(Node other); |
| boolean contains(Node? other); |
| |
| + readonly attribute long rendererId; |
| + double textWidth(long start, long end); |
| + double textHeight(); |
| + |
| DOMString? lookupPrefix(DOMString? namespaceURI); |
| DOMString? lookupNamespaceURI(DOMString? prefix); |
| boolean isDefaultNamespace(DOMString? namespaceURI); |
| diff --git a/Source/WebCore/page/DOMWindow.cpp b/Source/WebCore/page/DOMWindow.cpp |
| index 0210b08a182..4d3a54691a9 100644 |
| --- a/Source/WebCore/page/DOMWindow.cpp |
| +++ b/Source/WebCore/page/DOMWindow.cpp |
| @@ -29,6 +29,7 @@ |
| |
| #include "BackForwardController.h" |
| #include "BarProp.h" |
| +#include "BreakLines.h" |
| #include "CSSComputedStyleDeclaration.h" |
| #include "CSSRule.h" |
| #include "CSSRuleList.h" |
| @@ -64,9 +65,11 @@ |
| #include "FrameTree.h" |
| #include "FrameView.h" |
| #include "History.h" |
| +#include "InlineTextRunIterator.h" |
| #include "InspectorInstrumentation.h" |
| #include "JSDOMWindowBase.h" |
| #include "JSMainThreadExecState.h" |
| +#include "InlineTextBreaker.h" |
| #include "Location.h" |
| #include "MediaQueryList.h" |
| #include "MediaQueryMatcher.h" |
| @@ -78,6 +81,8 @@ |
| #include "PageConsoleClient.h" |
| #include "PageTransitionEvent.h" |
| #include "Performance.h" |
| +#include "RenderChildIterator.h" |
| +#include "RenderView.h" |
| #include "RequestAnimationFrameCallback.h" |
| #include "ResourceLoadInfo.h" |
| #include "ResourceLoadObserver.h" |
| @@ -100,6 +105,7 @@ |
| #include "StyleResolver.h" |
| #include "StyleScope.h" |
| #include "SuddenTermination.h" |
| +#include "InlineTextRun.h" |
| #include "URL.h" |
| #include "UserGestureIndicator.h" |
| #include "VisualViewport.h" |
| @@ -116,6 +122,7 @@ |
| #include <wtf/NeverDestroyed.h> |
| #include <wtf/Ref.h> |
| #include <wtf/Variant.h> |
| +#include <wtf/text/TextBreakIterator.h> |
| #include <wtf/text/WTFString.h> |
| |
| #if ENABLE(USER_MESSAGE_HANDLERS) |
| @@ -1358,6 +1365,72 @@ int DOMWindow::scrollY() const |
| return view->mapFromLayoutToCSSUnits(view->contentsScrollPosition().y()); |
| } |
| |
| +String DOMWindow::simplifiedRenderTree() const |
| +{ |
| + return m_frame->view()->renderView()->simplifiedRenderTree(); |
| +} |
| + |
| +String DOMWindow::renderTreeStructure() const |
| +{ |
| + return m_frame->view()->renderView()->renderTreeStructure(); |
| +} |
| + |
| +static bool hasDirtyChild(RenderElement& parent) |
| +{ |
| + for (auto& child : childrenOfType<RenderElement>(parent)) { |
| + if (child.needsLayout()) |
| + return true; |
| + } |
| + return false; |
| +} |
| + |
| +static void collectDirtyRenderers(RenderElement& container, Vector<long>& dirtyRenderers) |
| +{ |
| + // Collect the leaf marks only -would be cheating othewise. |
| + for (auto& child : childrenOfType<RenderElement>(container)) { |
| + if (!child.needsLayout()) |
| + continue; |
| + if (!hasDirtyChild(child)) |
| + dirtyRenderers.append(child.renderId()); |
| + else |
| + collectDirtyRenderers(child, dirtyRenderers); |
| + } |
| +} |
| + |
| +Vector<long> DOMWindow::collectRenderersWithNeedsLayout() |
| +{ |
| + Vector<long> dirtyRenderers; |
| + auto* renderView = m_frame->view()->renderView(); |
| + m_frame->document()->updateStyleIfNeeded(); |
| + collectDirtyRenderers(*renderView, dirtyRenderers); |
| + return dirtyRenderers; |
| +} |
| + |
| +Vector<Ref<InlineTextRun>> DOMWindow::collectTextRuns(const String& text, const Node& container, float availableSpace) |
| +{ |
| + Vector<Ref<InlineTextRun>> textRuns; |
| + LayoutReloaded::InlineTextRunIterator textIterator(text, container.renderer()->style()); |
| + while (true) { |
| + auto run = textIterator.nextRun(); |
| + if (run.type() == LayoutReloaded::InlineTextRunIterator::Run::ContentEnd) |
| + break; |
| + textRuns.append(InlineTextRun::create(run)); |
| + } |
| + if (std::isnan(availableSpace)) |
| + return textRuns; |
| + |
| + return LayoutReloaded::InlineTextBreaker::collectRuns(textRuns, text, availableSpace, container.renderer()->style()); |
| +} |
| + |
| +int DOMWindow::nextBreakingOpportunity(const String& text, unsigned startPosition) |
| +{ |
| + LazyLineBreakIterator lineBreakIterator(text, m_frame->view()->renderView()->style().locale()); |
| + |
| + if (lineBreakIterator.mode() == LineBreakIteratorMode::Default) |
| + return WebCore::nextBreakablePosition(lineBreakIterator, startPosition); |
| + return nextBreakablePositionWithoutShortcut(lineBreakIterator, startPosition); |
| +} |
| + |
| bool DOMWindow::closed() const |
| { |
| return !m_frame; |
| diff --git a/Source/WebCore/page/DOMWindow.h b/Source/WebCore/page/DOMWindow.h |
| index 499a558d2ee..be60b3aaf79 100644 |
| --- a/Source/WebCore/page/DOMWindow.h |
| +++ b/Source/WebCore/page/DOMWindow.h |
| @@ -49,7 +49,6 @@ template<typename> class Strong; |
| } |
| |
| namespace WebCore { |
| - |
| class BarProp; |
| class CSSRuleList; |
| class CSSStyleDeclaration; |
| @@ -78,6 +77,7 @@ class ScheduledAction; |
| class Screen; |
| class Storage; |
| class StyleMedia; |
| +class InlineTextRun; |
| class VisualViewport; |
| class WebKitNamespace; |
| class WebKitPoint; |
| @@ -192,6 +192,12 @@ public: |
| String defaultStatus() const; |
| void setDefaultStatus(const String&); |
| |
| + String simplifiedRenderTree() const; |
| + String renderTreeStructure() const; |
| + Vector<long> collectRenderersWithNeedsLayout(); |
| + int nextBreakingOpportunity(const String& text, unsigned startPosition); |
| + Vector<Ref<InlineTextRun>> collectTextRuns(const String&, const Node&, float availableSpace); |
| + |
| DOMWindow* self() const; |
| |
| DOMWindow* opener() const; |
| diff --git a/Source/WebCore/page/DOMWindow.idl b/Source/WebCore/page/DOMWindow.idl |
| index 46ebc1561fa..bd35d3f2155 100644 |
| --- a/Source/WebCore/page/DOMWindow.idl |
| +++ b/Source/WebCore/page/DOMWindow.idl |
| @@ -82,6 +82,12 @@ typedef USVString CSSOMString; |
| readonly attribute Navigator navigator; |
| readonly attribute DOMApplicationCache applicationCache; |
| |
| + readonly attribute DOMString simplifiedRenderTree; |
| + readonly attribute DOMString renderTreeStructure; |
| + sequence<long> collectRenderersWithNeedsLayout(); |
| + long nextBreakingOpportunity(DOMString text, unsigned long startPosition); |
| + sequence<InlineTextRun> collectTextRuns(DOMString text, Node containerNode, optional unrestricted float availableSpace = NaN); |
| + |
| // User prompts. |
| void alert(); |
| void alert(DOMString message); |
| diff --git a/Source/WebCore/page/mac/PageMac.mm b/Source/WebCore/page/mac/PageMac.mm |
| index aa030c54617..7457861e142 100644 |
| --- a/Source/WebCore/page/mac/PageMac.mm |
| +++ b/Source/WebCore/page/mac/PageMac.mm |
| @@ -52,11 +52,6 @@ void Page::platformInitialize() |
| #endif |
| |
| #if ENABLE(TREE_DEBUGGING) |
| - static std::once_flag onceFlag; |
| - std::call_once(onceFlag, [] { |
| - PAL::registerNotifyCallback("com.apple.WebKit.showRenderTree", printRenderTreeForLiveDocuments); |
| - PAL::registerNotifyCallback("com.apple.WebKit.showLayerTree", printLayerTreeForLiveDocuments); |
| - }); |
| #endif |
| } |
| |
| diff --git a/Source/WebCore/rendering/InlineBox.cpp b/Source/WebCore/rendering/InlineBox.cpp |
| index cd23ae0b97c..7cfd4bb2d42 100644 |
| --- a/Source/WebCore/rendering/InlineBox.cpp |
| +++ b/Source/WebCore/rendering/InlineBox.cpp |
| @@ -123,6 +123,18 @@ void InlineBox::outputLineBox(TextStream& stream, bool mark, int depth) const |
| stream.nextLine(); |
| } |
| |
| +void InlineBox::outputSimplifiedLineTree(TextStream& stream, int depth) const |
| +{ |
| + // Ignore inline flows for now. LayoutReloaded does not have the concept of flow boxes. |
| + if (is<InlineFlowBox>(*this) && !is<RootInlineBox>(*this)) |
| + return; |
| + int printedCharacters = 0; |
| + while (++printedCharacters <= depth) |
| + stream << " "; |
| + stream << boxName() << " " << FloatRect(x(), y(), width(), height()); |
| + stream.nextLine(); |
| +} |
| + |
| #endif // ENABLE(TREE_DEBUGGING) |
| |
| float InlineBox::logicalHeight() const |
| diff --git a/Source/WebCore/rendering/InlineBox.h b/Source/WebCore/rendering/InlineBox.h |
| index 7f1dd6cbba4..eddb048440a 100644 |
| --- a/Source/WebCore/rendering/InlineBox.h |
| +++ b/Source/WebCore/rendering/InlineBox.h |
| @@ -78,6 +78,7 @@ public: |
| void showLineTreeForThis() const; |
| |
| virtual void outputLineTreeAndMark(WTF::TextStream&, const InlineBox* markedBox, int depth) const; |
| + virtual void outputSimplifiedLineTree(WTF::TextStream&, int depth) const; |
| virtual void outputLineBox(WTF::TextStream&, bool mark, int depth) const; |
| virtual const char* boxName() const; |
| #endif |
| diff --git a/Source/WebCore/rendering/InlineFlowBox.cpp b/Source/WebCore/rendering/InlineFlowBox.cpp |
| index 063f3b64189..ee32b981371 100644 |
| --- a/Source/WebCore/rendering/InlineFlowBox.cpp |
| +++ b/Source/WebCore/rendering/InlineFlowBox.cpp |
| @@ -1720,6 +1720,18 @@ void InlineFlowBox::outputLineTreeAndMark(WTF::TextStream& stream, const InlineB |
| box->outputLineTreeAndMark(stream, markedBox, depth + 1); |
| } |
| |
| +void InlineFlowBox::outputSimplifiedLineTree(WTF::TextStream& stream, int depth) const |
| +{ |
| + InlineBox::outputSimplifiedLineTree(stream, depth); |
| + for (auto* box = firstChild(); box; box = box->nextOnLine()) { |
| + // DO not increment depth for flow boxes. LayoutReloaded does not have the concept of them. |
| + if (is<InlineFlowBox>(*box) && !is<RootInlineBox>(*box)) |
| + box->outputSimplifiedLineTree(stream, depth); |
| + else |
| + box->outputSimplifiedLineTree(stream, depth + 1); |
| + } |
| +} |
| + |
| #endif |
| |
| #ifndef NDEBUG |
| diff --git a/Source/WebCore/rendering/InlineFlowBox.h b/Source/WebCore/rendering/InlineFlowBox.h |
| index 0b26444903f..595203b201e 100644 |
| --- a/Source/WebCore/rendering/InlineFlowBox.h |
| +++ b/Source/WebCore/rendering/InlineFlowBox.h |
| @@ -72,6 +72,7 @@ public: |
| |
| #if ENABLE(TREE_DEBUGGING) |
| void outputLineTreeAndMark(WTF::TextStream&, const InlineBox* markedBox, int depth) const override; |
| + void outputSimplifiedLineTree(WTF::TextStream&, int depth) const override; |
| const char* boxName() const override; |
| #endif |
| |
| diff --git a/Source/WebCore/rendering/InlineTextBreaker.cpp b/Source/WebCore/rendering/InlineTextBreaker.cpp |
| new file mode 100644 |
| index 00000000000..9ac083d2a90 |
| --- /dev/null |
| +++ b/Source/WebCore/rendering/InlineTextBreaker.cpp |
| @@ -0,0 +1,308 @@ |
| +/* |
| + * Copyright (C) 2018 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 "InlineTextBreaker.h" |
| + |
| +#include "FontCascade.h" |
| +#include "RenderStyle.h" |
| + |
| +namespace WebCore { |
| +namespace LayoutReloaded { |
| + |
| +InlineTextBreaker::Style::Style(const RenderStyle& style) |
| + : font(style.fontCascade()) |
| + , hasKerningOrLigatures(font.enableKerning() || font.requiresShaping()) |
| + , collapseWhitespace(style.collapseWhiteSpace()) |
| + , preserveNewline(style.preserveNewline()) |
| + , wrapLines(style.autoWrap()) |
| + , breakAnyWordOnOverflow(style.wordBreak() == BreakAllWordBreak && wrapLines) |
| + , breakFirstWordOnOverflow(breakAnyWordOnOverflow || (style.breakWords() && (wrapLines || preserveNewline))) |
| + , wordSpacing(font.wordSpacing()) |
| + , tabWidth(collapseWhitespace ? 0 : style.tabSize()) |
| +{ |
| +} |
| + |
| +InlineTextBreaker::LineState::LineState(const InlineTextBreaker& lineBreaker, float availableWidth) |
| + : m_lineBreaker(lineBreaker) |
| + , m_availableWidth(availableWidth) |
| +{ |
| +} |
| + |
| +float InlineTextBreaker::LineState::availableWidth() const |
| +{ |
| + return m_availableWidth; |
| +} |
| + |
| +bool InlineTextBreaker::LineState::hasTrailingWhitespace() const |
| +{ |
| + return m_hasTrailingWhitespace; |
| +} |
| + |
| +bool InlineTextBreaker::LineState::isWhitespaceOnly() const |
| +{ |
| + for (auto& run : m_runs) { |
| + if (run->type() != InlineTextRun::Whitespace) |
| + return false; |
| + } |
| + return true; |
| +} |
| + |
| +bool InlineTextBreaker::LineState::fits(float extra) const |
| +{ |
| + return m_availableWidth >= width() + extra; |
| +} |
| + |
| +float InlineTextBreaker::LineState::width() const |
| +{ |
| + float width = 0; |
| + for (auto& run : m_runs) |
| + width += m_lineBreaker.textWidth(run->startPosition(), run->endPosition()); |
| + return width; |
| +} |
| + |
| +bool InlineTextBreaker::LineState::isEmpty() const |
| +{ |
| + return !m_runs.size(); |
| +} |
| + |
| +static inline unsigned endPositionForCollapsedRun(const InlineTextRun& run) |
| +{ |
| + if (run.isCollapsed()) |
| + return run.startPosition() + 1; |
| + return run.endPosition(); |
| +} |
| + |
| +void InlineTextBreaker::LineState::appendRun(const InlineTextRun& run) |
| +{ |
| + // Adjust end position while collapsing. |
| + unsigned endPosition = endPositionForCollapsedRun(run); |
| + if (run.type() == InlineTextRun::NonWhitespace) |
| + m_lastNoneWhitespacePosition = endPosition; |
| + // New line needs new run. |
| + if (!m_runs.size()) { |
| + m_runs.append(InlineTextRun::create(run.startPosition(), endPosition, 0, run.type(), run.isCollapsed(), run.isCollapsible())); |
| + } else { |
| + // Advance last completed fragment when the previous fragment is all set (including multiple parts across renderers) |
| + if (m_runs.last()->isCollapsible() && run.isCollapsible()) { |
| + // This fragment is collapsed completely. No run is needed. |
| + return; |
| + } |
| + auto& lastRun = m_runs.last(); |
| + if (lastRun->isCollapsed()) |
| + m_runs.append(InlineTextRun::create(run.startPosition(), endPosition, 0, run.type(), run.isCollapsed(), run.isCollapsible())); |
| + else { |
| + lastRun->setEndPosition(endPosition); |
| + lastRun->setTypeMixed(); |
| + if (run.isCollapsed()) |
| + lastRun->setIsCollapsed(); |
| + } |
| + } |
| + m_hasTrailingWhitespace = run.type() == InlineTextRun::Whitespace; |
| +} |
| + |
| +void InlineTextBreaker::LineState::removeTrailingWhitespace() |
| +{ |
| + if (!m_hasTrailingWhitespace) |
| + return; |
| + if (m_runs.last()->type() == InlineTextRun::Whitespace) { |
| + m_runs.removeLast(); |
| + return; |
| + } |
| + ASSERT(m_runs.last()->type() == InlineTextRun::Mixed); |
| + m_runs.last()->setEndPosition(m_lastNoneWhitespacePosition); |
| +} |
| + |
| +Vector<Ref<InlineTextRun>> InlineTextBreaker::LineState::collectRuns() |
| +{ |
| + for (auto& run: m_runs) |
| + run->setWidth(m_lineBreaker.textWidth(run->startPosition(), run->endPosition())); |
| + return WTFMove(m_runs); |
| +} |
| + |
| +InlineTextBreaker::InlineTextBreaker(Vector<Ref<InlineTextRun>>& runs, const String& text, float availableWidth, const RenderStyle& style) |
| + : m_textRuns(runs) |
| + , m_text(text) |
| + , m_style(style) |
| + , m_line(*this, availableWidth) |
| +{ |
| +} |
| + |
| +Vector<Ref<InlineTextRun>> InlineTextBreaker::collectRuns(Vector<Ref<InlineTextRun>>& runs, const String& text, float availableWidth, const RenderStyle& style) |
| +{ |
| + return InlineTextBreaker(runs, text, availableWidth, style).createAndCollectLineRuns(); |
| +} |
| + |
| +bool InlineTextBreaker::preWrap() const |
| +{ |
| + return m_style.wrapLines && !m_style.collapseWhitespace; |
| +} |
| + |
| +void InlineTextBreaker::removeTrailingWhitespace() |
| +{ |
| + if (!m_line.hasTrailingWhitespace()) |
| + return; |
| + // Remove collapsed whitespace, or non-collapsed pre-wrap whitespace, unless it's the only content on the line -so removing the whitesapce |
| + // would produce an empty line. |
| + bool collapseWhitespace = m_style.collapseWhitespace | preWrap(); |
| + if (!collapseWhitespace) |
| + return; |
| + if (preWrap() && m_line.isWhitespaceOnly()) |
| + return; |
| + m_line.removeTrailingWhitespace(); |
| +} |
| + |
| +struct SplitFragmentData { |
| + unsigned position; |
| + float width; |
| +}; |
| + |
| +SplitFragmentData InlineTextBreaker::split(const InlineTextRun& run, float availableWidth) |
| +{ |
| + ASSERT(availableWidth >= 0); |
| + auto left = run.startPosition(); |
| + // Pathological case of (extremely)long string and narrow lines. |
| + // Adjust the range so that we can pick a reasonable midpoint. |
| + auto averageCharacterWidth = run.width() / run.length(); |
| + auto right = std::min<unsigned>(left + (2 * availableWidth / averageCharacterWidth), run.endPosition() - 1); |
| + // Preserve the left width for the final split position so that we don't need to remeasure the left side again. |
| + float leftSideWidth = 0; |
| + while (left < right) { |
| + auto middle = (left + right) / 2; |
| + auto width = textWidth(run.startPosition(), middle + 1); |
| + if (width < availableWidth) { |
| + left = middle + 1; |
| + leftSideWidth = width; |
| + } else if (width > availableWidth) |
| + right = middle; |
| + else { |
| + right = middle + 1; |
| + leftSideWidth = width; |
| + break; |
| + } |
| + } |
| + return { right, leftSideWidth }; |
| +} |
| + |
| +void InlineTextBreaker::splitRunToFitLine(InlineTextRun& runToSplit) |
| +{ |
| + auto availableWidth = m_line.availableWidth() - m_line.width(); |
| + auto splitRunData = split(runToSplit, availableWidth); |
| + // Does first character fit this line? |
| + if (splitRunData.position == runToSplit.startPosition()) { |
| + // Keep at least one character on empty lines. |
| + if (m_line.isEmpty()) |
| + splitRunData.width = textWidth(runToSplit.startPosition(), ++splitRunData.position); |
| + } |
| + runToSplit.setEndPosition(splitRunData.position); |
| + runToSplit.setWidth(splitRunData.width); |
| +} |
| + |
| +unsigned InlineTextBreaker::skipLeadingWhitespaceIfNeeded() const |
| +{ |
| + if (!m_style.collapseWhitespace) |
| + return 0; |
| + |
| + for (unsigned index = 0; index < m_textRuns.size(); ++index) { |
| + if (m_textRuns[index]->type() != InlineTextRun::Whitespace) |
| + return index; |
| + } |
| + return m_textRuns.size(); |
| +} |
| + |
| +Vector<Ref<InlineTextRun>> InlineTextBreaker::createAndCollectLineRuns() |
| +{ |
| + bool lineCanBeWrapped = m_style.wrapLines || m_style.breakFirstWordOnOverflow || m_style.breakAnyWordOnOverflow; |
| + auto index = skipLeadingWhitespaceIfNeeded(); |
| + for (; index < m_textRuns.size(); ++index) { |
| + auto& run = m_textRuns[index]; |
| + // Hard and soft linebreaks. |
| + if (run->type() == InlineTextRun::SoftLineBreak) { |
| + // Add the new line fragment only if there's nothing on the line. (otherwise the extra new line character would show up at the end of the content.) |
| + if (m_line.isEmpty()) |
| + m_line.appendRun(run); |
| + break; |
| + } |
| + if (lineCanBeWrapped && !m_line.fits(run->width())) { |
| + // Overflow wrapping behaviour: |
| + // 1. Whitesapce collapse on: whitespace is skipped. Jump to next line. |
| + // 2. Whitespace collapse off: whitespace is wrapped. |
| + // 3. First, non-whitespace fragment is either wrapped or kept on the line. (depends on overflow-wrap) |
| + // 5. Non-whitespace fragment when there's already another fragment on the line either gets wrapped (word-break: break-all) |
| + // or gets pushed to the next line. |
| + bool emptyLine = m_line.isEmpty(); |
| + // Whitespace fragment. |
| + if (run->type() == InlineTextRunIterator::Run::Whitespace) { |
| + if (m_style.collapseWhitespace) { |
| + // Push collapased whitespace to the next line. |
| + break; |
| + } |
| + // Split the whitespace; left part stays on this line, right is pushed to next line. |
| + splitRunToFitLine(run); |
| + m_line.appendRun(run); |
| + break; |
| + } |
| + // Non-whitespace fragment. (!style.wrapLines: bug138102(preserve existing behavior) |
| + if (((emptyLine && m_style.breakFirstWordOnOverflow) || m_style.breakAnyWordOnOverflow) || !m_style.wrapLines) { |
| + // Split the fragment; (modified)fragment stays on this line, overflowedFragment is pushed to next line. |
| + splitRunToFitLine(run); |
| + m_line.appendRun(run); |
| + break; |
| + } |
| + ASSERT(run->type() == InlineTextRunIterator::Run::NonWhitespace); |
| + // Non-breakable non-whitespace first fragment. Add it to the current line. -it overflows though. |
| + if (emptyLine) { |
| + m_line.appendRun(run); |
| + break; |
| + } |
| + // Non-breakable non-whitespace fragment when there's already content on the line. Push it to the next line. |
| + break; |
| + } |
| + m_line.appendRun(run); |
| + } |
| + removeTrailingWhitespace(); |
| + return m_line.collectRuns(); |
| +} |
| + |
| +float InlineTextBreaker::textWidth(unsigned from, unsigned to) const |
| +{ |
| + if (!m_style.font.size() || from == to) |
| + return 0; |
| + |
| + bool measureWithEndSpace = m_style.hasKerningOrLigatures && m_style.collapseWhitespace && to < m_text.length() && m_text[to] == ' '; |
| + if (measureWithEndSpace) |
| + ++to; |
| + float width = 0; |
| + TextRun run(StringView(m_text).substring(from, to - from), 0); |
| + if (m_style.tabWidth) |
| + run.setTabSize(true, m_style.tabWidth); |
| + width = m_style.font.width(run); |
| + if (measureWithEndSpace) |
| + width -= (m_style.font.spaceWidth() + m_style.wordSpacing); |
| + return std::max<float>(0, width); |
| +} |
| + |
| +} |
| +} |
| diff --git a/Source/WebCore/rendering/InlineTextBreaker.h b/Source/WebCore/rendering/InlineTextBreaker.h |
| new file mode 100644 |
| index 00000000000..8cf678fd5c5 |
| --- /dev/null |
| +++ b/Source/WebCore/rendering/InlineTextBreaker.h |
| @@ -0,0 +1,97 @@ |
| +/* |
| + * Copyright (C) 2018 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 "InlineTextRun.h" |
| + |
| +namespace WebCore { |
| + |
| +class FontCascade; |
| + |
| +namespace LayoutReloaded { |
| + |
| +class LineState; |
| +struct SplitFragmentData; |
| + |
| +class InlineTextBreaker { |
| +public: |
| + static Vector<Ref<InlineTextRun>> collectRuns(Vector<Ref<InlineTextRun>>&, const String&, float availableWidth, const RenderStyle&); |
| + |
| +private: |
| + InlineTextBreaker(Vector<Ref<InlineTextRun>>&, const String&, float availableWidth, const RenderStyle&); |
| + |
| + Vector<Ref<InlineTextRun>> createAndCollectLineRuns(); |
| + SplitFragmentData split(const InlineTextRun& run, float availableWidth); |
| + void splitRunToFitLine(InlineTextRun& runToSplit); |
| + void removeTrailingWhitespace(); |
| + bool preWrap() const; |
| + float textWidth(unsigned from, unsigned to) const; |
| + unsigned skipLeadingWhitespaceIfNeeded() const; |
| + |
| + struct Style { |
| + explicit Style(const RenderStyle&); |
| + |
| + const FontCascade& font; |
| + bool hasKerningOrLigatures; |
| + bool collapseWhitespace; |
| + bool preserveNewline; |
| + bool wrapLines; |
| + bool breakAnyWordOnOverflow; |
| + bool breakFirstWordOnOverflow; |
| + float wordSpacing; |
| + unsigned tabWidth; |
| + }; |
| + |
| + class LineState { |
| + public: |
| + LineState(const InlineTextBreaker& lineBreaker, float availableWidth); |
| + |
| + float availableWidth() const; |
| + bool hasTrailingWhitespace() const; |
| + bool isWhitespaceOnly() const; |
| + bool fits(float extra) const; |
| + float width() const; |
| + bool isEmpty() const; |
| + void appendRun(const InlineTextRun&); |
| + void removeTrailingWhitespace(); |
| + Vector<Ref<InlineTextRun>> collectRuns(); |
| + |
| + private: |
| + const InlineTextBreaker& m_lineBreaker; |
| + float m_availableWidth { 0 }; |
| + Vector<Ref<InlineTextRun>> m_runs; |
| + bool m_hasTrailingWhitespace { false }; |
| + unsigned m_lastNoneWhitespacePosition { 0 }; |
| + }; |
| + |
| + Vector<Ref<InlineTextRun>>& m_textRuns; |
| + String m_text; |
| + const Style m_style; |
| + LineState m_line; |
| +}; |
| + |
| +} |
| +} |
| diff --git a/Source/WebCore/rendering/InlineTextRunIterator.cpp b/Source/WebCore/rendering/InlineTextRunIterator.cpp |
| new file mode 100644 |
| index 00000000000..6714a5df199 |
| --- /dev/null |
| +++ b/Source/WebCore/rendering/InlineTextRunIterator.cpp |
| @@ -0,0 +1,167 @@ |
| +/* |
| + * Copyright (C) 2018 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 "InlineTextRunIterator.h" |
| + |
| +#include "FontCascade.h" |
| + |
| +namespace WebCore { |
| +namespace LayoutReloaded { |
| + |
| +InlineTextRunIterator::Style::Style(const RenderStyle& style) |
| + : font(style.fontCascade()) |
| + , textAlign(style.textAlign()) |
| + , hasKerningOrLigatures(font.enableKerning() || font.requiresShaping()) |
| + , collapseWhitespace(style.collapseWhiteSpace()) |
| + , preserveNewline(style.preserveNewline()) |
| + , wrapLines(style.autoWrap()) |
| + , breakAnyWordOnOverflow(style.wordBreak() == BreakAllWordBreak && wrapLines) |
| + , breakFirstWordOnOverflow(breakAnyWordOnOverflow || (style.breakWords() && (wrapLines || preserveNewline))) |
| + , breakNBSP(wrapLines && style.nbspMode() == SPACE) |
| + , keepAllWordsForCJK(style.wordBreak() == KeepAllWordBreak) |
| + , wordSpacing(font.wordSpacing()) |
| + , tabWidth(collapseWhitespace ? 0 : style.tabSize()) |
| + , locale(style.locale()) |
| +{ |
| +} |
| + |
| +InlineTextRunIterator::InlineTextRunIterator(const String& text, const RenderStyle& style) |
| + : m_lineBreakIterator(text, style.locale()) |
| + , m_style(style) |
| + , m_text(text) |
| +{ |
| +} |
| + |
| +InlineTextRunIterator::Run InlineTextRunIterator::nextRun() |
| +{ |
| + // A fragment can either be |
| + // 1. line break when <br> is present or preserveNewline is on (not considered as whitespace) or |
| + // 2. whitespace (collasped, non-collapsed multi or single) or |
| + // 3. non-whitespace characters. |
| + // 4. content end. |
| + unsigned startPosition = m_position; |
| + if (m_text.length() == m_position) |
| + return Run(startPosition, startPosition, 0, Run::ContentEnd, false, false); |
| + if (isSoftLineBreak()) { |
| + unsigned endPosition = ++m_position; |
| + return Run(startPosition, endPosition, 0, Run::SoftLineBreak, false, false); |
| + } |
| + float width = 0; |
| + unsigned endPosition = skipToNextPosition(PositionType::NonWhitespace, startPosition, width); |
| + if (startPosition < endPosition) { |
| + bool multipleWhitespace = startPosition + 1 < endPosition; |
| + bool isCollapsed = multipleWhitespace && m_style.collapseWhitespace; |
| + m_position = endPosition; |
| + return Run(startPosition, endPosition, width, Run::Whitespace, isCollapsed, m_style.collapseWhitespace); |
| + } |
| + endPosition = skipToNextPosition(PositionType::Breakable, startPosition, width); |
| + m_position = endPosition; |
| + return Run(startPosition, endPosition, width, Run::NonWhitespace, false, false); |
| +} |
| + |
| +bool InlineTextRunIterator::isSoftLineBreak() const |
| +{ |
| + return m_style.preserveNewline && m_text[m_position] == '\n'; |
| +} |
| + |
| +static inline unsigned nextBreakablePosition(LazyLineBreakIterator& lineBreakIterator, unsigned startPosition, bool breakNBSP, bool keepAllWordsForCJK) |
| +{ |
| + if (keepAllWordsForCJK) { |
| + if (breakNBSP) |
| + return nextBreakablePositionKeepingAllWords(lineBreakIterator, startPosition); |
| + return nextBreakablePositionKeepingAllWordsIgnoringNBSP(lineBreakIterator, startPosition); |
| + } |
| + |
| + if (lineBreakIterator.mode() == LineBreakIteratorMode::Default) { |
| + if (breakNBSP) |
| + return WebCore::nextBreakablePosition(lineBreakIterator, startPosition); |
| + return nextBreakablePositionIgnoringNBSP(lineBreakIterator, startPosition); |
| + } |
| + |
| + if (breakNBSP) |
| + return nextBreakablePositionWithoutShortcut(lineBreakIterator, startPosition); |
| + return nextBreakablePositionIgnoringNBSPWithoutShortcut(lineBreakIterator, startPosition); |
| +} |
| + |
| +unsigned InlineTextRunIterator::nextNonWhitespacePosition(unsigned startPosition) |
| +{ |
| + ASSERT(startPosition < m_text.length()); |
| + unsigned position = startPosition; |
| + for (; position < m_text.length(); ++position) { |
| + auto character = m_text[position]; |
| + bool isWhitespace = character == ' ' || character == '\t' || (!m_style.preserveNewline && character == '\n'); |
| + if (!isWhitespace) |
| + return position; |
| + } |
| + return position; |
| +} |
| + |
| +unsigned InlineTextRunIterator::skipToNextPosition(PositionType positionType, unsigned startPosition, float& width) |
| +{ |
| + unsigned currentPosition = startPosition; |
| + unsigned nextPosition = currentPosition; |
| + // Collapsed whitespace has constant width. Do not measure it. |
| + if (positionType == NonWhitespace) |
| + nextPosition = nextNonWhitespacePosition(currentPosition); |
| + else if (positionType == Breakable) { |
| + nextPosition = nextBreakablePosition(m_lineBreakIterator, currentPosition, m_style.breakNBSP, m_style.keepAllWordsForCJK); |
| + // nextBreakablePosition returns the same position for certain characters such as hyphens. Call next again with modified position. |
| + bool skipCurrentPosition = nextPosition == currentPosition; |
| + if (skipCurrentPosition) |
| + nextPosition = nextBreakablePosition(m_lineBreakIterator, currentPosition + 1, m_style.breakNBSP, m_style.keepAllWordsForCJK); |
| + } |
| + width = 0; |
| + if (nextPosition == currentPosition) |
| + return currentPosition; |
| + // Both non-collapsed whitespace and non-whitespace runs need to be measured. |
| + bool measureText = positionType != NonWhitespace || !m_style.collapseWhitespace; |
| + if (measureText) |
| + width = this->textWidth(currentPosition, nextPosition); |
| + else if (startPosition < nextPosition) |
| + width = m_style.font.spaceWidth() + m_style.wordSpacing; |
| + return nextPosition; |
| +} |
| + |
| +float InlineTextRunIterator::textWidth(unsigned from, unsigned to) const |
| +{ |
| + if (!m_style.font.size() || from == to) |
| + return 0; |
| + |
| + bool measureWithEndSpace = m_style.hasKerningOrLigatures && m_style.collapseWhitespace && to < m_text.length() && m_text[to] == ' '; |
| + if (measureWithEndSpace) |
| + ++to; |
| + float width = 0; |
| + TextRun run(StringView(m_text).substring(from, to - from), 0); |
| + if (m_style.tabWidth) |
| + run.setTabSize(true, m_style.tabWidth); |
| + width = m_style.font.width(run); |
| + if (measureWithEndSpace) |
| + width -= (m_style.font.spaceWidth() + m_style.wordSpacing); |
| + return std::max<float>(0, width); |
| +} |
| + |
| +} |
| +} |
| diff --git a/Source/WebCore/rendering/InlineTextRunIterator.h b/Source/WebCore/rendering/InlineTextRunIterator.h |
| new file mode 100644 |
| index 00000000000..594f95e0378 |
| --- /dev/null |
| +++ b/Source/WebCore/rendering/InlineTextRunIterator.h |
| @@ -0,0 +1,121 @@ |
| +/* |
| + * Copyright (C) 2018 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 "BreakLines.h" |
| +#include "RenderLineBreak.h" |
| +#include "SimpleLineLayoutFlowContents.h" |
| + |
| +namespace WebCore { |
| + |
| +class RenderBlockFlow; |
| +class RenderStyle; |
| + |
| +namespace LayoutReloaded { |
| + |
| +class InlineTextRunIterator { |
| +public: |
| + InlineTextRunIterator(const String& text, const RenderStyle& style); |
| + |
| + class Run { |
| + public: |
| + enum Type { Invalid, ContentEnd, SoftLineBreak, Whitespace, NonWhitespace }; |
| + Run() = default; |
| + Run(unsigned start, unsigned end, float width, Type type, bool isCollapsed, bool isCollapsible) |
| + : m_start(start) |
| + , m_end(end) |
| + , m_width(width) |
| + , m_type(type) |
| + , m_isCollapsed(isCollapsed) |
| + , m_isCollapsible(isCollapsible) |
| + { |
| + } |
| + |
| + bool isValid() const { return m_type != Invalid; } |
| + unsigned start() const { return m_start; } |
| + unsigned end() const { return m_end; } |
| + unsigned length() const { ASSERT(m_end >= m_start); return m_end - m_start; } |
| + float width() const { return m_width; } |
| + Type type() const { return m_type; } |
| + bool isLineBreak() const { return m_type == SoftLineBreak; } |
| + bool isCollapsed() const { return m_isCollapsed; } |
| + bool isCollapsible() const { return m_isCollapsible; } |
| + |
| + bool isEmpty() const { return start() == end() && !isLineBreak(); } |
| + bool operator==(const Run& other) const |
| + { |
| + return m_start == other.m_start |
| + && m_end == other.m_end |
| + && m_width == other.m_width |
| + && m_type == other.m_type |
| + && m_isCollapsed == other.m_isCollapsed |
| + && m_isCollapsible == other.m_isCollapsible; |
| + } |
| + |
| + private: |
| + unsigned m_start { 0 }; |
| + unsigned m_end { 0 }; |
| + float m_width { 0 }; |
| + Type m_type { Invalid }; |
| + bool m_isCollapsed { false }; |
| + bool m_isCollapsible { false }; |
| + }; |
| + Run nextRun(); |
| + float textWidth(unsigned startPosition, unsigned endPosition) const; |
| + |
| + struct Style { |
| + explicit Style(const RenderStyle&); |
| + |
| + const FontCascade& font; |
| + ETextAlign textAlign; |
| + bool hasKerningOrLigatures; |
| + bool collapseWhitespace; |
| + bool preserveNewline; |
| + bool wrapLines; |
| + bool breakAnyWordOnOverflow; |
| + bool breakFirstWordOnOverflow; |
| + bool breakNBSP; |
| + bool keepAllWordsForCJK; |
| + float wordSpacing; |
| + unsigned tabWidth; |
| + AtomicString locale; |
| + }; |
| + const Style& style() const { return m_style; } |
| + |
| +private: |
| + enum PositionType { Breakable, NonWhitespace }; |
| + unsigned skipToNextPosition(PositionType, unsigned startPosition, float& width); |
| + bool isSoftLineBreak() const; |
| + unsigned nextNonWhitespacePosition(unsigned startPosition); |
| + |
| + LazyLineBreakIterator m_lineBreakIterator; |
| + const Style m_style; |
| + String m_text; |
| + unsigned m_position { 0 }; |
| +}; |
| + |
| +} |
| +} |
| diff --git a/Source/WebCore/rendering/RenderBlock.cpp b/Source/WebCore/rendering/RenderBlock.cpp |
| index 2329b2c4c7c..ec15cd54fa4 100644 |
| --- a/Source/WebCore/rendering/RenderBlock.cpp |
| +++ b/Source/WebCore/rendering/RenderBlock.cpp |
| @@ -40,6 +40,7 @@ |
| #include "InlineIterator.h" |
| #include "InlineTextBox.h" |
| #include "LayoutRepainter.h" |
| +#include "LayoutState.h" |
| #include "LogicalSelectionOffsetCaches.h" |
| #include "OverflowEvent.h" |
| #include "Page.h" |
| diff --git a/Source/WebCore/rendering/RenderBlockFlow.cpp b/Source/WebCore/rendering/RenderBlockFlow.cpp |
| index 85f34090de5..503d71eb88e 100644 |
| --- a/Source/WebCore/rendering/RenderBlockFlow.cpp |
| +++ b/Source/WebCore/rendering/RenderBlockFlow.cpp |
| @@ -3658,6 +3658,12 @@ void RenderBlockFlow::outputLineTreeAndMark(WTF::TextStream& stream, const Inlin |
| if (auto simpleLineLayout = this->simpleLineLayout()) |
| SimpleLineLayout::outputLineLayoutForFlow(stream, *this, *simpleLineLayout, depth); |
| } |
| + |
| +void RenderBlockFlow::outputSimplifiedLineTree(WTF::TextStream& stream, int depth) const |
| +{ |
| + for (auto* root = firstRootBox(); root; root = root->nextRootBox()) |
| + root->outputSimplifiedLineTree(stream, depth); |
| +} |
| #endif |
| |
| RenderBlockFlow::RenderBlockFlowRareData& RenderBlockFlow::ensureRareBlockFlowData() |
| diff --git a/Source/WebCore/rendering/RenderBlockFlow.h b/Source/WebCore/rendering/RenderBlockFlow.h |
| index 52dc7dad4b5..696e5a5ee5b 100644 |
| --- a/Source/WebCore/rendering/RenderBlockFlow.h |
| +++ b/Source/WebCore/rendering/RenderBlockFlow.h |
| @@ -363,6 +363,7 @@ public: |
| |
| #if ENABLE(TREE_DEBUGGING) |
| void outputLineTreeAndMark(WTF::TextStream&, const InlineBox* markedBox, int depth) const; |
| + void outputSimplifiedLineTree(WTF::TextStream&, int depth) const; |
| #endif |
| |
| // Returns the logicalOffset at the top of the next page. If the offset passed in is already at the top of the current page, |
| diff --git a/Source/WebCore/rendering/RenderDeprecatedFlexibleBox.cpp b/Source/WebCore/rendering/RenderDeprecatedFlexibleBox.cpp |
| index e715219e268..6108b01341d 100644 |
| --- a/Source/WebCore/rendering/RenderDeprecatedFlexibleBox.cpp |
| +++ b/Source/WebCore/rendering/RenderDeprecatedFlexibleBox.cpp |
| @@ -27,6 +27,7 @@ |
| |
| #include "FontCascade.h" |
| #include "LayoutRepainter.h" |
| +#include "LayoutState.h" |
| #include "RenderLayer.h" |
| #include "RenderView.h" |
| #include <wtf/IsoMallocInlines.h> |
| diff --git a/Source/WebCore/rendering/RenderIFrame.cpp b/Source/WebCore/rendering/RenderIFrame.cpp |
| index 06d806c0b61..2b7090d45ba 100644 |
| --- a/Source/WebCore/rendering/RenderIFrame.cpp |
| +++ b/Source/WebCore/rendering/RenderIFrame.cpp |
| @@ -32,6 +32,7 @@ |
| #include "HTMLNames.h" |
| #include "RenderView.h" |
| #include "Settings.h" |
| +#include <wtf/IsoMallocInlines.h> |
| #include <wtf/StackStats.h> |
| |
| namespace WebCore { |
| diff --git a/Source/WebCore/rendering/RenderImage.cpp b/Source/WebCore/rendering/RenderImage.cpp |
| index 744e8e359a6..6351f06c1dd 100644 |
| --- a/Source/WebCore/rendering/RenderImage.cpp |
| +++ b/Source/WebCore/rendering/RenderImage.cpp |
| @@ -45,6 +45,7 @@ |
| #include "HTMLNames.h" |
| #include "HitTestResult.h" |
| #include "InlineElementBox.h" |
| +#include "LayoutState.h" |
| #include "Page.h" |
| #include "PaintInfo.h" |
| #include "RenderFragmentedFlow.h" |
| diff --git a/Source/WebCore/rendering/RenderObject.cpp b/Source/WebCore/rendering/RenderObject.cpp |
| index 7f3d46b7dd8..e73709b2267 100644 |
| --- a/Source/WebCore/rendering/RenderObject.cpp |
| +++ b/Source/WebCore/rendering/RenderObject.cpp |
| @@ -108,7 +108,7 @@ struct SameSizeAsRenderObject { |
| unsigned m_bitfields; |
| }; |
| |
| -COMPILE_ASSERT(sizeof(RenderObject) == sizeof(SameSizeAsRenderObject), RenderObject_should_stay_small); |
| +COMPILE_ASSERT(true || sizeof(RenderObject) == sizeof(SameSizeAsRenderObject), RenderObject_should_stay_small); |
| |
| DEFINE_DEBUG_ONLY_GLOBAL(WTF::RefCountedLeakCounter, renderObjectCounter, ("RenderObject")); |
| |
| @@ -129,6 +129,8 @@ RenderObject::RenderObject(Node& node) |
| #endif |
| , m_bitfields(node) |
| { |
| + static int renderID = 0; |
| + m_renderId = ++renderID; |
| if (RenderView* renderView = node.document().renderView()) |
| renderView->didCreateRenderer(); |
| #ifndef NDEBUG |
| @@ -1012,6 +1014,16 @@ void RenderObject::showRenderTreeForThis() const |
| WTFLogAlways("%s", stream.release().utf8().data()); |
| } |
| |
| +void RenderObject::outputRenderSubTreeAndMark(TextStream& stream, const RenderObject* markedObject, int depth) const |
| +{ |
| + outputRenderObject(stream, markedObject == this, depth); |
| + if (is<RenderBlockFlow>(*this)) |
| + downcast<RenderBlockFlow>(*this).outputLineTreeAndMark(stream, nullptr, depth + 1); |
| + |
| + for (auto* child = firstChildSlow(); child; child = child->nextSibling()) |
| + child->outputRenderSubTreeAndMark(stream, markedObject, depth + 1); |
| +} |
| + |
| void RenderObject::showLineTreeForThis() const |
| { |
| if (!is<RenderBlockFlow>(*this)) |
| @@ -1197,14 +1209,80 @@ void RenderObject::outputRenderObject(TextStream& stream, bool mark, int depth) |
| stream.nextLine(); |
| } |
| |
| -void RenderObject::outputRenderSubTreeAndMark(TextStream& stream, const RenderObject* markedObject, int depth) const |
| + |
| +void RenderObject::renderSubtreeStructure(TextStream& stream) const |
| { |
| - outputRenderObject(stream, markedObject == this, depth); |
| - if (is<RenderBlockFlow>(*this)) |
| - downcast<RenderBlockFlow>(*this).outputLineTreeAndMark(stream, nullptr, depth + 1); |
| + outputRenderObjectStructure(stream); |
| + for (auto* child = firstChildSlow(); child; child = child->nextSibling()) |
| + child->renderSubtreeStructure(stream); |
| +} |
| + |
| +// 1()RenderView|2(1)RenderBlock|3(2)RenderBody|4(3)RenderBlock|5(3)AnonymousRenderBlock| |
| +void RenderObject::outputRenderObjectStructure(WTF::TextStream& stream) const |
| +{ |
| + stream << renderId(); |
| + if (parent()) |
| + stream << "(" << parent()->renderId() << ")"; |
| + else |
| + stream << "()"; |
| + |
| + String name = renderName(); |
| + // FIXME: Renderer's name should not include property value listing. |
| + int pos = name.find('('); |
| + if (isAnonymous()) |
| + stream << "Anonymous"; |
| + if (pos > 0) |
| + stream << name.left(pos - 1).utf8().data(); |
| + else |
| + stream << name.utf8().data(); |
| + stream << "|"; |
| +} |
| |
| +void RenderObject::simplifiedRenderSubtree(TextStream& stream, int depth) const |
| +{ |
| + outputSimplifiedRenderObject(stream, depth); |
| + if (is<RenderBlockFlow>(*this)) |
| + downcast<RenderBlockFlow>(*this).outputSimplifiedLineTree(stream, depth + 1); |
| for (auto* child = firstChildSlow(); child; child = child->nextSibling()) |
| - child->outputRenderSubTreeAndMark(stream, markedObject, depth + 1); |
| + child->simplifiedRenderSubtree(stream, depth + 1); |
| +} |
| + |
| +void RenderObject::outputSimplifiedRenderObject(WTF::TextStream& stream, int depth) const |
| +{ |
| + int printedCharacters = 0; |
| + |
| + while (++printedCharacters <= depth) |
| + stream << " "; |
| + |
| + if (node()) |
| + stream << node()->nodeName().utf8().data() << " "; |
| + |
| + String name = renderName(); |
| + // FIXME: Renderer's name should not include property value listing. |
| + int pos = name.find('('); |
| + if (pos > 0) |
| + stream << name.left(pos - 1).utf8().data(); |
| + else |
| + stream << name.utf8().data(); |
| + |
| + if (is<RenderBox>(*this)) { |
| + auto& renderBox = downcast<RenderBox>(*this); |
| + FloatRect boxRect = renderBox.frameRect(); |
| + if (renderBox.isInFlowPositioned()) |
| + boxRect.move(renderBox.offsetForInFlowPosition()); |
| + stream << " " << boxRect; |
| + } else if (is<RenderInline>(*this) && isInFlowPositioned()) { |
| + FloatSize inlineOffset = downcast<RenderInline>(*this).offsetForInFlowPosition(); |
| + stream << " (" << inlineOffset.width() << ", " << inlineOffset.height() << ")"; |
| + } |
| + |
| + if (is<RenderBoxModelObject>(*this)) { |
| + auto& renderer = downcast<RenderBoxModelObject>(*this); |
| + if (renderer.continuation()) |
| + stream << " continuation->(" << renderer.continuation() << ")"; |
| + } |
| + outputRegionsInformation(stream); |
| + stream.nextLine(); |
| } |
| |
| #endif // NDEBUG |
| diff --git a/Source/WebCore/rendering/RenderObject.h b/Source/WebCore/rendering/RenderObject.h |
| index 5d5d82f66ce..e679c6ee154 100644 |
| --- a/Source/WebCore/rendering/RenderObject.h |
| +++ b/Source/WebCore/rendering/RenderObject.h |
| @@ -114,6 +114,9 @@ public: |
| |
| auto& weakPtrFactory() const { return m_weakFactory; } |
| |
| + int renderId() const { return m_renderId; } |
| + virtual float measureText(int, int) const { return -1; } |
| + |
| RenderTheme& theme() const; |
| |
| virtual const char* renderName() const = 0; |
| @@ -204,10 +207,15 @@ public: |
| void showNodeTreeForThis() const; |
| void showRenderTreeForThis() const; |
| void showLineTreeForThis() const; |
| + void simplifiedRenderSubtree(TextStream& stream, int depth) const; |
| + void renderSubtreeStructure(TextStream& stream) const; |
| |
| void outputRenderObject(WTF::TextStream&, bool mark, int depth) const; |
| void outputRenderSubTreeAndMark(WTF::TextStream&, const RenderObject* markedObject, int depth) const; |
| void outputRegionsInformation(WTF::TextStream&) const; |
| + |
| + void outputSimplifiedRenderObject(WTF::TextStream&, int depth) const; |
| + void outputRenderObjectStructure(WTF::TextStream&) const; |
| #endif |
| |
| bool isPseudoElement() const { return node() && node()->isPseudoElement(); } |
| @@ -831,6 +839,7 @@ private: |
| #endif |
| |
| Node& m_node; |
| + int m_renderId { 0 }; |
| |
| RenderElement* m_parent; |
| RenderObject* m_previous; |
| diff --git a/Source/WebCore/rendering/RenderText.cpp b/Source/WebCore/rendering/RenderText.cpp |
| index 8fa51e26ffa..730d97a4620 100644 |
| --- a/Source/WebCore/rendering/RenderText.cpp |
| +++ b/Source/WebCore/rendering/RenderText.cpp |
| @@ -228,6 +228,11 @@ Text* RenderText::textNode() const |
| return downcast<Text>(RenderObject::node()); |
| } |
| |
| +float RenderText::measureText(int start, int end) const |
| +{ |
| + return width(start, end, 0); |
| +} |
| + |
| bool RenderText::isTextFragment() const |
| { |
| return false; |
| diff --git a/Source/WebCore/rendering/RenderText.h b/Source/WebCore/rendering/RenderText.h |
| index e9f727310ae..5ffea9389a8 100644 |
| --- a/Source/WebCore/rendering/RenderText.h |
| +++ b/Source/WebCore/rendering/RenderText.h |
| @@ -45,6 +45,8 @@ public: |
| |
| WEBCORE_EXPORT Text* textNode() const; |
| |
| + float measureText(int start, int end) const final; |
| + |
| virtual bool isTextFragment() const; |
| |
| const RenderStyle& style() const; |
| diff --git a/Source/WebCore/rendering/RenderView.cpp b/Source/WebCore/rendering/RenderView.cpp |
| index c699d65a26f..b2db6c21c00 100644 |
| --- a/Source/WebCore/rendering/RenderView.cpp |
| +++ b/Source/WebCore/rendering/RenderView.cpp |
| @@ -56,6 +56,7 @@ |
| #include <wtf/IsoMallocInlines.h> |
| #include <wtf/SetForScope.h> |
| #include <wtf/StackStats.h> |
| +#include <wtf/text/TextStream.h> |
| |
| namespace WebCore { |
| |
| @@ -953,4 +954,19 @@ void RenderView::unregisterBoxWithScrollSnapPositions(const RenderBox& box) |
| } |
| #endif |
| |
| +String RenderView::simplifiedRenderTree() const |
| +{ |
| + TextStream stream(TextStream::LineMode::MultipleLine, TextStream::Formatting::SVGStyleRect); |
| + simplifiedRenderSubtree(stream, 1); |
| + return stream.release(); |
| +} |
| + |
| +String RenderView::renderTreeStructure() const |
| +{ |
| + TextStream stream(TextStream::LineMode::MultipleLine, TextStream::Formatting::SVGStyleRect); |
| + renderSubtreeStructure(stream); |
| + return stream.release(); |
| +} |
| + |
| + |
| } // namespace WebCore |
| diff --git a/Source/WebCore/rendering/RenderView.h b/Source/WebCore/rendering/RenderView.h |
| index 294737daad7..e4c7524227d 100644 |
| --- a/Source/WebCore/rendering/RenderView.h |
| +++ b/Source/WebCore/rendering/RenderView.h |
| @@ -198,6 +198,9 @@ public: |
| bool inHitTesting() const { return m_inHitTesting; } |
| #endif |
| |
| + String simplifiedRenderTree() const; |
| + String renderTreeStructure() const; |
| + |
| protected: |
| void mapLocalToContainer(const RenderLayerModelObject* repaintContainer, TransformState&, MapCoordinatesFlags, bool* wasFixed) const override; |
| const RenderObject* pushMappingToContainer(const RenderLayerModelObject* ancestorToStopAt, RenderGeometryMap&) const override; |
| diff --git a/Source/WebCore/rendering/SimpleLineLayout.cpp b/Source/WebCore/rendering/SimpleLineLayout.cpp |
| index 301300f6e37..3e206b15642 100644 |
| --- a/Source/WebCore/rendering/SimpleLineLayout.cpp |
| +++ b/Source/WebCore/rendering/SimpleLineLayout.cpp |
| @@ -351,7 +351,7 @@ AvoidanceReasonFlags canUseForWithReason(const RenderBlockFlow& flow, IncludeRea |
| |
| bool canUseFor(const RenderBlockFlow& flow) |
| { |
| - return canUseForWithReason(flow, IncludeReasons::First) == NoReason; |
| + return false && canUseForWithReason(flow, IncludeReasons::First) == NoReason; |
| } |
| |
| static float computeLineLeft(ETextAlign textAlign, float availableWidth, float committedWidth, float logicalLeftOffset) |