ReResolveStyleContext needs to deal with :before/:after pseudos correctly. Bug

126072, r=dbaron, sr=roc+moz.  Also fixes bug 141259 (incorrect warnings about
style tree integrity).
This commit is contained in:
bzbarsky%mit.edu 2003-01-05 05:05:17 +00:00
Родитель 48739cd3f1
Коммит 8c8465ff11
20 изменённых файлов: 839 добавлений и 212 удалений

Просмотреть файл

@ -10336,6 +10336,22 @@ nsCSSFrameConstructor::ProcessRestyledFrames(nsStyleChangeList& aChangeList,
ApplyRenderingChangeToTree(aPresContext, frame, nsnull, hint);
}
}
#ifdef DEBUG
// reget from content since it may have been regenerated...
if (content) {
nsIFrame* frame;
nsCOMPtr<nsIPresShell> shell;
aPresContext->GetShell(getter_AddRefs(shell));
shell->GetPrimaryFrameFor(content, &frame);
if (frame) {
nsCOMPtr<nsIFrameManager> frameManager;
shell->GetFrameManager(getter_AddRefs(frameManager));
frameManager->DebugVerifyStyleTree(aPresContext, frame);
}
} else {
NS_WARNING("Unable to test style tree integrity -- no content node");
}
#endif
}
aChangeList.Clear();
return NS_OK;

Просмотреть файл

@ -45,6 +45,7 @@
#include "nsPlaceholderFrame.h"
#include "nsLayoutAtoms.h"
#include "nsCSSAnonBoxes.h"
#include "nsCSSPseudoElements.h"
#include "nsHTMLAtoms.h"
#ifdef NS_DEBUG
#include "nsISupportsArray.h"
@ -75,6 +76,7 @@
#include "nsPrintfCString.h"
#include "nsDummyLayoutRequest.h"
#include "nsLayoutErrors.h"
#include "nsLayoutUtils.h"
#ifdef DEBUG
//#define NOISY_DEBUG
@ -1742,9 +1744,26 @@ FrameManager::ReResolveStyleContext(nsIPresContext* aPresContext,
else if (pseudoTag) {
nsIContent* pseudoContent =
aParentContent ? aParentContent : localContent;
aPresContext->ResolvePseudoStyleContextFor(pseudoContent, pseudoTag,
if (pseudoTag == nsCSSPseudoElements::before ||
pseudoTag == nsCSSPseudoElements::after) {
// XXX what other pseudos do we need to treat like this?
aPresContext->ProbePseudoStyleContextFor(pseudoContent, pseudoTag,
parentContext,
&newContext);
if (!newContext) {
// This pseudo should no longer exist; gotta reframe
NS_UpdateHint(aMinChange, nsChangeHint_ReconstructFrame);
aChangeList.AppendChange(aFrame, pseudoContent,
nsChangeHint_ReconstructFrame);
// We're reframing anyway; just keep the same context
newContext = oldContext;
NS_ADDREF(newContext);
}
} else {
aPresContext->ResolvePseudoStyleContextFor(pseudoContent, pseudoTag,
parentContext,
&newContext);
}
NS_RELEASE(pseudoTag);
}
else {
@ -1895,6 +1914,67 @@ FrameManager::ReResolveStyleContext(nsIPresContext* aPresContext,
aResultChange = aMinChange;
if (!(aMinChange & (nsChangeHint_ReconstructFrame | nsChangeHint_ReconstructDoc))) {
if (localContent && localContent->IsContentOfType(nsIContent::eELEMENT)) {
// Check for a new :before pseudo and an existing :before
// frame, but only if the frame is the first-in-flow.
nsIFrame* prevInFlow = nsnull;
aFrame->GetPrevInFlow(&prevInFlow);
if (!prevInFlow) {
// Checking for a :before frame is cheaper than getting the
// :before style context.
nsIFrame* beforeFrame = nsLayoutUtils::GetBeforeFrame(aFrame,
aPresContext);
if (!beforeFrame) {
// Look for a new :before style context
nsCOMPtr<nsIStyleContext> newBeforeContext;
aPresContext->ProbePseudoStyleContextFor(localContent,
nsCSSPseudoElements::before,
newContext,
getter_AddRefs(newBeforeContext));
if (newBeforeContext) {
// Have to create the new :before frame
NS_UpdateHint(aMinChange, nsChangeHint_ReconstructFrame);
aChangeList.AppendChange(aFrame, content,
nsChangeHint_ReconstructFrame);
}
}
}
}
}
if (!(aMinChange & (nsChangeHint_ReconstructFrame | nsChangeHint_ReconstructDoc))) {
if (localContent && localContent->IsContentOfType(nsIContent::eELEMENT)) {
// Check for new :after content, but only if the frame is the first-in-flow.
nsIFrame* nextInFlow = nsnull;
aFrame->GetNextInFlow(&nextInFlow);
if (!nextInFlow) {
// Getting the :after frame is
// more expensive than getting the pseudo context, so get the
// pseudo context first.
nsCOMPtr<nsIStyleContext> newAfterContext;
aPresContext->ProbePseudoStyleContextFor(localContent,
nsCSSPseudoElements::after,
newContext,
getter_AddRefs(newAfterContext));
if (newAfterContext) {
// Check whether we already have an :after frame
nsIFrame* afterFrame = nsLayoutUtils::GetAfterFrame(aFrame,
aPresContext);
if (!afterFrame) {
// have to create one
NS_UpdateHint(aMinChange, nsChangeHint_ReconstructFrame);
aChangeList.AppendChange(aFrame, content,
nsChangeHint_ReconstructFrame);
}
}
}
}
}
if (!(aMinChange & (nsChangeHint_ReconstructFrame | nsChangeHint_ReconstructDoc))) {
// There is no need to waste time crawling into a frame's children on a frame change.
// The act of reconstructing frames will force new style contexts to be resolved on all
// of this frame's descendants anyway, so we want to avoid wasting time processing
@ -1993,9 +2073,6 @@ FrameManager::ComputeStyleChangeFor(nsIPresContext* aPresContext,
ReResolveStyleContext(aPresContext, frame, nsnull,
aAttrNameSpaceID, aAttribute,
aChangeList, aMinChange, frameChange);
#ifdef NS_DEBUG
VerifyStyleTree(aPresContext, frame, nsnull);
#endif
NS_UpdateHint(aTopLevelChange, frameChange);
if (aTopLevelChange & (nsChangeHint_ReconstructDoc | nsChangeHint_ReconstructFrame)) {

Просмотреть файл

@ -0,0 +1,175 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: NPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Netscape Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the NPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the NPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsLayoutUtils.h"
#include "nsIFrame.h"
#include "nsIPresContext.h"
#include "nsIContent.h"
#include "nsFrameList.h"
/**
* A namespace class for static layout utilities.
*/
/**
* GetFirstChildFrame returns the first "real" child frame of a
* given frame. It will descend down into pseudo-frames (unless the
* pseudo-frame is the :before generated frame).
* @param aPresContext the prescontext
* @param aFrame the frame
* @param aFrame the frame's content node
*/
static nsIFrame*
GetFirstChildFrame(nsIPresContext* aPresContext,
nsIFrame* aFrame,
nsIContent* aContent)
{
NS_PRECONDITION(aFrame, "NULL frame pointer");
nsIFrame* childFrame;
// Get the first child frame
aFrame->FirstChild(aPresContext, nsnull, &childFrame);
// If the child frame is a pseudo-frame, then return its first child.
// Note that the frame we create for the generated content is also a
// pseudo-frame and so don't drill down in that case
if (childFrame &&
childFrame->IsPseudoFrame(aContent) &&
!childFrame->IsGeneratedContentFrame()) {
return GetFirstChildFrame(aPresContext, childFrame, aContent);
}
return childFrame;
}
/**
* GetLastChildFrame returns the last "real" child frame of a
* given frame. It will descend down into pseudo-frames (unless the
* pseudo-frame is the :after generated frame).
* @param aPresContext the prescontext
* @param aFrame the frame
* @param aFrame the frame's content node
*/
static nsIFrame*
GetLastChildFrame(nsIPresContext* aPresContext,
nsIFrame* aFrame,
nsIContent* aContent)
{
NS_PRECONDITION(aFrame, "NULL frame pointer");
// Get the last in flow frame
nsIFrame* lastInFlow;
do {
lastInFlow = aFrame;
lastInFlow->GetNextInFlow(&aFrame);
} while (aFrame);
// Get the last child frame
nsIFrame* firstChildFrame;
lastInFlow->FirstChild(aPresContext, nsnull, &firstChildFrame);
if (firstChildFrame) {
nsFrameList frameList(firstChildFrame);
nsIFrame* lastChildFrame = frameList.LastChild();
NS_ASSERTION(lastChildFrame, "unexpected error");
// Get the frame's first-in-flow. This matters in case the frame has
// been continuted across multiple lines
while (PR_TRUE) {
nsIFrame* prevInFlow;
lastChildFrame->GetPrevInFlow(&prevInFlow);
if (prevInFlow) {
lastChildFrame = prevInFlow;
} else {
break;
}
}
// If the last child frame is a pseudo-frame, then return its last child.
// Note that the frame we create for the generated content is also a
// pseudo-frame and so don't drill down in that case
if (lastChildFrame &&
lastChildFrame->IsPseudoFrame(aContent) &&
!lastChildFrame->IsGeneratedContentFrame()) {
return GetLastChildFrame(aPresContext, lastChildFrame, aContent);
}
return lastChildFrame;
}
return nsnull;
}
// static
nsIFrame*
nsLayoutUtils::GetBeforeFrame(nsIFrame* aFrame, nsIPresContext* aPresContext)
{
NS_PRECONDITION(aFrame, "NULL frame pointer");
#ifdef DEBUG
nsIFrame* prevInFlow = nsnull;
aFrame->GetPrevInFlow(&prevInFlow);
NS_ASSERTION(!prevInFlow, "aFrame must be first-in-flow");
#endif
nsCOMPtr<nsIContent> content;
aFrame->GetContent(getter_AddRefs(content));
nsIFrame* firstFrame = GetFirstChildFrame(aPresContext, aFrame, content);
if (firstFrame && firstFrame->IsGeneratedContentFrame()) {
return firstFrame;
}
return nsnull;
}
// static
nsIFrame*
nsLayoutUtils::GetAfterFrame(nsIFrame* aFrame, nsIPresContext* aPresContext)
{
NS_PRECONDITION(aFrame, "NULL frame pointer");
nsCOMPtr<nsIContent> content;
aFrame->GetContent(getter_AddRefs(content));
nsIFrame* lastFrame = GetLastChildFrame(aPresContext, aFrame, content);
if (lastFrame && lastFrame->IsGeneratedContentFrame()) {
return lastFrame;
}
return nsnull;
}

Просмотреть файл

@ -0,0 +1,77 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: NPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Netscape Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla.org code.
*
* The Initial Developer of the Original Code is
* Boris Zbarsky <bzbarsky@mit.edu>.
* Portions created by the Initial Developer are Copyright (C) 2002
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the NPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the NPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef nsLayoutUtils_h__
#define nsLayoutUtils_h__
class nsIFrame;
class nsIPresContext;
/**
* nsLayoutUtils is a namespace class used for various helper
* functions that are useful in multiple places in layout. The goal
* is not to define multiple copies of the same static helper.
*/
class nsLayoutUtils
{
public:
/**
* GetBeforeFrame returns the :before frame of the given frame, if
* one exists. This is typically O(1). The frame passed in must be
* the first-in-flow.
*
* @param aFrame the frame whose :before is wanted
* @param aPresContext the prescontext
* @return the :before frame or nsnull if there isn't one
*/
static nsIFrame* GetBeforeFrame(nsIFrame* aFrame, nsIPresContext* aPresContext);
/**
* GetAfterFrame returns the :after frame of the given frame, if one
* exists. This will walk the in-flow chain to the last-in-flow if
* needed. This function is typically O(N) in the number of child
* frames, following in-flows, etc.
*
* @param aFrame the frame whose :after is wanted
* @param aPresContext the prescontext
* @return the :after frame or nsnull if there isn't one
*/
static nsIFrame* GetAfterFrame(nsIFrame* aFrame, nsIPresContext* aPresContext);
};
#endif // nsLayoutUtils_h__

Просмотреть файл

@ -108,6 +108,7 @@
#include "nsIDocShell.h" // for reflow observation
#include "nsIDOMRange.h"
#include "nsLayoutErrors.h"
#include "nsLayoutUtils.h"
#ifdef MOZ_PERF_METRICS
#include "nsITimeRecorder.h"
#endif
@ -4569,94 +4570,6 @@ PresShell::SetHistoryState(nsILayoutHistoryState* aLayoutHistoryState)
return NS_OK;
}
static PRBool
IsGeneratedContentFrame(nsIFrame* aFrame)
{
nsFrameState frameState;
aFrame->GetFrameState(&frameState);
return (frameState & NS_FRAME_GENERATED_CONTENT) != 0;
}
static PRBool
IsPseudoFrame(nsIFrame* aFrame, nsIContent* aParentContent)
{
nsCOMPtr<nsIContent> content;
aFrame->GetContent(getter_AddRefs(content));
return content.get() == aParentContent;
}
static nsIFrame*
GetFirstChildFrame(nsIPresContext* aPresContext,
nsIFrame* aFrame,
nsIContent* aContent)
{
nsIFrame* childFrame;
// Get the first child frame
aFrame->FirstChild(aPresContext, nsnull, &childFrame);
// If the child frame is a pseudo-frame, then return its first child.
// Note that the frame we create for the generated content is also a
// pseudo-frame and so don't drill down in that case
if (childFrame && IsPseudoFrame(childFrame, aContent) &&
!IsGeneratedContentFrame(childFrame)) {
return GetFirstChildFrame(aPresContext, childFrame, aContent);
}
return childFrame;
}
static nsIFrame*
GetLastChildFrame(nsIPresContext* aPresContext,
nsIFrame* aFrame,
nsIContent* aContent)
{
NS_PRECONDITION(aFrame, "NULL frame pointer");
// Get the last in flow frame
nsIFrame* lastInFlow;
do {
lastInFlow = aFrame;
lastInFlow->GetNextInFlow(&aFrame);
} while (aFrame);
// Get the last child frame
nsIFrame* firstChildFrame;
lastInFlow->FirstChild(aPresContext, nsnull, &firstChildFrame);
if (firstChildFrame) {
nsFrameList frameList(firstChildFrame);
nsIFrame* lastChildFrame = frameList.LastChild();
NS_ASSERTION(lastChildFrame, "unexpected error");
// Get the frame's first-in-flow. This matters in case the frame has
// been continuted across multiple lines
while (PR_TRUE) {
nsIFrame* prevInFlow;
lastChildFrame->GetPrevInFlow(&prevInFlow);
if (prevInFlow) {
lastChildFrame = prevInFlow;
} else {
break;
}
}
// If the last child frame is a pseudo-frame, then return its last child.
// Note that the frame we create for the generated content is also a
// pseudo-frame and so don't drill down in that case
if (lastChildFrame && IsPseudoFrame(lastChildFrame, aContent) &&
!IsGeneratedContentFrame(lastChildFrame)) {
return GetLastChildFrame(aPresContext, lastChildFrame, aContent);
}
return lastChildFrame;
}
return nsnull;
}
NS_IMETHODIMP
PresShell::GetGeneratedContentIterator(nsIContent* aContent,
GeneratedContentType aType,
@ -4673,17 +4586,16 @@ PresShell::GetGeneratedContentIterator(nsIContent* aContent,
if (primaryFrame) {
// See whether it's a request for the before or after generated content
if (Before == aType) {
// The most efficient thing to do is to get the first child frame,
// and see if it is associated with generated content
nsIFrame* firstChildFrame = GetFirstChildFrame(mPresContext, primaryFrame, aContent);
if (firstChildFrame && IsGeneratedContentFrame(firstChildFrame)) {
nsIFrame* beforeFrame = nsLayoutUtils::GetBeforeFrame(primaryFrame,
mPresContext);
if (beforeFrame) {
// Create an iterator
rv = NS_NewFrameContentIterator(mPresContext, firstChildFrame, aIterator);
rv = NS_NewFrameContentIterator(mPresContext, beforeFrame, aIterator);
}
} else {
// Avoid finding the last child frame unless we need to. Instead probe
// for the existence of the pseudo-element
// Avoid finding the :after frame unless we need to (it's
// expensive). Instead probe for the existence of the pseudo-element
nsCOMPtr<nsIStyleContext> styleContext;
nsCOMPtr<nsIStyleContext> pseudoStyleContext;
@ -4693,15 +4605,14 @@ PresShell::GetGeneratedContentIterator(nsIContent* aContent,
styleContext,
getter_AddRefs(pseudoStyleContext));
if (pseudoStyleContext) {
nsIFrame* lastChildFrame = GetLastChildFrame(mPresContext, primaryFrame, aContent);
if (lastChildFrame)
{ // it is now legal for GetLastChildFrame to return null. see bug 52307 (a regression from bug 18754)
// in the case of a null child frame, we treat the frame as having no "after" style
// the "before" handler above already does this check
NS_ASSERTION(IsGeneratedContentFrame(lastChildFrame),
nsIFrame* afterFrame = nsLayoutUtils::GetAfterFrame(primaryFrame,
mPresContext);
if (afterFrame)
{
NS_ASSERTION(afterFrame->IsGeneratedContentFrame(),
"can't find generated content frame");
// Create an iterator
rv = NS_NewFrameContentIterator(mPresContext, lastChildFrame, aIterator);
rv = NS_NewFrameContentIterator(mPresContext, afterFrame, aIterator);
}
}
}
@ -5490,6 +5401,9 @@ PresShell::ReconstructStyleData(PRBool aRebuildRuleTree)
if (aRebuildRuleTree)
set->EndRuleTreeReconstruct();
VERIFY_STYLE_TREE;
return NS_OK;
}

Просмотреть файл

@ -41,6 +41,7 @@ nsIStyleFrameConstruction.h
nsIStyleSet.h
nsITextFrame.h
nsLayoutErrors.h
nsLayoutUtils.h
nsReflowType.h
nsStyleChangeList.h
nsStyleConsts.h

Просмотреть файл

@ -68,6 +68,7 @@ nsIStyleContext.h \
nsIStyleFrameConstruction.h \
nsIStyleSet.h \
nsLayoutErrors.h \
nsLayoutUtils.h \
nsReflowType.h \
nsStyleChangeList.h \
nsStyleConsts.h \

Просмотреть файл

@ -1165,6 +1165,30 @@ public:
PRBool aIsPre,
PRBool* aResult) = 0;
/**
* IsGeneratedContentFrame returns whether a frame corresponds to
* generated content
*
* @return whether the frame correspods to generated content
*/
PRBool IsGeneratedContentFrame() {
return (mState & NS_FRAME_GENERATED_CONTENT) != 0;
}
/**
* IsPseudoFrame returns whether a frame is a pseudo frame (eg an
* anonymous table-row frame created for a CSS table-cell without an
* enclosing table-row.
*
* @param aParentContent the content node corresponding to the parent frame
* @return whether the frame is a pseudo frame
*/
PRBool IsPseudoFrame(nsIContent* aParentContent) {
return mContent == aParentContent;
}
virtual void* GetProperty(nsIPresContext* aPresContext,
nsIAtom* aPropertyName,
PRBool aRemoveProperty) const = 0;

Просмотреть файл

@ -0,0 +1,77 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: NPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Netscape Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla.org code.
*
* The Initial Developer of the Original Code is
* Boris Zbarsky <bzbarsky@mit.edu>.
* Portions created by the Initial Developer are Copyright (C) 2002
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the NPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the NPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef nsLayoutUtils_h__
#define nsLayoutUtils_h__
class nsIFrame;
class nsIPresContext;
/**
* nsLayoutUtils is a namespace class used for various helper
* functions that are useful in multiple places in layout. The goal
* is not to define multiple copies of the same static helper.
*/
class nsLayoutUtils
{
public:
/**
* GetBeforeFrame returns the :before frame of the given frame, if
* one exists. This is typically O(1). The frame passed in must be
* the first-in-flow.
*
* @param aFrame the frame whose :before is wanted
* @param aPresContext the prescontext
* @return the :before frame or nsnull if there isn't one
*/
static nsIFrame* GetBeforeFrame(nsIFrame* aFrame, nsIPresContext* aPresContext);
/**
* GetAfterFrame returns the :after frame of the given frame, if one
* exists. This will walk the in-flow chain to the last-in-flow if
* needed. This function is typically O(N) in the number of child
* frames, following in-flows, etc.
*
* @param aFrame the frame whose :after is wanted
* @param aPresContext the prescontext
* @return the :after frame or nsnull if there isn't one
*/
static nsIFrame* GetAfterFrame(nsIFrame* aFrame, nsIPresContext* aPresContext);
};
#endif // nsLayoutUtils_h__

Просмотреть файл

@ -58,6 +58,7 @@ CPPSRCS = \
nsIntervalSet.cpp \
nsLayoutDebugger.cpp \
nsLayoutHistoryState.cpp \
nsLayoutUtils.cpp \
nsPresContext.cpp \
nsPresState.cpp \
nsPrintContext.cpp \

Просмотреть файл

@ -0,0 +1,175 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: NPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Netscape Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the NPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the NPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsLayoutUtils.h"
#include "nsIFrame.h"
#include "nsIPresContext.h"
#include "nsIContent.h"
#include "nsFrameList.h"
/**
* A namespace class for static layout utilities.
*/
/**
* GetFirstChildFrame returns the first "real" child frame of a
* given frame. It will descend down into pseudo-frames (unless the
* pseudo-frame is the :before generated frame).
* @param aPresContext the prescontext
* @param aFrame the frame
* @param aFrame the frame's content node
*/
static nsIFrame*
GetFirstChildFrame(nsIPresContext* aPresContext,
nsIFrame* aFrame,
nsIContent* aContent)
{
NS_PRECONDITION(aFrame, "NULL frame pointer");
nsIFrame* childFrame;
// Get the first child frame
aFrame->FirstChild(aPresContext, nsnull, &childFrame);
// If the child frame is a pseudo-frame, then return its first child.
// Note that the frame we create for the generated content is also a
// pseudo-frame and so don't drill down in that case
if (childFrame &&
childFrame->IsPseudoFrame(aContent) &&
!childFrame->IsGeneratedContentFrame()) {
return GetFirstChildFrame(aPresContext, childFrame, aContent);
}
return childFrame;
}
/**
* GetLastChildFrame returns the last "real" child frame of a
* given frame. It will descend down into pseudo-frames (unless the
* pseudo-frame is the :after generated frame).
* @param aPresContext the prescontext
* @param aFrame the frame
* @param aFrame the frame's content node
*/
static nsIFrame*
GetLastChildFrame(nsIPresContext* aPresContext,
nsIFrame* aFrame,
nsIContent* aContent)
{
NS_PRECONDITION(aFrame, "NULL frame pointer");
// Get the last in flow frame
nsIFrame* lastInFlow;
do {
lastInFlow = aFrame;
lastInFlow->GetNextInFlow(&aFrame);
} while (aFrame);
// Get the last child frame
nsIFrame* firstChildFrame;
lastInFlow->FirstChild(aPresContext, nsnull, &firstChildFrame);
if (firstChildFrame) {
nsFrameList frameList(firstChildFrame);
nsIFrame* lastChildFrame = frameList.LastChild();
NS_ASSERTION(lastChildFrame, "unexpected error");
// Get the frame's first-in-flow. This matters in case the frame has
// been continuted across multiple lines
while (PR_TRUE) {
nsIFrame* prevInFlow;
lastChildFrame->GetPrevInFlow(&prevInFlow);
if (prevInFlow) {
lastChildFrame = prevInFlow;
} else {
break;
}
}
// If the last child frame is a pseudo-frame, then return its last child.
// Note that the frame we create for the generated content is also a
// pseudo-frame and so don't drill down in that case
if (lastChildFrame &&
lastChildFrame->IsPseudoFrame(aContent) &&
!lastChildFrame->IsGeneratedContentFrame()) {
return GetLastChildFrame(aPresContext, lastChildFrame, aContent);
}
return lastChildFrame;
}
return nsnull;
}
// static
nsIFrame*
nsLayoutUtils::GetBeforeFrame(nsIFrame* aFrame, nsIPresContext* aPresContext)
{
NS_PRECONDITION(aFrame, "NULL frame pointer");
#ifdef DEBUG
nsIFrame* prevInFlow = nsnull;
aFrame->GetPrevInFlow(&prevInFlow);
NS_ASSERTION(!prevInFlow, "aFrame must be first-in-flow");
#endif
nsCOMPtr<nsIContent> content;
aFrame->GetContent(getter_AddRefs(content));
nsIFrame* firstFrame = GetFirstChildFrame(aPresContext, aFrame, content);
if (firstFrame && firstFrame->IsGeneratedContentFrame()) {
return firstFrame;
}
return nsnull;
}
// static
nsIFrame*
nsLayoutUtils::GetAfterFrame(nsIFrame* aFrame, nsIPresContext* aPresContext)
{
NS_PRECONDITION(aFrame, "NULL frame pointer");
nsCOMPtr<nsIContent> content;
aFrame->GetContent(getter_AddRefs(content));
nsIFrame* lastFrame = GetLastChildFrame(aPresContext, aFrame, content);
if (lastFrame && lastFrame->IsGeneratedContentFrame()) {
return lastFrame;
}
return nsnull;
}

Просмотреть файл

@ -1165,6 +1165,30 @@ public:
PRBool aIsPre,
PRBool* aResult) = 0;
/**
* IsGeneratedContentFrame returns whether a frame corresponds to
* generated content
*
* @return whether the frame correspods to generated content
*/
PRBool IsGeneratedContentFrame() {
return (mState & NS_FRAME_GENERATED_CONTENT) != 0;
}
/**
* IsPseudoFrame returns whether a frame is a pseudo frame (eg an
* anonymous table-row frame created for a CSS table-cell without an
* enclosing table-row.
*
* @param aParentContent the content node corresponding to the parent frame
* @return whether the frame is a pseudo frame
*/
PRBool IsPseudoFrame(nsIContent* aParentContent) {
return mContent == aParentContent;
}
virtual void* GetProperty(nsIPresContext* aPresContext,
nsIAtom* aPropertyName,
PRBool aRemoveProperty) const = 0;

Просмотреть файл

@ -45,6 +45,7 @@
#include "nsPlaceholderFrame.h"
#include "nsLayoutAtoms.h"
#include "nsCSSAnonBoxes.h"
#include "nsCSSPseudoElements.h"
#include "nsHTMLAtoms.h"
#ifdef NS_DEBUG
#include "nsISupportsArray.h"
@ -75,6 +76,7 @@
#include "nsPrintfCString.h"
#include "nsDummyLayoutRequest.h"
#include "nsLayoutErrors.h"
#include "nsLayoutUtils.h"
#ifdef DEBUG
//#define NOISY_DEBUG
@ -1742,9 +1744,26 @@ FrameManager::ReResolveStyleContext(nsIPresContext* aPresContext,
else if (pseudoTag) {
nsIContent* pseudoContent =
aParentContent ? aParentContent : localContent;
aPresContext->ResolvePseudoStyleContextFor(pseudoContent, pseudoTag,
if (pseudoTag == nsCSSPseudoElements::before ||
pseudoTag == nsCSSPseudoElements::after) {
// XXX what other pseudos do we need to treat like this?
aPresContext->ProbePseudoStyleContextFor(pseudoContent, pseudoTag,
parentContext,
&newContext);
if (!newContext) {
// This pseudo should no longer exist; gotta reframe
NS_UpdateHint(aMinChange, nsChangeHint_ReconstructFrame);
aChangeList.AppendChange(aFrame, pseudoContent,
nsChangeHint_ReconstructFrame);
// We're reframing anyway; just keep the same context
newContext = oldContext;
NS_ADDREF(newContext);
}
} else {
aPresContext->ResolvePseudoStyleContextFor(pseudoContent, pseudoTag,
parentContext,
&newContext);
}
NS_RELEASE(pseudoTag);
}
else {
@ -1895,6 +1914,67 @@ FrameManager::ReResolveStyleContext(nsIPresContext* aPresContext,
aResultChange = aMinChange;
if (!(aMinChange & (nsChangeHint_ReconstructFrame | nsChangeHint_ReconstructDoc))) {
if (localContent && localContent->IsContentOfType(nsIContent::eELEMENT)) {
// Check for a new :before pseudo and an existing :before
// frame, but only if the frame is the first-in-flow.
nsIFrame* prevInFlow = nsnull;
aFrame->GetPrevInFlow(&prevInFlow);
if (!prevInFlow) {
// Checking for a :before frame is cheaper than getting the
// :before style context.
nsIFrame* beforeFrame = nsLayoutUtils::GetBeforeFrame(aFrame,
aPresContext);
if (!beforeFrame) {
// Look for a new :before style context
nsCOMPtr<nsIStyleContext> newBeforeContext;
aPresContext->ProbePseudoStyleContextFor(localContent,
nsCSSPseudoElements::before,
newContext,
getter_AddRefs(newBeforeContext));
if (newBeforeContext) {
// Have to create the new :before frame
NS_UpdateHint(aMinChange, nsChangeHint_ReconstructFrame);
aChangeList.AppendChange(aFrame, content,
nsChangeHint_ReconstructFrame);
}
}
}
}
}
if (!(aMinChange & (nsChangeHint_ReconstructFrame | nsChangeHint_ReconstructDoc))) {
if (localContent && localContent->IsContentOfType(nsIContent::eELEMENT)) {
// Check for new :after content, but only if the frame is the first-in-flow.
nsIFrame* nextInFlow = nsnull;
aFrame->GetNextInFlow(&nextInFlow);
if (!nextInFlow) {
// Getting the :after frame is
// more expensive than getting the pseudo context, so get the
// pseudo context first.
nsCOMPtr<nsIStyleContext> newAfterContext;
aPresContext->ProbePseudoStyleContextFor(localContent,
nsCSSPseudoElements::after,
newContext,
getter_AddRefs(newAfterContext));
if (newAfterContext) {
// Check whether we already have an :after frame
nsIFrame* afterFrame = nsLayoutUtils::GetAfterFrame(aFrame,
aPresContext);
if (!afterFrame) {
// have to create one
NS_UpdateHint(aMinChange, nsChangeHint_ReconstructFrame);
aChangeList.AppendChange(aFrame, content,
nsChangeHint_ReconstructFrame);
}
}
}
}
}
if (!(aMinChange & (nsChangeHint_ReconstructFrame | nsChangeHint_ReconstructDoc))) {
// There is no need to waste time crawling into a frame's children on a frame change.
// The act of reconstructing frames will force new style contexts to be resolved on all
// of this frame's descendants anyway, so we want to avoid wasting time processing
@ -1993,9 +2073,6 @@ FrameManager::ComputeStyleChangeFor(nsIPresContext* aPresContext,
ReResolveStyleContext(aPresContext, frame, nsnull,
aAttrNameSpaceID, aAttribute,
aChangeList, aMinChange, frameChange);
#ifdef NS_DEBUG
VerifyStyleTree(aPresContext, frame, nsnull);
#endif
NS_UpdateHint(aTopLevelChange, frameChange);
if (aTopLevelChange & (nsChangeHint_ReconstructDoc | nsChangeHint_ReconstructFrame)) {

Просмотреть файл

@ -108,6 +108,7 @@
#include "nsIDocShell.h" // for reflow observation
#include "nsIDOMRange.h"
#include "nsLayoutErrors.h"
#include "nsLayoutUtils.h"
#ifdef MOZ_PERF_METRICS
#include "nsITimeRecorder.h"
#endif
@ -4569,94 +4570,6 @@ PresShell::SetHistoryState(nsILayoutHistoryState* aLayoutHistoryState)
return NS_OK;
}
static PRBool
IsGeneratedContentFrame(nsIFrame* aFrame)
{
nsFrameState frameState;
aFrame->GetFrameState(&frameState);
return (frameState & NS_FRAME_GENERATED_CONTENT) != 0;
}
static PRBool
IsPseudoFrame(nsIFrame* aFrame, nsIContent* aParentContent)
{
nsCOMPtr<nsIContent> content;
aFrame->GetContent(getter_AddRefs(content));
return content.get() == aParentContent;
}
static nsIFrame*
GetFirstChildFrame(nsIPresContext* aPresContext,
nsIFrame* aFrame,
nsIContent* aContent)
{
nsIFrame* childFrame;
// Get the first child frame
aFrame->FirstChild(aPresContext, nsnull, &childFrame);
// If the child frame is a pseudo-frame, then return its first child.
// Note that the frame we create for the generated content is also a
// pseudo-frame and so don't drill down in that case
if (childFrame && IsPseudoFrame(childFrame, aContent) &&
!IsGeneratedContentFrame(childFrame)) {
return GetFirstChildFrame(aPresContext, childFrame, aContent);
}
return childFrame;
}
static nsIFrame*
GetLastChildFrame(nsIPresContext* aPresContext,
nsIFrame* aFrame,
nsIContent* aContent)
{
NS_PRECONDITION(aFrame, "NULL frame pointer");
// Get the last in flow frame
nsIFrame* lastInFlow;
do {
lastInFlow = aFrame;
lastInFlow->GetNextInFlow(&aFrame);
} while (aFrame);
// Get the last child frame
nsIFrame* firstChildFrame;
lastInFlow->FirstChild(aPresContext, nsnull, &firstChildFrame);
if (firstChildFrame) {
nsFrameList frameList(firstChildFrame);
nsIFrame* lastChildFrame = frameList.LastChild();
NS_ASSERTION(lastChildFrame, "unexpected error");
// Get the frame's first-in-flow. This matters in case the frame has
// been continuted across multiple lines
while (PR_TRUE) {
nsIFrame* prevInFlow;
lastChildFrame->GetPrevInFlow(&prevInFlow);
if (prevInFlow) {
lastChildFrame = prevInFlow;
} else {
break;
}
}
// If the last child frame is a pseudo-frame, then return its last child.
// Note that the frame we create for the generated content is also a
// pseudo-frame and so don't drill down in that case
if (lastChildFrame && IsPseudoFrame(lastChildFrame, aContent) &&
!IsGeneratedContentFrame(lastChildFrame)) {
return GetLastChildFrame(aPresContext, lastChildFrame, aContent);
}
return lastChildFrame;
}
return nsnull;
}
NS_IMETHODIMP
PresShell::GetGeneratedContentIterator(nsIContent* aContent,
GeneratedContentType aType,
@ -4673,17 +4586,16 @@ PresShell::GetGeneratedContentIterator(nsIContent* aContent,
if (primaryFrame) {
// See whether it's a request for the before or after generated content
if (Before == aType) {
// The most efficient thing to do is to get the first child frame,
// and see if it is associated with generated content
nsIFrame* firstChildFrame = GetFirstChildFrame(mPresContext, primaryFrame, aContent);
if (firstChildFrame && IsGeneratedContentFrame(firstChildFrame)) {
nsIFrame* beforeFrame = nsLayoutUtils::GetBeforeFrame(primaryFrame,
mPresContext);
if (beforeFrame) {
// Create an iterator
rv = NS_NewFrameContentIterator(mPresContext, firstChildFrame, aIterator);
rv = NS_NewFrameContentIterator(mPresContext, beforeFrame, aIterator);
}
} else {
// Avoid finding the last child frame unless we need to. Instead probe
// for the existence of the pseudo-element
// Avoid finding the :after frame unless we need to (it's
// expensive). Instead probe for the existence of the pseudo-element
nsCOMPtr<nsIStyleContext> styleContext;
nsCOMPtr<nsIStyleContext> pseudoStyleContext;
@ -4693,15 +4605,14 @@ PresShell::GetGeneratedContentIterator(nsIContent* aContent,
styleContext,
getter_AddRefs(pseudoStyleContext));
if (pseudoStyleContext) {
nsIFrame* lastChildFrame = GetLastChildFrame(mPresContext, primaryFrame, aContent);
if (lastChildFrame)
{ // it is now legal for GetLastChildFrame to return null. see bug 52307 (a regression from bug 18754)
// in the case of a null child frame, we treat the frame as having no "after" style
// the "before" handler above already does this check
NS_ASSERTION(IsGeneratedContentFrame(lastChildFrame),
nsIFrame* afterFrame = nsLayoutUtils::GetAfterFrame(primaryFrame,
mPresContext);
if (afterFrame)
{
NS_ASSERTION(afterFrame->IsGeneratedContentFrame(),
"can't find generated content frame");
// Create an iterator
rv = NS_NewFrameContentIterator(mPresContext, lastChildFrame, aIterator);
rv = NS_NewFrameContentIterator(mPresContext, afterFrame, aIterator);
}
}
}
@ -5490,6 +5401,9 @@ PresShell::ReconstructStyleData(PRBool aRebuildRuleTree)
if (aRebuildRuleTree)
set->EndRuleTreeReconstruct();
VERIFY_STYLE_TREE;
return NS_OK;
}

Просмотреть файл

@ -10336,6 +10336,22 @@ nsCSSFrameConstructor::ProcessRestyledFrames(nsStyleChangeList& aChangeList,
ApplyRenderingChangeToTree(aPresContext, frame, nsnull, hint);
}
}
#ifdef DEBUG
// reget from content since it may have been regenerated...
if (content) {
nsIFrame* frame;
nsCOMPtr<nsIPresShell> shell;
aPresContext->GetShell(getter_AddRefs(shell));
shell->GetPrimaryFrameFor(content, &frame);
if (frame) {
nsCOMPtr<nsIFrameManager> frameManager;
shell->GetFrameManager(getter_AddRefs(frameManager));
frameManager->DebugVerifyStyleTree(aPresContext, frame);
}
} else {
NS_WARNING("Unable to test style tree integrity -- no content node");
}
#endif
}
aChangeList.Clear();
return NS_OK;

Просмотреть файл

@ -941,6 +941,13 @@
<FILEKIND>Text</FILEKIND>
<FILEFLAGS>Debug</FILEFLAGS>
</FILE>
<FILE>
<PATHTYPE>Name</PATHTYPE>
<PATH>nsLayoutUtils.cpp</PATH>
<PATHFORMAT>MacOS</PATHFORMAT>
<FILEKIND>Text</FILEKIND>
<FILEFLAGS>Debug</FILEFLAGS>
</FILE>
<FILE>
<PATHTYPE>Name</PATHTYPE>
<PATH>nsPresContext.cpp</PATH>
@ -2137,6 +2144,11 @@
<PATH>nsGalleyContext.cpp</PATH>
<PATHFORMAT>MacOS</PATHFORMAT>
</FILEREF>
<FILEREF>
<PATHTYPE>Name</PATHTYPE>
<PATH>nsLayoutUtils.cpp</PATH>
<PATHFORMAT>MacOS</PATHFORMAT>
</FILEREF>
<FILEREF>
<PATHTYPE>Name</PATHTYPE>
<PATH>nsPresContext.cpp</PATH>
@ -3852,6 +3864,13 @@
<FILEKIND>Text</FILEKIND>
<FILEFLAGS>Debug</FILEFLAGS>
</FILE>
<FILE>
<PATHTYPE>Name</PATHTYPE>
<PATH>nsLayoutUtils.cpp</PATH>
<PATHFORMAT>MacOS</PATHFORMAT>
<FILEKIND>Text</FILEKIND>
<FILEFLAGS>Debug</FILEFLAGS>
</FILE>
<FILE>
<PATHTYPE>Name</PATHTYPE>
<PATH>nsPresContext.cpp</PATH>
@ -5068,6 +5087,11 @@
<PATH>nsGalleyContext.cpp</PATH>
<PATHFORMAT>MacOS</PATHFORMAT>
</FILEREF>
<FILEREF>
<PATHTYPE>Name</PATHTYPE>
<PATH>nsLayoutUtils.cpp</PATH>
<PATHFORMAT>MacOS</PATHFORMAT>
</FILEREF>
<FILEREF>
<PATHTYPE>Name</PATHTYPE>
<PATH>nsPresContext.cpp</PATH>
@ -5932,6 +5956,12 @@
<PATH>nsGalleyContext.cpp</PATH>
<PATHFORMAT>MacOS</PATHFORMAT>
</FILEREF>
<FILEREF>
<TARGETNAME>layout.shlb</TARGETNAME>
<PATHTYPE>Name</PATHTYPE>
<PATH>nsLayoutUtils.cpp</PATH>
<PATHFORMAT>MacOS</PATHFORMAT>
</FILEREF>
<FILEREF>
<TARGETNAME>layout.shlb</TARGETNAME>
<PATHTYPE>Name</PATHTYPE>

Просмотреть файл

@ -674,6 +674,13 @@ nsMathMLContainerFrame::PropagateScriptStyleFor(nsIPresContext* aPresContext,
fm->ComputeStyleChangeFor(aPresContext, aFrame,
kNameSpaceID_None, nsMathMLAtoms::fontsize,
changeList, minChange, maxChange);
#ifdef DEBUG
// Use the parent frame to make sure we catch in-flows and such
nsIFrame* parentFrame;
aFrame->GetParent(&parentFrame);
fm->DebugVerifyStyleTree(aPresContext,
parentFrame ? parentFrame : aFrame);
#endif
}
}
}

Просмотреть файл

@ -717,6 +717,13 @@ nsMathMLFrame::MapAttributesIntoCSS(nsIPresContext* aPresContext,
fm->ComputeStyleChangeFor(aPresContext, aFrame,
kNameSpaceID_None, nsnull,
changeList, minChange, maxChange);
#ifdef DEBUG
// Use the parent frame to make sure we catch in-flows and such
nsIFrame* parentFrame;
aFrame->GetParent(&parentFrame);
fm->DebugVerifyStyleTree(aPresContext,
parentFrame ? parentFrame : aFrame);
#endif
}
}

Просмотреть файл

@ -358,6 +358,13 @@ nsMathMLTokenFrame::SetTextStyle(nsIPresContext* aPresContext)
fm->ComputeStyleChangeFor(aPresContext, this,
kNameSpaceID_None, nsMathMLAtoms::fontstyle,
changeList, minChange, maxChange);
#ifdef DEBUG
// Use the parent frame to make sure we catch in-flows and such
nsIFrame* parentFrame;
GetParent(&parentFrame);
fm->DebugVerifyStyleTree(aPresContext,
parentFrame ? parentFrame : this);
#endif
}
}
}

Просмотреть файл

@ -299,6 +299,13 @@ MapAttributesInto(nsIPresContext* aPresContext,
nsStyleChangeList changeList;
fm->ComputeStyleChangeFor(aPresContext, aCellFrame, kNameSpaceID_None, nsnull,
changeList, minChange, maxChange);
#ifdef DEBUG
// Use the parent frame to make sure we catch in-flows and such
nsIFrame* parentFrame;
aCellFrame->GetParent(&parentFrame);
fm->DebugVerifyStyleTree(aPresContext,
parentFrame ? parentFrame : aCellFrame);
#endif
}
}
}