/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ /* * A base class which implements nsIStyleSheetLinkingElement and can * be subclassed by various content nodes that want to load * stylesheets (<style>, <link>, processing instructions, etc). */ #include "nsStyleLinkElement.h" #include "nsIContent.h" #include "nsICSSLoader.h" #include "nsICSSStyleSheet.h" #include "nsIDocument.h" #include "nsIDOMComment.h" #include "nsIDOMNode.h" #include "nsIDOMStyleSheet.h" #include "nsIDOMText.h" #include "nsIUnicharInputStream.h" #include "nsISimpleUnicharStreamFactory.h" #include "nsNetUtil.h" #include "nsUnicharUtils.h" #include "nsVoidArray.h" #include "nsCRT.h" #include "nsXPCOMCIDInternal.h" #include "nsUnicharInputStream.h" #include "nsContentUtils.h" nsStyleLinkElement::nsStyleLinkElement() : mDontLoadStyle(PR_FALSE) , mUpdatesEnabled(PR_TRUE) , mLineNumber(1) { } nsStyleLinkElement::~nsStyleLinkElement() { nsStyleLinkElement::SetStyleSheet(nsnull); } NS_IMETHODIMP nsStyleLinkElement::SetStyleSheet(nsIStyleSheet* aStyleSheet) { nsCOMPtr<nsICSSStyleSheet> cssSheet = do_QueryInterface(mStyleSheet); if (cssSheet) { cssSheet->SetOwningNode(nsnull); } mStyleSheet = aStyleSheet; cssSheet = do_QueryInterface(mStyleSheet); if (cssSheet) { nsCOMPtr<nsIDOMNode> node; CallQueryInterface(this, static_cast<nsIDOMNode**>(getter_AddRefs(node))); if (node) { cssSheet->SetOwningNode(node); } } return NS_OK; } NS_IMETHODIMP nsStyleLinkElement::GetStyleSheet(nsIStyleSheet*& aStyleSheet) { aStyleSheet = mStyleSheet; NS_IF_ADDREF(aStyleSheet); return NS_OK; } NS_IMETHODIMP nsStyleLinkElement::InitStyleLinkElement(PRBool aDontLoadStyle) { mDontLoadStyle = aDontLoadStyle; return NS_OK; } NS_IMETHODIMP nsStyleLinkElement::GetSheet(nsIDOMStyleSheet** aSheet) { NS_ENSURE_ARG_POINTER(aSheet); *aSheet = nsnull; if (mStyleSheet) { CallQueryInterface(mStyleSheet, aSheet); } // Always return NS_OK to avoid throwing JS exceptions if mStyleSheet // is not a nsIDOMStyleSheet return NS_OK; } NS_IMETHODIMP nsStyleLinkElement::SetEnableUpdates(PRBool aEnableUpdates) { mUpdatesEnabled = aEnableUpdates; return NS_OK; } NS_IMETHODIMP nsStyleLinkElement::GetCharset(nsAString& aCharset) { // descendants have to implement this themselves return NS_ERROR_NOT_IMPLEMENTED; } /* virtual */ void nsStyleLinkElement::OverrideBaseURI(nsIURI* aNewBaseURI) { NS_NOTREACHED("Base URI can't be overriden in this implementation " "of nsIStyleSheetLinkingElement."); } /* virtual */ void nsStyleLinkElement::SetLineNumber(PRUint32 aLineNumber) { mLineNumber = aLineNumber; } void nsStyleLinkElement::ParseLinkTypes(const nsAString& aTypes, nsStringArray& aResult) { nsAString::const_iterator start, done; aTypes.BeginReading(start); aTypes.EndReading(done); if (start == done) return; nsAString::const_iterator current(start); PRBool inString = !nsCRT::IsAsciiSpace(*current); nsAutoString subString; while (current != done) { if (nsCRT::IsAsciiSpace(*current)) { if (inString) { ToLowerCase(Substring(start, current), subString); aResult.AppendString(subString); inString = PR_FALSE; } } else { if (!inString) { start = current; inString = PR_TRUE; } } ++current; } if (inString) { ToLowerCase(Substring(start, current), subString); aResult.AppendString(subString); } } NS_IMETHODIMP nsStyleLinkElement::UpdateStyleSheet(nsICSSLoaderObserver* aObserver, PRBool* aWillNotify, PRBool* aIsAlternate) { return DoUpdateStyleSheet(nsnull, aObserver, aWillNotify, aIsAlternate, PR_FALSE); } nsresult nsStyleLinkElement::UpdateStyleSheetInternal(nsIDocument *aOldDocument, PRBool aForceUpdate) { PRBool notify, alternate; return DoUpdateStyleSheet(aOldDocument, nsnull, ¬ify, &alternate, aForceUpdate); } nsresult nsStyleLinkElement::DoUpdateStyleSheet(nsIDocument *aOldDocument, nsICSSLoaderObserver* aObserver, PRBool* aWillNotify, PRBool* aIsAlternate, PRBool aForceUpdate) { *aWillNotify = PR_FALSE; if (mStyleSheet && aOldDocument) { // We're removing the link element from the document, unload the // stylesheet. We want to do this even if updates are disabled, since // otherwise a sheet with a stale linking element pointer will be hanging // around -- not good! aOldDocument->BeginUpdate(UPDATE_STYLE); aOldDocument->RemoveStyleSheet(mStyleSheet); aOldDocument->EndUpdate(UPDATE_STYLE); nsStyleLinkElement::SetStyleSheet(nsnull); } if (mDontLoadStyle || !mUpdatesEnabled) { return NS_OK; } nsCOMPtr<nsIContent> thisContent; QueryInterface(NS_GET_IID(nsIContent), getter_AddRefs(thisContent)); NS_ENSURE_TRUE(thisContent, NS_ERROR_FAILURE); nsCOMPtr<nsIDocument> doc = thisContent->GetDocument(); if (!doc) { return NS_OK; } PRBool enabled = PR_FALSE; doc->CSSLoader()->GetEnabled(&enabled); if (!enabled) { return NS_OK; } nsCOMPtr<nsIURI> uri; PRBool isInline; GetStyleSheetURL(&isInline, getter_AddRefs(uri)); if (!aForceUpdate && mStyleSheet && !isInline && uri) { nsCOMPtr<nsIURI> oldURI; mStyleSheet->GetSheetURI(getter_AddRefs(oldURI)); if (oldURI) { PRBool equal; nsresult rv = oldURI->Equals(uri, &equal); if (NS_SUCCEEDED(rv) && equal) { return NS_OK; // We already loaded this stylesheet } } } if (mStyleSheet) { doc->BeginUpdate(UPDATE_STYLE); doc->RemoveStyleSheet(mStyleSheet); doc->EndUpdate(UPDATE_STYLE); nsStyleLinkElement::SetStyleSheet(nsnull); } if (!uri && !isInline) { return NS_OK; // If href is empty and this is not inline style then just bail } nsAutoString title, type, media; PRBool isAlternate; GetStyleSheetInfo(title, type, media, &isAlternate); if (!type.LowerCaseEqualsLiteral("text/css")) { return NS_OK; } PRBool doneLoading = PR_FALSE; nsresult rv = NS_OK; if (isInline) { nsAutoString content; nsContentUtils::GetNodeTextContent(thisContent, PR_FALSE, content); nsCOMPtr<nsIUnicharInputStream> uin; rv = nsSimpleUnicharStreamFactory::GetInstance()-> CreateInstanceFromString(content, getter_AddRefs(uin)); if (NS_FAILED(rv)) { return rv; } // Now that we have a url and a unicode input stream, parse the // style sheet. rv = doc->CSSLoader()-> LoadInlineStyle(thisContent, uin, mLineNumber, title, media, aObserver, &doneLoading, &isAlternate); } else { rv = doc->CSSLoader()-> LoadStyleLink(thisContent, uri, title, media, isAlternate, aObserver, &isAlternate); if (NS_FAILED(rv)) { // Don't propagate LoadStyleLink() errors further than this, since some // consumers (e.g. nsXMLContentSink) will completely abort on innocuous // things like a stylesheet load being blocked by the security system. doneLoading = PR_TRUE; isAlternate = PR_FALSE; rv = NS_OK; } } NS_ENSURE_SUCCESS(rv, rv); *aWillNotify = !doneLoading; *aIsAlternate = isAlternate; return NS_OK; }