1 | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
---|
2 | /* ***** BEGIN LICENSE BLOCK ***** |
---|
3 | * Version: NPL 1.1/GPL 2.0/LGPL 2.1 |
---|
4 | * |
---|
5 | * The contents of this file are subject to the Netscape Public License |
---|
6 | * Version 1.1 (the "License"); you may not use this file except in |
---|
7 | * compliance with the License. You may obtain a copy of the License at |
---|
8 | * http://www.mozilla.org/NPL/ |
---|
9 | * |
---|
10 | * Software distributed under the License is distributed on an "AS IS" basis, |
---|
11 | * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License |
---|
12 | * for the specific language governing rights and limitations under the |
---|
13 | * License. |
---|
14 | * |
---|
15 | * The Original Code is mozilla.org code. |
---|
16 | * |
---|
17 | * The Initial Developer of the Original Code is |
---|
18 | * Netscape Communications Corporation. |
---|
19 | * Portions created by the Initial Developer are Copyright (C) 1998 |
---|
20 | * the Initial Developer. All Rights Reserved. |
---|
21 | * |
---|
22 | * Contributor(s): |
---|
23 | * rickg@netscape.com |
---|
24 | * |
---|
25 | * |
---|
26 | * Alternatively, the contents of this file may be used under the terms of |
---|
27 | * either the GNU General Public License Version 2 or later (the "GPL"), or |
---|
28 | * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), |
---|
29 | * in which case the provisions of the GPL or the LGPL are applicable instead |
---|
30 | * of those above. If you wish to allow use of your version of this file only |
---|
31 | * under the terms of either the GPL or the LGPL, and not to allow others to |
---|
32 | * use your version of this file under the terms of the NPL, indicate your |
---|
33 | * decision by deleting the provisions above and replace them with the notice |
---|
34 | * and other provisions required by the GPL or the LGPL. If you do not delete |
---|
35 | * the provisions above, a recipient may use your version of this file under |
---|
36 | * the terms of any one of the NPL, the GPL or the LGPL. |
---|
37 | * |
---|
38 | * ***** END LICENSE BLOCK ***** */ |
---|
39 | |
---|
40 | //#define ENABLE_CRC |
---|
41 | |
---|
42 | #include "nsDebug.h" |
---|
43 | #include "nsIAtom.h" |
---|
44 | #include "COtherDTD.h" |
---|
45 | #include "nsHTMLTokens.h" |
---|
46 | #include "nsCRT.h" |
---|
47 | #include "nsParser.h" |
---|
48 | #include "nsIParser.h" |
---|
49 | #include "nsIHTMLContentSink.h" |
---|
50 | #include "nsScanner.h" |
---|
51 | #include "prenv.h" //this is here for debug reasons... |
---|
52 | #include "prtypes.h" //this is here for debug reasons... |
---|
53 | #include "prio.h" |
---|
54 | #include "plstr.h" |
---|
55 | #include "nsDTDUtils.h" |
---|
56 | #include "nsHTMLTokenizer.h" |
---|
57 | #include "nsTime.h" |
---|
58 | #include "nsParserNode.h" |
---|
59 | #include "nsHTMLEntities.h" |
---|
60 | #include "nsLinebreakConverter.h" |
---|
61 | #include "nsUnicharUtils.h" |
---|
62 | |
---|
63 | #include "prmem.h" |
---|
64 | |
---|
65 | static NS_DEFINE_IID(kIHTMLContentSinkIID, NS_IHTML_CONTENT_SINK_IID); |
---|
66 | static NS_DEFINE_IID(kISupportsIID, NS_ISUPPORTS_IID); |
---|
67 | static NS_DEFINE_IID(kIDTDIID, NS_IDTD_IID); |
---|
68 | static NS_DEFINE_IID(kClassIID, NS_IOTHERHTML_DTD_IID); |
---|
69 | static const char kVerificationDir[] = "c:/temp"; |
---|
70 | |
---|
71 | |
---|
72 | #ifdef ENABLE_CRC |
---|
73 | static char gShowCRC; |
---|
74 | #endif |
---|
75 | |
---|
76 | |
---|
77 | |
---|
78 | #ifdef MOZ_PERF_METRICS |
---|
79 | # define START_TIMER() \ |
---|
80 | if(mParser) MOZ_TIMER_START(mParser->mParseTime); \ |
---|
81 | if(mParser) MOZ_TIMER_START(mParser->mDTDTime); |
---|
82 | |
---|
83 | # define STOP_TIMER() \ |
---|
84 | if(mParser) MOZ_TIMER_STOP(mParser->mParseTime); \ |
---|
85 | if(mParser) MOZ_TIMER_STOP(mParser->mDTDTime); |
---|
86 | #else |
---|
87 | # define STOP_TIMER() |
---|
88 | # define START_TIMER() |
---|
89 | #endif |
---|
90 | |
---|
91 | |
---|
92 | #include "COtherElements.h" |
---|
93 | |
---|
94 | |
---|
95 | /************************************************************************ |
---|
96 | And now for the main class -- COtherDTD... |
---|
97 | ************************************************************************/ |
---|
98 | |
---|
99 | /** |
---|
100 | * This method gets called as part of our COM-like interfaces. |
---|
101 | * Its purpose is to create an interface to parser object |
---|
102 | * of some type. |
---|
103 | * |
---|
104 | * @update gess 4/8/98 |
---|
105 | * @param nsIID id of object to discover |
---|
106 | * @param aInstancePtr ptr to newly discovered interface |
---|
107 | * @return NS_xxx result code |
---|
108 | */ |
---|
109 | nsresult COtherDTD::QueryInterface(const nsIID& aIID, void** aInstancePtr) |
---|
110 | { |
---|
111 | if (NULL == aInstancePtr) { |
---|
112 | return NS_ERROR_NULL_POINTER; |
---|
113 | } |
---|
114 | |
---|
115 | if(aIID.Equals(kISupportsIID)) { //do IUnknown... |
---|
116 | *aInstancePtr = (nsIDTD*)(this); |
---|
117 | } |
---|
118 | else if(aIID.Equals(kIDTDIID)) { //do IParser base class... |
---|
119 | *aInstancePtr = (nsIDTD*)(this); |
---|
120 | } |
---|
121 | else if(aIID.Equals(kClassIID)) { //do this class... |
---|
122 | *aInstancePtr = (COtherDTD*)(this); |
---|
123 | } |
---|
124 | else { |
---|
125 | *aInstancePtr=0; |
---|
126 | return NS_NOINTERFACE; |
---|
127 | } |
---|
128 | NS_ADDREF_THIS(); |
---|
129 | return NS_OK; |
---|
130 | } |
---|
131 | |
---|
132 | NS_IMPL_ADDREF(COtherDTD) |
---|
133 | NS_IMPL_RELEASE(COtherDTD) |
---|
134 | |
---|
135 | /** |
---|
136 | * Default constructor |
---|
137 | * |
---|
138 | * @update gess 4/9/98 |
---|
139 | * @param |
---|
140 | * @return |
---|
141 | */ |
---|
142 | COtherDTD::COtherDTD() : nsIDTD() { |
---|
143 | mSink = 0; |
---|
144 | mParser=0; |
---|
145 | mLineNumber=1; |
---|
146 | mHasOpenBody=PR_FALSE; |
---|
147 | mHasOpenHead=0; |
---|
148 | mHasOpenForm=PR_FALSE; |
---|
149 | mHasOpenMap=PR_FALSE; |
---|
150 | mTokenizer=0; |
---|
151 | mTokenAllocator=0; |
---|
152 | mComputedCRC32=0; |
---|
153 | mExpectedCRC32=0; |
---|
154 | mDTDState=NS_OK; |
---|
155 | mDocType=eHTML_Strict; |
---|
156 | mHadFrameset=PR_FALSE; |
---|
157 | mHadBody=PR_FALSE; |
---|
158 | mHasOpenScript=PR_FALSE; |
---|
159 | mParserCommand=eViewNormal; |
---|
160 | mNodeAllocator=new nsNodeAllocator(); |
---|
161 | mBodyContext=new nsDTDContext(); |
---|
162 | |
---|
163 | #if 0 //set this to 1 if you want strictDTD to be based on the environment setting. |
---|
164 | char* theEnvString = PR_GetEnv("MOZ_DISABLE_STRICT"); |
---|
165 | mEnableStrict=PRBool(0==theEnvString); |
---|
166 | #else |
---|
167 | mEnableStrict=PR_TRUE; |
---|
168 | #endif |
---|
169 | |
---|
170 | if(!gElementTable) { |
---|
171 | gElementTable = new CElementTable(); |
---|
172 | } |
---|
173 | } |
---|
174 | |
---|
175 | /** |
---|
176 | * |
---|
177 | * @update gess1/8/99 |
---|
178 | * @param |
---|
179 | * @return |
---|
180 | */ |
---|
181 | const nsIID& COtherDTD::GetMostDerivedIID(void)const { |
---|
182 | return kClassIID; |
---|
183 | } |
---|
184 | |
---|
185 | /** |
---|
186 | * Default destructor |
---|
187 | * |
---|
188 | * @update gess 4/9/98 |
---|
189 | * @param |
---|
190 | * @return |
---|
191 | */ |
---|
192 | COtherDTD::~COtherDTD(){ |
---|
193 | delete mBodyContext; |
---|
194 | |
---|
195 | if(mNodeAllocator) { |
---|
196 | delete mNodeAllocator; |
---|
197 | mNodeAllocator=nsnull; |
---|
198 | } |
---|
199 | |
---|
200 | NS_IF_RELEASE(mSink); |
---|
201 | } |
---|
202 | |
---|
203 | /** |
---|
204 | * This method is defined in nsIParser. It is used to |
---|
205 | * cause the COM-like construction of an nsParser. |
---|
206 | * |
---|
207 | * @update gess 4/8/98 |
---|
208 | * @param nsIParser** ptr to newly instantiated parser |
---|
209 | * @return NS_xxx error result |
---|
210 | */ |
---|
211 | nsresult NS_NewOtherHTMLDTD(nsIDTD** aInstancePtrResult) { |
---|
212 | COtherDTD* it = new COtherDTD(); |
---|
213 | |
---|
214 | if (it == 0) { |
---|
215 | return NS_ERROR_OUT_OF_MEMORY; |
---|
216 | } |
---|
217 | |
---|
218 | return it->QueryInterface(kClassIID, (void **) aInstancePtrResult); |
---|
219 | } |
---|
220 | |
---|
221 | /** |
---|
222 | * Call this method if you want the DTD to construct a fresh |
---|
223 | * instance of itself. |
---|
224 | * @update gess7/23/98 |
---|
225 | * @param |
---|
226 | * @return |
---|
227 | */ |
---|
228 | NS_IMETHODIMP |
---|
229 | COtherDTD::CreateNewInstance(nsIDTD** aInstancePtrResult) |
---|
230 | { |
---|
231 | nsresult result=NS_NewOtherHTMLDTD(aInstancePtrResult); |
---|
232 | |
---|
233 | if(aInstancePtrResult) { |
---|
234 | COtherDTD *theOtherDTD=(COtherDTD*)*aInstancePtrResult; |
---|
235 | if(theOtherDTD) { |
---|
236 | theOtherDTD->mDTDMode=mDTDMode; |
---|
237 | theOtherDTD->mParserCommand=mParserCommand; |
---|
238 | theOtherDTD->mDocType=mDocType; |
---|
239 | theOtherDTD->mEnableStrict=mEnableStrict; |
---|
240 | } |
---|
241 | } |
---|
242 | |
---|
243 | return result; |
---|
244 | } |
---|
245 | |
---|
246 | /** |
---|
247 | * This method is called to determine if the given DTD can parse |
---|
248 | * a document in a given source-type. |
---|
249 | * NOTE: Parsing always assumes that the end result will involve |
---|
250 | * storing the result in the main content model. |
---|
251 | * @update gess6/24/98 |
---|
252 | * @param |
---|
253 | * @return TRUE if this DTD can satisfy the request; FALSE otherwise. |
---|
254 | */ |
---|
255 | NS_IMETHODIMP_(eAutoDetectResult) |
---|
256 | COtherDTD::CanParse(CParserContext& aParserContext, const nsString& aBuffer, |
---|
257 | PRInt32 aVersion) |
---|
258 | { |
---|
259 | eAutoDetectResult result=eUnknownDetect; |
---|
260 | |
---|
261 | if(mEnableStrict) { |
---|
262 | if(aParserContext.mParserCommand != eViewSource) { |
---|
263 | if(PR_TRUE==aParserContext.mMimeType.EqualsWithConversion(kPlainTextContentType)) { |
---|
264 | result=eValidDetect; |
---|
265 | } |
---|
266 | else if(PR_TRUE==aParserContext.mMimeType.EqualsWithConversion(kHTMLTextContentType)) { |
---|
267 | switch(aParserContext.mDTDMode) { |
---|
268 | case eDTDMode_full_standards: |
---|
269 | case eDTDMode_almost_standards: |
---|
270 | result=ePrimaryDetect; |
---|
271 | break; |
---|
272 | default: |
---|
273 | result=eValidDetect; |
---|
274 | break; |
---|
275 | } |
---|
276 | } |
---|
277 | else { |
---|
278 | //otherwise, look into the buffer to see if you recognize anything... |
---|
279 | PRBool theBufHasXML=PR_FALSE; |
---|
280 | if(BufferContainsHTML(aBuffer,theBufHasXML)){ |
---|
281 | result = eValidDetect ; |
---|
282 | if(0==aParserContext.mMimeType.Length()) { |
---|
283 | aParserContext.SetMimeType(NS_LITERAL_CSTRING(kHTMLTextContentType)); |
---|
284 | if(!theBufHasXML) { |
---|
285 | switch(aParserContext.mDTDMode) { |
---|
286 | case eDTDMode_full_standards: |
---|
287 | case eDTDMode_almost_standards: |
---|
288 | result=ePrimaryDetect; |
---|
289 | break; |
---|
290 | default: |
---|
291 | result=eValidDetect; |
---|
292 | break; |
---|
293 | } |
---|
294 | } |
---|
295 | else result=eValidDetect; |
---|
296 | } |
---|
297 | } |
---|
298 | } |
---|
299 | } |
---|
300 | } |
---|
301 | return result; |
---|
302 | } |
---|
303 | |
---|
304 | |
---|
305 | /** |
---|
306 | * The parser uses a code sandwich to wrap the parsing process. Before |
---|
307 | * the process begins, WillBuildModel() is called. Afterwards the parser |
---|
308 | * calls DidBuildModel(). |
---|
309 | * @update rickg 03.20.2000 |
---|
310 | * @param aParserContext |
---|
311 | * @param aSink |
---|
312 | * @return error code (almost always 0) |
---|
313 | */ |
---|
314 | nsresult COtherDTD::WillBuildModel(const CParserContext& aParserContext, |
---|
315 | nsITokenizer* aTokenizer, |
---|
316 | nsIContentSink* aSink){ |
---|
317 | nsresult result=NS_OK; |
---|
318 | |
---|
319 | mFilename=aParserContext.mScanner->GetFilename(); |
---|
320 | mHasOpenBody=PR_FALSE; |
---|
321 | mHadFrameset=PR_FALSE; |
---|
322 | mLineNumber=1; |
---|
323 | mHasOpenScript=PR_FALSE; |
---|
324 | mDTDMode=aParserContext.mDTDMode; |
---|
325 | mParserCommand=aParserContext.mParserCommand; |
---|
326 | mTokenizer = aTokenizer; |
---|
327 | |
---|
328 | if((!aParserContext.mPrevContext) && (aSink)) { |
---|
329 | |
---|
330 | STOP_TIMER(); |
---|
331 | MOZ_TIMER_DEBUGLOG(("Stop: Parse Time: COtherDTD::WillBuildModel(), this=%p\n", this)); |
---|
332 | |
---|
333 | mDocType=aParserContext.mDocType; |
---|
334 | mBodyContext->mFlags.mTransitional=PR_FALSE; |
---|
335 | |
---|
336 | if(aSink && (!mSink)) { |
---|
337 | result=aSink->QueryInterface(kIHTMLContentSinkIID, (void **)&mSink); |
---|
338 | } |
---|
339 | |
---|
340 | if(result==NS_OK) { |
---|
341 | result = aSink->WillBuildModel(); |
---|
342 | |
---|
343 | #ifdef DEBUG |
---|
344 | mBodyContext->ResetCounters(); |
---|
345 | #endif |
---|
346 | |
---|
347 | MOZ_TIMER_DEBUGLOG(("Start: Parse Time: COtherDTD::WillBuildModel(), this=%p\n", this)); |
---|
348 | START_TIMER(); |
---|
349 | |
---|
350 | mSkipTarget=eHTMLTag_unknown; |
---|
351 | mComputedCRC32=0; |
---|
352 | mExpectedCRC32=0; |
---|
353 | } |
---|
354 | } |
---|
355 | |
---|
356 | return result; |
---|
357 | } |
---|
358 | |
---|
359 | |
---|
360 | /** |
---|
361 | * This is called when it's time to read as many tokens from the tokenizer |
---|
362 | * as you can. Not all tokens may make sense, so you may not be able to |
---|
363 | * read them all (until more come in later). |
---|
364 | * |
---|
365 | * @update gess5/18/98 |
---|
366 | * @param aParser is the parser object that's driving this process |
---|
367 | * @return error code (almost always NS_OK) |
---|
368 | */ |
---|
369 | nsresult COtherDTD::BuildModel(nsIParser* aParser,nsITokenizer* aTokenizer,nsITokenObserver* anObserver,nsIContentSink* aSink) { |
---|
370 | nsresult result=NS_OK; |
---|
371 | |
---|
372 | if(aTokenizer) { |
---|
373 | nsITokenizer* oldTokenizer=mTokenizer; |
---|
374 | mTokenizer=aTokenizer; |
---|
375 | mParser=(nsParser*)aParser; |
---|
376 | |
---|
377 | if(mTokenizer) { |
---|
378 | |
---|
379 | mTokenAllocator=mTokenizer->GetTokenAllocator(); |
---|
380 | |
---|
381 | mBodyContext->SetTokenAllocator(mTokenAllocator); |
---|
382 | mBodyContext->SetNodeAllocator(mNodeAllocator); |
---|
383 | |
---|
384 | if(mSink) { |
---|
385 | |
---|
386 | if(!mBodyContext->GetCount()) { |
---|
387 | //if the content model is empty, then begin by opening <html>... |
---|
388 | CStartToken *theToken=(CStartToken*)mTokenAllocator->CreateTokenOfType(eToken_start,eHTMLTag_html,NS_LITERAL_STRING("html")); |
---|
389 | HandleStartToken(theToken); //this token should get pushed on the context stack, don't recycle it. |
---|
390 | } |
---|
391 | |
---|
392 | while(NS_SUCCEEDED(result)){ |
---|
393 | |
---|
394 | if(mDTDState!=NS_ERROR_HTMLPARSER_STOPPARSING) { |
---|
395 | CToken* theToken=mTokenizer->PopToken(); |
---|
396 | if(theToken) { |
---|
397 | result=HandleToken(theToken,aParser); |
---|
398 | } |
---|
399 | else break; |
---|
400 | } |
---|
401 | else { |
---|
402 | result=mDTDState; |
---|
403 | break; |
---|
404 | } |
---|
405 | }//while |
---|
406 | mTokenizer=oldTokenizer; |
---|
407 | } |
---|
408 | } |
---|
409 | } |
---|
410 | else result=NS_ERROR_HTMLPARSER_BADTOKENIZER; |
---|
411 | return result; |
---|
412 | } |
---|
413 | |
---|
414 | /** |
---|
415 | * |
---|
416 | * @update gess5/18/98 |
---|
417 | * @param |
---|
418 | * @return |
---|
419 | */ |
---|
420 | nsresult COtherDTD::DidBuildModel(nsresult anErrorCode,PRBool aNotifySink,nsIParser* aParser,nsIContentSink* aSink){ |
---|
421 | nsresult result=NS_OK; |
---|
422 | |
---|
423 | if(aSink) { |
---|
424 | |
---|
425 | if(aParser && (NS_OK==result)){ |
---|
426 | if(aNotifySink){ |
---|
427 | if((NS_OK==anErrorCode) && (mBodyContext->GetCount()>0)) { |
---|
428 | |
---|
429 | PRInt32 theIndex=mBodyContext->GetCount()-1; |
---|
430 | eHTMLTags theChild = mBodyContext->TagAt(theIndex); |
---|
431 | while (theIndex>0) { |
---|
432 | eHTMLTags theParent = mBodyContext->TagAt(--theIndex); |
---|
433 | CElement *theElement = gElementTable->mElements[theParent]; |
---|
434 | nsCParserNode *theNode = mBodyContext->PeekNode(); |
---|
435 | theElement->HandleEndToken(theNode,theChild,mBodyContext,mSink); |
---|
436 | theChild = theParent; |
---|
437 | } |
---|
438 | |
---|
439 | nsEntryStack* theChildStyles = 0; |
---|
440 | nsCParserNode* theNode = (nsCParserNode*)mBodyContext->Pop(theChildStyles); |
---|
441 | if (theNode) { |
---|
442 | mSink->CloseHTML(); |
---|
443 | } |
---|
444 | NS_ASSERTION(!theChildStyles, "there should no residual style information in this dtd"); |
---|
445 | IF_DELETE(theChildStyles, mNodeAllocator); |
---|
446 | } |
---|
447 | else { |
---|
448 | //If you're here, then an error occured, but we still have nodes on the stack. |
---|
449 | //At a minimum, we should grab the nodes and recycle them. |
---|
450 | //Just to be correct, we'll also recycle the nodes. |
---|
451 | |
---|
452 | while (mBodyContext->GetCount() > 0) { |
---|
453 | |
---|
454 | nsEntryStack *theChildStyles = 0; |
---|
455 | nsCParserNode* theNode = (nsCParserNode*)mBodyContext->Pop(theChildStyles); |
---|
456 | if (theNode) { |
---|
457 | theNode->mUseCount = 0; |
---|
458 | if (theChildStyles) { |
---|
459 | delete theChildStyles; |
---|
460 | } |
---|
461 | IF_FREE(theNode, mNodeAllocator); |
---|
462 | } |
---|
463 | NS_ASSERTION(!theChildStyles, "there should no residual style information in this dtd"); |
---|
464 | IF_DELETE(theChildStyles, mNodeAllocator); |
---|
465 | } |
---|
466 | } |
---|
467 | |
---|
468 | } |
---|
469 | } //if aparser |
---|
470 | |
---|
471 | //No matter what, you need to call did build model. |
---|
472 | result = aSink->DidBuildModel(); |
---|
473 | |
---|
474 | } //if asink |
---|
475 | return result; |
---|
476 | } |
---|
477 | |
---|
478 | NS_IMETHODIMP_(void) |
---|
479 | COtherDTD::Terminate() |
---|
480 | { |
---|
481 | mDTDState = NS_ERROR_HTMLPARSER_STOPPARSING; |
---|
482 | } |
---|
483 | |
---|
484 | NS_IMETHODIMP_(PRInt32) |
---|
485 | COtherDTD::GetType() |
---|
486 | { |
---|
487 | return NS_IPARSER_FLAG_HTML; |
---|
488 | } |
---|
489 | |
---|
490 | NS_IMETHODIMP |
---|
491 | COtherDTD::CollectSkippedContent(PRInt32 aTag, nsAString& aContent, PRInt32 &aLineNo) |
---|
492 | { |
---|
493 | return NS_OK; |
---|
494 | } |
---|
495 | |
---|
496 | /** |
---|
497 | * This big dispatch method is used to route token handler calls to the right place. |
---|
498 | * What's wrong with it? This table, and the dispatch methods themselves need to be |
---|
499 | * moved over to the delegate. Ah, so much to do... |
---|
500 | * |
---|
501 | * @update gess 12/1/99 |
---|
502 | * @param aToken |
---|
503 | * @param aParser |
---|
504 | * @return |
---|
505 | */ |
---|
506 | nsresult COtherDTD::HandleToken(CToken* aToken,nsIParser* aParser){ |
---|
507 | nsresult result=NS_OK; |
---|
508 | |
---|
509 | if(aToken) { |
---|
510 | CHTMLToken* theToken= (CHTMLToken*)(aToken); |
---|
511 | eHTMLTokenTypes theType=eHTMLTokenTypes(theToken->GetTokenType()); |
---|
512 | |
---|
513 | // theToken->mUseCount=0; //assume every token coming into this system needs recycling. |
---|
514 | |
---|
515 | mParser=(nsParser*)aParser; |
---|
516 | |
---|
517 | switch(theType) { |
---|
518 | case eToken_text: |
---|
519 | case eToken_start: |
---|
520 | case eToken_whitespace: |
---|
521 | case eToken_newline: |
---|
522 | case eToken_doctypeDecl: |
---|
523 | case eToken_markupDecl: |
---|
524 | result=HandleStartToken(theToken); break; |
---|
525 | |
---|
526 | case eToken_entity: |
---|
527 | result=HandleEntityToken(theToken); break; |
---|
528 | |
---|
529 | case eToken_end: |
---|
530 | result=HandleEndToken(theToken); break; |
---|
531 | |
---|
532 | default: |
---|
533 | break; |
---|
534 | }//switch |
---|
535 | |
---|
536 | |
---|
537 | if(NS_SUCCEEDED(result) || (NS_ERROR_HTMLPARSER_BLOCK==result)) { |
---|
538 | IF_FREE(theToken, mTokenAllocator); |
---|
539 | } |
---|
540 | else if(result==NS_ERROR_HTMLPARSER_STOPPARSING) |
---|
541 | mDTDState=result; |
---|
542 | else return NS_OK; |
---|
543 | |
---|
544 | }//if |
---|
545 | return result; |
---|
546 | } |
---|
547 | |
---|
548 | |
---|
549 | /** |
---|
550 | * This gets called after we've handled a given start tag. |
---|
551 | * It's a generic hook to let us to post processing. |
---|
552 | * @param aToken contains the tag in question |
---|
553 | * @param aTag is the tag itself. |
---|
554 | * @return status |
---|
555 | */ |
---|
556 | nsresult COtherDTD::DidHandleStartTag(nsIParserNode& aNode,eHTMLTags aChildTag){ |
---|
557 | nsresult result=NS_OK; |
---|
558 | |
---|
559 | switch(aChildTag){ |
---|
560 | |
---|
561 | case eHTMLTag_script: |
---|
562 | mHasOpenScript=PR_TRUE; |
---|
563 | break; |
---|
564 | |
---|
565 | case eHTMLTag_pre: |
---|
566 | case eHTMLTag_listing: |
---|
567 | { |
---|
568 | CToken* theNextToken=mTokenizer->PeekToken(); |
---|
569 | if(theNextToken) { |
---|
570 | eHTMLTokenTypes theType=eHTMLTokenTypes(theNextToken->GetTokenType()); |
---|
571 | if(eToken_newline==theType){ |
---|
572 | ++mLineNumber; |
---|
573 | mTokenizer->PopToken(); //skip 1st newline inside PRE and LISTING |
---|
574 | }//if |
---|
575 | }//if |
---|
576 | } |
---|
577 | break; |
---|
578 | |
---|
579 | #ifdef DEBUG |
---|
580 | case eHTMLTag_meta: |
---|
581 | { |
---|
582 | //we should only enable user-defined entities in debug builds... |
---|
583 | |
---|
584 | PRInt32 theCount=aNode.GetAttributeCount(); |
---|
585 | const nsAString* theNamePtr=0; |
---|
586 | const nsAString* theValuePtr=0; |
---|
587 | |
---|
588 | if(theCount) { |
---|
589 | PRInt32 theIndex=0; |
---|
590 | for(theIndex=0;theIndex<theCount;++theIndex){ |
---|
591 | const nsAString& theKey = aNode.GetKeyAt(theIndex); |
---|
592 | if(theKey.Equals(NS_LITERAL_STRING("ENTITY"), nsCaseInsensitiveStringComparator())) { |
---|
593 | const nsAString& theName=aNode.GetValueAt(theIndex); |
---|
594 | theNamePtr=&theName; |
---|
595 | } |
---|
596 | else if(theKey.Equals(NS_LITERAL_STRING("VALUE"), nsCaseInsensitiveStringComparator())) { |
---|
597 | //store the named enity with the context... |
---|
598 | const nsAString& theValue=aNode.GetValueAt(theIndex); |
---|
599 | theValuePtr=&theValue; |
---|
600 | } |
---|
601 | } |
---|
602 | } |
---|
603 | if(theNamePtr && theValuePtr) { |
---|
604 | mBodyContext->RegisterEntity(*theNamePtr,*theValuePtr); |
---|
605 | } |
---|
606 | } |
---|
607 | break; |
---|
608 | #endif |
---|
609 | |
---|
610 | default: |
---|
611 | break; |
---|
612 | }//switch |
---|
613 | |
---|
614 | return result; |
---|
615 | } |
---|
616 | |
---|
617 | /** |
---|
618 | * This gets called before we've handled a given start tag. |
---|
619 | * It's a generic hook to let us do pre processing. |
---|
620 | * @param aToken contains the tag in question |
---|
621 | * @param aChildTag is the tag itself. |
---|
622 | * @param aNode is the node (tag) with associated attributes. |
---|
623 | * @return TRUE if tag processing should continue; FALSE if the tag has been handled. |
---|
624 | */ |
---|
625 | nsresult COtherDTD::WillHandleStartTag(CToken* aToken,eHTMLTags aTag,nsIParserNode& aNode){ |
---|
626 | nsresult result=NS_OK; |
---|
627 | |
---|
628 | //first let's see if there's some skipped content to deal with... |
---|
629 | #if 0 |
---|
630 | PRInt32 theAttrCount = aNode.GetAttributeCount(); |
---|
631 | if(*gElementTable->mElements[aTag].mSkipTarget) { |
---|
632 | result=CollectSkippedContent(aNode,theAttrCount); |
---|
633 | } |
---|
634 | #endif |
---|
635 | |
---|
636 | STOP_TIMER() |
---|
637 | MOZ_TIMER_DEBUGLOG(("Stop: Parse Time: COtherDTD::WillHandleStartTag(), this=%p\n", this)); |
---|
638 | |
---|
639 | if(mParser) { |
---|
640 | |
---|
641 | switch(aTag) { |
---|
642 | case eHTMLTag_newline: |
---|
643 | ++mLineNumber; |
---|
644 | break; |
---|
645 | default: |
---|
646 | break; |
---|
647 | } |
---|
648 | mSink->NotifyTagObservers(&aNode); |
---|
649 | } |
---|
650 | |
---|
651 | MOZ_TIMER_DEBUGLOG(("Start: Parse Time: COtherDTD::WillHandleStartTag(), this=%p\n", this)); |
---|
652 | START_TIMER() |
---|
653 | |
---|
654 | return result; |
---|
655 | } |
---|
656 | |
---|
657 | |
---|
658 | /** |
---|
659 | * This method gets called when a start token has been |
---|
660 | * encountered in the parse process. If the current container |
---|
661 | * can contain this tag, then add it. Otherwise, you have |
---|
662 | * two choices: 1) create an implicit container for this tag |
---|
663 | * to be stored in |
---|
664 | * 2) close the top container, and add this to |
---|
665 | * whatever container ends up on top. |
---|
666 | * |
---|
667 | * @update gess 1/04/99 |
---|
668 | * @param aToken -- next (start) token to be handled |
---|
669 | * @param aNode -- CParserNode representing this start token |
---|
670 | * @return PR_TRUE if all went well; PR_FALSE if error occured |
---|
671 | */ |
---|
672 | nsresult COtherDTD::HandleStartToken(CToken* aToken) { |
---|
673 | |
---|
674 | //Begin by gathering up attributes... |
---|
675 | |
---|
676 | nsresult result=NS_OK; |
---|
677 | nsCParserNode* theNode=mNodeAllocator->CreateNode(aToken, mTokenAllocator); |
---|
678 | if(theNode) { |
---|
679 | |
---|
680 | eHTMLTags theChildTag=(eHTMLTags)aToken->GetTypeID(); |
---|
681 | PRInt16 attrCount=aToken->GetAttributeCount(); |
---|
682 | eHTMLTags theParent=mBodyContext->Last(); |
---|
683 | |
---|
684 | result=(0==attrCount) ? NS_OK : CollectAttributes(*theNode,theChildTag,attrCount); |
---|
685 | |
---|
686 | if(NS_OK==result) { |
---|
687 | result=WillHandleStartTag(aToken,theChildTag,*theNode); |
---|
688 | if(NS_OK==result) { |
---|
689 | |
---|
690 | mLineNumber += aToken->GetNewlineCount(); |
---|
691 | |
---|
692 | PRBool theTagWasHandled=PR_FALSE; |
---|
693 | |
---|
694 | switch(theChildTag) { |
---|
695 | |
---|
696 | case eHTMLTag_html: |
---|
697 | if(!mBodyContext->HasOpenContainer(theChildTag)){ |
---|
698 | mSink->OpenHTML(*theNode); |
---|
699 | mBodyContext->Push(theNode, 0, PR_FALSE); |
---|
700 | } |
---|
701 | theTagWasHandled=PR_TRUE; |
---|
702 | break; |
---|
703 | |
---|
704 | default: |
---|
705 | CElement* theElement=gElementTable->mElements[theParent]; |
---|
706 | if(theElement) { |
---|
707 | result=theElement->HandleStartToken(theNode,theChildTag,mBodyContext,mSink); |
---|
708 | theTagWasHandled=PR_TRUE; |
---|
709 | } |
---|
710 | break; |
---|
711 | }//switch |
---|
712 | |
---|
713 | if(theTagWasHandled) { |
---|
714 | DidHandleStartTag(*theNode,theChildTag); |
---|
715 | } |
---|
716 | |
---|
717 | } //if |
---|
718 | }//if |
---|
719 | IF_FREE(theNode, mNodeAllocator); |
---|
720 | } |
---|
721 | else result=NS_ERROR_OUT_OF_MEMORY; |
---|
722 | |
---|
723 | return result; |
---|
724 | } |
---|
725 | |
---|
726 | /** |
---|
727 | * This method gets called when an end token has been |
---|
728 | * encountered in the parse process. If the end tag matches |
---|
729 | * the start tag on the stack, then simply close it. Otherwise, |
---|
730 | * we have a erroneous state condition. This can be because we |
---|
731 | * have a close tag with no prior open tag (user error) or because |
---|
732 | * we screwed something up in the parse process. I'm not sure |
---|
733 | * yet how to tell the difference. |
---|
734 | * |
---|
735 | * @update gess 3/25/98 |
---|
736 | * @param aToken -- next (start) token to be handled |
---|
737 | * @return PR_TRUE if all went well; PR_FALSE if error occured |
---|
738 | */ |
---|
739 | nsresult COtherDTD::HandleEndToken(CToken* aToken) { |
---|
740 | nsresult result=NS_OK; |
---|
741 | eHTMLTags theChildTag=(eHTMLTags)aToken->GetTypeID(); |
---|
742 | |
---|
743 | switch(theChildTag) { |
---|
744 | |
---|
745 | case eHTMLTag_body: //we intentionally don't let the user close HTML or BODY |
---|
746 | case eHTMLTag_html: |
---|
747 | break; |
---|
748 | |
---|
749 | case eHTMLTag_script: |
---|
750 | mHasOpenScript=PR_FALSE; |
---|
751 | |
---|
752 | default: |
---|
753 | PRInt32 theCount=mBodyContext->GetCount(); |
---|
754 | eHTMLTags theParent=mBodyContext->TagAt(theCount-1); |
---|
755 | if(theChildTag==theParent) { |
---|
756 | theParent=mBodyContext->TagAt(theCount-2); |
---|
757 | } |
---|
758 | CElement* theElement=gElementTable->mElements[theParent]; |
---|
759 | if(theElement) { |
---|
760 | nsCParserNode* theNode=mNodeAllocator->CreateNode(aToken, mTokenAllocator); |
---|
761 | if(theNode) { |
---|
762 | result=theElement->HandleEndToken(theNode,theChildTag,mBodyContext,mSink); |
---|
763 | IF_FREE(theNode, mNodeAllocator); |
---|
764 | } |
---|
765 | } |
---|
766 | break; |
---|
767 | } |
---|
768 | |
---|
769 | return result; |
---|
770 | } |
---|
771 | |
---|
772 | /** |
---|
773 | * Retrieve the attributes for this node, and add then into |
---|
774 | * the node. |
---|
775 | * |
---|
776 | * @update gess4/22/98 |
---|
777 | * @param aNode is the node you want to collect attributes for |
---|
778 | * @param aCount is the # of attributes you're expecting |
---|
779 | * @return error code (should be 0) |
---|
780 | */ |
---|
781 | nsresult COtherDTD::CollectAttributes(nsIParserNode& aNode,eHTMLTags aTag,PRInt32 aCount){ |
---|
782 | int attr=0; |
---|
783 | |
---|
784 | nsresult result=NS_OK; |
---|
785 | int theAvailTokenCount=mTokenizer->GetCount(); |
---|
786 | if(aCount<=theAvailTokenCount) { |
---|
787 | //gElementTable->mElements[aTag]->GetSkipTarget(); |
---|
788 | CToken* theToken=0; |
---|
789 | for(attr=0;attr<aCount;++attr){ |
---|
790 | theToken=mTokenizer->PopToken(); |
---|
791 | if(theToken) { |
---|
792 | // Sanitize the key for it might contain some non-alpha-non-digit characters |
---|
793 | // at its end. Ex. <OPTION SELECTED/> - This will be tokenized as "<" "OPTION", |
---|
794 | // "SELECTED/", and ">". In this case the "SELECTED/" key will be sanitized to |
---|
795 | // a legitimate "SELECTED" key. |
---|
796 | ((CAttributeToken*)theToken)->SanitizeKey(); |
---|
797 | |
---|
798 | aNode.AddAttribute(theToken); |
---|
799 | } |
---|
800 | } |
---|
801 | } |
---|
802 | else { |
---|
803 | result=kEOF; |
---|
804 | } |
---|
805 | return result; |
---|
806 | } |
---|
807 | |
---|
808 | /** |
---|
809 | * This method gets called when an entity token has been |
---|
810 | * encountered in the parse process. |
---|
811 | * |
---|
812 | * @update gess 3/25/98 |
---|
813 | * @param aToken -- next (start) token to be handled |
---|
814 | * @return PR_TRUE if all went well; PR_FALSE if error occured |
---|
815 | */ |
---|
816 | nsresult COtherDTD::HandleEntityToken(CToken* aToken) { |
---|
817 | nsresult result=NS_OK; |
---|
818 | |
---|
819 | nsAutoString theStr; |
---|
820 | aToken->GetSource(theStr); |
---|
821 | PRUnichar theChar=theStr.CharAt(0); |
---|
822 | CToken *theToken=0; |
---|
823 | |
---|
824 | if((kHashsign!=theChar) && (-1==nsHTMLEntities::EntityToUnicode(theStr))){ |
---|
825 | |
---|
826 | #ifdef DEBUG |
---|
827 | //before we just toss this away as a bogus entity, let's check... |
---|
828 | CNamedEntity *theEntity=mBodyContext->GetEntity(theStr); |
---|
829 | if(theEntity) { |
---|
830 | theToken=(CTextToken*)mTokenAllocator->CreateTokenOfType(eToken_text,eHTMLTag_text,theEntity->mValue); |
---|
831 | } |
---|
832 | else { |
---|
833 | #endif |
---|
834 | //if you're here we have a bogus entity. |
---|
835 | //convert it into a text token. |
---|
836 | nsAutoString entityName; |
---|
837 | entityName.Assign(NS_LITERAL_STRING("&")); |
---|
838 | entityName.Append(theStr); //should append the entity name; fix bug 51161. |
---|
839 | theToken=(CTextToken*)mTokenAllocator->CreateTokenOfType(eToken_text,eHTMLTag_text,entityName); |
---|
840 | #ifdef DEBUG |
---|
841 | } |
---|
842 | #endif |
---|
843 | result=HandleStartToken(theToken); |
---|
844 | } |
---|
845 | else { |
---|
846 | |
---|
847 | //add this code to fix bug 42629 (entities were getting dropped). |
---|
848 | eHTMLTags theParent=mBodyContext->Last(); |
---|
849 | CElement* theElement=gElementTable->mElements[theParent]; |
---|
850 | if(theElement) { |
---|
851 | nsCParserNode theNode(aToken, 0); |
---|
852 | result=theElement->HandleStartToken(&theNode,eHTMLTag_text,mBodyContext,mSink); |
---|
853 | } |
---|
854 | } |
---|
855 | return result; |
---|
856 | } |
---|
857 | |
---|
858 | /*********************************************************************************** |
---|
859 | The preceeding tables determine the set of elements each tag can contain... |
---|
860 | ***********************************************************************************/ |
---|
861 | |
---|
862 | /** |
---|
863 | * This method is called to determine whether or not a tag |
---|
864 | * of one type can contain a tag of another type. |
---|
865 | * |
---|
866 | * @update gess 4/8/98 |
---|
867 | * @param aParent -- tag enum of parent container |
---|
868 | * @param aChild -- tag enum of child container |
---|
869 | * @return PR_TRUE if parent can contain child |
---|
870 | */ |
---|
871 | PRBool COtherDTD::CanContain(PRInt32 aParent,PRInt32 aChild) const { |
---|
872 | CElement *theParent=gElementTable->mElements[eHTMLTags(aParent)]; |
---|
873 | if(theParent) { |
---|
874 | CElement *theChild=gElementTable->mElements[eHTMLTags(aChild)]; |
---|
875 | if(aChild) { |
---|
876 | if(eHTMLTag_userdefined == aChild)//bug #67007, dont strip userdefined tags |
---|
877 | return PR_TRUE; |
---|
878 | else |
---|
879 | return theParent->CanContain(theChild,mBodyContext); |
---|
880 | } |
---|
881 | } |
---|
882 | return PR_FALSE; |
---|
883 | } |
---|
884 | |
---|
885 | /** |
---|
886 | * Give rest of world access to our tag enums, so that CanContain(), etc, |
---|
887 | * become useful. |
---|
888 | */ |
---|
889 | NS_IMETHODIMP |
---|
890 | COtherDTD::StringTagToIntTag(const nsAString &aTag, |
---|
891 | PRInt32* aIntTag) const |
---|
892 | { |
---|
893 | *aIntTag = nsHTMLTags::LookupTag(aTag); |
---|
894 | |
---|
895 | return NS_OK; |
---|
896 | } |
---|
897 | |
---|
898 | NS_IMETHODIMP_(const PRUnichar *) |
---|
899 | COtherDTD::IntTagToStringTag(PRInt32 aIntTag) const |
---|
900 | { |
---|
901 | const PRUnichar *str_ptr = nsHTMLTags::GetStringValue((nsHTMLTag)aIntTag); |
---|
902 | |
---|
903 | NS_ASSERTION(str_ptr, "Bad tag enum passed to COtherDTD::IntTagToStringTag()" |
---|
904 | "!!"); |
---|
905 | |
---|
906 | return str_ptr; |
---|
907 | } |
---|
908 | |
---|
909 | NS_IMETHODIMP_(nsIAtom *) |
---|
910 | COtherDTD::IntTagToAtom(PRInt32 aIntTag) const |
---|
911 | { |
---|
912 | nsIAtom *atom = nsHTMLTags::GetAtom((nsHTMLTag)aIntTag); |
---|
913 | |
---|
914 | NS_ASSERTION(atom, "Bad tag enum passed to COtherDTD::IntTagToAtom()" |
---|
915 | "!!"); |
---|
916 | |
---|
917 | return atom; |
---|
918 | } |
---|
919 | |
---|
920 | /** |
---|
921 | * This method is called to determine whether or not |
---|
922 | * the given childtag is a block element. |
---|
923 | * |
---|
924 | * @update gess 6June2000 |
---|
925 | * @param aChildID -- tag id of child |
---|
926 | * @param aParentID -- tag id of parent (or eHTMLTag_unknown) |
---|
927 | * @return PR_TRUE if this tag is a block tag |
---|
928 | */ |
---|
929 | PRBool COtherDTD::IsBlockElement(PRInt32 aChildID,PRInt32 aParentID) const { |
---|
930 | PRBool result=PR_FALSE; |
---|
931 | |
---|
932 | if(gElementTable) { |
---|
933 | CElement *theElement=gElementTable->GetElement((eHTMLTags)aChildID); |
---|
934 | result = (theElement) ? theElement->IsBlockElement((eHTMLTags)aParentID) : PR_FALSE; |
---|
935 | } |
---|
936 | return result; |
---|
937 | } |
---|
938 | |
---|
939 | /** |
---|
940 | * This method is called to determine whether or not |
---|
941 | * the given childtag is an inline element. |
---|
942 | * |
---|
943 | * @update gess 6June2000 |
---|
944 | * @param aChildID -- tag id of child |
---|
945 | * @param aParentID -- tag id of parent (or eHTMLTag_unknown) |
---|
946 | * @return PR_TRUE if this tag is an inline element |
---|
947 | */ |
---|
948 | PRBool COtherDTD::IsInlineElement(PRInt32 aChildID,PRInt32 aParentID) const { |
---|
949 | PRBool result=PR_FALSE; |
---|
950 | |
---|
951 | if(gElementTable) { |
---|
952 | CElement *theElement=gElementTable->GetElement((eHTMLTags)aChildID); |
---|
953 | result = (theElement) ? theElement->IsInlineElement((eHTMLTags)aParentID) : PR_FALSE; |
---|
954 | } |
---|
955 | return result; |
---|
956 | } |
---|
957 | |
---|
958 | /** |
---|
959 | * This method gets called to determine whether a given |
---|
960 | * tag is itself a container |
---|
961 | * |
---|
962 | * @update gess 4/8/98 |
---|
963 | * @param aTag -- tag to test as a container |
---|
964 | * @return PR_TRUE if given tag can contain other tags |
---|
965 | */ |
---|
966 | PRBool COtherDTD::IsContainer(PRInt32 aTag) const { |
---|
967 | return gElementTable->mElements[eHTMLTags(aTag)]->IsContainer(); |
---|
968 | } |
---|
969 | |
---|
970 | /** |
---|
971 | * |
---|
972 | * @update gess5/18/98 |
---|
973 | * @param |
---|
974 | * @return |
---|
975 | */ |
---|
976 | nsresult COtherDTD::WillResumeParse(nsIContentSink* aSink) { |
---|
977 | |
---|
978 | STOP_TIMER(); |
---|
979 | MOZ_TIMER_DEBUGLOG(("Stop: Parse Time: COtherDTD::WillResumeParse(), this=%p\n", this)); |
---|
980 | |
---|
981 | nsresult result=(aSink) ? aSink->WillResume() : NS_OK; |
---|
982 | |
---|
983 | MOZ_TIMER_DEBUGLOG(("Start: Parse Time: COtherDTD::WillResumeParse(), this=%p\n", this)); |
---|
984 | START_TIMER(); |
---|
985 | |
---|
986 | return result; |
---|
987 | } |
---|
988 | |
---|
989 | /** |
---|
990 | * This method gets called when the parsing process is interrupted |
---|
991 | * due to lack of data (waiting for netlib). |
---|
992 | * @update gess5/18/98 |
---|
993 | * @return error code |
---|
994 | */ |
---|
995 | nsresult COtherDTD::WillInterruptParse(nsIContentSink* aSink){ |
---|
996 | |
---|
997 | STOP_TIMER(); |
---|
998 | MOZ_TIMER_DEBUGLOG(("Stop: Parse Time: COtherDTD::WillInterruptParse(), this=%p\n", this)); |
---|
999 | |
---|
1000 | nsresult result=(aSink) ? aSink->WillInterrupt() : NS_OK; |
---|
1001 | |
---|
1002 | MOZ_TIMER_DEBUGLOG(("Start: Parse Time: COtherDTD::WillInterruptParse(), this=%p\n", this)); |
---|
1003 | START_TIMER(); |
---|
1004 | |
---|
1005 | return result; |
---|
1006 | } |
---|
1007 | |
---|
1008 | // CTransitionalDTD is a subclass of COtherDTD that defaults to transitional mode. |
---|
1009 | // Used by the editor |
---|
1010 | |
---|
1011 | CTransitionalDTD::CTransitionalDTD() |
---|
1012 | { |
---|
1013 | if (mBodyContext) mBodyContext->mFlags.mTransitional = PR_TRUE; |
---|
1014 | } |
---|
1015 | |
---|
1016 | CTransitionalDTD::~CTransitionalDTD() {} |
---|
1017 | |
---|