Back to index

lightning-sunbird  0.9+nobinonly
nsContentSink.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is 
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either the GNU General Public License Version 2 or later (the "GPL"), or
00026  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 #include "nsContentSink.h"
00039 #include "nsIScriptLoader.h"
00040 #include "nsIDocument.h"
00041 #include "nsICSSLoader.h"
00042 #include "nsStyleConsts.h"
00043 #include "nsStyleLinkElement.h"
00044 #include "nsINodeInfo.h"
00045 #include "nsIDocShell.h"
00046 #include "nsIDocShellTreeItem.h"
00047 #include "nsCPrefetchService.h"
00048 #include "nsIURI.h"
00049 #include "nsNetUtil.h"
00050 #include "nsIHttpChannel.h"
00051 #include "nsIContent.h"
00052 #include "nsIScriptElement.h"
00053 #include "nsIParser.h"
00054 #include "nsContentErrors.h"
00055 #include "nsIPresShell.h"
00056 #include "nsPresContext.h"
00057 #include "nsIViewManager.h"
00058 #include "nsIScrollableView.h"
00059 #include "nsIContentViewer.h"
00060 #include "nsIAtom.h"
00061 #include "nsHTMLAtoms.h"
00062 #include "nsIDOMWindowInternal.h"
00063 #include "nsIPrincipal.h"
00064 #include "nsIScriptSecurityManager.h"
00065 #include "nsIScriptGlobalObject.h"
00066 #include "nsNetCID.h"
00067 #include "nsICookieService.h"
00068 #include "nsIPrompt.h"
00069 #include "nsServiceManagerUtils.h"
00070 #include "nsICharsetConverterManager.h"
00071 #include "nsContentUtils.h"
00072 #include "nsParserUtils.h"
00073 #include "nsCRT.h"
00074 #include "nsEscape.h"
00075 #include "nsWeakReference.h"
00076 #include "nsUnicharUtils.h"
00077 #include "nsNodeInfoManager.h"
00078 
00079 
00080 #ifdef ALLOW_ASYNCH_STYLE_SHEETS
00081 const PRBool kBlockByDefault = PR_FALSE;
00082 #else
00083 const PRBool kBlockByDefault = PR_TRUE;
00084 #endif
00085 
00086 
00087 class nsScriptLoaderObserverProxy : public nsIScriptLoaderObserver
00088 {
00089 public:
00090   nsScriptLoaderObserverProxy(nsIScriptLoaderObserver* aInner)
00091     : mInner(do_GetWeakReference(aInner))
00092   {
00093   }
00094   virtual ~nsScriptLoaderObserverProxy()
00095   {
00096   }
00097   
00098   NS_DECL_ISUPPORTS
00099   NS_DECL_NSISCRIPTLOADEROBSERVER
00100 
00101   nsWeakPtr mInner;
00102 };
00103 
00104 NS_IMPL_ISUPPORTS1(nsScriptLoaderObserverProxy, nsIScriptLoaderObserver)
00105 
00106 NS_IMETHODIMP
00107 nsScriptLoaderObserverProxy::ScriptAvailable(nsresult aResult,
00108                                              nsIScriptElement *aElement,
00109                                              PRBool aIsInline,
00110                                              PRBool aWasPending,
00111                                              nsIURI *aURI,
00112                                              PRInt32 aLineNo,
00113                                              const nsAString & aScript)
00114 {
00115   nsCOMPtr<nsIScriptLoaderObserver> inner = do_QueryReferent(mInner);
00116 
00117   if (inner) {
00118     return inner->ScriptAvailable(aResult, aElement, aIsInline, aWasPending,
00119                                   aURI, aLineNo, aScript);
00120   }
00121 
00122   return NS_OK;
00123 }
00124 
00125 NS_IMETHODIMP
00126 nsScriptLoaderObserverProxy::ScriptEvaluated(nsresult aResult,
00127                                              nsIScriptElement *aElement,
00128                                              PRBool aIsInline,
00129                                              PRBool aWasPending)
00130 {
00131   nsCOMPtr<nsIScriptLoaderObserver> inner = do_QueryReferent(mInner);
00132 
00133   if (inner) {
00134     return inner->ScriptEvaluated(aResult, aElement, aIsInline, aWasPending);
00135   }
00136 
00137   return NS_OK;
00138 }
00139 
00140 
00141 NS_IMPL_ISUPPORTS3(nsContentSink,
00142                    nsICSSLoaderObserver,
00143                    nsISupportsWeakReference,
00144                    nsIScriptLoaderObserver)
00145 
00146 nsContentSink::nsContentSink()
00147   : mNeedToBlockParser(PR_FALSE)
00148 {
00149 }
00150 
00151 nsContentSink::~nsContentSink()
00152 {
00153 }
00154 
00155 nsresult
00156 nsContentSink::Init(nsIDocument* aDoc,
00157                     nsIURI* aURI,
00158                     nsISupports* aContainer,
00159                     nsIChannel* aChannel)
00160 {
00161   NS_PRECONDITION(aDoc, "null ptr");
00162   NS_PRECONDITION(aURI, "null ptr");
00163 
00164   if (!aDoc || !aURI) {
00165     return NS_ERROR_NULL_POINTER;
00166   }
00167 
00168   mDocument = aDoc;
00169 
00170   mDocumentURI = aURI;
00171   mDocumentBaseURI = aURI;
00172   mDocShell = do_QueryInterface(aContainer);
00173 
00174   // use this to avoid a circular reference sink->document->scriptloader->sink
00175   nsCOMPtr<nsIScriptLoaderObserver> proxy =
00176       new nsScriptLoaderObserverProxy(this);
00177   NS_ENSURE_TRUE(proxy, NS_ERROR_OUT_OF_MEMORY);
00178 
00179   nsIScriptLoader *loader = mDocument->GetScriptLoader();
00180   NS_ENSURE_TRUE(loader, NS_ERROR_FAILURE);
00181   nsresult rv = loader->AddObserver(proxy);
00182   NS_ENSURE_SUCCESS(rv, rv);
00183 
00184   mCSSLoader = aDoc->CSSLoader();
00185 
00186   ProcessHTTPHeaders(aChannel);
00187 
00188   mNodeInfoManager = aDoc->NodeInfoManager();
00189   return NS_OK;
00190 
00191 }
00192 
00193 NS_IMETHODIMP
00194 nsContentSink::StyleSheetLoaded(nsICSSStyleSheet* aSheet, 
00195                                 PRBool aDidNotify)
00196 {
00197   return NS_OK;
00198 }
00199 
00200 NS_IMETHODIMP
00201 nsContentSink::ScriptAvailable(nsresult aResult,
00202                                nsIScriptElement *aElement,
00203                                PRBool aIsInline,
00204                                PRBool aWasPending,
00205                                nsIURI *aURI,
00206                                PRInt32 aLineNo,
00207                                const nsAString& aScript)
00208 {
00209   PRUint32 count = mScriptElements.Count();
00210 
00211   if (count == 0) {
00212     return NS_OK;
00213   }
00214 
00215   // Check if this is the element we were waiting for
00216   if (aElement != mScriptElements[count - 1]) {
00217     return NS_OK;
00218   }
00219 
00220   if (mParser && !mParser->IsParserEnabled()) {
00221     // make sure to unblock the parser before evaluating the script,
00222     // we must unblock the parser even if loading the script failed or
00223     // if the script was empty, if we don't, the parser will never be
00224     // unblocked.
00225     mParser->UnblockParser();
00226   }
00227 
00228   // Mark the current script as loaded
00229   mNeedToBlockParser = PR_FALSE;
00230 
00231   if (NS_SUCCEEDED(aResult) && aResult != NS_CONTENT_SCRIPT_IS_EVENTHANDLER) {
00232     PreEvaluateScript();
00233   } else {
00234     mScriptElements.RemoveObjectAt(count - 1);
00235 
00236     if (mParser && aWasPending && aResult != NS_BINDING_ABORTED) {
00237       // Loading external script failed!. So, resume parsing since the parser
00238       // got blocked when loading external script. See
00239       // http://bugzilla.mozilla.org/show_bug.cgi?id=94903.
00240       //
00241       // XXX We don't resume parsing if we get NS_BINDING_ABORTED from the
00242       //     script load, assuming that that error code means that the user
00243       //     stopped the load through some action (like clicking a link). See
00244       //     http://bugzilla.mozilla.org/show_bug.cgi?id=243392.
00245       mParser->ContinueInterruptedParsing();
00246     }
00247   }
00248 
00249   return NS_OK;
00250 }
00251 
00252 NS_IMETHODIMP
00253 nsContentSink::ScriptEvaluated(nsresult aResult,
00254                                nsIScriptElement *aElement,
00255                                PRBool aIsInline,
00256                                PRBool aWasPending)
00257 {
00258   // Check if this is the element we were waiting for
00259   PRInt32 count = mScriptElements.Count();
00260   if (count == 0) {
00261     return NS_OK;
00262   }
00263   
00264   if (aElement != mScriptElements[count - 1]) {
00265     return NS_OK;
00266   }
00267 
00268   // Pop the script element stack
00269   mScriptElements.RemoveObjectAt(count - 1); 
00270 
00271   if (NS_SUCCEEDED(aResult)) {
00272     PostEvaluateScript();
00273   }
00274 
00275   if (mParser && mParser->IsParserEnabled() && aWasPending) {
00276     mParser->ContinueInterruptedParsing();
00277   }
00278 
00279   return NS_OK;
00280 }
00281 
00282 nsresult
00283 nsContentSink::ProcessHTTPHeaders(nsIChannel* aChannel)
00284 {
00285   nsCOMPtr<nsIHttpChannel> httpchannel(do_QueryInterface(aChannel));
00286   
00287   if (!httpchannel) {
00288     return NS_OK;
00289   }
00290 
00291   // Note that the only header we care about is the "link" header, since we
00292   // have all the infrastructure for kicking off stylesheet loads.
00293   
00294   nsCAutoString linkHeader;
00295   
00296   nsresult rv = httpchannel->GetResponseHeader(NS_LITERAL_CSTRING("link"),
00297                                                linkHeader);
00298   if (NS_SUCCEEDED(rv) && !linkHeader.IsEmpty()) {
00299     ProcessHeaderData(nsHTMLAtoms::link,
00300                       NS_ConvertASCIItoUTF16(linkHeader));
00301   }
00302   
00303   return NS_OK;
00304 }
00305 
00306 nsresult
00307 nsContentSink::ProcessHeaderData(nsIAtom* aHeader, const nsAString& aValue,
00308                                  nsIContent* aContent)
00309 {
00310   nsresult rv = NS_OK;
00311   // necko doesn't process headers coming in from the parser
00312 
00313   mDocument->SetHeaderData(aHeader, aValue);
00314 
00315   if (aHeader == nsHTMLAtoms::setcookie) {
00316     // Note: Necko already handles cookies set via the channel.  We can't just
00317     // call SetCookie on the channel because we want to do some security checks
00318     // here and want to use the prompt associated to our current window, not
00319     // the window where the channel was dispatched.
00320     nsCOMPtr<nsICookieService> cookieServ =
00321       do_GetService(NS_COOKIESERVICE_CONTRACTID, &rv);
00322     if (NS_FAILED(rv)) {
00323       return rv;
00324     }
00325 
00326     // Get a URI from the document principal
00327 
00328     // We use the original codebase in case the codebase was changed
00329     // by SetDomain
00330 
00331     nsIPrincipal *docPrincipal = mDocument->GetPrincipal();
00332     if (!docPrincipal) {
00333       return NS_ERROR_FAILURE;
00334     }
00335 
00336     nsCOMPtr<nsIPrincipal> systemPrincipal;
00337     nsContentUtils::GetSecurityManager()->
00338       GetSystemPrincipal(getter_AddRefs(systemPrincipal));
00339     NS_ASSERTION(systemPrincipal, "No system principal");
00340     
00341     if (docPrincipal == systemPrincipal) {
00342       // Document's principal is not a codebase, so we can't set cookies
00343       return NS_OK;
00344     }
00345 
00346     nsCOMPtr<nsIURI> codebaseURI;
00347     rv = docPrincipal->GetURI(getter_AddRefs(codebaseURI));
00348     NS_ENSURE_SUCCESS(rv, rv);
00349 
00350     nsCOMPtr<nsIPrompt> prompt;
00351     nsCOMPtr<nsIDOMWindowInternal> window (do_QueryInterface(mDocument->GetScriptGlobalObject()));
00352     if (window) {
00353       window->GetPrompter(getter_AddRefs(prompt));
00354     }
00355 
00356     nsCOMPtr<nsIChannel> channel;
00357     if (mParser) {
00358       mParser->GetChannel(getter_AddRefs(channel));
00359     }
00360 
00361     rv = cookieServ->SetCookieString(codebaseURI,
00362                                      prompt,
00363                                      NS_ConvertUCS2toUTF8(aValue).get(),
00364                                      channel);
00365     if (NS_FAILED(rv)) {
00366       return rv;
00367     }
00368   }
00369   else if (aHeader == nsHTMLAtoms::link) {
00370     rv = ProcessLinkHeader(aContent, aValue);
00371   }
00372   else if (aHeader == nsHTMLAtoms::msthemecompatible) {
00373     // Disable theming for the presshell if the value is no.
00374     // XXXbz don't we want to support this as an HTTP header too?
00375     nsAutoString value(aValue);
00376     if (value.LowerCaseEqualsLiteral("no")) {
00377       nsIPresShell* shell = mDocument->GetShellAt(0);
00378       if (shell) {
00379         shell->DisableThemeSupport();
00380       }
00381     }
00382   }
00383   // Don't report "refresh" headers back to necko, since our document handles
00384   // them
00385   else if (aHeader != nsHTMLAtoms::refresh && mParser) {
00386     // we also need to report back HTTP-EQUIV headers to the channel
00387     // so that it can process things like pragma: no-cache or other
00388     // cache-control headers. Ideally this should also be the way for
00389     // cookies to be set! But we'll worry about that in the next
00390     // iteration
00391     nsCOMPtr<nsIChannel> channel;
00392     if (NS_SUCCEEDED(mParser->GetChannel(getter_AddRefs(channel)))) {
00393       nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
00394       if (httpChannel) {
00395         const char* header;
00396         (void)aHeader->GetUTF8String(&header);
00397         (void)httpChannel->SetResponseHeader(nsDependentCString(header),
00398                                              NS_ConvertUCS2toUTF8(aValue),
00399                                              PR_TRUE);
00400       }
00401     }
00402   }
00403 
00404   return rv;
00405 }
00406 
00407 
00408 static const PRUnichar kSemiCh = PRUnichar(';');
00409 static const PRUnichar kCommaCh = PRUnichar(',');
00410 static const PRUnichar kEqualsCh = PRUnichar('=');
00411 static const PRUnichar kLessThanCh = PRUnichar('<');
00412 static const PRUnichar kGreaterThanCh = PRUnichar('>');
00413 
00414 nsresult
00415 nsContentSink::ProcessLinkHeader(nsIContent* aElement,
00416                                  const nsAString& aLinkData)
00417 {
00418   nsresult rv = NS_OK;
00419 
00420   // parse link content and call process style link
00421   nsAutoString href;
00422   nsAutoString rel;
00423   nsAutoString title;
00424   nsAutoString type;
00425   nsAutoString media;
00426   PRBool didBlock = PR_FALSE;
00427 
00428   // copy to work buffer
00429   nsAutoString stringList(aLinkData);
00430 
00431   // put an extra null at the end
00432   stringList.Append(kNullCh);
00433 
00434   PRUnichar* start = stringList.BeginWriting();
00435   PRUnichar* end   = start;
00436   PRUnichar* last  = start;
00437   PRUnichar  endCh;
00438 
00439   while (*start != kNullCh) {
00440     // skip leading space
00441     while ((*start != kNullCh) && nsCRT::IsAsciiSpace(*start)) {
00442       ++start;
00443     }
00444 
00445     end = start;
00446     last = end - 1;
00447 
00448     // look for semicolon or comma
00449     while (*end != kNullCh && *end != kSemiCh && *end != kCommaCh) {
00450       PRUnichar ch = *end;
00451 
00452       if (ch == kApostrophe || ch == kQuote || ch == kLessThanCh) {
00453         // quoted string
00454 
00455         PRUnichar quote = *end;
00456         if (quote == kLessThanCh) {
00457           quote = kGreaterThanCh;
00458         }
00459 
00460         PRUnichar* closeQuote = (end + 1);
00461 
00462         // seek closing quote
00463         while (*closeQuote != kNullCh && quote != *closeQuote) {
00464           ++closeQuote;
00465         }
00466 
00467         if (quote == *closeQuote) {
00468           // found closer
00469 
00470           // skip to close quote
00471           end = closeQuote;
00472 
00473           last = end - 1;
00474 
00475           ch = *(end + 1);
00476 
00477           if (ch != kNullCh && ch != kSemiCh && ch != kCommaCh) {
00478             // end string here
00479             *(++end) = kNullCh;
00480 
00481             ch = *(end + 1);
00482 
00483             // keep going until semi or comma
00484             while (ch != kNullCh && ch != kSemiCh && ch != kCommaCh) {
00485               ++end;
00486 
00487               ch = *end;
00488             }
00489           }
00490         }
00491       }
00492 
00493       ++end;
00494       ++last;
00495     }
00496 
00497     endCh = *end;
00498 
00499     // end string here
00500     *end = kNullCh;
00501 
00502     if (start < end) {
00503       if ((*start == kLessThanCh) && (*last == kGreaterThanCh)) {
00504         *last = kNullCh;
00505 
00506         if (href.IsEmpty()) { // first one wins
00507           href = (start + 1);
00508           href.StripWhitespace();
00509         }
00510       } else {
00511         PRUnichar* equals = start;
00512 
00513         while ((*equals != kNullCh) && (*equals != kEqualsCh)) {
00514           equals++;
00515         }
00516 
00517         if (*equals != kNullCh) {
00518           *equals = kNullCh;
00519           nsAutoString  attr(start);
00520           attr.StripWhitespace();
00521 
00522           PRUnichar* value = ++equals;
00523           while (nsCRT::IsAsciiSpace(*value)) {
00524             value++;
00525           }
00526 
00527           if (((*value == kApostrophe) || (*value == kQuote)) &&
00528               (*value == *last)) {
00529             *last = kNullCh;
00530             value++;
00531           }
00532 
00533           if (attr.LowerCaseEqualsLiteral("rel")) {
00534             if (rel.IsEmpty()) {
00535               rel = value;
00536               rel.CompressWhitespace();
00537             }
00538           } else if (attr.LowerCaseEqualsLiteral("title")) {
00539             if (title.IsEmpty()) {
00540               title = value;
00541               title.CompressWhitespace();
00542             }
00543           } else if (attr.LowerCaseEqualsLiteral("type")) {
00544             if (type.IsEmpty()) {
00545               type = value;
00546               type.StripWhitespace();
00547             }
00548           } else if (attr.LowerCaseEqualsLiteral("media")) {
00549             if (media.IsEmpty()) {
00550               media = value;
00551 
00552               // HTML4.0 spec is inconsistent, make it case INSENSITIVE
00553               ToLowerCase(media);
00554             }
00555           }
00556         }
00557       }
00558     }
00559 
00560     if (endCh == kCommaCh) {
00561       // hit a comma, process what we've got so far
00562 
00563       if (!href.IsEmpty() && !rel.IsEmpty()) {
00564         rv = ProcessLink(aElement, href, rel, title, type, media);
00565         if (rv == NS_ERROR_HTMLPARSER_BLOCK) {
00566           didBlock = PR_TRUE;
00567         }
00568       }
00569 
00570       href.Truncate();
00571       rel.Truncate();
00572       title.Truncate();
00573       type.Truncate();
00574       media.Truncate();
00575     }
00576 
00577     start = ++end;
00578   }
00579 
00580   if (!href.IsEmpty() && !rel.IsEmpty()) {
00581     rv = ProcessLink(aElement, href, rel, title, type, media);
00582 
00583     if (NS_SUCCEEDED(rv) && didBlock) {
00584       rv = NS_ERROR_HTMLPARSER_BLOCK;
00585     }
00586   }
00587 
00588   return rv;
00589 }
00590 
00591 
00592 nsresult
00593 nsContentSink::ProcessLink(nsIContent* aElement,
00594                            const nsSubstring& aHref, const nsSubstring& aRel,
00595                            const nsSubstring& aTitle, const nsSubstring& aType,
00596                            const nsSubstring& aMedia)
00597 {
00598   // XXX seems overkill to generate this string array
00599   nsStringArray linkTypes;
00600   nsStyleLinkElement::ParseLinkTypes(aRel, linkTypes);
00601 
00602   PRBool hasPrefetch = (linkTypes.IndexOf(NS_LITERAL_STRING("prefetch")) != -1);
00603   // prefetch href if relation is "next" or "prefetch"
00604   if (hasPrefetch || linkTypes.IndexOf(NS_LITERAL_STRING("next")) != -1) {
00605     PrefetchHref(aHref, hasPrefetch);
00606   }
00607 
00608   // is it a stylesheet link?
00609   if (linkTypes.IndexOf(NS_LITERAL_STRING("stylesheet")) == -1) {
00610     return NS_OK;
00611   }
00612 
00613   PRBool isAlternate = linkTypes.IndexOf(NS_LITERAL_STRING("alternate")) != -1;
00614   return ProcessStyleLink(aElement, aHref, isAlternate, aTitle, aType,
00615                           aMedia);
00616 }
00617 
00618 nsresult
00619 nsContentSink::ProcessStyleLink(nsIContent* aElement,
00620                                 const nsSubstring& aHref,
00621                                 PRBool aAlternate,
00622                                 const nsSubstring& aTitle,
00623                                 const nsSubstring& aType,
00624                                 const nsSubstring& aMedia)
00625 {
00626   if (aAlternate && aTitle.IsEmpty()) {
00627     // alternates must have title return without error, for now
00628     return NS_OK;
00629   }
00630 
00631   nsAutoString  mimeType;
00632   nsAutoString  params;
00633   nsParserUtils::SplitMimeType(aType, mimeType, params);
00634 
00635   // see bug 18817
00636   if (!mimeType.IsEmpty() && !mimeType.LowerCaseEqualsLiteral("text/css")) {
00637     // Unknown stylesheet language
00638     return NS_OK;
00639   }
00640 
00641   nsCOMPtr<nsIURI> url;
00642   nsresult rv = NS_NewURI(getter_AddRefs(url), aHref, nsnull, mDocumentBaseURI);
00643   
00644   if (NS_FAILED(rv)) {
00645     // The URI is bad, move along, don't propagate the error (for now)
00646     return NS_OK;
00647   }
00648 
00649   if (!aAlternate) {
00650     // possibly preferred sheet
00651 
00652     if (!aTitle.IsEmpty()) {
00653       nsAutoString preferredStyle;
00654       mDocument->GetHeaderData(nsHTMLAtoms::headerDefaultStyle,
00655                                preferredStyle);
00656       if (preferredStyle.IsEmpty()) {
00657         mDocument->SetHeaderData(nsHTMLAtoms::headerDefaultStyle, aTitle);
00658       }
00659     }
00660   }
00661 
00662   PRBool blockParser = kBlockByDefault;
00663   if (aAlternate) {
00664     blockParser = PR_FALSE;
00665   }
00666 
00667   // NOTE: no longer honoring the important keyword to indicate
00668   // blocking as it is proprietary and unnecessary since all
00669   // non-alternate will block the parser now -mja
00670 #if 0
00671   if (linkTypes.IndexOf("important") != -1) {
00672     blockParser = PR_TRUE;
00673   }
00674 #endif
00675 
00676   PRBool doneLoading;
00677   nsIParser* parser = nsnull;
00678   if (blockParser) {
00679     parser = mParser;
00680   }
00681   rv = mCSSLoader->LoadStyleLink(aElement, url, aTitle, aMedia,
00682                                  parser, doneLoading, this);
00683 
00684   if (NS_SUCCEEDED(rv) && blockParser && !doneLoading) {
00685     rv = NS_ERROR_HTMLPARSER_BLOCK;
00686   }
00687 
00688   return rv;
00689 }
00690 
00691 
00692 nsresult
00693 nsContentSink::ProcessMETATag(nsIContent* aContent)
00694 {
00695   NS_ASSERTION(aContent, "missing base-element");
00696 
00697   nsresult rv = NS_OK;
00698 
00699   // set any HTTP-EQUIV data into document's header data as well as url
00700   nsAutoString header;
00701   aContent->GetAttr(kNameSpaceID_None, nsHTMLAtoms::httpEquiv, header);
00702   if (!header.IsEmpty()) {
00703     nsAutoString result;
00704     aContent->GetAttr(kNameSpaceID_None, nsHTMLAtoms::content, result);
00705     if (!result.IsEmpty()) {
00706       ToLowerCase(header);
00707       nsCOMPtr<nsIAtom> fieldAtom(do_GetAtom(header));
00708       rv = ProcessHeaderData(fieldAtom, result, aContent); 
00709     }
00710   }
00711 
00712   return rv;
00713 }
00714 
00715 
00716 void
00717 nsContentSink::PrefetchHref(const nsAString &aHref, PRBool aExplicit)
00718 {
00719   //
00720   // SECURITY CHECK: disable prefetching from mailnews!
00721   //
00722   // walk up the docshell tree to see if any containing
00723   // docshell are of type MAIL.
00724   //
00725   if (!mDocShell)
00726     return;
00727 
00728   nsCOMPtr<nsIDocShell> docshell = mDocShell;
00729 
00730   nsCOMPtr<nsIDocShellTreeItem> treeItem, parentItem;
00731   do {
00732     PRUint32 appType = 0;
00733     nsresult rv = docshell->GetAppType(&appType);
00734     if (NS_FAILED(rv) || appType == nsIDocShell::APP_TYPE_MAIL)
00735       return; // do not prefetch from mailnews
00736     if (treeItem = do_QueryInterface(docshell)) {
00737       treeItem->GetParent(getter_AddRefs(parentItem));
00738       if (parentItem) {
00739         treeItem = parentItem;
00740         docshell = do_QueryInterface(treeItem);
00741         if (!docshell) {
00742           NS_ERROR("cannot get a docshell from a treeItem!");
00743           return;
00744         }
00745       }
00746     }
00747   } while (parentItem);
00748   
00749   // OK, we passed the security check...
00750   
00751   nsCOMPtr<nsIPrefetchService> prefetchService(do_GetService(NS_PREFETCHSERVICE_CONTRACTID));
00752   if (prefetchService) {
00753     // construct URI using document charset
00754     const nsACString &charset = mDocument->GetDocumentCharacterSet();
00755     nsCOMPtr<nsIURI> uri;
00756     NS_NewURI(getter_AddRefs(uri), aHref,
00757               charset.IsEmpty() ? nsnull : PromiseFlatCString(charset).get(),
00758               mDocumentBaseURI);
00759     if (uri) {
00760       prefetchService->PrefetchURI(uri, mDocumentURI, aExplicit);
00761     }
00762   }
00763 }
00764 
00765 
00766 // Convert the ref from document charset to unicode.
00767 static nsresult
00768 CharsetConvRef(const nsACString& aDocCharset,
00769                const nsCString& aRefInDocCharset,
00770                nsString& aRefInUnicode)
00771 {
00772   nsresult rv;
00773 
00774   nsCOMPtr <nsIAtom> docCharsetAtom;
00775   nsCOMPtr<nsICharsetConverterManager> ccm =
00776     do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &rv);
00777 
00778   if (NS_FAILED(rv)) {
00779     return rv;
00780   }
00781 
00782   nsCOMPtr<nsIUnicodeDecoder> decoder;
00783   rv = ccm->GetUnicodeDecoder(PromiseFlatCString(aDocCharset).get(),
00784                               getter_AddRefs(decoder));
00785   if (NS_FAILED(rv)) {
00786     return rv;
00787   }
00788 
00789   PRInt32 srcLen = aRefInDocCharset.Length();
00790   PRInt32 dstLen;
00791   rv = decoder->GetMaxLength(aRefInDocCharset.get(), srcLen, &dstLen);
00792   if (NS_FAILED(rv)) {
00793     return rv;
00794   }
00795 
00796   PRUnichar *ustr = (PRUnichar *)nsMemory::Alloc((dstLen + 1) *
00797                                                  sizeof(PRUnichar));
00798   if (!ustr) {
00799     return NS_ERROR_OUT_OF_MEMORY;
00800   }
00801 
00802   rv = decoder->Convert(aRefInDocCharset.get(), &srcLen, ustr, &dstLen);
00803   if (NS_SUCCEEDED(rv)) {
00804     ustr[dstLen] = 0;
00805     aRefInUnicode.Assign(ustr, dstLen);
00806   }
00807 
00808   nsMemory::Free(ustr);
00809 
00810   return rv;
00811 }
00812 
00813 PRBool
00814 nsContentSink::ScrollToRef(PRBool aReallyScroll)
00815 {
00816   if (mRef.IsEmpty()) {
00817     return PR_FALSE;
00818   }
00819 
00820   PRBool didScroll = PR_FALSE;
00821 
00822   char* tmpstr = ToNewCString(mRef);
00823   if (!tmpstr) {
00824     return PR_FALSE;
00825   }
00826 
00827   nsUnescape(tmpstr);
00828   nsCAutoString unescapedRef;
00829   unescapedRef.Assign(tmpstr);
00830   nsMemory::Free(tmpstr);
00831 
00832   nsresult rv = NS_ERROR_FAILURE;
00833   // We assume that the bytes are in UTF-8, as it says in the spec:
00834   // http://www.w3.org/TR/html4/appendix/notes.html#h-B.2.1
00835   NS_ConvertUTF8toUCS2 ref(unescapedRef);
00836 
00837   PRInt32 i, ns = mDocument->GetNumberOfShells();
00838   for (i = 0; i < ns; i++) {
00839     nsIPresShell* shell = mDocument->GetShellAt(i);
00840     if (shell) {
00841       // Check an empty string which might be caused by the UTF-8 conversion
00842       if (!ref.IsEmpty()) {
00843         // Note that GoToAnchor will handle flushing layout as needed.
00844         rv = shell->GoToAnchor(ref, aReallyScroll);
00845       } else {
00846         rv = NS_ERROR_FAILURE;
00847       }
00848 
00849       // If UTF-8 URI failed then try to assume the string as a
00850       // document's charset.
00851 
00852       if (NS_FAILED(rv)) {
00853         const nsACString &docCharset = mDocument->GetDocumentCharacterSet();
00854 
00855         rv = CharsetConvRef(docCharset, unescapedRef, ref);
00856 
00857         if (NS_SUCCEEDED(rv) && !ref.IsEmpty())
00858           rv = shell->GoToAnchor(ref, aReallyScroll);
00859       }
00860       if (NS_SUCCEEDED(rv)) {
00861         didScroll = PR_TRUE;
00862       }
00863     }
00864   }
00865 
00866   return didScroll;
00867 }
00868 
00869 nsresult
00870 nsContentSink::RefreshIfEnabled(nsIViewManager* vm)
00871 {
00872   if (!vm) {
00873     return NS_OK;
00874   }
00875 
00876   NS_ENSURE_TRUE(mDocShell, NS_ERROR_FAILURE);
00877 
00878   nsCOMPtr<nsIContentViewer> contentViewer;
00879   mDocShell->GetContentViewer(getter_AddRefs(contentViewer));
00880   if (contentViewer) {
00881     PRBool enabled;
00882     contentViewer->GetEnableRendering(&enabled);
00883     if (enabled) {
00884       vm->EnableRefresh(NS_VMREFRESH_IMMEDIATE);
00885     }
00886   }
00887 
00888   return NS_OK;
00889 }
00890 
00891 void
00892 nsContentSink::StartLayout(PRBool aIsFrameset)
00893 {
00894   PRUint32 i, ns = mDocument->GetNumberOfShells();
00895   for (i = 0; i < ns; i++) {
00896     nsIPresShell *shell = mDocument->GetShellAt(i);
00897 
00898     if (shell) {
00899       // Make sure we don't call InitialReflow() for a shell that has
00900       // already called it. This can happen when the layout frame for
00901       // an iframe is constructed *between* the Embed() call for the
00902       // docshell in the iframe, and the content sink's call to OpenBody().
00903       // (Bug 153815)
00904 
00905       PRBool didInitialReflow = PR_FALSE;
00906       shell->GetDidInitialReflow(&didInitialReflow);
00907       if (didInitialReflow) {
00908         // XXX: The assumption here is that if something already
00909         // called InitialReflow() on this shell, it also did some of
00910         // the setup below, so we do nothing and just move on to the
00911         // next shell in the list.
00912 
00913         continue;
00914       }
00915 
00916       // Make shell an observer for next time
00917       shell->BeginObservingDocument();
00918 
00919       // Resize-reflow this time
00920       nsRect r = shell->GetPresContext()->GetVisibleArea();
00921       shell->InitialReflow(r.width, r.height);
00922 
00923       // Now trigger a refresh
00924       RefreshIfEnabled(shell->GetViewManager());
00925     }
00926   }
00927 
00928   // If the document we are loading has a reference or it is a
00929   // frameset document, disable the scroll bars on the views.
00930 
00931   if (mDocumentURI) {
00932     nsCAutoString ref;
00933 
00934     // Since all URI's that pass through here aren't URL's we can't
00935     // rely on the nsIURI implementation for providing a way for
00936     // finding the 'ref' part of the URI, we'll haveto revert to
00937     // string routines for finding the data past '#'
00938 
00939     mDocumentURI->GetSpec(ref);
00940 
00941     nsReadingIterator<char> start, end;
00942 
00943     ref.BeginReading(start);
00944     ref.EndReading(end);
00945 
00946     if (FindCharInReadable('#', start, end)) {
00947       ++start; // Skip over the '#'
00948 
00949       mRef = Substring(start, end);
00950     }
00951   }
00952 }