mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-28 21:28:55 +00:00
Bug 918185. When Range.getBoundingClientRect/getClientRects needs a textframe for a node, create one if the textframe was optimized away. r=bz
This commit is contained in:
parent
9b8ce9a9f5
commit
81d19c4037
@ -50,7 +50,11 @@ enum {
|
||||
// This bit is only meaningful if the NS_CACHED_TEXT_IS_ONLY_WHITESPACE
|
||||
// bit is set, and if so it indicates whether we're only whitespace or
|
||||
// not.
|
||||
NS_TEXT_IS_ONLY_WHITESPACE = DATA_NODE_FLAG_BIT(3)
|
||||
NS_TEXT_IS_ONLY_WHITESPACE = DATA_NODE_FLAG_BIT(3),
|
||||
|
||||
// This bit is set to indicate that we must create frames for this text node
|
||||
// even if it's only whitespace next to a block boundary.
|
||||
NS_CREATE_FRAME_FOR_IGNORABLE_WHITESPACE = DATA_NODE_FLAG_BIT(4)
|
||||
};
|
||||
|
||||
// Make sure we have enough space for those bits
|
||||
|
@ -32,6 +32,7 @@
|
||||
#include "mozilla/dom/RangeBinding.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
#include "mozilla/Likely.h"
|
||||
#include "nsCSSFrameConstructor.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
@ -2747,12 +2748,25 @@ static void ExtractRectFromOffset(nsIFrame* aFrame,
|
||||
}
|
||||
}
|
||||
|
||||
static nsTextFrame*
|
||||
GetTextFrameForContent(nsIContent* aContent)
|
||||
{
|
||||
nsIPresShell* presShell = aContent->OwnerDoc()->GetShell();
|
||||
if (presShell) {
|
||||
nsIFrame* frame = presShell->FrameConstructor()->EnsureFrameForTextNode(
|
||||
static_cast<nsGenericDOMDataNode*>(aContent));
|
||||
if (frame && frame->GetType() == nsGkAtoms::textFrame) {
|
||||
return static_cast<nsTextFrame*>(frame);
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static nsresult GetPartialTextRect(nsLayoutUtils::RectCallback* aCallback,
|
||||
nsIContent* aContent, int32_t aStartOffset, int32_t aEndOffset)
|
||||
{
|
||||
nsIFrame* frame = aContent->GetPrimaryFrame();
|
||||
if (frame && frame->GetType() == nsGkAtoms::textFrame) {
|
||||
nsTextFrame* textFrame = static_cast<nsTextFrame*>(frame);
|
||||
nsTextFrame* textFrame = GetTextFrameForContent(aContent);
|
||||
if (textFrame) {
|
||||
nsIFrame* relativeTo = nsLayoutUtils::GetContainingBlockForClientRect(textFrame);
|
||||
for (nsTextFrame* f = textFrame; f; f = static_cast<nsTextFrame*>(f->GetNextContinuation())) {
|
||||
int32_t fstart = f->GetContentOffset(), fend = f->GetContentEnd();
|
||||
@ -2808,9 +2822,8 @@ static void CollectClientRects(nsLayoutUtils::RectCallback* aCollector,
|
||||
// the range is collapsed, only continue if the cursor is in a text node
|
||||
nsCOMPtr<nsIContent> content = do_QueryInterface(aStartParent);
|
||||
if (content && content->IsNodeOfType(nsINode::eTEXT)) {
|
||||
nsIFrame* frame = content->GetPrimaryFrame();
|
||||
if (frame && frame->GetType() == nsGkAtoms::textFrame) {
|
||||
nsTextFrame* textFrame = static_cast<nsTextFrame*>(frame);
|
||||
nsTextFrame* textFrame = GetTextFrameForContent(content);
|
||||
if (textFrame) {
|
||||
int32_t outOffset;
|
||||
nsIFrame* outFrame;
|
||||
textFrame->GetChildFrameContainingOffset(aStartOffset, false,
|
||||
|
@ -5433,12 +5433,16 @@ nsCSSFrameConstructor::ConstructFramesFromItem(nsFrameConstructorState& aState,
|
||||
// due to dynamic changes.
|
||||
// We don't do it for SVG text, since we might need to position and
|
||||
// measure the white space glyphs due to x/y/dx/dy attributes.
|
||||
// We check that NS_CREATE_FRAME_FOR_IGNORABLE_WHITESPACE is not set on
|
||||
// the node. This lets us disable this optimization for specific nodes
|
||||
// (e.g. nodes whose geometry is being queried via DOM APIs).
|
||||
if (AtLineBoundary(aIter) &&
|
||||
!styleContext->StyleText()->WhiteSpaceOrNewlineIsSignificant() &&
|
||||
aIter.List()->ParentHasNoXBLChildren() &&
|
||||
!(aState.mAdditionalStateBits & NS_FRAME_GENERATED_CONTENT) &&
|
||||
(item.mFCData->mBits & FCDATA_IS_LINE_PARTICIPANT) &&
|
||||
!(item.mFCData->mBits & FCDATA_IS_SVG_TEXT) &&
|
||||
!item.mContent->HasFlag(NS_CREATE_FRAME_FOR_IGNORABLE_WHITESPACE) &&
|
||||
item.IsWhitespace(aState))
|
||||
return;
|
||||
|
||||
@ -7613,6 +7617,22 @@ InvalidateCanvasIfNeeded(nsIPresShell* presShell, nsIContent* node)
|
||||
rootFrame->InvalidateFrameSubtree();
|
||||
}
|
||||
|
||||
nsIFrame*
|
||||
nsCSSFrameConstructor::EnsureFrameForTextNode(nsGenericDOMDataNode* aContent)
|
||||
{
|
||||
if (aContent->HasFlag(NS_CREATE_FRAME_IF_NON_WHITESPACE) &&
|
||||
!aContent->HasFlag(NS_CREATE_FRAME_FOR_IGNORABLE_WHITESPACE)) {
|
||||
// Text frame may have been suppressed. Disable suppression and signal
|
||||
// that a flush should be performed.
|
||||
aContent->SetFlags(NS_CREATE_FRAME_FOR_IGNORABLE_WHITESPACE);
|
||||
nsAutoScriptBlocker blocker;
|
||||
BeginUpdate();
|
||||
RecreateFramesForContent(aContent, false);
|
||||
EndUpdate();
|
||||
}
|
||||
return aContent->GetPrimaryFrame();
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsCSSFrameConstructor::CharacterDataChanged(nsIContent* aContent,
|
||||
CharacterDataChangeInfo* aInfo)
|
||||
|
@ -31,6 +31,7 @@ struct nsGenConInitializer;
|
||||
class nsICSSAnonBoxPseudo;
|
||||
class nsPageContentFrame;
|
||||
struct PendingBinding;
|
||||
class nsGenericDOMDataNode;
|
||||
|
||||
class nsFrameConstructorState;
|
||||
class nsFrameConstructorSaveState;
|
||||
@ -198,6 +199,13 @@ public:
|
||||
nsresult CharacterDataChanged(nsIContent* aContent,
|
||||
CharacterDataChangeInfo* aInfo);
|
||||
|
||||
// If aContent is a text node that has been optimized away due to being
|
||||
// whitespace next to a block boundary (or for some other reason), stop
|
||||
// doing that and create a frame for it if it should have one. This recreates
|
||||
// frames so be careful (although this should not change actual layout).
|
||||
// Returns the frame for aContent if there is one.
|
||||
nsIFrame* EnsureFrameForTextNode(nsGenericDOMDataNode* aContent);
|
||||
|
||||
// generate the child frames and process bindings
|
||||
nsresult GenerateChildFrames(nsIFrame* aFrame);
|
||||
|
||||
|
@ -143,6 +143,7 @@ MOCHITEST_FILES = \
|
||||
bug851445_helper.html \
|
||||
test_emulateMedium.html \
|
||||
enableTestPlugin.js \
|
||||
test_getClientRects_emptytext.html \
|
||||
$(NULL)
|
||||
|
||||
ifeq (,$(filter gonk,$(MOZ_WIDGET_TOOLKIT)))
|
||||
|
26
layout/base/tests/test_getClientRects_emptytext.html
Normal file
26
layout/base/tests/test_getClientRects_emptytext.html
Normal file
@ -0,0 +1,26 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
</div>
|
||||
<div id="testDiv"> </div>
|
||||
<script>
|
||||
var textNode = testDiv.firstChild;
|
||||
var range = new Range();
|
||||
range.selectNodeContents(textNode);
|
||||
is(range.getClientRects().length, 1, "Text node should have a rectangle");
|
||||
var rect = range.getClientRects()[0];
|
||||
ok(rect.left > 0, "Rectangle x should be greater than zero");
|
||||
ok(rect.top > 0, "Rectangle y should be greater than zero");
|
||||
is(rect.width, 0, "Rectangle should be zero width");
|
||||
is(rect.height, 0, "Rectangle should be zero height");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
Loading…
x
Reference in New Issue
Block a user