Back to index

lightning-sunbird  0.9+nobinonly
nsStyleLinkElement.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
00002  * 
00003  * ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is Mozilla Communicator client code, released
00017  * March 31, 1998.
00018  *
00019  * The Initial Developer of the Original Code is
00020  * Netscape Communications Corporation.
00021  * Portions created by the Initial Developer are Copyright (C) 1998-1999
00022  * the Initial Developer. All Rights Reserved.
00023  *
00024  * Contributor(s):
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either of the GNU General Public License Version 2 or later (the "GPL"),
00028  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the MPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the MPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK ***** */
00039 
00040 #include "nsStyleLinkElement.h"
00041 
00042 #include "nsHTMLAtoms.h"
00043 #include "nsIContent.h"
00044 #include "nsICSSLoader.h"
00045 #include "nsICSSStyleSheet.h"
00046 #include "nsIDocument.h"
00047 #include "nsIDOMComment.h"
00048 #include "nsIDOMNode.h"
00049 #include "nsIDOMStyleSheet.h"
00050 #include "nsIDOMText.h"
00051 #include "nsIUnicharInputStream.h"
00052 #include "nsNetUtil.h"
00053 #include "nsUnicharUtils.h"
00054 #include "nsVoidArray.h"
00055 #include "nsCRT.h"
00056 
00057 nsStyleLinkElement::nsStyleLinkElement()
00058   : mDontLoadStyle(PR_FALSE)
00059   , mUpdatesEnabled(PR_TRUE)
00060   , mLineNumber(1)
00061 {
00062 }
00063 
00064 nsStyleLinkElement::~nsStyleLinkElement()
00065 {
00066   nsCOMPtr<nsICSSStyleSheet> cssSheet = do_QueryInterface(mStyleSheet);
00067   if (cssSheet) {
00068     cssSheet->SetOwningNode(nsnull);
00069   }
00070 }
00071 
00072 NS_IMETHODIMP 
00073 nsStyleLinkElement::SetStyleSheet(nsIStyleSheet* aStyleSheet)
00074 {
00075   nsCOMPtr<nsICSSStyleSheet> cssSheet = do_QueryInterface(mStyleSheet);
00076   if (cssSheet) {
00077     cssSheet->SetOwningNode(nsnull);
00078   }
00079 
00080   mStyleSheet = aStyleSheet;
00081   cssSheet = do_QueryInterface(mStyleSheet);
00082   if (cssSheet) {
00083     nsCOMPtr<nsIDOMNode> node;
00084     CallQueryInterface(this,
00085                        NS_STATIC_CAST(nsIDOMNode**, getter_AddRefs(node)));
00086     if (node) {
00087       cssSheet->SetOwningNode(node);
00088     }
00089   }
00090     
00091   return NS_OK;
00092 }
00093 
00094 NS_IMETHODIMP 
00095 nsStyleLinkElement::GetStyleSheet(nsIStyleSheet*& aStyleSheet)
00096 {
00097   aStyleSheet = mStyleSheet;
00098   NS_IF_ADDREF(aStyleSheet);
00099 
00100   return NS_OK;
00101 }
00102 
00103 NS_IMETHODIMP 
00104 nsStyleLinkElement::InitStyleLinkElement(nsIParser* aParser,
00105                                          PRBool aDontLoadStyle)
00106 {
00107   mParser = aParser;
00108   mDontLoadStyle = aDontLoadStyle;
00109 
00110   return NS_OK;
00111 }
00112 
00113 NS_IMETHODIMP
00114 nsStyleLinkElement::GetSheet(nsIDOMStyleSheet** aSheet)
00115 {
00116   NS_ENSURE_ARG_POINTER(aSheet);
00117   *aSheet = nsnull;
00118 
00119   if (mStyleSheet) {
00120     CallQueryInterface(mStyleSheet, aSheet);
00121   }
00122 
00123   // Always return NS_OK to avoid throwing JS exceptions if mStyleSheet 
00124   // is not a nsIDOMStyleSheet
00125   return NS_OK;
00126 }
00127 
00128 NS_IMETHODIMP
00129 nsStyleLinkElement::SetEnableUpdates(PRBool aEnableUpdates)
00130 {
00131   mUpdatesEnabled = aEnableUpdates;
00132 
00133   return NS_OK;
00134 }
00135 
00136 NS_IMETHODIMP
00137 nsStyleLinkElement::GetCharset(nsAString& aCharset)
00138 {
00139   // descendants have to implement this themselves
00140   return NS_ERROR_NOT_IMPLEMENTED;
00141 }
00142 
00143 /* virtual */ void
00144 nsStyleLinkElement::SetLineNumber(PRUint32 aLineNumber)
00145 {
00146   mLineNumber = aLineNumber;
00147 }
00148 
00149 void nsStyleLinkElement::ParseLinkTypes(const nsAString& aTypes,
00150                                         nsStringArray& aResult)
00151 {
00152   nsAString::const_iterator start, done;
00153   aTypes.BeginReading(start);
00154   aTypes.EndReading(done);
00155   if (start == done)
00156     return;
00157 
00158   nsAString::const_iterator current(start);
00159   PRBool inString = !nsCRT::IsAsciiSpace(*current);
00160   nsAutoString subString;
00161 
00162   while (current != done) {
00163     if (nsCRT::IsAsciiSpace(*current)) {
00164       if (inString) {
00165         ToLowerCase(Substring(start, current), subString);
00166         aResult.AppendString(subString);
00167         inString = PR_FALSE;
00168       }
00169     }
00170     else {
00171       if (!inString) {
00172         start = current;
00173         inString = PR_TRUE;
00174       }
00175     }
00176     ++current;
00177   }
00178   if (inString) {
00179     ToLowerCase(Substring(start, current), subString);
00180     aResult.AppendString(subString);
00181   }
00182 }
00183 
00184 #ifdef ALLOW_ASYNCH_STYLE_SHEETS
00185 const PRBool kBlockByDefault=PR_FALSE;
00186 #else
00187 const PRBool kBlockByDefault=PR_TRUE;
00188 #endif
00189 
00190 NS_IMETHODIMP
00191 nsStyleLinkElement::UpdateStyleSheet(nsIDocument *aOldDocument,
00192                                      nsICSSLoaderObserver* aObserver)
00193 {
00194   if (mStyleSheet && aOldDocument) {
00195     // We're removing the link element from the document, unload the
00196     // stylesheet.  We want to do this even if updates are disabled, since
00197     // otherwise a sheet with a stale linking element pointer will be hanging
00198     // around -- not good!
00199     aOldDocument->BeginUpdate(UPDATE_STYLE);
00200     aOldDocument->RemoveStyleSheet(mStyleSheet);
00201     aOldDocument->EndUpdate(UPDATE_STYLE);
00202     mStyleSheet = nsnull;
00203   }
00204 
00205   if (mDontLoadStyle || !mUpdatesEnabled) {
00206     return NS_OK;
00207   }
00208 
00209   // Keep a strong ref to the parser so it's still around when we pass it
00210   // to the CSS loader. Release strong ref in mParser so we don't hang on
00211   // to the parser once we start the load or if we fail to load the
00212   // stylesheet.
00213   nsCOMPtr<nsIParser> parser = mParser;
00214   mParser = nsnull;
00215 
00216   nsCOMPtr<nsIContent> thisContent;
00217   QueryInterface(NS_GET_IID(nsIContent), getter_AddRefs(thisContent));
00218 
00219   NS_ENSURE_TRUE(thisContent, NS_ERROR_FAILURE);
00220 
00221   nsCOMPtr<nsIDocument> doc = thisContent->GetDocument();
00222 
00223   if (!doc) {
00224     return NS_OK;
00225   }
00226 
00227   nsCOMPtr<nsIURI> uri;
00228   PRBool isInline;
00229   GetStyleSheetURL(&isInline, getter_AddRefs(uri));
00230 
00231   if (mStyleSheet && !isInline && uri) {
00232     nsCOMPtr<nsIURI> oldURI;
00233 
00234     mStyleSheet->GetSheetURI(getter_AddRefs(oldURI));
00235     if (oldURI) {
00236       PRBool equal;
00237       nsresult rv = oldURI->Equals(uri, &equal);
00238       if (NS_SUCCEEDED(rv) && equal) {
00239         return NS_OK; // We already loaded this stylesheet
00240       }
00241     }
00242   }
00243 
00244   if (mStyleSheet) {
00245     doc->BeginUpdate(UPDATE_STYLE);
00246     doc->RemoveStyleSheet(mStyleSheet);
00247     doc->EndUpdate(UPDATE_STYLE);
00248     mStyleSheet = nsnull;
00249   }
00250 
00251   if (!uri && !isInline) {
00252     return NS_OK; // If href is empty and this is not inline style then just bail
00253   }
00254 
00255   nsAutoString title, type, media;
00256   PRBool isAlternate;
00257 
00258   GetStyleSheetInfo(title, type, media, &isAlternate);
00259 
00260   if (!type.LowerCaseEqualsLiteral("text/css")) {
00261     return NS_OK;
00262   }
00263 
00264   PRBool blockParser = kBlockByDefault;
00265   if (isAlternate) {
00266     blockParser = PR_FALSE;
00267   }
00268 
00269   /* NOTE: no longer honoring the important keyword to indicate blocking
00270            as it is proprietary and unnecessary since all non-alternate 
00271            will block the parser now  -mja
00272     if (-1 != linkTypes.IndexOf("important")) {
00273       blockParser = PR_TRUE;
00274     }
00275   */
00276 
00277   if (!isAlternate && !title.IsEmpty()) {  // possibly preferred sheet
00278     nsAutoString prefStyle;
00279     doc->GetHeaderData(nsHTMLAtoms::headerDefaultStyle, prefStyle);
00280 
00281     if (prefStyle.IsEmpty()) {
00282       doc->SetHeaderData(nsHTMLAtoms::headerDefaultStyle, title);
00283     }
00284   }
00285 
00286   PRBool doneLoading;
00287   nsresult rv = NS_OK;
00288   if (isInline) {
00289     PRUint32 count = thisContent->GetChildCount();
00290 
00291     nsAutoString content;
00292 
00293     PRUint32 i;
00294     for (i = 0; i < count; ++i) {
00295       nsIContent *node = thisContent->GetChildAt(i);
00296       nsCOMPtr<nsIDOMText> tc = do_QueryInterface(node);
00297       // Ignore nodes that are not DOMText.
00298       if (!tc) {
00299         nsCOMPtr<nsIDOMComment> comment = do_QueryInterface(node);
00300         if (comment)
00301           // Skip a comment
00302           continue;
00303         break;
00304       }
00305 
00306       nsAutoString tcString;
00307       tc->GetData(tcString);
00308       content.Append(tcString);
00309     }
00310 
00311     // Use of the stream will be done before parsing returns.  So it will go
00312     // out of scope before |content| does.
00313     nsCOMPtr<nsIUnicharInputStream> uin;
00314     rv = NS_NewStringUnicharInputStream(getter_AddRefs(uin), &content,
00315                                         PR_FALSE);
00316     if (NS_FAILED(rv)) {
00317       return rv;
00318     }
00319 
00320     // Now that we have a url and a unicode input stream, parse the
00321     // style sheet.
00322     rv = doc->CSSLoader()->
00323       LoadInlineStyle(thisContent, uin, mLineNumber, title, media,
00324                       ((blockParser) ? parser.get() : nsnull),
00325                       doneLoading, aObserver);
00326   }
00327   else {
00328     rv = doc->CSSLoader()->
00329       LoadStyleLink(thisContent, uri, title, media,
00330                     ((blockParser) ? parser.get() : nsnull),
00331                     doneLoading, aObserver);
00332   }
00333 
00334   if (NS_SUCCEEDED(rv) && blockParser && !doneLoading) {
00335     rv = NS_ERROR_HTMLPARSER_BLOCK;
00336   }
00337 
00338   return rv;
00339 }
00340