blob: 53829b1481da6d4726c89f3e70c1252aaa395582 [file] [log] [blame]
/*
* (C) 1999 Lars Knoll ([email protected])
* (C) 2000 Dirk Mueller ([email protected])
* 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
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
#include "config.h"
#include "LegacyInlineTextBox.h"
#include "BreakablePositions.h"
#include "CompositionHighlight.h"
#include "DashArray.h"
#include "Document.h"
#include "DocumentMarkerController.h"
#include "Editor.h"
#include "ElementRuleCollector.h"
#include "EventRegion.h"
#include "FloatRoundedRect.h"
#include "GlyphDisplayListCache.h"
#include "GraphicsContext.h"
#include "HitTestResult.h"
#include "ImageBuffer.h"
#include "InlineIteratorBoxInlines.h"
#include "InlineIteratorTextBox.h"
#include "InlineIteratorTextBoxInlines.h"
#include "InlineTextBoxStyle.h"
#include "LocalFrame.h"
#include "Page.h"
#include "RenderBlock.h"
#include "RenderCombineText.h"
#include "RenderElementInlines.h"
#include "RenderHighlight.h"
#include "RenderLineBreak.h"
#include "RenderObjectStyle.h"
#include "RenderSVGInlineText.h"
#include "RenderStyle+GettersInlines.h"
#include "RenderTheme.h"
#include "RenderView.h"
#include "RenderedDocumentMarker.h"
#include "Settings.h"
#include "StyledMarkedText.h"
#include "Text.h"
#include "TextBoxPainter.h"
#include "TextBoxSelectableRange.h"
#include <stdio.h>
#include <wtf/TZoneMallocInlines.h>
#include <wtf/text/CString.h>
#include <wtf/text/TextStream.h>
namespace WebCore {
WTF_MAKE_TZONE_ALLOCATED_IMPL(LegacyInlineTextBox);
struct SameSizeAsLegacyInlineTextBox : public LegacyInlineBox {
void* pointers[2];
unsigned variables[2];
};
static_assert(sizeof(LegacyInlineTextBox) == sizeof(SameSizeAsLegacyInlineTextBox), "LegacyInlineTextBox should stay small");
typedef HashMap<const LegacyInlineTextBox*, LayoutRect> LegacyInlineTextBoxOverflowMap;
static LegacyInlineTextBoxOverflowMap* gTextBoxesWithOverflow;
LegacyInlineTextBox::LegacyInlineTextBox(RenderSVGInlineText& renderer)
: LegacyInlineBox(renderer)
{
}
LegacyInlineTextBox::~LegacyInlineTextBox()
{
if (!knownToHaveNoOverflow() && gTextBoxesWithOverflow)
gTextBoxesWithOverflow->remove(this);
if (isInGlyphDisplayListCache())
removeBoxFromGlyphDisplayListCache(*this);
}
RenderSVGInlineText& LegacyInlineTextBox::renderer() const
{
return downcast<RenderSVGInlineText>(LegacyInlineBox::renderer());
}
const RenderStyle& LegacyInlineTextBox::lineStyle() const
{
return isFirstLine() ? renderer().firstLineStyle() : renderer().style();
}
bool LegacyInlineTextBox::hasTextContent() const
{
return m_len;
}
void LegacyInlineTextBox::markDirty(bool dirty)
{
if (dirty) {
m_len = 0;
m_start = 0;
}
LegacyInlineBox::markDirty(dirty);
}
void LegacyInlineTextBox::setLogicalOverflowRect(const LayoutRect& rect)
{
ASSERT(!knownToHaveNoOverflow());
if (!gTextBoxesWithOverflow)
gTextBoxesWithOverflow = new LegacyInlineTextBoxOverflowMap;
gTextBoxesWithOverflow->add(this, rect);
}
LayoutUnit LegacyInlineTextBox::selectionTop() const
{
return root().selectionTop();
}
LayoutUnit LegacyInlineTextBox::selectionBottom() const
{
return root().selectionBottom();
}
LayoutUnit LegacyInlineTextBox::selectionHeight() const
{
return root().selectionHeight();
}
RenderObject::HighlightState LegacyInlineTextBox::selectionState() const
{
return renderer().view().selection().highlightStateForTextBox(renderer(), selectableRange());
}
const FontCascade& LegacyInlineTextBox::lineFont() const
{
return lineStyle().fontCascade();
}
LayoutRect snappedSelectionRect(const LayoutRect& selectionRect, float logicalRight, WritingMode writingMode)
{
auto snappedSelectionRect = enclosingIntRect(selectionRect);
LayoutUnit logicalWidth = snappedSelectionRect.width();
if (snappedSelectionRect.x() > logicalRight)
logicalWidth = 0;
else if (snappedSelectionRect.maxX() > logicalRight)
logicalWidth = logicalRight - snappedSelectionRect.x();
if (writingMode.isHorizontal()) {
return {
snappedSelectionRect.x(), selectionRect.y(),
logicalWidth, selectionRect.height(),
};
}
return {
selectionRect.y(), snappedSelectionRect.x(),
selectionRect.height(), logicalWidth,
};
}
LayoutRect LegacyInlineTextBox::localSelectionRect(unsigned startPos, unsigned endPos) const
{
auto [clampedStart, clampedEnd] = selectableRange().clamp(startPos, endPos);
if (clampedStart >= clampedEnd && !(startPos == endPos && startPos >= start() && startPos <= (start() + len())))
return { };
TextRun textRun = createTextRun();
auto writingMode = renderer().writingMode();
auto width = LayoutUnit { logicalWidth() };
LayoutRect selectionRect { 0, this->selectionTop(), width, this->selectionHeight() };
// Avoid measuring the text when the entire line box is selected as an optimization.
if (clampedStart || clampedEnd != textRun.length())
lineFont().adjustSelectionRectForText(renderer().canUseSimplifiedTextMeasuring().value_or(false), textRun, selectionRect, clampedStart, clampedEnd);
if (!writingMode.isLogicalLeftLineLeft())
selectionRect.setX(width - selectionRect.x());
selectionRect.move(logicalLeft(), 0);
// FIXME: The computation of the snapped selection rect differs from the computation of this rect
// in paintMarkedTextBackground(). See <https://bugs.webkit.org/show_bug.cgi?id=138913>.
return snappedSelectionRect(selectionRect, logicalRight(), writingMode);
}
void LegacyInlineTextBox::deleteLine()
{
renderer().removeTextBox(*this);
delete this;
}
bool LegacyInlineTextBox::isLineBreak() const
{
return renderer().style().preserveNewline() && len() == 1 && renderer().text()[start()] == '\n';
}
TextBoxSelectableRange LegacyInlineTextBox::selectableRange() const
{
// Fix up the offset if we are combined text because we manage these embellishments.
// That is, they are not reflected in renderer().text(). We treat combined text as a single unit.
return {
m_start,
m_len,
0u,
isLineBreak()
};
}
std::pair<unsigned, unsigned> LegacyInlineTextBox::selectionStartEnd() const
{
return renderer().view().selection().rangeForTextBox(renderer(), selectableRange());
}
bool LegacyInlineTextBox::hasMarkers() const
{
return MarkedText::collectForDocumentMarkers(renderer(), selectableRange(), MarkedText::PaintPhase::Decoration).size();
}
int LegacyInlineTextBox::caretMinOffset() const
{
return m_start;
}
int LegacyInlineTextBox::caretMaxOffset() const
{
return m_start + m_len;
}
float LegacyInlineTextBox::textPos() const
{
// When computing the width of a text run, RenderBlock::computeInlineDirectionPositionsForLine() doesn't include the actual offset
// from the containing block edge in its measurement. textPos() should be consistent so the text are rendered in the same width.
if (!logicalLeft())
return 0;
return logicalLeft() - root().logicalLeft();
}
TextRun LegacyInlineTextBox::createTextRun() const
{
const auto& style = lineStyle();
TextRun textRun { text(), textPos(), 0, ExpansionBehavior::forbidAll(), direction(), style.rtlOrdering() == Order::Visual, !renderer().canUseSimpleFontCodePath() };
textRun.setTabSize(!style.collapseWhiteSpace(), Style::toPlatform(style.tabSize()));
return textRun;
}
String LegacyInlineTextBox::text() const
{
String result = renderer().text().substring(m_start, m_len);
// This works because this replacement doesn't affect string indices. We're replacing a single Unicode code unit with another Unicode code unit.
// How convenient.
return RenderBlock::updateSecurityDiscCharacters(lineStyle(), WTF::move(result));
}
#if ENABLE(TREE_DEBUGGING)
ASCIILiteral LegacyInlineTextBox::boxName() const
{
return "InlineTextBox"_s;
}
void LegacyInlineTextBox::outputLineBox(TextStream& stream, bool mark, int depth) const
{
stream << "-------- "_s << (isDirty() ? "D"_s : "-"_s) << "-"_s;
int printedCharacters = 0;
if (mark) {
stream << "*"_s;
++printedCharacters;
}
while (++printedCharacters <= depth * 2)
stream << " "_s;
String value = renderer().text();
value = value.substring(start(), len());
value = makeStringByReplacingAll(value, '\\', "\\\\"_s);
value = makeStringByReplacingAll(value, '\n', "\\n"_s);
stream << boxName() << " "_s << FloatRect(x(), y(), width(), height()) << " ("_s << this << ") renderer->("_s << &renderer() << ") run("_s << start() << ", "_s << start() + len() << ") \""_s << value.utf8().data() << '"';
stream.nextLine();
}
#endif
} // namespace WebCore