зеркало из https://github.com/mozilla/gecko-dev.git
mondo improvements to DTD
This commit is contained in:
Родитель
9f4f989790
Коммит
06970832e3
|
@ -1268,7 +1268,7 @@ nsHTMLDocument::WriteCommon(JSContext *cx,
|
|||
str.Append('\n');
|
||||
}
|
||||
|
||||
result = mParser->Parse(str, PR_TRUE,PR_FALSE,PR_FALSE);
|
||||
result = mParser->Parse(str, PR_TRUE,PR_FALSE,PR_TRUE);
|
||||
if (NS_OK != result) {
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -696,23 +696,31 @@ PRInt32 GetTopmostIndexOf(eHTMLTags aTag,nsTagStack& aTagStack) {
|
|||
*/
|
||||
eHTMLTags FindAutoCloseTargetForStartTag(eHTMLTags aCurrentTag,nsTagStack& aTagStack) {
|
||||
int theTopIndex=aTagStack.mCount;
|
||||
eHTMLTags aPrevTag=aTagStack.mTags[theTopIndex-1];
|
||||
eHTMLTags thePrevTag=aTagStack.Last();
|
||||
|
||||
if(nsHTMLElement::IsContainer(aCurrentTag)){
|
||||
if(aPrevTag==aCurrentTag) {
|
||||
if(thePrevTag==aCurrentTag) {
|
||||
return (gHTMLElements[aCurrentTag].CanContainSelf()) ? eHTMLTag_unknown: aCurrentTag;
|
||||
}
|
||||
if(nsHTMLElement::IsBlockCloser(aCurrentTag)) {
|
||||
|
||||
PRInt32 theRootIndex=kNotFound;
|
||||
CTagList* theRootTags=gHTMLElements[aCurrentTag].GetRootTags();
|
||||
if(theRootTags) {
|
||||
PRInt32 theRootIndex=theRootTags->GetTopmostIndexOf(aTagStack);
|
||||
theRootIndex=theRootTags->GetTopmostIndexOf(aTagStack);
|
||||
CTagList* theStartTags=gHTMLElements[aCurrentTag].GetAutoCloseStartTags();
|
||||
PRInt32 thePeerIndex=kNotFound;
|
||||
if(theStartTags){
|
||||
thePeerIndex=theStartTags->GetBottommostIndexOf(aTagStack,theRootIndex+1);
|
||||
}
|
||||
else thePeerIndex=GetTopmostIndexOf(aCurrentTag,aTagStack);
|
||||
else {
|
||||
//this extra check is need to handle case like this: <DIV><P><DIV>
|
||||
//the new div can close the P,but doesn't close the top DIV.
|
||||
thePeerIndex=GetTopmostIndexOf(aCurrentTag,aTagStack);
|
||||
if(gHTMLElements[aCurrentTag].CanContainSelf()) {
|
||||
thePeerIndex++;
|
||||
}
|
||||
}
|
||||
if(theRootIndex<thePeerIndex) {
|
||||
return aTagStack.mTags[thePeerIndex]; //return the tag that was used in peer test.
|
||||
}
|
||||
|
@ -724,14 +732,19 @@ eHTMLTags FindAutoCloseTargetForStartTag(eHTMLTags aCurrentTag,nsTagStack& aTagS
|
|||
if(kNotFound!=thePeerIndex){
|
||||
if(thePeerIndex==theTopIndex-1) {
|
||||
//the guy you can autoclose is on the top of the stack...
|
||||
return aPrevTag;
|
||||
return thePrevTag;
|
||||
} //if
|
||||
} //if
|
||||
}//if
|
||||
else if(kNotFound<theRootIndex) {
|
||||
//This block handles our fallback cases like: <html><body><center><p> <- <table>
|
||||
while((theRootIndex<--theTopIndex) && (!gHTMLElements[aTagStack.mTags[theTopIndex]].CanContain(aCurrentTag))) {
|
||||
}
|
||||
return aTagStack.mTags[theTopIndex+1];
|
||||
//return aTagStack.mTags[theRootIndex+1];
|
||||
}
|
||||
|
||||
} //if
|
||||
else if(nsHTMLElement::IsInlineElement(aCurrentTag)) {
|
||||
}//if
|
||||
} //if
|
||||
return eHTMLTag_unknown;
|
||||
}
|
||||
|
@ -748,6 +761,24 @@ eHTMLTags FindAutoCloseTargetForStartTag(eHTMLTags aCurrentTag,nsTagStack& aTagS
|
|||
*/
|
||||
PRBool CanBeContained(eHTMLTags aChildTag,nsTagStack& aTagStack) {
|
||||
PRBool result=PR_TRUE;
|
||||
|
||||
CTagList* theRootTags=gHTMLElements[aChildTag].GetRootTags();
|
||||
if(theRootTags) {
|
||||
PRInt32 theRootIndex=theRootTags->GetTopmostIndexOf(aTagStack);
|
||||
PRInt32 theChildIndex=aTagStack.GetTopmostIndexOf(aChildTag);
|
||||
if(kNotFound==theChildIndex) {
|
||||
CTagList* theSynTags=gHTMLElements[aChildTag].GetSynonymousTags(); //get the list of tags that THIS tag can close
|
||||
if(theSynTags) {
|
||||
theChildIndex=theSynTags->GetTopmostIndexOf(aTagStack);
|
||||
}
|
||||
}
|
||||
if(theRootIndex<theChildIndex){
|
||||
eHTMLTags thePrevTag=aTagStack.Last();
|
||||
result=gHTMLElements[thePrevTag].CanContainType(gHTMLElements[aChildTag].mParentBits);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -977,6 +1008,114 @@ nsresult CNavDTD::HandleStartToken(CToken* aToken) {
|
|||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this to see if you have a closeable peer on the stack that
|
||||
* is ABOVE one of its root tags.
|
||||
*
|
||||
* @update gess 3/25/98
|
||||
* @param aRootTagList -- list of root tags for aTag
|
||||
* @param aTag -- tag to test for containership
|
||||
* @return PR_TRUE if given tag can contain other tags
|
||||
*/
|
||||
PRBool HasCloseablePeerAboveRoot(CTagList& aRootTagList,nsTagStack& aTagStack,eHTMLTags aTag) {
|
||||
PRInt32 theRootIndex=aRootTagList.GetTopmostIndexOf(aTagStack);
|
||||
CTagList* theCloseTags=gHTMLElements[aTag].GetAutoCloseStartTags();
|
||||
PRInt32 theChildIndex=-1;
|
||||
PRBool result=PR_FALSE;
|
||||
if(theCloseTags) {
|
||||
theChildIndex=theCloseTags->GetTopmostIndexOf(aTagStack);
|
||||
}
|
||||
else {
|
||||
theChildIndex=aTagStack.GetTopmostIndexOf(aTag);
|
||||
}
|
||||
return PRBool(theRootIndex<theChildIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this to find the index of a given child, or (if not found)
|
||||
* the index of its nearest synonym.
|
||||
*
|
||||
* @update gess 3/25/98
|
||||
* @param aTagStack -- list of open tags
|
||||
* @param aTag -- tag to test for containership
|
||||
* @return index of kNotFound
|
||||
*/
|
||||
PRInt32 GetIndexOfChildOrSynonym(nsTagStack& aTagStack,eHTMLTags aChildTag) {
|
||||
PRInt32 theChildIndex=aTagStack.GetTopmostIndexOf(aChildTag);
|
||||
if(kNotFound==theChildIndex) {
|
||||
CTagList* theSynTags=gHTMLElements[aChildTag].GetSynonymousTags(); //get the list of tags that THIS tag can close
|
||||
if(theSynTags) {
|
||||
theChildIndex=theSynTags->GetTopmostIndexOf(aTagStack);
|
||||
}
|
||||
}
|
||||
return theChildIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called to determine whether or not an END tag
|
||||
* can be autoclosed. This means that based on the current
|
||||
* context, the stack should be closed to the nearest matching
|
||||
* tag.
|
||||
*
|
||||
* @param aTag -- tag enum of child to be tested
|
||||
* @return PR_TRUE if autoclosure should occur
|
||||
*/
|
||||
eHTMLTags FindAutoCloseTargetForEndTag(eHTMLTags aCurrentTag,nsTagStack& aTagStack) {
|
||||
int theTopIndex=aTagStack.mCount;
|
||||
eHTMLTags thePrevTag=aTagStack.Last();
|
||||
|
||||
if(nsHTMLElement::IsContainer(aCurrentTag)){
|
||||
PRInt32 theChildIndex=GetIndexOfChildOrSynonym(aTagStack,aCurrentTag);
|
||||
|
||||
if(kNotFound<theChildIndex) {
|
||||
if(thePrevTag==aTagStack.mTags[theChildIndex]){
|
||||
return aTagStack.mTags[theChildIndex];
|
||||
}
|
||||
|
||||
if(nsHTMLElement::IsBlockCloser(aCurrentTag)) {
|
||||
|
||||
/*here's what to do:
|
||||
Our here is sitting at aChildIndex. There are other tags above it
|
||||
on the stack. We have to try to close them out, but we may encounter
|
||||
one that can block us. The way to tell is by comparing each tag on
|
||||
the stack against our closeTag and rootTag list.
|
||||
|
||||
For each tag above our hero on the stack, ask 3 questions:
|
||||
1. Is it in the closeTag list? If so, the we can skip over it
|
||||
2. Is it in the rootTag list? If so, then we're gated by it
|
||||
3. Otherwise its non-specified and we simply presume we can close it.
|
||||
*/
|
||||
|
||||
CTagList* theCloseTags=gHTMLElements[aCurrentTag].GetAutoCloseEndTags();
|
||||
CTagList* theRootTags=gHTMLElements[aCurrentTag].GetEndRootTags();
|
||||
|
||||
if(theCloseTags){
|
||||
//at a min., this code is needed for H1..H6
|
||||
|
||||
while(theChildIndex<--theTopIndex) {
|
||||
eHTMLTags theNextTag=aTagStack.mTags[theTopIndex];
|
||||
if(PR_FALSE==theCloseTags->Contains(theNextTag)) {
|
||||
if(PR_TRUE==theRootTags->Contains(theNextTag)) {
|
||||
return eHTMLTag_unknown; //we encountered a tag in root list so fail (because we're gated).
|
||||
}
|
||||
//otherwise presume it's something we can simply ignore and continue search...
|
||||
}
|
||||
//otherwise its in the close list so skip to next tag...
|
||||
}
|
||||
return aCurrentTag; //if you make it here, we're ungated and found a target!
|
||||
}//if
|
||||
else if(theRootTags) {
|
||||
//since we didn't find any close tags, see if there is an instance of aCurrentTag
|
||||
//above the stack from the roottag.
|
||||
if(HasCloseablePeerAboveRoot(*theRootTags,aTagStack,aCurrentTag))
|
||||
return aCurrentTag;
|
||||
else return eHTMLTag_unknown;
|
||||
}
|
||||
} //if
|
||||
}//if
|
||||
} //if
|
||||
return eHTMLTag_unknown;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method gets called when an end token has been
|
||||
|
@ -995,23 +1134,7 @@ nsresult CNavDTD::HandleEndToken(CToken* aToken) {
|
|||
NS_PRECONDITION(0!=aToken,kNullToken);
|
||||
|
||||
nsresult result=NS_OK;
|
||||
CEndToken* et = (CEndToken*)(aToken);
|
||||
eHTMLTags theChildTag=(eHTMLTags)et->GetTypeID();
|
||||
|
||||
// Here's the hacky part:
|
||||
// Because we're trying to be backward compatible with Nav4/5,
|
||||
// we have to handle explicit styles the way it does. That means
|
||||
// that we keep an internal style stack.When an EndToken occurs,
|
||||
// we should see if it is an explicit style tag. If so, we can
|
||||
// close the explicit style tag (goofy, huh?)
|
||||
|
||||
|
||||
//now check to see if this token should be omitted, or
|
||||
//if it's gated from closing by the presence of another tag.
|
||||
if(PR_TRUE==CanOmitEndTag(mBodyContext->Last(),theChildTag)) {
|
||||
UpdateStyleStackForCloseTag(theChildTag,theChildTag);
|
||||
return result;
|
||||
}
|
||||
eHTMLTags theChildTag=(eHTMLTags)aToken->GetTypeID();
|
||||
|
||||
nsCParserNode theNode((CHTMLToken*)aToken,mLineNumber);
|
||||
switch(theChildTag) {
|
||||
|
@ -1029,26 +1152,20 @@ nsresult CNavDTD::HandleEndToken(CToken* aToken) {
|
|||
result=CloseContainer(theNode,theChildTag,PR_FALSE);
|
||||
break;
|
||||
|
||||
case eHTMLTag_td:
|
||||
case eHTMLTag_th:
|
||||
//result=CloseContainersTo(theChildTag,PR_TRUE);
|
||||
// Empty the transient style stack (we just closed any extra
|
||||
// ones off so it's safe to do it now) because they don't carry
|
||||
// forward across table cell boundaries.
|
||||
//mBodyContext->mStyles->mCount=0;
|
||||
//break;
|
||||
|
||||
default:
|
||||
if(IsContainer(theChildTag)){
|
||||
CTagList* theCloseTags=gHTMLElements[theChildTag].GetAutoCloseEndTags();
|
||||
|
||||
if(theCloseTags){
|
||||
PRInt32 thePeerIndex=theCloseTags->GetTopmostIndexOf(mBodyContext->mTags);
|
||||
theChildTag=(kNotFound<thePeerIndex) ? mBodyContext->mTags[thePeerIndex] : theChildTag;
|
||||
{
|
||||
//now check to see if this token should be omitted, or
|
||||
//if it's gated from closing by the presence of another tag.
|
||||
if(PR_TRUE==CanOmitEndTag(mBodyContext->Last(),theChildTag)) {
|
||||
UpdateStyleStackForCloseTag(theChildTag,theChildTag);
|
||||
}
|
||||
else {
|
||||
eHTMLTags theTarget=FindAutoCloseTargetForEndTag(theChildTag,mBodyContext->mTags);
|
||||
if(eHTMLTag_unknown!=theTarget) {
|
||||
result=CloseContainersTo(theTarget,PR_TRUE);
|
||||
}
|
||||
}
|
||||
result=CloseContainersTo(theChildTag,PR_TRUE);
|
||||
}
|
||||
//
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
|
@ -1145,11 +1262,6 @@ nsresult CNavDTD::HandleScriptToken(nsCParserNode& aNode) {
|
|||
PRInt32 attrCount=aNode.GetAttributeCount(PR_TRUE);
|
||||
|
||||
nsresult result=AddLeaf(aNode);
|
||||
CParserContext* theContext=mParser->PeekContext();
|
||||
if(theContext && theContext->mPrevContext && (CParserContext::eCTString==theContext->mContextType)) {
|
||||
mParser->PopContext();
|
||||
delete theContext;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -1337,28 +1449,6 @@ PRBool CNavDTD::CanPropagate(eHTMLTags aParentTag,eHTMLTags aChildTag) const {
|
|||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this to see if you have a closeable peer on the stack that
|
||||
* is ABOVE one of its root tags.
|
||||
*
|
||||
* @update gess 3/25/98
|
||||
* @param aRootTagList -- list of root tags for aTag
|
||||
* @param aTag -- tag to test for containership
|
||||
* @return PR_TRUE if given tag can contain other tags
|
||||
*/
|
||||
PRBool HasCloseablePeerAboveRoot(CTagList& aRootTagList,nsTagStack& aTagStack,eHTMLTags aTag) {
|
||||
PRInt32 theRootIndex=aRootTagList.GetTopmostIndexOf(aTagStack);
|
||||
CTagList* theCloseTags=gHTMLElements[aTag].GetAutoCloseStartTags();
|
||||
PRInt32 theChildIndex=-1;
|
||||
PRBool result=PR_FALSE;
|
||||
if(theCloseTags) {
|
||||
theChildIndex=theCloseTags->GetTopmostIndexOf(aTagStack);
|
||||
}
|
||||
else {
|
||||
theChildIndex=aTagStack.GetTopmostIndexOf(aTag);
|
||||
}
|
||||
return PRBool(theRootIndex<theChildIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method gets called to determine whether a given
|
||||
|
@ -1407,7 +1497,9 @@ PRBool CNavDTD::CanOmit(eHTMLTags aParent,eHTMLTags aChild) const {
|
|||
default:
|
||||
if(FindTagInSet(aChild,gFormElementTags,sizeof(gFormElementTags)/sizeof(eHTMLTag_unknown)))
|
||||
result=!HasOpenContainer(eHTMLTag_form);
|
||||
else result=FindTagInSet(aChild,gWhitespaceTags,sizeof(gWhitespaceTags)/sizeof(eHTMLTag_unknown));
|
||||
else if(!gHTMLElements[aParent].CanContain(aChild)) {
|
||||
result=PR_TRUE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -1431,6 +1523,9 @@ PRBool CNavDTD::CanOmit(eHTMLTags aParent,eHTMLTags aChild) const {
|
|||
//ok, since no parent claimed it, test based on the child...
|
||||
switch(aChild) {
|
||||
|
||||
case eHTMLTag_textarea:
|
||||
break;
|
||||
|
||||
case eHTMLTag_userdefined:
|
||||
case eHTMLTag_comment:
|
||||
result=PR_TRUE;
|
||||
|
@ -1443,7 +1538,7 @@ PRBool CNavDTD::CanOmit(eHTMLTags aParent,eHTMLTags aChild) const {
|
|||
// case eHTMLTag_input:
|
||||
case eHTMLTag_fieldset: case eHTMLTag_isindex:
|
||||
case eHTMLTag_label: case eHTMLTag_legend:
|
||||
case eHTMLTag_select: case eHTMLTag_textarea:
|
||||
case eHTMLTag_select: //case eHTMLTag_textarea:
|
||||
case eHTMLTag_option:
|
||||
result=!HasOpenContainer(eHTMLTag_form);
|
||||
break;
|
||||
|
@ -1542,65 +1637,6 @@ PRBool IsCompatibleTag(eHTMLTags aTag1,eHTMLTags aTag2) {
|
|||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called to determine whether or not an END tag
|
||||
* can be autoclosed. This means that based on the current
|
||||
* context, the stack should be closed to the nearest matching
|
||||
* tag.
|
||||
*
|
||||
* @param aTag -- tag enum of child to be tested
|
||||
* @return PR_TRUE if autoclosure should occur
|
||||
*/
|
||||
eHTMLTags FindAutoCloseTargetForEndTag(eHTMLTags aCurrentTag,nsTagStack& aTagStack,PRInt32 aChildIndex) {
|
||||
int theTopIndex=aTagStack.mCount;
|
||||
eHTMLTags aPrevTag=aTagStack.mTags[theTopIndex-1];
|
||||
|
||||
if(nsHTMLElement::IsContainer(aCurrentTag)){
|
||||
if(aPrevTag==aCurrentTag){
|
||||
return aCurrentTag;
|
||||
}
|
||||
|
||||
if(nsHTMLElement::IsBlockCloser(aCurrentTag)) {
|
||||
|
||||
/*here's what to do:
|
||||
Our here is sitting at aChildIndex. There are other tags above it
|
||||
on the stack. We have to try to close them out, but we may encounter
|
||||
one that can block us. The way to tell is by comparing each tag on
|
||||
the stack against our closeTag and rootTag list.
|
||||
|
||||
For each tag above our here on the stack, ask 3 questions:
|
||||
1. Is it in the closeTag list? If so, the we can skip over it
|
||||
2. Is it in the rootTag list? If so, then we're gated by it
|
||||
3. Otherwise its non-specified and we simply presume we can close it.
|
||||
*/
|
||||
|
||||
CTagList* theCloseTags=gHTMLElements[aCurrentTag].GetAutoCloseEndTags();
|
||||
CTagList* theRootTags=gHTMLElements[aCurrentTag].GetRootTags();
|
||||
if(theCloseTags){
|
||||
|
||||
while(aChildIndex<--theTopIndex) {
|
||||
eHTMLTags theNextTag=aTagStack.mTags[theTopIndex];
|
||||
if(PR_FALSE==theCloseTags->Contains(theNextTag)) {
|
||||
if(PR_TRUE==theRootTags->Contains(theNextTag)) {
|
||||
return eHTMLTag_unknown; //we encountered a tag in root list so fail (because we're gated).
|
||||
}
|
||||
//otherwise presume it's something we can simply ignore and continue search...
|
||||
}
|
||||
//otherwise its in the close list so skip to next tag...
|
||||
}
|
||||
return aCurrentTag; //if you make it here, we're ungated and found a target!
|
||||
}//if
|
||||
else if(theRootTags) {
|
||||
//since we didn't find any close tags, see if there is an instance of aCurrentTag
|
||||
//above the stack from the roottag.
|
||||
if(HasCloseablePeerAboveRoot(*theRootTags,aTagStack,aCurrentTag))
|
||||
return aCurrentTag;
|
||||
else return eHTMLTag_unknown;
|
||||
}
|
||||
} //if
|
||||
} //if
|
||||
return eHTMLTag_unknown;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method gets called to determine whether a given
|
||||
|
@ -1613,62 +1649,34 @@ eHTMLTags FindAutoCloseTargetForEndTag(eHTMLTags aCurrentTag,nsTagStack& aTagSta
|
|||
PRBool CNavDTD::CanOmitEndTag(eHTMLTags aParent,eHTMLTags aChild) const {
|
||||
PRBool result=PR_FALSE;
|
||||
|
||||
//begin with some simple (and obvious) cases...
|
||||
switch((eHTMLTags)aChild) {
|
||||
if(gHTMLElements[aChild].CanOmitEndTag(aParent)) {
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
||||
case eHTMLTag_userdefined:
|
||||
case eHTMLTag_comment:
|
||||
result=PR_TRUE;
|
||||
break;
|
||||
|
||||
case eHTMLTag_a:
|
||||
result=!HasOpenContainer(aChild);
|
||||
break;
|
||||
|
||||
case eHTMLTag_html:
|
||||
case eHTMLTag_body:
|
||||
result=PR_TRUE;
|
||||
break;
|
||||
|
||||
case eHTMLTag_newline:
|
||||
case eHTMLTag_whitespace:
|
||||
|
||||
switch(aParent) {
|
||||
case eHTMLTag_html: case eHTMLTag_head:
|
||||
case eHTMLTag_title: case eHTMLTag_map:
|
||||
case eHTMLTag_tr: case eHTMLTag_table:
|
||||
case eHTMLTag_thead: case eHTMLTag_tfoot:
|
||||
case eHTMLTag_tbody: case eHTMLTag_col:
|
||||
case eHTMLTag_colgroup: case eHTMLTag_unknown:
|
||||
result=PR_TRUE;
|
||||
default:
|
||||
break;
|
||||
} //switch
|
||||
break;
|
||||
|
||||
//It turns out that a <Hn> can be closed by any other <H?>
|
||||
//This code makes them all seem compatible.
|
||||
case eHTMLTag_h1: case eHTMLTag_h2:
|
||||
case eHTMLTag_h3: case eHTMLTag_h4:
|
||||
case eHTMLTag_h5: case eHTMLTag_h6:
|
||||
if(FindTagInSet(aParent,gHeadingTags,sizeof(gHeadingTags)/sizeof(eHTMLTag_unknown))) {
|
||||
result=PR_FALSE;
|
||||
break;
|
||||
/*
|
||||
CTagList* theRootTags=gHTMLElements[aChild].GetRootTags();
|
||||
if(theRootTags) {
|
||||
PRInt32 theRootIndex=theRootTags->GetTopmostIndexOf(mBodyContext->mTags);
|
||||
PRInt32 theChildIndex=GetTopmostIndexOf(aChild);
|
||||
if(kNotFound==theChildIndex) {
|
||||
CTagList* theSynTags=gHTMLElements[aChild].GetSynonymousTags(); //get the list of tags that THIS tag can close
|
||||
if(theSynTags) {
|
||||
theChildIndex=theSynTags->GetTopmostIndexOf(mBodyContext->mTags);
|
||||
}
|
||||
//Otherwise, IT's OK TO FALL THROUGH HERE...
|
||||
}
|
||||
result=!PRBool(theRootIndex<theChildIndex);
|
||||
}
|
||||
*/
|
||||
|
||||
PRInt32 theChildIndex=GetTopmostIndexOf(aChild);
|
||||
if(kNotFound==theChildIndex) {
|
||||
CTagList* theSynTags=gHTMLElements[aChild].GetSynonymousTags(); //get the list of tags that THIS tag can close
|
||||
if(theSynTags) {
|
||||
theChildIndex=theSynTags->GetTopmostIndexOf(mBodyContext->mTags);
|
||||
}
|
||||
}
|
||||
result=PRBool(kNotFound==theChildIndex);
|
||||
|
||||
default:
|
||||
{
|
||||
PRInt32 theTagPos=GetTopmostIndexOf(aChild);
|
||||
if(kNotFound!=theTagPos) {
|
||||
eHTMLTags theTarget=FindAutoCloseTargetForEndTag(aChild,mBodyContext->mTags,theTagPos);
|
||||
result=PRBool(eHTMLTag_unknown==theTarget);
|
||||
}
|
||||
else result=PR_TRUE;
|
||||
}
|
||||
break;
|
||||
} //switch
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -48,7 +48,7 @@ public:
|
|||
|
||||
PRInt32 GetTopmostIndexOf(nsTagStack& aTagStack);
|
||||
PRInt32 GetBottommostIndexOf(nsTagStack& aTagStack,PRInt32 aStartOffset);
|
||||
PRBool Contains(eHTMLTags aTag);
|
||||
inline PRBool Contains(eHTMLTags aTag);
|
||||
|
||||
eHTMLTags mTags[5];
|
||||
eHTMLTags* mTagList;
|
||||
|
@ -76,8 +76,10 @@ struct nsHTMLElement {
|
|||
static PRBool IsBlockCloser(eHTMLTags aTag);
|
||||
|
||||
CTagList* GetRootTags(void) const {return mRootNodes;}
|
||||
CTagList* GetEndRootTags(void) const {return mEndRootNodes;}
|
||||
CTagList* GetAutoCloseStartTags(void) const {return mAutocloseStart;}
|
||||
CTagList* GetAutoCloseEndTags(void) const {return mAutocloseEnd;}
|
||||
CTagList* GetSynonymousTags(void) const {return mSynonymousTags;}
|
||||
|
||||
static PRBool IsBlockParent(eHTMLTags aTag);
|
||||
static PRBool IsInlineParent(eHTMLTags aTag);
|
||||
|
@ -85,11 +87,14 @@ struct nsHTMLElement {
|
|||
|
||||
CTagList* GetSpecialChildren(void) const {return mSpecialKids;}
|
||||
CTagList* GetSpecialParents(void) const {return mSpecialParents;}
|
||||
|
||||
|
||||
PRBool IsMemberOf(PRInt32 aType) const;
|
||||
PRBool CanContainType(PRInt32 aType) const;
|
||||
|
||||
eHTMLTags GetTag(void) const {return mTagID;}
|
||||
PRBool CanContain(eHTMLTags aChild) const;
|
||||
PRBool CanOmitStartTag(eHTMLTags aChild) const;
|
||||
PRBool CanOmitEndTag(eHTMLTags aChild) const;
|
||||
PRBool CanOmitEndTag(eHTMLTags aParent) const;
|
||||
PRBool CanContainSelf() const;
|
||||
PRBool HasSpecialProperty(PRInt32 aProperty) const;
|
||||
|
||||
|
@ -101,9 +106,11 @@ struct nsHTMLElement {
|
|||
static PRBool IsTextTag(eHTMLTags aTag);
|
||||
|
||||
eHTMLTags mTagID;
|
||||
CTagList* mRootNodes; //These are the tags above which you many not autoclose
|
||||
CTagList* mRootNodes; //These are the tags above which you many not autoclose a START tag
|
||||
CTagList* mEndRootNodes; //These are the tags above which you many not autoclose an END tag
|
||||
CTagList* mAutocloseStart; //these are the start tags that you can automatically close with this START tag
|
||||
CTagList* mAutocloseEnd; //these are the start tags that you can automatically close with this END tag
|
||||
CTagList* mSynonymousTags; //These are morally equivalent; an end tag for one can close a start tag for another (like <Hn>)
|
||||
int mParentBits; //defines groups that can contain this element
|
||||
int mInclusionBits; //defines parental and containment rules
|
||||
int mExclusionBits; //defines things you CANNOT contain
|
||||
|
@ -118,6 +125,7 @@ extern nsHTMLElement gHTMLElements[];
|
|||
|
||||
//special property bits...
|
||||
static const int kDiscardTag = 0x0001; //tells us to toss this tag
|
||||
static const int kOmitEndTag = 0x0002; //safely ignore end tag
|
||||
|
||||
|
||||
#endif
|
|
@ -580,7 +580,7 @@ nsresult CCDATASectionToken::Consume(PRUnichar aChar, nsScanner& aScanner) {
|
|||
|
||||
|
||||
/*
|
||||
* default constructor
|
||||
* Default constructor
|
||||
*
|
||||
* @update gess 3/25/98
|
||||
* @param aName -- string to init token name with
|
||||
|
@ -591,7 +591,7 @@ CCommentToken::CCommentToken() : CHTMLToken(eHTMLTag_comment) {
|
|||
|
||||
|
||||
/*
|
||||
* Default constructor
|
||||
* Copy constructor
|
||||
*
|
||||
* @update gess 3/25/98
|
||||
* @param
|
||||
|
@ -601,47 +601,140 @@ CCommentToken::CCommentToken(const nsString& aName) : CHTMLToken(aName) {
|
|||
mTypeID=eHTMLTag_comment;
|
||||
}
|
||||
|
||||
/*
|
||||
* This method consumes a comment using the (CORRECT) comment parsing
|
||||
* algorithm supplied by W3C.
|
||||
*
|
||||
* @update gess 01/04/99
|
||||
* @param
|
||||
* @param
|
||||
* @return
|
||||
*/
|
||||
nsresult ConsumeStrictComment(PRUnichar aChar, nsScanner& aScanner,nsString& aString) {
|
||||
static nsAutoString gMinus("-");
|
||||
nsresult result=NS_OK;
|
||||
|
||||
/*********************************************************
|
||||
NOTE: This algorithm does a fine job of handling comments
|
||||
when they're formatted per spec, but if they're not
|
||||
we don't handle them well. For example, we gack
|
||||
on the following:
|
||||
|
||||
<!-- xx -- xx -->
|
||||
*********************************************************/
|
||||
|
||||
aString="<!";
|
||||
while(NS_OK==result) {
|
||||
result=aScanner.GetChar(aChar);
|
||||
if(NS_OK==result) {
|
||||
aString+=aChar;
|
||||
if(kMinus==aChar) {
|
||||
result=aScanner.GetChar(aChar);
|
||||
if(NS_OK==result) {
|
||||
if(kMinus==aChar) {
|
||||
//in this case, we're reading a long-form comment <-- xxx -->
|
||||
aString+=aChar;
|
||||
result=aScanner.ReadWhile(aString,gMinus,PR_TRUE,PR_FALSE); //get all available '---'
|
||||
if(NS_OK==result) {
|
||||
PRInt32 findpos=-1;
|
||||
nsAutoString temp("");
|
||||
//Read to the first ending sequence '--'
|
||||
while((kNotFound==findpos) && (NS_OK==result)) {
|
||||
result=aScanner.ReadUntil(temp,kMinus,PR_TRUE);
|
||||
findpos=temp.RFind("--");
|
||||
}
|
||||
aString+=temp;
|
||||
if(NS_OK==result) {
|
||||
result=aScanner.ReadWhile(aString,gMinus,PR_TRUE,PR_FALSE); //get all available '---'
|
||||
if(NS_OK==result) {
|
||||
temp="->";
|
||||
result=aScanner.ReadUntil(aString,temp,PR_FALSE,PR_FALSE);
|
||||
}
|
||||
}
|
||||
}
|
||||
} //
|
||||
else break; //go find '>'
|
||||
}
|
||||
}//if
|
||||
else if(kGreaterThan==aChar) {
|
||||
return result;
|
||||
}
|
||||
else break; //go find '>'
|
||||
}//if
|
||||
}//while
|
||||
if(NS_OK==result) {
|
||||
//if you're here, we're consuming a "short-form" comment
|
||||
result=aScanner.ReadUntil(aString,kGreaterThan,PR_TRUE);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* This method consumes a comment using common (actually non-standard)
|
||||
* algorithm that seems to work against the content on the web.
|
||||
*
|
||||
* @update gess 01/04/99
|
||||
* @param
|
||||
* @param
|
||||
* @return
|
||||
*/
|
||||
nsresult ConsumeComment(PRUnichar aChar, nsScanner& aScanner,nsString& aString) {
|
||||
static nsAutoString gMinus("-");
|
||||
nsresult result=NS_OK;
|
||||
|
||||
/*********************************************************
|
||||
NOTE: This algorithm does a fine job of handling comments
|
||||
commonly used, but it doesn't really consume them
|
||||
per spec (But then, neither does IE or Nav).
|
||||
*********************************************************/
|
||||
|
||||
aString="<!";
|
||||
result=aScanner.GetChar(aChar);
|
||||
if(NS_OK==result) {
|
||||
aString+=aChar;
|
||||
if(kMinus==aChar) {
|
||||
result=aScanner.GetChar(aChar);
|
||||
if(NS_OK==result) {
|
||||
if(kMinus==aChar) {
|
||||
//in this case, we're reading a long-form comment <-- xxx -->
|
||||
aString+=aChar;
|
||||
nsAutoString temp("");
|
||||
PRBool done=PR_FALSE;
|
||||
PRInt32 findpos=kNotFound;
|
||||
result=aScanner.ReadWhile(temp,gMinus,PR_TRUE,PR_TRUE); //get all available '---'
|
||||
while((kNotFound==findpos) && (NS_OK==result)) {
|
||||
result=aScanner.ReadUntil(temp,kMinus,PR_TRUE);
|
||||
if(NS_OK==result) {
|
||||
result=aScanner.ReadWhile(temp,gMinus,PR_TRUE,PR_TRUE); //get all available '---'
|
||||
}
|
||||
findpos=temp.RFind("->");
|
||||
aString+=temp;
|
||||
temp="";
|
||||
} //while
|
||||
return result;
|
||||
} //if
|
||||
}//if
|
||||
}//if
|
||||
}//if
|
||||
if(NS_OK==result) {
|
||||
//Read up to the closing '>'
|
||||
result=aScanner.ReadUntil(aString,kGreaterThan,PR_TRUE);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Consume the identifier portion of the comment.
|
||||
* Note that we've already eaten the "<!" portion.
|
||||
*
|
||||
* @update gess 3/25/98
|
||||
* @update gess 1/27/99
|
||||
* @param aChar -- last char consumed from stream
|
||||
* @param aScanner -- controller of underlying input source
|
||||
* @return error result
|
||||
*/
|
||||
nsresult CCommentToken::Consume(PRUnichar aChar, nsScanner& aScanner) {
|
||||
|
||||
nsresult result=NS_OK;
|
||||
|
||||
result=aScanner.GetChar(aChar);
|
||||
if(NS_OK==result) {
|
||||
mTextValue="<!";
|
||||
if(kMinus==aChar) {
|
||||
mTextValue+="-";
|
||||
result=aScanner.GetChar(aChar);
|
||||
if(NS_OK==result) {
|
||||
if(kMinus==aChar) {
|
||||
//in this case, we're reading a long-form comment <-- xxx -->
|
||||
mTextValue+="-";
|
||||
PRInt32 findpos=-1;
|
||||
while((findpos<3) && (NS_OK==result)) {
|
||||
result=aScanner.ReadUntil(mTextValue,kMinus,PR_TRUE);
|
||||
findpos=mTextValue.RFind("--");
|
||||
}
|
||||
if(NS_OK==result) {
|
||||
result=aScanner.ReadUntil(mTextValue,kGreaterThan,PR_TRUE); //now skip to '>'
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(NS_OK==result) {
|
||||
//if you're here, we're consuming a "short-form" comment
|
||||
mTextValue+=aChar;
|
||||
result=aScanner.ReadUntil(mTextValue,kGreaterThan,PR_TRUE);
|
||||
}
|
||||
PRBool theStrictForm=PR_FALSE;
|
||||
nsresult result=(theStrictForm) ? ConsumeStrictComment(aChar,aScanner,mTextValue) : ConsumeComment(aChar,aScanner,mTextValue);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -842,7 +935,7 @@ void CAttributeToken::DebugDumpToken(ostream& out) {
|
|||
mTextValue.ToCString(buffer,sizeof(buffer)-1);
|
||||
out << buffer << ": " << mTypeID << endl;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* This general purpose method is used when you want to
|
||||
|
@ -872,6 +965,7 @@ nsresult ConsumeQuotedString(PRUnichar aChar,nsString& aString,nsScanner& aScann
|
|||
PRUnichar ch=aString.Last();
|
||||
if(ch!=aChar)
|
||||
aString+=aChar;
|
||||
aString.StripChars("\r\n");
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
@ -141,7 +141,7 @@ CSharedParserObjects gSharedParserObjects;
|
|||
* @param
|
||||
* @return
|
||||
*/
|
||||
nsParser::nsParser(nsITokenObserver* anObserver) : mCommand("") {
|
||||
nsParser::nsParser(nsITokenObserver* anObserver) : mCommand(""), mUnusedInput("") {
|
||||
NS_INIT_REFCNT();
|
||||
mParserFilter = 0;
|
||||
mObserver = 0;
|
||||
|
@ -519,7 +519,21 @@ CParserContext* nsParser::PopContext() {
|
|||
* and tokenize input (TRUE), or whether it just caches input to be
|
||||
* parsed later (FALSE).
|
||||
*
|
||||
* @update vidur 12/11/98
|
||||
* @update gess 1/29/99
|
||||
* @param aState determines whether we parse/tokenize or just cache.
|
||||
* @return current state
|
||||
*/
|
||||
void nsParser::SetUnusedInput(nsString& aBuffer) {
|
||||
mUnusedInput=aBuffer;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Call this when you want control whether or not the parser will parse
|
||||
* and tokenize input (TRUE), or whether it just caches input to be
|
||||
* parsed later (FALSE).
|
||||
*
|
||||
* @update gess 1/29/99
|
||||
* @param aState determines whether we parse/tokenize or just cache.
|
||||
* @return current state
|
||||
*/
|
||||
|
@ -629,32 +643,36 @@ nsresult nsParser::Parse(nsString& aSourceBuffer,PRBool anHTMLString,PRBool aEna
|
|||
//NOTE: Make sure that updates to this method don't cause
|
||||
// bug #2361 to break again!
|
||||
|
||||
mDTDVerification=aEnableVerify;
|
||||
nsresult result=NS_OK;
|
||||
CParserContext* pc=0;
|
||||
if(aSourceBuffer.Length() || mUnusedInput.Length()) {
|
||||
mDTDVerification=aEnableVerify;
|
||||
CParserContext* pc=0;
|
||||
|
||||
if((!mParserContext) || (mParserContext->mKey!=&aSourceBuffer)) {
|
||||
//only make a new context if we dont have one, OR if we do, but has a different context key...
|
||||
pc=new CParserContext(new nsScanner(aSourceBuffer),&aSourceBuffer,0);
|
||||
if(pc) {
|
||||
PushContext(*pc);
|
||||
pc->mStreamListenerState=eOnStart;
|
||||
pc->mContextType=CParserContext::eCTString;
|
||||
if(PR_TRUE==anHTMLString)
|
||||
pc->mSourceType="text/html";
|
||||
}
|
||||
else return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
else {
|
||||
pc=mParserContext;
|
||||
if((!mParserContext) || (mParserContext->mKey!=&aSourceBuffer)) {
|
||||
//only make a new context if we dont have one, OR if we do, but has a different context key...
|
||||
pc=new CParserContext(new nsScanner(mUnusedInput),&aSourceBuffer,0);
|
||||
if(pc) {
|
||||
PushContext(*pc);
|
||||
pc->mStreamListenerState=eOnStart;
|
||||
pc->mContextType=CParserContext::eCTString;
|
||||
if(PR_TRUE==anHTMLString)
|
||||
pc->mSourceType="text/html";
|
||||
}
|
||||
else return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
else {
|
||||
pc=mParserContext;
|
||||
pc->mScanner->Append(mUnusedInput);
|
||||
}
|
||||
pc->mScanner->Append(aSourceBuffer);
|
||||
}
|
||||
pc->mMultipart=!aLastCall;
|
||||
result=ResumeParse();
|
||||
if(aLastCall) {
|
||||
pc=PopContext();
|
||||
delete pc;
|
||||
}
|
||||
pc->mMultipart=!aLastCall;
|
||||
result=ResumeParse();
|
||||
if(aLastCall) {
|
||||
pc->mScanner->CopyUnusedData(mUnusedInput);
|
||||
pc=PopContext();
|
||||
delete pc;
|
||||
}//if
|
||||
}//if
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
@ -177,6 +177,19 @@ friend class CTokenHandler;
|
|||
*/
|
||||
virtual PRBool EnableParser(PRBool aState);
|
||||
|
||||
|
||||
/**
|
||||
* This rather arcane method (hack) is used as a signal between the
|
||||
* DTD and the parser. It allows the DTD to tell the parser that content
|
||||
* that comes through (parser::parser(string)) but not consumed should
|
||||
* propagate into the next string based parse call.
|
||||
*
|
||||
* @update gess 9/1/98
|
||||
* @param aState determines whether we propagate unused string content.
|
||||
* @return current state
|
||||
*/
|
||||
void SetUnusedInput(nsString& aBuffer);
|
||||
|
||||
/**
|
||||
* This method gets called (automatically) during incremental parsing
|
||||
* @update gess5/11/98
|
||||
|
@ -304,6 +317,7 @@ protected:
|
|||
nsString mCommand;
|
||||
PRInt32 mStreamStatus;
|
||||
nsITokenObserver* mTokenObserver;
|
||||
nsString mUnusedInput;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -520,6 +520,21 @@ nsString& nsScanner::GetBuffer(void) {
|
|||
return mBuffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this to copy bytes out of the scanner that have not yet been consumed
|
||||
* by the tokenization process.
|
||||
*
|
||||
* @update gess 5/12/98
|
||||
* @param aCopyBuffer is where the scanner buffer will be copied to
|
||||
* @return nada
|
||||
*/
|
||||
void nsScanner::CopyUnusedData(nsString& aCopyBuffer) {
|
||||
PRInt32 theLen=mBuffer.Length();
|
||||
if(0<theLen) {
|
||||
mBuffer.Right(aCopyBuffer,theLen-mOffset);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the name of the file that the scanner is reading from.
|
||||
* In some cases, it's just a given name, because the scanner isn't
|
||||
|
|
|
@ -243,6 +243,16 @@ class nsScanner {
|
|||
*/
|
||||
nsString& GetBuffer(void);
|
||||
|
||||
/**
|
||||
* Call this to copy bytes out of the scanner that have not yet been consumed
|
||||
* by the tokenization process.
|
||||
*
|
||||
* @update gess 5/12/98
|
||||
* @param aCopyBuffer is where the scanner buffer will be copied to
|
||||
* @return nada
|
||||
*/
|
||||
void CopyUnusedData(nsString& aCopyBuffer);
|
||||
|
||||
/**
|
||||
* Retrieve the name of the file that the scanner is reading from.
|
||||
* In some cases, it's just a given name, because the scanner isn't
|
||||
|
|
|
@ -1268,7 +1268,7 @@ nsHTMLDocument::WriteCommon(JSContext *cx,
|
|||
str.Append('\n');
|
||||
}
|
||||
|
||||
result = mParser->Parse(str, PR_TRUE,PR_FALSE,PR_FALSE);
|
||||
result = mParser->Parse(str, PR_TRUE,PR_FALSE,PR_TRUE);
|
||||
if (NS_OK != result) {
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -696,23 +696,31 @@ PRInt32 GetTopmostIndexOf(eHTMLTags aTag,nsTagStack& aTagStack) {
|
|||
*/
|
||||
eHTMLTags FindAutoCloseTargetForStartTag(eHTMLTags aCurrentTag,nsTagStack& aTagStack) {
|
||||
int theTopIndex=aTagStack.mCount;
|
||||
eHTMLTags aPrevTag=aTagStack.mTags[theTopIndex-1];
|
||||
eHTMLTags thePrevTag=aTagStack.Last();
|
||||
|
||||
if(nsHTMLElement::IsContainer(aCurrentTag)){
|
||||
if(aPrevTag==aCurrentTag) {
|
||||
if(thePrevTag==aCurrentTag) {
|
||||
return (gHTMLElements[aCurrentTag].CanContainSelf()) ? eHTMLTag_unknown: aCurrentTag;
|
||||
}
|
||||
if(nsHTMLElement::IsBlockCloser(aCurrentTag)) {
|
||||
|
||||
PRInt32 theRootIndex=kNotFound;
|
||||
CTagList* theRootTags=gHTMLElements[aCurrentTag].GetRootTags();
|
||||
if(theRootTags) {
|
||||
PRInt32 theRootIndex=theRootTags->GetTopmostIndexOf(aTagStack);
|
||||
theRootIndex=theRootTags->GetTopmostIndexOf(aTagStack);
|
||||
CTagList* theStartTags=gHTMLElements[aCurrentTag].GetAutoCloseStartTags();
|
||||
PRInt32 thePeerIndex=kNotFound;
|
||||
if(theStartTags){
|
||||
thePeerIndex=theStartTags->GetBottommostIndexOf(aTagStack,theRootIndex+1);
|
||||
}
|
||||
else thePeerIndex=GetTopmostIndexOf(aCurrentTag,aTagStack);
|
||||
else {
|
||||
//this extra check is need to handle case like this: <DIV><P><DIV>
|
||||
//the new div can close the P,but doesn't close the top DIV.
|
||||
thePeerIndex=GetTopmostIndexOf(aCurrentTag,aTagStack);
|
||||
if(gHTMLElements[aCurrentTag].CanContainSelf()) {
|
||||
thePeerIndex++;
|
||||
}
|
||||
}
|
||||
if(theRootIndex<thePeerIndex) {
|
||||
return aTagStack.mTags[thePeerIndex]; //return the tag that was used in peer test.
|
||||
}
|
||||
|
@ -724,14 +732,19 @@ eHTMLTags FindAutoCloseTargetForStartTag(eHTMLTags aCurrentTag,nsTagStack& aTagS
|
|||
if(kNotFound!=thePeerIndex){
|
||||
if(thePeerIndex==theTopIndex-1) {
|
||||
//the guy you can autoclose is on the top of the stack...
|
||||
return aPrevTag;
|
||||
return thePrevTag;
|
||||
} //if
|
||||
} //if
|
||||
}//if
|
||||
else if(kNotFound<theRootIndex) {
|
||||
//This block handles our fallback cases like: <html><body><center><p> <- <table>
|
||||
while((theRootIndex<--theTopIndex) && (!gHTMLElements[aTagStack.mTags[theTopIndex]].CanContain(aCurrentTag))) {
|
||||
}
|
||||
return aTagStack.mTags[theTopIndex+1];
|
||||
//return aTagStack.mTags[theRootIndex+1];
|
||||
}
|
||||
|
||||
} //if
|
||||
else if(nsHTMLElement::IsInlineElement(aCurrentTag)) {
|
||||
}//if
|
||||
} //if
|
||||
return eHTMLTag_unknown;
|
||||
}
|
||||
|
@ -748,6 +761,24 @@ eHTMLTags FindAutoCloseTargetForStartTag(eHTMLTags aCurrentTag,nsTagStack& aTagS
|
|||
*/
|
||||
PRBool CanBeContained(eHTMLTags aChildTag,nsTagStack& aTagStack) {
|
||||
PRBool result=PR_TRUE;
|
||||
|
||||
CTagList* theRootTags=gHTMLElements[aChildTag].GetRootTags();
|
||||
if(theRootTags) {
|
||||
PRInt32 theRootIndex=theRootTags->GetTopmostIndexOf(aTagStack);
|
||||
PRInt32 theChildIndex=aTagStack.GetTopmostIndexOf(aChildTag);
|
||||
if(kNotFound==theChildIndex) {
|
||||
CTagList* theSynTags=gHTMLElements[aChildTag].GetSynonymousTags(); //get the list of tags that THIS tag can close
|
||||
if(theSynTags) {
|
||||
theChildIndex=theSynTags->GetTopmostIndexOf(aTagStack);
|
||||
}
|
||||
}
|
||||
if(theRootIndex<theChildIndex){
|
||||
eHTMLTags thePrevTag=aTagStack.Last();
|
||||
result=gHTMLElements[thePrevTag].CanContainType(gHTMLElements[aChildTag].mParentBits);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -977,6 +1008,114 @@ nsresult CNavDTD::HandleStartToken(CToken* aToken) {
|
|||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this to see if you have a closeable peer on the stack that
|
||||
* is ABOVE one of its root tags.
|
||||
*
|
||||
* @update gess 3/25/98
|
||||
* @param aRootTagList -- list of root tags for aTag
|
||||
* @param aTag -- tag to test for containership
|
||||
* @return PR_TRUE if given tag can contain other tags
|
||||
*/
|
||||
PRBool HasCloseablePeerAboveRoot(CTagList& aRootTagList,nsTagStack& aTagStack,eHTMLTags aTag) {
|
||||
PRInt32 theRootIndex=aRootTagList.GetTopmostIndexOf(aTagStack);
|
||||
CTagList* theCloseTags=gHTMLElements[aTag].GetAutoCloseStartTags();
|
||||
PRInt32 theChildIndex=-1;
|
||||
PRBool result=PR_FALSE;
|
||||
if(theCloseTags) {
|
||||
theChildIndex=theCloseTags->GetTopmostIndexOf(aTagStack);
|
||||
}
|
||||
else {
|
||||
theChildIndex=aTagStack.GetTopmostIndexOf(aTag);
|
||||
}
|
||||
return PRBool(theRootIndex<theChildIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this to find the index of a given child, or (if not found)
|
||||
* the index of its nearest synonym.
|
||||
*
|
||||
* @update gess 3/25/98
|
||||
* @param aTagStack -- list of open tags
|
||||
* @param aTag -- tag to test for containership
|
||||
* @return index of kNotFound
|
||||
*/
|
||||
PRInt32 GetIndexOfChildOrSynonym(nsTagStack& aTagStack,eHTMLTags aChildTag) {
|
||||
PRInt32 theChildIndex=aTagStack.GetTopmostIndexOf(aChildTag);
|
||||
if(kNotFound==theChildIndex) {
|
||||
CTagList* theSynTags=gHTMLElements[aChildTag].GetSynonymousTags(); //get the list of tags that THIS tag can close
|
||||
if(theSynTags) {
|
||||
theChildIndex=theSynTags->GetTopmostIndexOf(aTagStack);
|
||||
}
|
||||
}
|
||||
return theChildIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called to determine whether or not an END tag
|
||||
* can be autoclosed. This means that based on the current
|
||||
* context, the stack should be closed to the nearest matching
|
||||
* tag.
|
||||
*
|
||||
* @param aTag -- tag enum of child to be tested
|
||||
* @return PR_TRUE if autoclosure should occur
|
||||
*/
|
||||
eHTMLTags FindAutoCloseTargetForEndTag(eHTMLTags aCurrentTag,nsTagStack& aTagStack) {
|
||||
int theTopIndex=aTagStack.mCount;
|
||||
eHTMLTags thePrevTag=aTagStack.Last();
|
||||
|
||||
if(nsHTMLElement::IsContainer(aCurrentTag)){
|
||||
PRInt32 theChildIndex=GetIndexOfChildOrSynonym(aTagStack,aCurrentTag);
|
||||
|
||||
if(kNotFound<theChildIndex) {
|
||||
if(thePrevTag==aTagStack.mTags[theChildIndex]){
|
||||
return aTagStack.mTags[theChildIndex];
|
||||
}
|
||||
|
||||
if(nsHTMLElement::IsBlockCloser(aCurrentTag)) {
|
||||
|
||||
/*here's what to do:
|
||||
Our here is sitting at aChildIndex. There are other tags above it
|
||||
on the stack. We have to try to close them out, but we may encounter
|
||||
one that can block us. The way to tell is by comparing each tag on
|
||||
the stack against our closeTag and rootTag list.
|
||||
|
||||
For each tag above our hero on the stack, ask 3 questions:
|
||||
1. Is it in the closeTag list? If so, the we can skip over it
|
||||
2. Is it in the rootTag list? If so, then we're gated by it
|
||||
3. Otherwise its non-specified and we simply presume we can close it.
|
||||
*/
|
||||
|
||||
CTagList* theCloseTags=gHTMLElements[aCurrentTag].GetAutoCloseEndTags();
|
||||
CTagList* theRootTags=gHTMLElements[aCurrentTag].GetEndRootTags();
|
||||
|
||||
if(theCloseTags){
|
||||
//at a min., this code is needed for H1..H6
|
||||
|
||||
while(theChildIndex<--theTopIndex) {
|
||||
eHTMLTags theNextTag=aTagStack.mTags[theTopIndex];
|
||||
if(PR_FALSE==theCloseTags->Contains(theNextTag)) {
|
||||
if(PR_TRUE==theRootTags->Contains(theNextTag)) {
|
||||
return eHTMLTag_unknown; //we encountered a tag in root list so fail (because we're gated).
|
||||
}
|
||||
//otherwise presume it's something we can simply ignore and continue search...
|
||||
}
|
||||
//otherwise its in the close list so skip to next tag...
|
||||
}
|
||||
return aCurrentTag; //if you make it here, we're ungated and found a target!
|
||||
}//if
|
||||
else if(theRootTags) {
|
||||
//since we didn't find any close tags, see if there is an instance of aCurrentTag
|
||||
//above the stack from the roottag.
|
||||
if(HasCloseablePeerAboveRoot(*theRootTags,aTagStack,aCurrentTag))
|
||||
return aCurrentTag;
|
||||
else return eHTMLTag_unknown;
|
||||
}
|
||||
} //if
|
||||
}//if
|
||||
} //if
|
||||
return eHTMLTag_unknown;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method gets called when an end token has been
|
||||
|
@ -995,23 +1134,7 @@ nsresult CNavDTD::HandleEndToken(CToken* aToken) {
|
|||
NS_PRECONDITION(0!=aToken,kNullToken);
|
||||
|
||||
nsresult result=NS_OK;
|
||||
CEndToken* et = (CEndToken*)(aToken);
|
||||
eHTMLTags theChildTag=(eHTMLTags)et->GetTypeID();
|
||||
|
||||
// Here's the hacky part:
|
||||
// Because we're trying to be backward compatible with Nav4/5,
|
||||
// we have to handle explicit styles the way it does. That means
|
||||
// that we keep an internal style stack.When an EndToken occurs,
|
||||
// we should see if it is an explicit style tag. If so, we can
|
||||
// close the explicit style tag (goofy, huh?)
|
||||
|
||||
|
||||
//now check to see if this token should be omitted, or
|
||||
//if it's gated from closing by the presence of another tag.
|
||||
if(PR_TRUE==CanOmitEndTag(mBodyContext->Last(),theChildTag)) {
|
||||
UpdateStyleStackForCloseTag(theChildTag,theChildTag);
|
||||
return result;
|
||||
}
|
||||
eHTMLTags theChildTag=(eHTMLTags)aToken->GetTypeID();
|
||||
|
||||
nsCParserNode theNode((CHTMLToken*)aToken,mLineNumber);
|
||||
switch(theChildTag) {
|
||||
|
@ -1029,26 +1152,20 @@ nsresult CNavDTD::HandleEndToken(CToken* aToken) {
|
|||
result=CloseContainer(theNode,theChildTag,PR_FALSE);
|
||||
break;
|
||||
|
||||
case eHTMLTag_td:
|
||||
case eHTMLTag_th:
|
||||
//result=CloseContainersTo(theChildTag,PR_TRUE);
|
||||
// Empty the transient style stack (we just closed any extra
|
||||
// ones off so it's safe to do it now) because they don't carry
|
||||
// forward across table cell boundaries.
|
||||
//mBodyContext->mStyles->mCount=0;
|
||||
//break;
|
||||
|
||||
default:
|
||||
if(IsContainer(theChildTag)){
|
||||
CTagList* theCloseTags=gHTMLElements[theChildTag].GetAutoCloseEndTags();
|
||||
|
||||
if(theCloseTags){
|
||||
PRInt32 thePeerIndex=theCloseTags->GetTopmostIndexOf(mBodyContext->mTags);
|
||||
theChildTag=(kNotFound<thePeerIndex) ? mBodyContext->mTags[thePeerIndex] : theChildTag;
|
||||
{
|
||||
//now check to see if this token should be omitted, or
|
||||
//if it's gated from closing by the presence of another tag.
|
||||
if(PR_TRUE==CanOmitEndTag(mBodyContext->Last(),theChildTag)) {
|
||||
UpdateStyleStackForCloseTag(theChildTag,theChildTag);
|
||||
}
|
||||
else {
|
||||
eHTMLTags theTarget=FindAutoCloseTargetForEndTag(theChildTag,mBodyContext->mTags);
|
||||
if(eHTMLTag_unknown!=theTarget) {
|
||||
result=CloseContainersTo(theTarget,PR_TRUE);
|
||||
}
|
||||
}
|
||||
result=CloseContainersTo(theChildTag,PR_TRUE);
|
||||
}
|
||||
//
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
|
@ -1145,11 +1262,6 @@ nsresult CNavDTD::HandleScriptToken(nsCParserNode& aNode) {
|
|||
PRInt32 attrCount=aNode.GetAttributeCount(PR_TRUE);
|
||||
|
||||
nsresult result=AddLeaf(aNode);
|
||||
CParserContext* theContext=mParser->PeekContext();
|
||||
if(theContext && theContext->mPrevContext && (CParserContext::eCTString==theContext->mContextType)) {
|
||||
mParser->PopContext();
|
||||
delete theContext;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -1337,28 +1449,6 @@ PRBool CNavDTD::CanPropagate(eHTMLTags aParentTag,eHTMLTags aChildTag) const {
|
|||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this to see if you have a closeable peer on the stack that
|
||||
* is ABOVE one of its root tags.
|
||||
*
|
||||
* @update gess 3/25/98
|
||||
* @param aRootTagList -- list of root tags for aTag
|
||||
* @param aTag -- tag to test for containership
|
||||
* @return PR_TRUE if given tag can contain other tags
|
||||
*/
|
||||
PRBool HasCloseablePeerAboveRoot(CTagList& aRootTagList,nsTagStack& aTagStack,eHTMLTags aTag) {
|
||||
PRInt32 theRootIndex=aRootTagList.GetTopmostIndexOf(aTagStack);
|
||||
CTagList* theCloseTags=gHTMLElements[aTag].GetAutoCloseStartTags();
|
||||
PRInt32 theChildIndex=-1;
|
||||
PRBool result=PR_FALSE;
|
||||
if(theCloseTags) {
|
||||
theChildIndex=theCloseTags->GetTopmostIndexOf(aTagStack);
|
||||
}
|
||||
else {
|
||||
theChildIndex=aTagStack.GetTopmostIndexOf(aTag);
|
||||
}
|
||||
return PRBool(theRootIndex<theChildIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method gets called to determine whether a given
|
||||
|
@ -1407,7 +1497,9 @@ PRBool CNavDTD::CanOmit(eHTMLTags aParent,eHTMLTags aChild) const {
|
|||
default:
|
||||
if(FindTagInSet(aChild,gFormElementTags,sizeof(gFormElementTags)/sizeof(eHTMLTag_unknown)))
|
||||
result=!HasOpenContainer(eHTMLTag_form);
|
||||
else result=FindTagInSet(aChild,gWhitespaceTags,sizeof(gWhitespaceTags)/sizeof(eHTMLTag_unknown));
|
||||
else if(!gHTMLElements[aParent].CanContain(aChild)) {
|
||||
result=PR_TRUE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -1431,6 +1523,9 @@ PRBool CNavDTD::CanOmit(eHTMLTags aParent,eHTMLTags aChild) const {
|
|||
//ok, since no parent claimed it, test based on the child...
|
||||
switch(aChild) {
|
||||
|
||||
case eHTMLTag_textarea:
|
||||
break;
|
||||
|
||||
case eHTMLTag_userdefined:
|
||||
case eHTMLTag_comment:
|
||||
result=PR_TRUE;
|
||||
|
@ -1443,7 +1538,7 @@ PRBool CNavDTD::CanOmit(eHTMLTags aParent,eHTMLTags aChild) const {
|
|||
// case eHTMLTag_input:
|
||||
case eHTMLTag_fieldset: case eHTMLTag_isindex:
|
||||
case eHTMLTag_label: case eHTMLTag_legend:
|
||||
case eHTMLTag_select: case eHTMLTag_textarea:
|
||||
case eHTMLTag_select: //case eHTMLTag_textarea:
|
||||
case eHTMLTag_option:
|
||||
result=!HasOpenContainer(eHTMLTag_form);
|
||||
break;
|
||||
|
@ -1542,65 +1637,6 @@ PRBool IsCompatibleTag(eHTMLTags aTag1,eHTMLTags aTag2) {
|
|||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called to determine whether or not an END tag
|
||||
* can be autoclosed. This means that based on the current
|
||||
* context, the stack should be closed to the nearest matching
|
||||
* tag.
|
||||
*
|
||||
* @param aTag -- tag enum of child to be tested
|
||||
* @return PR_TRUE if autoclosure should occur
|
||||
*/
|
||||
eHTMLTags FindAutoCloseTargetForEndTag(eHTMLTags aCurrentTag,nsTagStack& aTagStack,PRInt32 aChildIndex) {
|
||||
int theTopIndex=aTagStack.mCount;
|
||||
eHTMLTags aPrevTag=aTagStack.mTags[theTopIndex-1];
|
||||
|
||||
if(nsHTMLElement::IsContainer(aCurrentTag)){
|
||||
if(aPrevTag==aCurrentTag){
|
||||
return aCurrentTag;
|
||||
}
|
||||
|
||||
if(nsHTMLElement::IsBlockCloser(aCurrentTag)) {
|
||||
|
||||
/*here's what to do:
|
||||
Our here is sitting at aChildIndex. There are other tags above it
|
||||
on the stack. We have to try to close them out, but we may encounter
|
||||
one that can block us. The way to tell is by comparing each tag on
|
||||
the stack against our closeTag and rootTag list.
|
||||
|
||||
For each tag above our here on the stack, ask 3 questions:
|
||||
1. Is it in the closeTag list? If so, the we can skip over it
|
||||
2. Is it in the rootTag list? If so, then we're gated by it
|
||||
3. Otherwise its non-specified and we simply presume we can close it.
|
||||
*/
|
||||
|
||||
CTagList* theCloseTags=gHTMLElements[aCurrentTag].GetAutoCloseEndTags();
|
||||
CTagList* theRootTags=gHTMLElements[aCurrentTag].GetRootTags();
|
||||
if(theCloseTags){
|
||||
|
||||
while(aChildIndex<--theTopIndex) {
|
||||
eHTMLTags theNextTag=aTagStack.mTags[theTopIndex];
|
||||
if(PR_FALSE==theCloseTags->Contains(theNextTag)) {
|
||||
if(PR_TRUE==theRootTags->Contains(theNextTag)) {
|
||||
return eHTMLTag_unknown; //we encountered a tag in root list so fail (because we're gated).
|
||||
}
|
||||
//otherwise presume it's something we can simply ignore and continue search...
|
||||
}
|
||||
//otherwise its in the close list so skip to next tag...
|
||||
}
|
||||
return aCurrentTag; //if you make it here, we're ungated and found a target!
|
||||
}//if
|
||||
else if(theRootTags) {
|
||||
//since we didn't find any close tags, see if there is an instance of aCurrentTag
|
||||
//above the stack from the roottag.
|
||||
if(HasCloseablePeerAboveRoot(*theRootTags,aTagStack,aCurrentTag))
|
||||
return aCurrentTag;
|
||||
else return eHTMLTag_unknown;
|
||||
}
|
||||
} //if
|
||||
} //if
|
||||
return eHTMLTag_unknown;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method gets called to determine whether a given
|
||||
|
@ -1613,62 +1649,34 @@ eHTMLTags FindAutoCloseTargetForEndTag(eHTMLTags aCurrentTag,nsTagStack& aTagSta
|
|||
PRBool CNavDTD::CanOmitEndTag(eHTMLTags aParent,eHTMLTags aChild) const {
|
||||
PRBool result=PR_FALSE;
|
||||
|
||||
//begin with some simple (and obvious) cases...
|
||||
switch((eHTMLTags)aChild) {
|
||||
if(gHTMLElements[aChild].CanOmitEndTag(aParent)) {
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
||||
case eHTMLTag_userdefined:
|
||||
case eHTMLTag_comment:
|
||||
result=PR_TRUE;
|
||||
break;
|
||||
|
||||
case eHTMLTag_a:
|
||||
result=!HasOpenContainer(aChild);
|
||||
break;
|
||||
|
||||
case eHTMLTag_html:
|
||||
case eHTMLTag_body:
|
||||
result=PR_TRUE;
|
||||
break;
|
||||
|
||||
case eHTMLTag_newline:
|
||||
case eHTMLTag_whitespace:
|
||||
|
||||
switch(aParent) {
|
||||
case eHTMLTag_html: case eHTMLTag_head:
|
||||
case eHTMLTag_title: case eHTMLTag_map:
|
||||
case eHTMLTag_tr: case eHTMLTag_table:
|
||||
case eHTMLTag_thead: case eHTMLTag_tfoot:
|
||||
case eHTMLTag_tbody: case eHTMLTag_col:
|
||||
case eHTMLTag_colgroup: case eHTMLTag_unknown:
|
||||
result=PR_TRUE;
|
||||
default:
|
||||
break;
|
||||
} //switch
|
||||
break;
|
||||
|
||||
//It turns out that a <Hn> can be closed by any other <H?>
|
||||
//This code makes them all seem compatible.
|
||||
case eHTMLTag_h1: case eHTMLTag_h2:
|
||||
case eHTMLTag_h3: case eHTMLTag_h4:
|
||||
case eHTMLTag_h5: case eHTMLTag_h6:
|
||||
if(FindTagInSet(aParent,gHeadingTags,sizeof(gHeadingTags)/sizeof(eHTMLTag_unknown))) {
|
||||
result=PR_FALSE;
|
||||
break;
|
||||
/*
|
||||
CTagList* theRootTags=gHTMLElements[aChild].GetRootTags();
|
||||
if(theRootTags) {
|
||||
PRInt32 theRootIndex=theRootTags->GetTopmostIndexOf(mBodyContext->mTags);
|
||||
PRInt32 theChildIndex=GetTopmostIndexOf(aChild);
|
||||
if(kNotFound==theChildIndex) {
|
||||
CTagList* theSynTags=gHTMLElements[aChild].GetSynonymousTags(); //get the list of tags that THIS tag can close
|
||||
if(theSynTags) {
|
||||
theChildIndex=theSynTags->GetTopmostIndexOf(mBodyContext->mTags);
|
||||
}
|
||||
//Otherwise, IT's OK TO FALL THROUGH HERE...
|
||||
}
|
||||
result=!PRBool(theRootIndex<theChildIndex);
|
||||
}
|
||||
*/
|
||||
|
||||
PRInt32 theChildIndex=GetTopmostIndexOf(aChild);
|
||||
if(kNotFound==theChildIndex) {
|
||||
CTagList* theSynTags=gHTMLElements[aChild].GetSynonymousTags(); //get the list of tags that THIS tag can close
|
||||
if(theSynTags) {
|
||||
theChildIndex=theSynTags->GetTopmostIndexOf(mBodyContext->mTags);
|
||||
}
|
||||
}
|
||||
result=PRBool(kNotFound==theChildIndex);
|
||||
|
||||
default:
|
||||
{
|
||||
PRInt32 theTagPos=GetTopmostIndexOf(aChild);
|
||||
if(kNotFound!=theTagPos) {
|
||||
eHTMLTags theTarget=FindAutoCloseTargetForEndTag(aChild,mBodyContext->mTags,theTagPos);
|
||||
result=PRBool(eHTMLTag_unknown==theTarget);
|
||||
}
|
||||
else result=PR_TRUE;
|
||||
}
|
||||
break;
|
||||
} //switch
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -48,7 +48,7 @@ public:
|
|||
|
||||
PRInt32 GetTopmostIndexOf(nsTagStack& aTagStack);
|
||||
PRInt32 GetBottommostIndexOf(nsTagStack& aTagStack,PRInt32 aStartOffset);
|
||||
PRBool Contains(eHTMLTags aTag);
|
||||
inline PRBool Contains(eHTMLTags aTag);
|
||||
|
||||
eHTMLTags mTags[5];
|
||||
eHTMLTags* mTagList;
|
||||
|
@ -76,8 +76,10 @@ struct nsHTMLElement {
|
|||
static PRBool IsBlockCloser(eHTMLTags aTag);
|
||||
|
||||
CTagList* GetRootTags(void) const {return mRootNodes;}
|
||||
CTagList* GetEndRootTags(void) const {return mEndRootNodes;}
|
||||
CTagList* GetAutoCloseStartTags(void) const {return mAutocloseStart;}
|
||||
CTagList* GetAutoCloseEndTags(void) const {return mAutocloseEnd;}
|
||||
CTagList* GetSynonymousTags(void) const {return mSynonymousTags;}
|
||||
|
||||
static PRBool IsBlockParent(eHTMLTags aTag);
|
||||
static PRBool IsInlineParent(eHTMLTags aTag);
|
||||
|
@ -85,11 +87,14 @@ struct nsHTMLElement {
|
|||
|
||||
CTagList* GetSpecialChildren(void) const {return mSpecialKids;}
|
||||
CTagList* GetSpecialParents(void) const {return mSpecialParents;}
|
||||
|
||||
|
||||
PRBool IsMemberOf(PRInt32 aType) const;
|
||||
PRBool CanContainType(PRInt32 aType) const;
|
||||
|
||||
eHTMLTags GetTag(void) const {return mTagID;}
|
||||
PRBool CanContain(eHTMLTags aChild) const;
|
||||
PRBool CanOmitStartTag(eHTMLTags aChild) const;
|
||||
PRBool CanOmitEndTag(eHTMLTags aChild) const;
|
||||
PRBool CanOmitEndTag(eHTMLTags aParent) const;
|
||||
PRBool CanContainSelf() const;
|
||||
PRBool HasSpecialProperty(PRInt32 aProperty) const;
|
||||
|
||||
|
@ -101,9 +106,11 @@ struct nsHTMLElement {
|
|||
static PRBool IsTextTag(eHTMLTags aTag);
|
||||
|
||||
eHTMLTags mTagID;
|
||||
CTagList* mRootNodes; //These are the tags above which you many not autoclose
|
||||
CTagList* mRootNodes; //These are the tags above which you many not autoclose a START tag
|
||||
CTagList* mEndRootNodes; //These are the tags above which you many not autoclose an END tag
|
||||
CTagList* mAutocloseStart; //these are the start tags that you can automatically close with this START tag
|
||||
CTagList* mAutocloseEnd; //these are the start tags that you can automatically close with this END tag
|
||||
CTagList* mSynonymousTags; //These are morally equivalent; an end tag for one can close a start tag for another (like <Hn>)
|
||||
int mParentBits; //defines groups that can contain this element
|
||||
int mInclusionBits; //defines parental and containment rules
|
||||
int mExclusionBits; //defines things you CANNOT contain
|
||||
|
@ -118,6 +125,7 @@ extern nsHTMLElement gHTMLElements[];
|
|||
|
||||
//special property bits...
|
||||
static const int kDiscardTag = 0x0001; //tells us to toss this tag
|
||||
static const int kOmitEndTag = 0x0002; //safely ignore end tag
|
||||
|
||||
|
||||
#endif
|
|
@ -580,7 +580,7 @@ nsresult CCDATASectionToken::Consume(PRUnichar aChar, nsScanner& aScanner) {
|
|||
|
||||
|
||||
/*
|
||||
* default constructor
|
||||
* Default constructor
|
||||
*
|
||||
* @update gess 3/25/98
|
||||
* @param aName -- string to init token name with
|
||||
|
@ -591,7 +591,7 @@ CCommentToken::CCommentToken() : CHTMLToken(eHTMLTag_comment) {
|
|||
|
||||
|
||||
/*
|
||||
* Default constructor
|
||||
* Copy constructor
|
||||
*
|
||||
* @update gess 3/25/98
|
||||
* @param
|
||||
|
@ -601,47 +601,140 @@ CCommentToken::CCommentToken(const nsString& aName) : CHTMLToken(aName) {
|
|||
mTypeID=eHTMLTag_comment;
|
||||
}
|
||||
|
||||
/*
|
||||
* This method consumes a comment using the (CORRECT) comment parsing
|
||||
* algorithm supplied by W3C.
|
||||
*
|
||||
* @update gess 01/04/99
|
||||
* @param
|
||||
* @param
|
||||
* @return
|
||||
*/
|
||||
nsresult ConsumeStrictComment(PRUnichar aChar, nsScanner& aScanner,nsString& aString) {
|
||||
static nsAutoString gMinus("-");
|
||||
nsresult result=NS_OK;
|
||||
|
||||
/*********************************************************
|
||||
NOTE: This algorithm does a fine job of handling comments
|
||||
when they're formatted per spec, but if they're not
|
||||
we don't handle them well. For example, we gack
|
||||
on the following:
|
||||
|
||||
<!-- xx -- xx -->
|
||||
*********************************************************/
|
||||
|
||||
aString="<!";
|
||||
while(NS_OK==result) {
|
||||
result=aScanner.GetChar(aChar);
|
||||
if(NS_OK==result) {
|
||||
aString+=aChar;
|
||||
if(kMinus==aChar) {
|
||||
result=aScanner.GetChar(aChar);
|
||||
if(NS_OK==result) {
|
||||
if(kMinus==aChar) {
|
||||
//in this case, we're reading a long-form comment <-- xxx -->
|
||||
aString+=aChar;
|
||||
result=aScanner.ReadWhile(aString,gMinus,PR_TRUE,PR_FALSE); //get all available '---'
|
||||
if(NS_OK==result) {
|
||||
PRInt32 findpos=-1;
|
||||
nsAutoString temp("");
|
||||
//Read to the first ending sequence '--'
|
||||
while((kNotFound==findpos) && (NS_OK==result)) {
|
||||
result=aScanner.ReadUntil(temp,kMinus,PR_TRUE);
|
||||
findpos=temp.RFind("--");
|
||||
}
|
||||
aString+=temp;
|
||||
if(NS_OK==result) {
|
||||
result=aScanner.ReadWhile(aString,gMinus,PR_TRUE,PR_FALSE); //get all available '---'
|
||||
if(NS_OK==result) {
|
||||
temp="->";
|
||||
result=aScanner.ReadUntil(aString,temp,PR_FALSE,PR_FALSE);
|
||||
}
|
||||
}
|
||||
}
|
||||
} //
|
||||
else break; //go find '>'
|
||||
}
|
||||
}//if
|
||||
else if(kGreaterThan==aChar) {
|
||||
return result;
|
||||
}
|
||||
else break; //go find '>'
|
||||
}//if
|
||||
}//while
|
||||
if(NS_OK==result) {
|
||||
//if you're here, we're consuming a "short-form" comment
|
||||
result=aScanner.ReadUntil(aString,kGreaterThan,PR_TRUE);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* This method consumes a comment using common (actually non-standard)
|
||||
* algorithm that seems to work against the content on the web.
|
||||
*
|
||||
* @update gess 01/04/99
|
||||
* @param
|
||||
* @param
|
||||
* @return
|
||||
*/
|
||||
nsresult ConsumeComment(PRUnichar aChar, nsScanner& aScanner,nsString& aString) {
|
||||
static nsAutoString gMinus("-");
|
||||
nsresult result=NS_OK;
|
||||
|
||||
/*********************************************************
|
||||
NOTE: This algorithm does a fine job of handling comments
|
||||
commonly used, but it doesn't really consume them
|
||||
per spec (But then, neither does IE or Nav).
|
||||
*********************************************************/
|
||||
|
||||
aString="<!";
|
||||
result=aScanner.GetChar(aChar);
|
||||
if(NS_OK==result) {
|
||||
aString+=aChar;
|
||||
if(kMinus==aChar) {
|
||||
result=aScanner.GetChar(aChar);
|
||||
if(NS_OK==result) {
|
||||
if(kMinus==aChar) {
|
||||
//in this case, we're reading a long-form comment <-- xxx -->
|
||||
aString+=aChar;
|
||||
nsAutoString temp("");
|
||||
PRBool done=PR_FALSE;
|
||||
PRInt32 findpos=kNotFound;
|
||||
result=aScanner.ReadWhile(temp,gMinus,PR_TRUE,PR_TRUE); //get all available '---'
|
||||
while((kNotFound==findpos) && (NS_OK==result)) {
|
||||
result=aScanner.ReadUntil(temp,kMinus,PR_TRUE);
|
||||
if(NS_OK==result) {
|
||||
result=aScanner.ReadWhile(temp,gMinus,PR_TRUE,PR_TRUE); //get all available '---'
|
||||
}
|
||||
findpos=temp.RFind("->");
|
||||
aString+=temp;
|
||||
temp="";
|
||||
} //while
|
||||
return result;
|
||||
} //if
|
||||
}//if
|
||||
}//if
|
||||
}//if
|
||||
if(NS_OK==result) {
|
||||
//Read up to the closing '>'
|
||||
result=aScanner.ReadUntil(aString,kGreaterThan,PR_TRUE);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Consume the identifier portion of the comment.
|
||||
* Note that we've already eaten the "<!" portion.
|
||||
*
|
||||
* @update gess 3/25/98
|
||||
* @update gess 1/27/99
|
||||
* @param aChar -- last char consumed from stream
|
||||
* @param aScanner -- controller of underlying input source
|
||||
* @return error result
|
||||
*/
|
||||
nsresult CCommentToken::Consume(PRUnichar aChar, nsScanner& aScanner) {
|
||||
|
||||
nsresult result=NS_OK;
|
||||
|
||||
result=aScanner.GetChar(aChar);
|
||||
if(NS_OK==result) {
|
||||
mTextValue="<!";
|
||||
if(kMinus==aChar) {
|
||||
mTextValue+="-";
|
||||
result=aScanner.GetChar(aChar);
|
||||
if(NS_OK==result) {
|
||||
if(kMinus==aChar) {
|
||||
//in this case, we're reading a long-form comment <-- xxx -->
|
||||
mTextValue+="-";
|
||||
PRInt32 findpos=-1;
|
||||
while((findpos<3) && (NS_OK==result)) {
|
||||
result=aScanner.ReadUntil(mTextValue,kMinus,PR_TRUE);
|
||||
findpos=mTextValue.RFind("--");
|
||||
}
|
||||
if(NS_OK==result) {
|
||||
result=aScanner.ReadUntil(mTextValue,kGreaterThan,PR_TRUE); //now skip to '>'
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(NS_OK==result) {
|
||||
//if you're here, we're consuming a "short-form" comment
|
||||
mTextValue+=aChar;
|
||||
result=aScanner.ReadUntil(mTextValue,kGreaterThan,PR_TRUE);
|
||||
}
|
||||
PRBool theStrictForm=PR_FALSE;
|
||||
nsresult result=(theStrictForm) ? ConsumeStrictComment(aChar,aScanner,mTextValue) : ConsumeComment(aChar,aScanner,mTextValue);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -842,7 +935,7 @@ void CAttributeToken::DebugDumpToken(ostream& out) {
|
|||
mTextValue.ToCString(buffer,sizeof(buffer)-1);
|
||||
out << buffer << ": " << mTypeID << endl;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* This general purpose method is used when you want to
|
||||
|
@ -872,6 +965,7 @@ nsresult ConsumeQuotedString(PRUnichar aChar,nsString& aString,nsScanner& aScann
|
|||
PRUnichar ch=aString.Last();
|
||||
if(ch!=aChar)
|
||||
aString+=aChar;
|
||||
aString.StripChars("\r\n");
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
@ -141,7 +141,7 @@ CSharedParserObjects gSharedParserObjects;
|
|||
* @param
|
||||
* @return
|
||||
*/
|
||||
nsParser::nsParser(nsITokenObserver* anObserver) : mCommand("") {
|
||||
nsParser::nsParser(nsITokenObserver* anObserver) : mCommand(""), mUnusedInput("") {
|
||||
NS_INIT_REFCNT();
|
||||
mParserFilter = 0;
|
||||
mObserver = 0;
|
||||
|
@ -519,7 +519,21 @@ CParserContext* nsParser::PopContext() {
|
|||
* and tokenize input (TRUE), or whether it just caches input to be
|
||||
* parsed later (FALSE).
|
||||
*
|
||||
* @update vidur 12/11/98
|
||||
* @update gess 1/29/99
|
||||
* @param aState determines whether we parse/tokenize or just cache.
|
||||
* @return current state
|
||||
*/
|
||||
void nsParser::SetUnusedInput(nsString& aBuffer) {
|
||||
mUnusedInput=aBuffer;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Call this when you want control whether or not the parser will parse
|
||||
* and tokenize input (TRUE), or whether it just caches input to be
|
||||
* parsed later (FALSE).
|
||||
*
|
||||
* @update gess 1/29/99
|
||||
* @param aState determines whether we parse/tokenize or just cache.
|
||||
* @return current state
|
||||
*/
|
||||
|
@ -629,32 +643,36 @@ nsresult nsParser::Parse(nsString& aSourceBuffer,PRBool anHTMLString,PRBool aEna
|
|||
//NOTE: Make sure that updates to this method don't cause
|
||||
// bug #2361 to break again!
|
||||
|
||||
mDTDVerification=aEnableVerify;
|
||||
nsresult result=NS_OK;
|
||||
CParserContext* pc=0;
|
||||
if(aSourceBuffer.Length() || mUnusedInput.Length()) {
|
||||
mDTDVerification=aEnableVerify;
|
||||
CParserContext* pc=0;
|
||||
|
||||
if((!mParserContext) || (mParserContext->mKey!=&aSourceBuffer)) {
|
||||
//only make a new context if we dont have one, OR if we do, but has a different context key...
|
||||
pc=new CParserContext(new nsScanner(aSourceBuffer),&aSourceBuffer,0);
|
||||
if(pc) {
|
||||
PushContext(*pc);
|
||||
pc->mStreamListenerState=eOnStart;
|
||||
pc->mContextType=CParserContext::eCTString;
|
||||
if(PR_TRUE==anHTMLString)
|
||||
pc->mSourceType="text/html";
|
||||
}
|
||||
else return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
else {
|
||||
pc=mParserContext;
|
||||
if((!mParserContext) || (mParserContext->mKey!=&aSourceBuffer)) {
|
||||
//only make a new context if we dont have one, OR if we do, but has a different context key...
|
||||
pc=new CParserContext(new nsScanner(mUnusedInput),&aSourceBuffer,0);
|
||||
if(pc) {
|
||||
PushContext(*pc);
|
||||
pc->mStreamListenerState=eOnStart;
|
||||
pc->mContextType=CParserContext::eCTString;
|
||||
if(PR_TRUE==anHTMLString)
|
||||
pc->mSourceType="text/html";
|
||||
}
|
||||
else return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
else {
|
||||
pc=mParserContext;
|
||||
pc->mScanner->Append(mUnusedInput);
|
||||
}
|
||||
pc->mScanner->Append(aSourceBuffer);
|
||||
}
|
||||
pc->mMultipart=!aLastCall;
|
||||
result=ResumeParse();
|
||||
if(aLastCall) {
|
||||
pc=PopContext();
|
||||
delete pc;
|
||||
}
|
||||
pc->mMultipart=!aLastCall;
|
||||
result=ResumeParse();
|
||||
if(aLastCall) {
|
||||
pc->mScanner->CopyUnusedData(mUnusedInput);
|
||||
pc=PopContext();
|
||||
delete pc;
|
||||
}//if
|
||||
}//if
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
@ -177,6 +177,19 @@ friend class CTokenHandler;
|
|||
*/
|
||||
virtual PRBool EnableParser(PRBool aState);
|
||||
|
||||
|
||||
/**
|
||||
* This rather arcane method (hack) is used as a signal between the
|
||||
* DTD and the parser. It allows the DTD to tell the parser that content
|
||||
* that comes through (parser::parser(string)) but not consumed should
|
||||
* propagate into the next string based parse call.
|
||||
*
|
||||
* @update gess 9/1/98
|
||||
* @param aState determines whether we propagate unused string content.
|
||||
* @return current state
|
||||
*/
|
||||
void SetUnusedInput(nsString& aBuffer);
|
||||
|
||||
/**
|
||||
* This method gets called (automatically) during incremental parsing
|
||||
* @update gess5/11/98
|
||||
|
@ -304,6 +317,7 @@ protected:
|
|||
nsString mCommand;
|
||||
PRInt32 mStreamStatus;
|
||||
nsITokenObserver* mTokenObserver;
|
||||
nsString mUnusedInput;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -520,6 +520,21 @@ nsString& nsScanner::GetBuffer(void) {
|
|||
return mBuffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this to copy bytes out of the scanner that have not yet been consumed
|
||||
* by the tokenization process.
|
||||
*
|
||||
* @update gess 5/12/98
|
||||
* @param aCopyBuffer is where the scanner buffer will be copied to
|
||||
* @return nada
|
||||
*/
|
||||
void nsScanner::CopyUnusedData(nsString& aCopyBuffer) {
|
||||
PRInt32 theLen=mBuffer.Length();
|
||||
if(0<theLen) {
|
||||
mBuffer.Right(aCopyBuffer,theLen-mOffset);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the name of the file that the scanner is reading from.
|
||||
* In some cases, it's just a given name, because the scanner isn't
|
||||
|
|
|
@ -243,6 +243,16 @@ class nsScanner {
|
|||
*/
|
||||
nsString& GetBuffer(void);
|
||||
|
||||
/**
|
||||
* Call this to copy bytes out of the scanner that have not yet been consumed
|
||||
* by the tokenization process.
|
||||
*
|
||||
* @update gess 5/12/98
|
||||
* @param aCopyBuffer is where the scanner buffer will be copied to
|
||||
* @return nada
|
||||
*/
|
||||
void CopyUnusedData(nsString& aCopyBuffer);
|
||||
|
||||
/**
|
||||
* Retrieve the name of the file that the scanner is reading from.
|
||||
* In some cases, it's just a given name, because the scanner isn't
|
||||
|
|
Загрузка…
Ссылка в новой задаче