blob: 7c8ecbb0c4ad827c20cc48e960c36a223acb4fee [file] [log] [blame]
/*
* Copyright (C) 2004, 2008, 2009, 2010 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 COMPUTER, INC. ``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 COMPUTER, INC. OR
* 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 "core/editing/CaretBase.h"
#include "core/editing/EditingUtilities.h"
#include "core/editing/VisibleUnits.h"
#include "core/frame/FrameView.h"
#include "core/frame/Settings.h"
#include "core/layout/LayoutBlock.h"
#include "core/layout/LayoutView.h"
#include "core/layout/api/LayoutBlockItem.h"
#include "core/layout/api/LayoutItem.h"
#include "core/paint/PaintInfo.h"
#include "platform/graphics/GraphicsContext.h"
namespace blink {
CaretBase::CaretBase(CaretVisibility visibility)
: m_caretVisibility(visibility)
{
}
void CaretBase::clearCaretRect()
{
m_caretLocalRect = LayoutRect();
}
static inline bool caretRendersInsideNode(Node* node)
{
return node && !isDisplayInsideTable(node) && !editingIgnoresContent(node);
}
LayoutBlock* CaretBase::caretLayoutObject(Node* node)
{
if (!node)
return nullptr;
LayoutObject* layoutObject = node->layoutObject();
if (!layoutObject)
return nullptr;
// if caretNode is a block and caret is inside it then caret should be painted by that block
bool paintedByBlock = layoutObject->isLayoutBlock() && caretRendersInsideNode(node);
return paintedByBlock ? toLayoutBlock(layoutObject) : layoutObject->containingBlock();
}
static void mapCaretRectToCaretPainter(LayoutItem caretLayoutItem, LayoutBlockItem caretPainterItem, LayoutRect& caretRect)
{
// FIXME: This shouldn't be called on un-rooted subtrees.
// FIXME: This should probably just use mapLocalToAncestor.
// Compute an offset between the caretLayoutItem and the caretPainterItem.
ASSERT(caretLayoutItem.isDescendantOf(caretPainterItem));
bool unrooted = false;
while (caretLayoutItem != caretPainterItem) {
LayoutItem containerItem = caretLayoutItem.container();
if (containerItem.isNull()) {
unrooted = true;
break;
}
caretRect.move(caretLayoutItem.offsetFromContainer(containerItem));
caretLayoutItem = containerItem;
}
if (unrooted)
caretRect = LayoutRect();
}
bool CaretBase::updateCaretRect(const PositionWithAffinity& caretPosition)
{
m_caretLocalRect = LayoutRect();
if (caretPosition.position().isNull())
return false;
ASSERT(caretPosition.position().anchorNode()->layoutObject());
// First compute a rect local to the layoutObject at the selection start.
LayoutObject* layoutObject;
m_caretLocalRect = localCaretRectOfPosition(caretPosition, layoutObject);
// Get the layoutObject that will be responsible for painting the caret
// (which is either the layoutObject we just found, or one of its containers).
LayoutBlockItem caretPainterItem = LayoutBlockItem(caretLayoutObject(caretPosition.position().anchorNode()));
mapCaretRectToCaretPainter(LayoutItem(layoutObject), caretPainterItem, m_caretLocalRect);
return true;
}
bool CaretBase::updateCaretRect(const VisiblePosition& caretPosition)
{
return updateCaretRect(caretPosition.toPositionWithAffinity());
}
IntRect CaretBase::absoluteBoundsForLocalRect(Node* node, const LayoutRect& rect) const
{
LayoutBlock* caretPainter = caretLayoutObject(node);
if (!caretPainter)
return IntRect();
LayoutRect localRect(rect);
caretPainter->flipForWritingMode(localRect);
return caretPainter->localToAbsoluteQuad(FloatRect(localRect)).enclosingBoundingBox();
}
void CaretBase::invalidateLocalCaretRect(Node* node, const LayoutRect& rect)
{
LayoutBlockItem caretPainter = LayoutBlockItem(caretLayoutObject(node));
if (!caretPainter)
return;
// FIXME: Need to over-paint 1 pixel to workaround some rounding problems.
// https://bugs.webkit.org/show_bug.cgi?id=108283
LayoutRect inflatedRect = rect;
inflatedRect.inflate(LayoutUnit(1));
// FIXME: We should use mapLocalToAncestor() since we know we're not un-rooted.
mapCaretRectToCaretPainter(LayoutItem(node->layoutObject()), caretPainter, inflatedRect);
// FIXME: We should not allow paint invalidation out of paint invalidation state. crbug.com/457415
DisablePaintInvalidationStateAsserts disabler;
caretPainter.invalidatePaintRectangle(inflatedRect);
}
bool CaretBase::shouldRepaintCaret(Node& node) const
{
// If PositionAnchorType::BeforeAnchor or PositionAnchorType::AfterAnchor,
// carets need to be repainted not only when the node is contentEditable but
// also when its parentNode() is contentEditable.
return node.isContentEditable() || (node.parentNode() && node.parentNode()->isContentEditable());
}
bool CaretBase::shouldRepaintCaret(const LayoutView* view) const
{
ASSERT(view);
if (FrameView* frameView = view->frameView()) {
LocalFrame& frame = frameView->frame(); // The frame where the selection started
return frame.settings() && frame.settings()->caretBrowsingEnabled();
}
return false;
}
void CaretBase::invalidateCaretRect(Node* node, bool caretRectChanged)
{
if (caretRectChanged)
return;
if (LayoutView* view = node->document().layoutView()) {
if (node->isContentEditable(Node::UserSelectAllIsAlwaysNonEditable) || shouldRepaintCaret(view))
invalidateLocalCaretRect(node, localCaretRectWithoutUpdate());
}
}
void CaretBase::paintCaret(Node* node, GraphicsContext& context, const LayoutPoint& paintOffset) const
{
if (m_caretVisibility == CaretVisibility::Hidden)
return;
LayoutRect drawingRect = localCaretRectWithoutUpdate();
if (LayoutBlock* layoutObject = caretLayoutObject(node))
layoutObject->flipForWritingMode(drawingRect);
drawingRect.moveBy(roundedIntPoint(paintOffset));
Color caretColor = Color::black;
Element* element;
if (node->isElementNode())
element = toElement(node);
else
element = node->parentElement();
if (element && element->layoutObject())
caretColor = element->layoutObject()->resolveColor(CSSPropertyColor);
context.fillRect(FloatRect(drawingRect), caretColor);
}
} // namespace blink