Bug 1413361: EnsureFrameForTextNode shouldn't reconstruct synchronously without up-to-date styles. r=bz

MozReview-Commit-ID: IzpcdCdcPjr
This commit is contained in:
Emilio Cobos Álvarez 2017-11-01 13:00:04 +01:00
parent e333168b51
commit 75916bf75c
6 changed files with 66 additions and 42 deletions

View File

@ -3212,21 +3212,26 @@ static void ExtractRectFromOffset(nsIFrame* aFrame,
static nsTextFrame*
GetTextFrameForContent(nsIContent* aContent, bool aFlushLayout)
{
nsIPresShell* presShell = aContent->OwnerDoc()->GetShell();
if (presShell) {
presShell->FrameConstructor()->EnsureFrameForTextNode(
static_cast<nsGenericDOMDataNode*>(aContent));
if (aFlushLayout) {
aContent->OwnerDoc()->FlushPendingNotifications(FlushType::Layout);
}
nsIFrame* frame = aContent->GetPrimaryFrame();
if (frame && frame->IsTextFrame()) {
return static_cast<nsTextFrame*>(frame);
}
nsIDocument* doc = aContent->OwnerDoc();
nsIPresShell* presShell = doc->GetShell();
if (!presShell) {
return nullptr;
}
return nullptr;
const bool frameWillBeUnsuppressed =
presShell->FrameConstructor()->EnsureFrameForTextNodeIsCreatedAfterFlush(
static_cast<nsGenericDOMDataNode*>(aContent));
if (aFlushLayout) {
doc->FlushPendingNotifications(FlushType::Layout);
} else if (frameWillBeUnsuppressed) {
doc->FlushPendingNotifications(FlushType::Frames);
}
nsIFrame* frame = aContent->GetPrimaryFrame();
if (!frame || !frame->IsTextFrame()) {
return nullptr;
}
return static_cast<nsTextFrame*>(frame);
}
static nsresult GetPartialTextRect(nsLayoutUtils::RectCallback* aCallback,

View File

@ -35,18 +35,18 @@ static nsIFrame*
GetFrameForNode(nsINode* aNode, GeometryNodeType aType)
{
nsIDocument* doc = aNode->OwnerDoc();
doc->FlushPendingNotifications(FlushType::Layout);
switch (aType) {
case GEOMETRY_NODE_ELEMENT:
return aNode->AsContent()->GetPrimaryFrame();
case GEOMETRY_NODE_TEXT: {
nsIPresShell* presShell = doc->GetShell();
if (presShell) {
return presShell->FrameConstructor()->EnsureFrameForTextNode(
if (aType == GEOMETRY_NODE_TEXT) {
if (nsIPresShell* shell = doc->GetShell()) {
shell->FrameConstructor()->EnsureFrameForTextNodeIsCreatedAfterFlush(
static_cast<nsGenericDOMDataNode*>(aNode));
}
return nullptr;
}
doc->FlushPendingNotifications(FlushType::Layout);
switch (aType) {
case GEOMETRY_NODE_TEXT:
case GEOMETRY_NODE_ELEMENT:
return aNode->AsContent()->GetPrimaryFrame();
case GEOMETRY_NODE_DOCUMENT: {
nsIPresShell* presShell = doc->GetShell();
return presShell ? presShell->GetRootFrame() : nullptr;

View File

@ -8955,22 +8955,31 @@ InvalidateCanvasIfNeeded(nsIPresShell* presShell, nsIContent* node)
rootFrame->InvalidateFrameSubtree();
}
nsIFrame*
nsCSSFrameConstructor::EnsureFrameForTextNode(nsGenericDOMDataNode* aContent)
bool
nsCSSFrameConstructor::EnsureFrameForTextNodeIsCreatedAfterFlush(
nsGenericDOMDataNode* aContent)
{
if (aContent->HasFlag(NS_CREATE_FRAME_IF_NON_WHITESPACE) &&
!mAlwaysCreateFramesForIgnorableWhitespace) {
// Text frame may have been suppressed. Disable suppression and signal
// that a flush should be performed. We do this on a document-wide
// basis so that pages that repeatedly query metrics for
// collapsed-whitespace text nodes don't trigger pathological behavior.
mAlwaysCreateFramesForIgnorableWhitespace = true;
nsAutoScriptBlocker blocker;
BeginUpdate();
ReconstructDocElementHierarchy(InsertionKind::Sync);
EndUpdate();
if (!aContent->HasFlag(NS_CREATE_FRAME_IF_NON_WHITESPACE)) {
return false;
}
return aContent->GetPrimaryFrame();
if (mAlwaysCreateFramesForIgnorableWhitespace) {
return false;
}
// Text frame may have been suppressed. Disable suppression and signal that a
// flush should be performed. We do this on a document-wide basis so that
// pages that repeatedly query metrics for collapsed-whitespace text nodes
// don't trigger pathological behavior.
mAlwaysCreateFramesForIgnorableWhitespace = true;
Element* root = mDocument->GetRootElement();
if (!root) {
return false;
}
RestyleManager()->PostRestyleEvent(
root, nsRestyleHint(0), nsChangeHint_ReconstructFrame);
return true;
}
void

View File

@ -317,11 +317,12 @@ public:
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);
// whitespace next to a block boundary (or for some other reason), ensure that
// a frame for it is created the next time frames are flushed, if it can
// possibly have a frame at all.
//
// Returns whether there are chances for the frame to be unsuppressed.
bool EnsureFrameForTextNodeIsCreatedAfterFlush(nsGenericDOMDataNode* aContent);
// Generate the child frames and process bindings
void GenerateChildFrames(nsContainerFrame* aFrame);

View File

@ -0,0 +1,8 @@
<script>
function go() {
window.getSelection().selectAllChildren(document.body.firstChild);
document.createElement("body").aLink = "-moz-mac-menuselect";
window.getSelection().getRangeAt(0).getClientRects();
}
</script>
<body onload=go()>

View File

@ -259,3 +259,4 @@ load 1410226-2.html
load 1411143.html
load 1411478.html
load 1413288.html
load 1413361.html