/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ // vim:cindent:tabstop=2:expandtab:shiftwidth=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.org code. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * L. David Baron * Pierre Phaneuf * Daniel Glazman * * 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 ***** */ /* representation of a CSS style sheet */ #include "nsCSSStyleSheet.h" #include "nsCRT.h" #include "nsIAtom.h" #include "nsIServiceManager.h" #include "nsCSSRuleProcessor.h" #include "nsICSSStyleRule.h" #include "nsICSSNameSpaceRule.h" #include "nsICSSGroupRule.h" #include "nsICSSImportRule.h" #include "nsIMediaList.h" #include "nsIDocument.h" #include "nsPresContext.h" #include "nsGkAtoms.h" #include "nsString.h" #include "nsVoidArray.h" #include "nsIDOMStyleSheetList.h" #include "nsIDOMCSSStyleSheet.h" #include "nsIDOMCSSRule.h" #include "nsIDOMCSSImportRule.h" #include "nsIDOMCSSRuleList.h" #include "nsIDOMMediaList.h" #include "nsIDOMNode.h" #include "nsDOMError.h" #include "nsICSSParser.h" #include "nsICSSLoader.h" #include "nsICSSLoaderObserver.h" #include "nsINameSpaceManager.h" #include "nsXMLNameSpaceMap.h" #include "nsCOMPtr.h" #include "nsContentUtils.h" #include "nsIJSContextStack.h" #include "nsIScriptSecurityManager.h" // ------------------------------- // Style Rule List for the DOM // class CSSRuleListImpl : public nsIDOMCSSRuleList { public: CSSRuleListImpl(nsCSSStyleSheet *aStyleSheet); NS_DECL_ISUPPORTS // nsIDOMCSSRuleList interface NS_IMETHOD GetLength(PRUint32* aLength); NS_IMETHOD Item(PRUint32 aIndex, nsIDOMCSSRule** aReturn); void DropReference() { mStyleSheet = nsnull; } protected: virtual ~CSSRuleListImpl(); nsCSSStyleSheet* mStyleSheet; public: PRBool mRulesAccessed; }; CSSRuleListImpl::CSSRuleListImpl(nsCSSStyleSheet *aStyleSheet) { // Not reference counted to avoid circular references. // The style sheet will tell us when its going away. mStyleSheet = aStyleSheet; mRulesAccessed = PR_FALSE; } CSSRuleListImpl::~CSSRuleListImpl() { } // QueryInterface implementation for CSSRuleList NS_INTERFACE_MAP_BEGIN(CSSRuleListImpl) NS_INTERFACE_MAP_ENTRY(nsIDOMCSSRuleList) NS_INTERFACE_MAP_ENTRY(nsISupports) NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(CSSRuleList) NS_INTERFACE_MAP_END NS_IMPL_ADDREF(CSSRuleListImpl) NS_IMPL_RELEASE(CSSRuleListImpl) NS_IMETHODIMP CSSRuleListImpl::GetLength(PRUint32* aLength) { if (nsnull != mStyleSheet) { PRInt32 count; mStyleSheet->StyleRuleCount(count); *aLength = (PRUint32)count; } else { *aLength = 0; } return NS_OK; } NS_IMETHODIMP CSSRuleListImpl::Item(PRUint32 aIndex, nsIDOMCSSRule** aReturn) { nsresult result = NS_OK; *aReturn = nsnull; if (mStyleSheet) { result = mStyleSheet->EnsureUniqueInner(); // needed to ensure rules have correct parent if (NS_SUCCEEDED(result)) { nsCOMPtr rule; result = mStyleSheet->GetStyleRuleAt(aIndex, *getter_AddRefs(rule)); if (rule) { result = rule->GetDOMRule(aReturn); mRulesAccessed = PR_TRUE; // signal to never share rules again } else if (result == NS_ERROR_ILLEGAL_VALUE) { result = NS_OK; // per spec: "Return Value ... null if ... not a valid index." } } } return result; } NS_INTERFACE_MAP_BEGIN(nsMediaList) NS_INTERFACE_MAP_ENTRY(nsIDOMMediaList) NS_INTERFACE_MAP_ENTRY(nsISupports) NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(MediaList) NS_INTERFACE_MAP_END NS_IMPL_ADDREF(nsMediaList) NS_IMPL_RELEASE(nsMediaList) nsMediaList::nsMediaList() : mStyleSheet(nsnull) { } nsMediaList::~nsMediaList() { } nsresult nsMediaList::GetText(nsAString& aMediaText) { aMediaText.Truncate(); for (PRInt32 i = 0, i_end = mArray.Count(); i < i_end; ++i) { nsIAtom* medium = mArray[i]; NS_ENSURE_TRUE(medium, NS_ERROR_FAILURE); nsAutoString buffer; medium->ToString(buffer); aMediaText.Append(buffer); if (i + 1 < i_end) { aMediaText.AppendLiteral(", "); } } return NS_OK; } // XXXbz this is so ill-defined in the spec, it's not clear quite what // it should be doing.... nsresult nsMediaList::SetText(const nsAString& aMediaText) { nsCOMPtr parser; nsresult rv = NS_NewCSSParser(getter_AddRefs(parser)); NS_ENSURE_SUCCESS(rv, rv); PRBool htmlMode = PR_FALSE; nsCOMPtr domSheet = do_QueryInterface(NS_STATIC_CAST(nsICSSStyleSheet*, mStyleSheet)); if (domSheet) { nsCOMPtr node; domSheet->GetOwnerNode(getter_AddRefs(node)); htmlMode = !!node; } return parser->ParseMediaList(nsString(aMediaText), nsnull, 0, this, htmlMode); } /* * aMatch is true when we contain the desired medium or contain the * "all" medium or contain no media at all, which is the same as * containing "all" */ PRBool nsMediaList::Matches(nsPresContext* aPresContext) { if (-1 != mArray.IndexOf(aPresContext->Medium()) || -1 != mArray.IndexOf(nsGkAtoms::all)) return PR_TRUE; return mArray.Count() == 0; } nsresult nsMediaList::SetStyleSheet(nsICSSStyleSheet *aSheet) { NS_ASSERTION(aSheet == mStyleSheet || !aSheet || !mStyleSheet, "multiple style sheets competing for one media list"); mStyleSheet = NS_STATIC_CAST(nsCSSStyleSheet*, aSheet); return NS_OK; } nsresult nsMediaList::Clone(nsMediaList** aResult) { nsRefPtr result = new nsMediaList(); if (!result) return NS_ERROR_OUT_OF_MEMORY; if (!result->mArray.AppendObjects(mArray)) return NS_ERROR_OUT_OF_MEMORY; NS_ADDREF(*aResult = result); return NS_OK; } NS_IMETHODIMP nsMediaList::GetMediaText(nsAString& aMediaText) { return GetText(aMediaText); } // "sheet" should be an nsCSSStyleSheet and "doc" should be an // nsCOMPtr #define BEGIN_MEDIA_CHANGE(sheet, doc) \ if (sheet) { \ rv = sheet->GetOwningDocument(*getter_AddRefs(doc)); \ NS_ENSURE_SUCCESS(rv, rv); \ } \ mozAutoDocUpdate updateBatch(doc, UPDATE_STYLE, PR_TRUE); \ if (sheet) { \ rv = sheet->WillDirty(); \ NS_ENSURE_SUCCESS(rv, rv); \ } #define END_MEDIA_CHANGE(sheet, doc) \ if (sheet) { \ sheet->DidDirty(); \ } \ /* XXXldb Pass something meaningful? */ \ if (doc) { \ doc->StyleRuleChanged(sheet, nsnull, nsnull); \ } NS_IMETHODIMP nsMediaList::SetMediaText(const nsAString& aMediaText) { nsresult rv = NS_OK; nsCOMPtr doc; BEGIN_MEDIA_CHANGE(mStyleSheet, doc) rv = SetText(aMediaText); if (NS_FAILED(rv)) return rv; END_MEDIA_CHANGE(mStyleSheet, doc) return rv; } NS_IMETHODIMP nsMediaList::GetLength(PRUint32* aLength) { NS_ENSURE_ARG_POINTER(aLength); *aLength = mArray.Count(); return NS_OK; } NS_IMETHODIMP nsMediaList::Item(PRUint32 aIndex, nsAString& aReturn) { PRInt32 index = aIndex; if (0 <= index && index < Count()) { MediumAt(aIndex)->ToString(aReturn); } else { SetDOMStringToNull(aReturn); } return NS_OK; } NS_IMETHODIMP nsMediaList::DeleteMedium(const nsAString& aOldMedium) { nsresult rv = NS_OK; nsCOMPtr doc; BEGIN_MEDIA_CHANGE(mStyleSheet, doc) rv = Delete(aOldMedium); if (NS_FAILED(rv)) return rv; END_MEDIA_CHANGE(mStyleSheet, doc) return rv; } NS_IMETHODIMP nsMediaList::AppendMedium(const nsAString& aNewMedium) { nsresult rv = NS_OK; nsCOMPtr doc; BEGIN_MEDIA_CHANGE(mStyleSheet, doc) rv = Append(aNewMedium); if (NS_FAILED(rv)) return rv; END_MEDIA_CHANGE(mStyleSheet, doc) return rv; } nsresult nsMediaList::Delete(const nsAString& aOldMedium) { if (aOldMedium.IsEmpty()) return NS_ERROR_DOM_NOT_FOUND_ERR; nsCOMPtr old = do_GetAtom(aOldMedium); NS_ENSURE_TRUE(old, NS_ERROR_OUT_OF_MEMORY); PRInt32 indx = mArray.IndexOf(old); if (indx < 0) { return NS_ERROR_DOM_NOT_FOUND_ERR; } mArray.RemoveObjectAt(indx); return NS_OK; } nsresult nsMediaList::Append(const nsAString& aNewMedium) { if (aNewMedium.IsEmpty()) return NS_ERROR_DOM_NOT_FOUND_ERR; nsCOMPtr media = do_GetAtom(aNewMedium); NS_ENSURE_TRUE(media, NS_ERROR_OUT_OF_MEMORY); PRInt32 indx = mArray.IndexOf(media); if (indx >= 0) { mArray.RemoveObjectAt(indx); } mArray.AppendObject(media); return NS_OK; } // ------------------------------- // Imports Collection for the DOM // class CSSImportsCollectionImpl : public nsIDOMStyleSheetList { public: CSSImportsCollectionImpl(nsICSSStyleSheet *aStyleSheet); NS_DECL_ISUPPORTS // nsIDOMCSSStyleSheetList interface NS_IMETHOD GetLength(PRUint32* aLength); NS_IMETHOD Item(PRUint32 aIndex, nsIDOMStyleSheet** aReturn); void DropReference() { mStyleSheet = nsnull; } protected: virtual ~CSSImportsCollectionImpl(); nsICSSStyleSheet* mStyleSheet; }; CSSImportsCollectionImpl::CSSImportsCollectionImpl(nsICSSStyleSheet *aStyleSheet) { // Not reference counted to avoid circular references. // The style sheet will tell us when its going away. mStyleSheet = aStyleSheet; } CSSImportsCollectionImpl::~CSSImportsCollectionImpl() { } // QueryInterface implementation for CSSImportsCollectionImpl NS_INTERFACE_MAP_BEGIN(CSSImportsCollectionImpl) NS_INTERFACE_MAP_ENTRY(nsIDOMStyleSheetList) NS_INTERFACE_MAP_ENTRY(nsISupports) NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(StyleSheetList) NS_INTERFACE_MAP_END NS_IMPL_ADDREF(CSSImportsCollectionImpl) NS_IMPL_RELEASE(CSSImportsCollectionImpl) NS_IMETHODIMP CSSImportsCollectionImpl::GetLength(PRUint32* aLength) { if (nsnull != mStyleSheet) { PRInt32 count; mStyleSheet->StyleSheetCount(count); *aLength = (PRUint32)count; } else { *aLength = 0; } return NS_OK; } NS_IMETHODIMP CSSImportsCollectionImpl::Item(PRUint32 aIndex, nsIDOMStyleSheet** aReturn) { nsresult result = NS_OK; *aReturn = nsnull; if (mStyleSheet) { nsCOMPtr sheet; result = mStyleSheet->GetStyleSheetAt(aIndex, *getter_AddRefs(sheet)); if (NS_SUCCEEDED(result)) { result = CallQueryInterface(sheet, aReturn); } } return result; } // ------------------------------- // CSS Style Sheet Inner Data Container // static PRBool SetStyleSheetReference(nsICSSRule* aRule, void* aSheet) { if (aRule) { aRule->SetStyleSheet((nsICSSStyleSheet*)aSheet); } return PR_TRUE; } nsCSSStyleSheetInner::nsCSSStyleSheetInner(nsICSSStyleSheet* aParentSheet) : mSheets(), mComplete(PR_FALSE) #ifdef DEBUG , mPrincipalSet(PR_FALSE) #endif { MOZ_COUNT_CTOR(nsCSSStyleSheetInner); mSheets.AppendElement(aParentSheet); mPrincipal = do_CreateInstance("@mozilla.org/nullprincipal;1"); } static PRBool CloneRuleInto(nsICSSRule* aRule, void* aArray) { nsICSSRule* clone = nsnull; aRule->Clone(clone); if (clone) { NS_STATIC_CAST(nsCOMArray*, aArray)->AppendObject(clone); NS_RELEASE(clone); } return PR_TRUE; } nsCSSStyleSheetInner::nsCSSStyleSheetInner(nsCSSStyleSheetInner& aCopy, nsICSSStyleSheet* aParentSheet) : mSheets(), mSheetURI(aCopy.mSheetURI), mBaseURI(aCopy.mBaseURI), mPrincipal(aCopy.mPrincipal), mComplete(aCopy.mComplete) #ifdef DEBUG , mPrincipalSet(aCopy.mPrincipalSet) #endif { MOZ_COUNT_CTOR(nsCSSStyleSheetInner); mSheets.AppendElement(aParentSheet); aCopy.mOrderedRules.EnumerateForwards(CloneRuleInto, &mOrderedRules); mOrderedRules.EnumerateForwards(SetStyleSheetReference, aParentSheet); RebuildNameSpaces(); } nsCSSStyleSheetInner::~nsCSSStyleSheetInner() { MOZ_COUNT_DTOR(nsCSSStyleSheetInner); mOrderedRules.EnumerateForwards(SetStyleSheetReference, nsnull); } nsCSSStyleSheetInner* nsCSSStyleSheetInner::CloneFor(nsICSSStyleSheet* aParentSheet) { return new nsCSSStyleSheetInner(*this, aParentSheet); } void nsCSSStyleSheetInner::AddSheet(nsICSSStyleSheet* aParentSheet) { mSheets.AppendElement(aParentSheet); } void nsCSSStyleSheetInner::RemoveSheet(nsICSSStyleSheet* aParentSheet) { if (1 == mSheets.Count()) { NS_ASSERTION(aParentSheet == (nsICSSStyleSheet*)mSheets.ElementAt(0), "bad parent"); delete this; return; } if (aParentSheet == (nsICSSStyleSheet*)mSheets.ElementAt(0)) { mSheets.RemoveElementAt(0); NS_ASSERTION(mSheets.Count(), "no parents"); mOrderedRules.EnumerateForwards(SetStyleSheetReference, (nsICSSStyleSheet*)mSheets.ElementAt(0)); } else { mSheets.RemoveElement(aParentSheet); } } static PRBool CreateNameSpace(nsICSSRule* aRule, void* aNameSpacePtr) { PRInt32 type = nsICSSRule::UNKNOWN_RULE; aRule->GetType(type); if (nsICSSRule::NAMESPACE_RULE == type) { nsICSSNameSpaceRule* nameSpaceRule = (nsICSSNameSpaceRule*)aRule; nsXMLNameSpaceMap *nameSpaceMap = NS_STATIC_CAST(nsXMLNameSpaceMap*, aNameSpacePtr); nsIAtom* prefix = nsnull; nsAutoString urlSpec; nameSpaceRule->GetPrefix(prefix); nameSpaceRule->GetURLSpec(urlSpec); nameSpaceMap->AddPrefix(prefix, urlSpec); return PR_TRUE; } // stop if not namespace, import or charset because namespace can't follow anything else return (((nsICSSRule::CHARSET_RULE == type) || (nsICSSRule::IMPORT_RULE)) ? PR_TRUE : PR_FALSE); } void nsCSSStyleSheetInner::RebuildNameSpaces() { if (mNameSpaceMap) { mNameSpaceMap->Clear(); } else { mNameSpaceMap = nsXMLNameSpaceMap::Create(); if (!mNameSpaceMap) { return; // out of memory } } mOrderedRules.EnumerateForwards(CreateNameSpace, mNameSpaceMap); } // ------------------------------- // CSS Style Sheet // nsCSSStyleSheet::nsCSSStyleSheet() : nsICSSStyleSheet(), mRefCnt(0), mTitle(), mMedia(nsnull), mFirstChild(nsnull), mNext(nsnull), mParent(nsnull), mOwnerRule(nsnull), mImportsCollection(nsnull), mRuleCollection(nsnull), mDocument(nsnull), mOwningNode(nsnull), mDisabled(PR_FALSE), mDirty(PR_FALSE), mRuleProcessors(nsnull) { mInner = new nsCSSStyleSheetInner(this); } nsCSSStyleSheet::nsCSSStyleSheet(const nsCSSStyleSheet& aCopy, nsICSSStyleSheet* aParentToUse, nsICSSImportRule* aOwnerRuleToUse, nsIDocument* aDocumentToUse, nsIDOMNode* aOwningNodeToUse) : nsICSSStyleSheet(), mRefCnt(0), mTitle(aCopy.mTitle), mMedia(nsnull), mFirstChild(nsnull), mNext(nsnull), mParent(aParentToUse), mOwnerRule(aOwnerRuleToUse), mImportsCollection(nsnull), // re-created lazily mRuleCollection(nsnull), // re-created lazily mDocument(aDocumentToUse), mOwningNode(aOwningNodeToUse), mDisabled(aCopy.mDisabled), mDirty(PR_FALSE), mInner(aCopy.mInner), mRuleProcessors(nsnull) { mInner->AddSheet(this); if (aCopy.mRuleCollection && aCopy.mRuleCollection->mRulesAccessed) { // CSSOM's been there, force full copy now NS_ASSERTION(mInner->mComplete, "Why have rules been accessed on an incomplete sheet?"); EnsureUniqueInner(); } if (aCopy.mMedia) { aCopy.mMedia->Clone(getter_AddRefs(mMedia)); } if (aCopy.mFirstChild) { nsCSSStyleSheet* otherChild = aCopy.mFirstChild; nsCSSStyleSheet** ourSlot = &mFirstChild; do { // XXX This is wrong; we should be keeping @import rules and // sheets in sync! nsCSSStyleSheet* child = new nsCSSStyleSheet(*otherChild, this, nsnull, aDocumentToUse, nsnull); if (child) { NS_ADDREF(child); (*ourSlot) = child; ourSlot = &(child->mNext); } otherChild = otherChild->mNext; } while (otherChild && ourSlot); } } nsCSSStyleSheet::~nsCSSStyleSheet() { if (mFirstChild) { nsCSSStyleSheet* child = mFirstChild; do { child->mParent = nsnull; child->mDocument = nsnull; child = child->mNext; } while (child); NS_RELEASE(mFirstChild); } NS_IF_RELEASE(mNext); if (nsnull != mRuleCollection) { mRuleCollection->DropReference(); NS_RELEASE(mRuleCollection); } if (nsnull != mImportsCollection) { mImportsCollection->DropReference(); NS_RELEASE(mImportsCollection); } if (mMedia) { mMedia->SetStyleSheet(nsnull); mMedia = nsnull; } mInner->RemoveSheet(this); // XXX The document reference is not reference counted and should // not be released. The document will let us know when it is going // away. if (mRuleProcessors) { NS_ASSERTION(mRuleProcessors->Count() == 0, "destructing sheet with rule processor reference"); delete mRuleProcessors; // weak refs, should be empty here anyway } } // QueryInterface implementation for nsCSSStyleSheet NS_INTERFACE_MAP_BEGIN(nsCSSStyleSheet) NS_INTERFACE_MAP_ENTRY(nsICSSStyleSheet) NS_INTERFACE_MAP_ENTRY(nsIStyleSheet) NS_INTERFACE_MAP_ENTRY(nsIDOMStyleSheet) NS_INTERFACE_MAP_ENTRY(nsIDOMCSSStyleSheet) NS_INTERFACE_MAP_ENTRY(nsICSSLoaderObserver) NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsICSSStyleSheet) NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(CSSStyleSheet) NS_INTERFACE_MAP_END NS_IMPL_ADDREF(nsCSSStyleSheet) NS_IMPL_RELEASE(nsCSSStyleSheet) NS_IMETHODIMP nsCSSStyleSheet::AddRuleProcessor(nsCSSRuleProcessor* aProcessor) { if (! mRuleProcessors) { mRuleProcessors = new nsAutoVoidArray(); if (!mRuleProcessors) return NS_ERROR_OUT_OF_MEMORY; } NS_ASSERTION(-1 == mRuleProcessors->IndexOf(aProcessor), "processor already registered"); mRuleProcessors->AppendElement(aProcessor); // weak ref return NS_OK; } NS_IMETHODIMP nsCSSStyleSheet::DropRuleProcessor(nsCSSRuleProcessor* aProcessor) { if (!mRuleProcessors) return NS_ERROR_FAILURE; return mRuleProcessors->RemoveElement(aProcessor) ? NS_OK : NS_ERROR_FAILURE; } NS_IMETHODIMP nsCSSStyleSheet::SetURIs(nsIURI* aSheetURI, nsIURI* aBaseURI) { NS_PRECONDITION(aSheetURI && aBaseURI, "null ptr"); NS_ASSERTION(mInner->mOrderedRules.Count() == 0 && !mInner->mComplete, "Can't call SetURL on sheets that are complete or have rules"); mInner->mSheetURI = aSheetURI; mInner->mBaseURI = aBaseURI; return NS_OK; } void nsCSSStyleSheet::SetPrincipal(nsIPrincipal* aPrincipal) { NS_PRECONDITION(!mInner->mPrincipalSet, "Should have an inner whose principal has not yet been set"); if (aPrincipal) { mInner->mPrincipal = aPrincipal; #ifdef DEBUG mInner->mPrincipalSet = PR_TRUE; #endif } } nsIPrincipal* nsCSSStyleSheet::Principal() const { return mInner->mPrincipal; } NS_IMETHODIMP nsCSSStyleSheet::GetSheetURI(nsIURI** aSheetURI) const { NS_IF_ADDREF(*aSheetURI = mInner->mSheetURI.get()); return NS_OK; } NS_IMETHODIMP nsCSSStyleSheet::GetBaseURI(nsIURI** aBaseURI) const { NS_IF_ADDREF(*aBaseURI = mInner->mBaseURI.get()); return NS_OK; } NS_IMETHODIMP nsCSSStyleSheet::SetTitle(const nsAString& aTitle) { mTitle = aTitle; return NS_OK; } NS_IMETHODIMP nsCSSStyleSheet::GetType(nsString& aType) const { aType.AssignLiteral("text/css"); return NS_OK; } NS_IMETHODIMP_(PRBool) nsCSSStyleSheet::UseForMedium(nsPresContext* aPresContext) const { if (mMedia) { return mMedia->Matches(aPresContext); } return PR_TRUE; } NS_IMETHODIMP nsCSSStyleSheet::SetMedia(nsMediaList* aMedia) { mMedia = aMedia; return NS_OK; } NS_IMETHODIMP_(PRBool) nsCSSStyleSheet::HasRules() const { PRInt32 count; StyleRuleCount(count); return count != 0; } NS_IMETHODIMP nsCSSStyleSheet::GetApplicable(PRBool& aApplicable) const { aApplicable = !mDisabled && mInner->mComplete; return NS_OK; } NS_IMETHODIMP nsCSSStyleSheet::SetEnabled(PRBool aEnabled) { // Internal method, so callers must handle BeginUpdate/EndUpdate PRBool oldDisabled = mDisabled; mDisabled = !aEnabled; if (mDocument && mInner->mComplete && oldDisabled != mDisabled) { ClearRuleCascades(); mDocument->SetStyleSheetApplicableState(this, !mDisabled); } return NS_OK; } NS_IMETHODIMP nsCSSStyleSheet::GetComplete(PRBool& aComplete) const { aComplete = mInner->mComplete; return NS_OK; } NS_IMETHODIMP nsCSSStyleSheet::SetComplete() { NS_ASSERTION(!mDirty, "Can't set a dirty sheet complete!"); mInner->mComplete = PR_TRUE; if (mDocument && !mDisabled) { // Let the document know mDocument->BeginUpdate(UPDATE_STYLE); mDocument->SetStyleSheetApplicableState(this, PR_TRUE); mDocument->EndUpdate(UPDATE_STYLE); } return NS_OK; } NS_IMETHODIMP nsCSSStyleSheet::GetParentSheet(nsIStyleSheet*& aParent) const { aParent = mParent; NS_IF_ADDREF(aParent); return NS_OK; } NS_IMETHODIMP nsCSSStyleSheet::GetOwningDocument(nsIDocument*& aDocument) const { aDocument = mDocument; NS_IF_ADDREF(aDocument); return NS_OK; } NS_IMETHODIMP nsCSSStyleSheet::SetOwningDocument(nsIDocument* aDocument) { // not ref counted mDocument = aDocument; // Now set the same document on all our child sheets.... for (nsCSSStyleSheet* child = mFirstChild; child; child = child->mNext) { child->SetOwningDocument(aDocument); } return NS_OK; } NS_IMETHODIMP nsCSSStyleSheet::SetOwningNode(nsIDOMNode* aOwningNode) { // not ref counted mOwningNode = aOwningNode; return NS_OK; } NS_IMETHODIMP nsCSSStyleSheet::SetOwnerRule(nsICSSImportRule* aOwnerRule) { // not ref counted mOwnerRule = aOwnerRule; return NS_OK; } NS_IMETHODIMP nsCSSStyleSheet::GetOwnerRule(nsICSSImportRule** aOwnerRule) { *aOwnerRule = mOwnerRule; NS_IF_ADDREF(*aOwnerRule); return NS_OK; } NS_IMETHODIMP nsCSSStyleSheet::ContainsStyleSheet(nsIURI* aURL, PRBool& aContains, nsIStyleSheet** aTheChild /*=nsnull*/) { NS_PRECONDITION(nsnull != aURL, "null arg"); if (!mInner->mSheetURI) { // We're not yet far enough along in our load to know what our URL is (we // may still get redirected and such). Assert (caller should really not be // calling this on us at this stage) and return. NS_ERROR("ContainsStyleSheet called on a sheet that's still loading"); aContains = PR_FALSE; return NS_OK; } // first check ourself out nsresult rv = mInner->mSheetURI->Equals(aURL, &aContains); if (NS_FAILED(rv)) aContains = PR_FALSE; if (aContains) { // if we found it and the out-param is there, set it and addref if (aTheChild) { rv = QueryInterface( NS_GET_IID(nsIStyleSheet), (void **)aTheChild); } } else { nsCSSStyleSheet* child = mFirstChild; // now check the chil'ins out (recursively) while ((PR_FALSE == aContains) && (nsnull != child)) { child->ContainsStyleSheet(aURL, aContains, aTheChild); if (aContains) { break; } else { child = child->mNext; } } } // NOTE: if there are errors in the above we are handling them locally // and not promoting them to the caller return NS_OK; } NS_IMETHODIMP nsCSSStyleSheet::AppendStyleSheet(nsICSSStyleSheet* aSheet) { NS_PRECONDITION(nsnull != aSheet, "null arg"); if (NS_SUCCEEDED(WillDirty())) { NS_ADDREF(aSheet); nsCSSStyleSheet* sheet = (nsCSSStyleSheet*)aSheet; if (! mFirstChild) { mFirstChild = sheet; } else { nsCSSStyleSheet* child = mFirstChild; while (child->mNext) { child = child->mNext; } child->mNext = sheet; } // This is not reference counted. Our parent tells us when // it's going away. sheet->mParent = this; sheet->mDocument = mDocument; DidDirty(); } return NS_OK; } NS_IMETHODIMP nsCSSStyleSheet::InsertStyleSheetAt(nsICSSStyleSheet* aSheet, PRInt32 aIndex) { NS_PRECONDITION(nsnull != aSheet, "null arg"); nsresult result = WillDirty(); if (NS_SUCCEEDED(result)) { NS_ADDREF(aSheet); nsCSSStyleSheet* sheet = (nsCSSStyleSheet*)aSheet; nsCSSStyleSheet* child = mFirstChild; if (aIndex && child) { while ((0 < --aIndex) && child->mNext) { child = child->mNext; } sheet->mNext = child->mNext; child->mNext = sheet; } else { sheet->mNext = mFirstChild; mFirstChild = sheet; } // This is not reference counted. Our parent tells us when // it's going away. sheet->mParent = this; sheet->mDocument = mDocument; DidDirty(); } return result; } NS_IMETHODIMP nsCSSStyleSheet::PrependStyleRule(nsICSSRule* aRule) { NS_PRECONDITION(nsnull != aRule, "null arg"); if (NS_SUCCEEDED(WillDirty())) { mInner->mOrderedRules.InsertObjectAt(aRule, 0); aRule->SetStyleSheet(this); DidDirty(); PRInt32 type = nsICSSRule::UNKNOWN_RULE; aRule->GetType(type); if (nsICSSRule::NAMESPACE_RULE == type) { // no api to prepend a namespace (ugh), release old ones and re-create them all mInner->RebuildNameSpaces(); } } return NS_OK; } NS_IMETHODIMP nsCSSStyleSheet::AppendStyleRule(nsICSSRule* aRule) { NS_PRECONDITION(nsnull != aRule, "null arg"); if (NS_SUCCEEDED(WillDirty())) { mInner->mOrderedRules.AppendObject(aRule); aRule->SetStyleSheet(this); DidDirty(); PRInt32 type = nsICSSRule::UNKNOWN_RULE; aRule->GetType(type); if (nsICSSRule::NAMESPACE_RULE == type) { if (!mInner->mNameSpaceMap) { mInner->mNameSpaceMap = nsXMLNameSpaceMap::Create(); NS_ENSURE_TRUE(mInner->mNameSpaceMap, NS_ERROR_OUT_OF_MEMORY); } nsCOMPtr nameSpaceRule(do_QueryInterface(aRule)); nsCOMPtr prefix; nsAutoString urlSpec; nameSpaceRule->GetPrefix(*getter_AddRefs(prefix)); nameSpaceRule->GetURLSpec(urlSpec); mInner->mNameSpaceMap->AddPrefix(prefix, urlSpec); } } return NS_OK; } NS_IMETHODIMP nsCSSStyleSheet::ReplaceStyleRule(nsICSSRule* aOld, nsICSSRule* aNew) { NS_PRECONDITION(mInner->mOrderedRules.Count() != 0, "can't have old rule"); NS_PRECONDITION(mInner->mComplete, "No replacing in an incomplete sheet!"); if (NS_SUCCEEDED(WillDirty())) { PRInt32 index = mInner->mOrderedRules.IndexOf(aOld); NS_ENSURE_TRUE(index != -1, NS_ERROR_UNEXPECTED); mInner->mOrderedRules.ReplaceObjectAt(aNew, index); aNew->SetStyleSheet(this); aOld->SetStyleSheet(nsnull); DidDirty(); #ifdef DEBUG PRInt32 type = nsICSSRule::UNKNOWN_RULE; aNew->GetType(type); NS_ASSERTION(nsICSSRule::NAMESPACE_RULE != type, "not yet implemented"); aOld->GetType(type); NS_ASSERTION(nsICSSRule::NAMESPACE_RULE != type, "not yet implemented"); #endif } return NS_OK; } NS_IMETHODIMP nsCSSStyleSheet::StyleRuleCount(PRInt32& aCount) const { aCount = mInner->mOrderedRules.Count(); return NS_OK; } NS_IMETHODIMP nsCSSStyleSheet::GetStyleRuleAt(PRInt32 aIndex, nsICSSRule*& aRule) const { // Important: If this function is ever made scriptable, we must add // a security check here. See GetCSSRules below for an example. aRule = mInner->mOrderedRules.SafeObjectAt(aIndex); if (aRule) { NS_ADDREF(aRule); return NS_OK; } return NS_ERROR_ILLEGAL_VALUE; } nsXMLNameSpaceMap* nsCSSStyleSheet::GetNameSpaceMap() const { return mInner->mNameSpaceMap; } NS_IMETHODIMP nsCSSStyleSheet::StyleSheetCount(PRInt32& aCount) const { // XXX Far from an ideal way to do this, but the hope is that // it won't be done too often. If it is, we might want to // consider storing the children in an array. aCount = 0; const nsCSSStyleSheet* child = mFirstChild; while (child) { aCount++; child = child->mNext; } return NS_OK; } NS_IMETHODIMP nsCSSStyleSheet::GetStyleSheetAt(PRInt32 aIndex, nsICSSStyleSheet*& aSheet) const { // XXX Ughh...an O(n^2) method for doing iteration. Again, we hope // that this isn't done too often. If it is, we need to change the // underlying storage mechanism aSheet = nsnull; if (mFirstChild) { const nsCSSStyleSheet* child = mFirstChild; while ((child) && (0 != aIndex)) { --aIndex; child = child->mNext; } aSheet = (nsICSSStyleSheet*)child; NS_IF_ADDREF(aSheet); } return NS_OK; } nsresult nsCSSStyleSheet::EnsureUniqueInner() { if (1 < mInner->mSheets.Count()) { nsCSSStyleSheetInner* clone = mInner->CloneFor(this); if (clone) { mInner->RemoveSheet(this); mInner = clone; } else { return NS_ERROR_OUT_OF_MEMORY; } } return NS_OK; } NS_IMETHODIMP nsCSSStyleSheet::Clone(nsICSSStyleSheet* aCloneParent, nsICSSImportRule* aCloneOwnerRule, nsIDocument* aCloneDocument, nsIDOMNode* aCloneOwningNode, nsICSSStyleSheet** aClone) const { NS_PRECONDITION(aClone, "Null out param!"); nsCSSStyleSheet* clone = new nsCSSStyleSheet(*this, aCloneParent, aCloneOwnerRule, aCloneDocument, aCloneOwningNode); if (clone) { *aClone = NS_STATIC_CAST(nsICSSStyleSheet*, clone); NS_ADDREF(*aClone); } return NS_OK; } #ifdef DEBUG static void ListRules(const nsCOMArray& aRules, FILE* aOut, PRInt32 aIndent) { for (PRInt32 index = aRules.Count() - 1; index >= 0; --index) { aRules.ObjectAt(index)->List(aOut, aIndent); } } struct ListEnumData { ListEnumData(FILE* aOut, PRInt32 aIndent) : mOut(aOut), mIndent(aIndent) { } FILE* mOut; PRInt32 mIndent; }; void nsCSSStyleSheet::List(FILE* out, PRInt32 aIndent) const { PRInt32 index; // Indent for (index = aIndent; --index >= 0; ) fputs(" ", out); fputs("CSS Style Sheet: ", out); nsCAutoString urlSpec; nsresult rv = mInner->mSheetURI->GetSpec(urlSpec); if (NS_SUCCEEDED(rv) && !urlSpec.IsEmpty()) { fputs(urlSpec.get(), out); } if (mMedia) { fputs(" media: ", out); nsAutoString buffer; mMedia->GetText(buffer); fputs(NS_ConvertUTF16toUTF8(buffer).get(), out); } fputs("\n", out); const nsCSSStyleSheet* child = mFirstChild; while (nsnull != child) { child->List(out, aIndent + 1); child = child->mNext; } fputs("Rules in source order:\n", out); ListRules(mInner->mOrderedRules, out, aIndent); } #endif static PRBool PR_CALLBACK EnumClearRuleCascades(void* aProcessor, void* aData) { nsCSSRuleProcessor* processor = NS_STATIC_CAST(nsCSSRuleProcessor*, aProcessor); processor->ClearRuleCascades(); return PR_TRUE; } void nsCSSStyleSheet::ClearRuleCascades() { if (mRuleProcessors) { mRuleProcessors->EnumerateForwards(EnumClearRuleCascades, nsnull); } if (mParent) { nsCSSStyleSheet* parent = (nsCSSStyleSheet*)mParent; parent->ClearRuleCascades(); } } nsresult nsCSSStyleSheet::WillDirty() { if (!mInner->mComplete) { // Do nothing return NS_OK; } return EnsureUniqueInner(); } void nsCSSStyleSheet::DidDirty() { ClearRuleCascades(); mDirty = PR_TRUE; } NS_IMETHODIMP nsCSSStyleSheet::IsModified(PRBool* aSheetModified) const { *aSheetModified = mDirty; return NS_OK; } NS_IMETHODIMP nsCSSStyleSheet::SetModified(PRBool aModified) { mDirty = aModified; return NS_OK; } // nsIDOMStyleSheet interface NS_IMETHODIMP nsCSSStyleSheet::GetType(nsAString& aType) { aType.AssignLiteral("text/css"); return NS_OK; } NS_IMETHODIMP nsCSSStyleSheet::GetDisabled(PRBool* aDisabled) { *aDisabled = mDisabled; return NS_OK; } NS_IMETHODIMP nsCSSStyleSheet::SetDisabled(PRBool aDisabled) { // DOM method, so handle BeginUpdate/EndUpdate MOZ_AUTO_DOC_UPDATE(mDocument, UPDATE_STYLE, PR_TRUE); nsresult rv = nsCSSStyleSheet::SetEnabled(!aDisabled); return rv; } NS_IMETHODIMP nsCSSStyleSheet::GetOwnerNode(nsIDOMNode** aOwnerNode) { *aOwnerNode = mOwningNode; NS_IF_ADDREF(*aOwnerNode); return NS_OK; } NS_IMETHODIMP nsCSSStyleSheet::GetParentStyleSheet(nsIDOMStyleSheet** aParentStyleSheet) { NS_ENSURE_ARG_POINTER(aParentStyleSheet); nsresult rv = NS_OK; if (mParent) { rv = mParent->QueryInterface(NS_GET_IID(nsIDOMStyleSheet), (void **)aParentStyleSheet); } else { *aParentStyleSheet = nsnull; } return rv; } NS_IMETHODIMP nsCSSStyleSheet::GetHref(nsAString& aHref) { nsCAutoString str; // XXXldb The DOM spec says that this should be null for inline style sheets. if (mInner->mSheetURI) { mInner->mSheetURI->GetSpec(str); } CopyUTF8toUTF16(str, aHref); return NS_OK; } NS_IMETHODIMP nsCSSStyleSheet::GetTitle(nsString& aTitle) const { aTitle = mTitle; return NS_OK; } NS_IMETHODIMP nsCSSStyleSheet::GetTitle(nsAString& aTitle) { aTitle.Assign(mTitle); return NS_OK; } NS_IMETHODIMP nsCSSStyleSheet::GetMedia(nsIDOMMediaList** aMedia) { NS_ENSURE_ARG_POINTER(aMedia); *aMedia = nsnull; if (!mMedia) { mMedia = new nsMediaList(); NS_ENSURE_TRUE(mMedia, NS_ERROR_OUT_OF_MEMORY); mMedia->SetStyleSheet(this); } *aMedia = mMedia; NS_ADDREF(*aMedia); return NS_OK; } NS_IMETHODIMP nsCSSStyleSheet::GetOwnerRule(nsIDOMCSSRule** aOwnerRule) { if (mOwnerRule) { return mOwnerRule->GetDOMRule(aOwnerRule); } *aOwnerRule = nsnull; return NS_OK; } NS_IMETHODIMP nsCSSStyleSheet::GetCssRules(nsIDOMCSSRuleList** aCssRules) { // No doing this on incomplete sheets! PRBool complete; GetComplete(complete); if (!complete) { return NS_ERROR_DOM_INVALID_ACCESS_ERR; } //-- Security check: Only scripts whose principal subsumes that of the // style sheet can access rule collections. // Get the security manager and do the subsumes check nsIScriptSecurityManager *securityManager = nsContentUtils::GetSecurityManager(); nsCOMPtr subjectPrincipal; securityManager->GetSubjectPrincipal(getter_AddRefs(subjectPrincipal)); nsresult rv = NS_OK; if (subjectPrincipal) { PRBool subsumes; rv = subjectPrincipal->Subsumes(mInner->mPrincipal, &subsumes); if (NS_SUCCEEDED(rv) && !subsumes && !nsContentUtils::IsCallerTrustedForRead()) { rv = NS_ERROR_DOM_SECURITY_ERR; } if (NS_FAILED(rv)) { return rv; } } // OK, security check passed, so get the rule collection if (nsnull == mRuleCollection) { mRuleCollection = new CSSRuleListImpl(this); if (nsnull == mRuleCollection) { return NS_ERROR_OUT_OF_MEMORY; } NS_ADDREF(mRuleCollection); } *aCssRules = mRuleCollection; NS_ADDREF(mRuleCollection); return NS_OK; } NS_IMETHODIMP nsCSSStyleSheet::InsertRule(const nsAString& aRule, PRUint32 aIndex, PRUint32* aReturn) { // No doing this if the sheet is not complete! PRBool complete; GetComplete(complete); if (!complete) { return NS_ERROR_DOM_INVALID_ACCESS_ERR; } if (aRule.IsEmpty()) { // Nothing to do here return NS_OK; } nsresult result; result = WillDirty(); if (NS_FAILED(result)) return result; if (aIndex > PRUint32(mInner->mOrderedRules.Count())) return NS_ERROR_DOM_INDEX_SIZE_ERR; NS_ASSERTION(PRUint32(mInner->mOrderedRules.Count()) <= PR_INT32_MAX, "Too many style rules!"); // Hold strong ref to the CSSLoader in case the document update // kills the document nsCOMPtr loader; if (mDocument) { loader = mDocument->CSSLoader(); NS_ASSERTION(loader, "Document with no CSS loader!"); } nsCOMPtr css; if (loader) { result = loader->GetParserFor(this, getter_AddRefs(css)); } else { result = NS_NewCSSParser(getter_AddRefs(css)); if (css) { css->SetStyleSheet(this); } } if (NS_FAILED(result)) return result; mozAutoDocUpdate updateBatch(mDocument, UPDATE_STYLE, PR_TRUE); nsCOMArray rules; result = css->ParseRule(aRule, mInner->mSheetURI, mInner->mBaseURI, mInner->mPrincipal, rules); if (NS_FAILED(result)) return result; PRInt32 rulecount = rules.Count(); if (rulecount == 0) { // Since we know aRule was not an empty string, just throw return NS_ERROR_DOM_SYNTAX_ERR; } // Hierarchy checking. Just check the first and last rule in the list. // check that we're not inserting before a charset rule PRInt32 nextType = nsICSSRule::UNKNOWN_RULE; nsICSSRule* nextRule = mInner->mOrderedRules.SafeObjectAt(aIndex); if (nextRule) { nextRule->GetType(nextType); if (nextType == nsICSSRule::CHARSET_RULE) { return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR; } // check last rule in list nsICSSRule* lastRule = rules.ObjectAt(rulecount - 1); PRInt32 lastType = nsICSSRule::UNKNOWN_RULE; lastRule->GetType(lastType); if (nextType == nsICSSRule::IMPORT_RULE && lastType != nsICSSRule::CHARSET_RULE && lastType != nsICSSRule::IMPORT_RULE) { return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR; } if (nextType == nsICSSRule::NAMESPACE_RULE && lastType != nsICSSRule::CHARSET_RULE && lastType != nsICSSRule::IMPORT_RULE && lastType != nsICSSRule::NAMESPACE_RULE) { return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR; } } // check first rule in list nsICSSRule* firstRule = rules.ObjectAt(0); PRInt32 firstType = nsICSSRule::UNKNOWN_RULE; firstRule->GetType(firstType); if (aIndex != 0) { if (firstType == nsICSSRule::CHARSET_RULE) { // no inserting charset at nonzero position return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR; } nsICSSRule* prevRule = mInner->mOrderedRules.SafeObjectAt(aIndex - 1); PRInt32 prevType = nsICSSRule::UNKNOWN_RULE; prevRule->GetType(prevType); if (firstType == nsICSSRule::IMPORT_RULE && prevType != nsICSSRule::CHARSET_RULE && prevType != nsICSSRule::IMPORT_RULE) { return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR; } if (firstType == nsICSSRule::NAMESPACE_RULE && prevType != nsICSSRule::CHARSET_RULE && prevType != nsICSSRule::IMPORT_RULE && prevType != nsICSSRule::NAMESPACE_RULE) { return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR; } } PRBool insertResult = mInner->mOrderedRules.InsertObjectsAt(rules, aIndex); NS_ENSURE_TRUE(insertResult, NS_ERROR_OUT_OF_MEMORY); DidDirty(); for (PRInt32 counter = 0; counter < rulecount; counter++) { nsICSSRule* cssRule = rules.ObjectAt(counter); cssRule->SetStyleSheet(this); PRInt32 type = nsICSSRule::UNKNOWN_RULE; cssRule->GetType(type); if (type == nsICSSRule::NAMESPACE_RULE) { if (!mInner->mNameSpaceMap) { mInner->mNameSpaceMap = nsXMLNameSpaceMap::Create(); NS_ENSURE_TRUE(mInner->mNameSpaceMap, NS_ERROR_OUT_OF_MEMORY); } nsCOMPtr nameSpaceRule(do_QueryInterface(cssRule)); nsCOMPtr prefix; nsAutoString urlSpec; nameSpaceRule->GetPrefix(*getter_AddRefs(prefix)); nameSpaceRule->GetURLSpec(urlSpec); mInner->mNameSpaceMap->AddPrefix(prefix, urlSpec); } // We don't notify immediately for @import rules, but rather when // the sheet the rule is importing is loaded PRBool notify = PR_TRUE; if (type == nsICSSRule::IMPORT_RULE) { nsCOMPtr importRule(do_QueryInterface(cssRule)); NS_ASSERTION(importRule, "Rule which has type IMPORT_RULE and does not implement nsIDOMCSSImportRule!"); nsCOMPtr childSheet; importRule->GetStyleSheet(getter_AddRefs(childSheet)); if (!childSheet) { notify = PR_FALSE; } } if (mDocument && notify) { mDocument->StyleRuleAdded(this, cssRule); } } if (loader) { loader->RecycleParser(css); } *aReturn = aIndex; return NS_OK; } NS_IMETHODIMP nsCSSStyleSheet::DeleteRule(PRUint32 aIndex) { nsresult result = NS_ERROR_DOM_INDEX_SIZE_ERR; // No doing this if the sheet is not complete! PRBool complete; GetComplete(complete); if (!complete) { return NS_ERROR_DOM_INVALID_ACCESS_ERR; } // XXX TBI: handle @rule types mozAutoDocUpdate updateBatch(mDocument, UPDATE_STYLE, PR_TRUE); result = WillDirty(); if (NS_SUCCEEDED(result)) { if (aIndex >= PRUint32(mInner->mOrderedRules.Count())) return NS_ERROR_DOM_INDEX_SIZE_ERR; NS_ASSERTION(PRUint32(mInner->mOrderedRules.Count()) <= PR_INT32_MAX, "Too many style rules!"); // Hold a strong ref to the rule so it doesn't die when we RemoveObjectAt nsCOMPtr rule = mInner->mOrderedRules.ObjectAt(aIndex); if (rule) { mInner->mOrderedRules.RemoveObjectAt(aIndex); rule->SetStyleSheet(nsnull); DidDirty(); if (mDocument) { mDocument->StyleRuleRemoved(this, rule); } } } return result; } NS_IMETHODIMP nsCSSStyleSheet::DeleteRuleFromGroup(nsICSSGroupRule* aGroup, PRUint32 aIndex) { NS_ENSURE_ARG_POINTER(aGroup); NS_ASSERTION(mInner->mComplete, "No deleting from an incomplete sheet!"); nsresult result; nsCOMPtr rule; result = aGroup->GetStyleRuleAt(aIndex, *getter_AddRefs(rule)); NS_ENSURE_SUCCESS(result, result); // check that the rule actually belongs to this sheet! nsCOMPtr ruleSheet; rule->GetStyleSheet(*getter_AddRefs(ruleSheet)); if (this != ruleSheet) { return NS_ERROR_INVALID_ARG; } mozAutoDocUpdate updateBatch(mDocument, UPDATE_STYLE, PR_TRUE); result = WillDirty(); NS_ENSURE_SUCCESS(result, result); result = aGroup->DeleteStyleRuleAt(aIndex); NS_ENSURE_SUCCESS(result, result); rule->SetStyleSheet(nsnull); DidDirty(); if (mDocument) { mDocument->StyleRuleRemoved(this, rule); } return NS_OK; } NS_IMETHODIMP nsCSSStyleSheet::InsertRuleIntoGroup(const nsAString & aRule, nsICSSGroupRule* aGroup, PRUint32 aIndex, PRUint32* _retval) { nsresult result; NS_ASSERTION(mInner->mComplete, "No inserting into an incomplete sheet!"); // check that the group actually belongs to this sheet! nsCOMPtr groupSheet; aGroup->GetStyleSheet(*getter_AddRefs(groupSheet)); if (this != groupSheet) { return NS_ERROR_INVALID_ARG; } if (aRule.IsEmpty()) { // Nothing to do here return NS_OK; } // Hold strong ref to the CSSLoader in case the document update // kills the document nsCOMPtr loader; if (mDocument) { loader = mDocument->CSSLoader(); NS_ASSERTION(loader, "Document with no CSS loader!"); } nsCOMPtr css; if (loader) { result = loader->GetParserFor(this, getter_AddRefs(css)); } else { result = NS_NewCSSParser(getter_AddRefs(css)); if (css) { css->SetStyleSheet(this); } } NS_ENSURE_SUCCESS(result, result); // parse and grab the rule mozAutoDocUpdate updateBatch(mDocument, UPDATE_STYLE, PR_TRUE); result = WillDirty(); NS_ENSURE_SUCCESS(result, result); nsCOMArray rules; result = css->ParseRule(aRule, mInner->mSheetURI, mInner->mBaseURI, mInner->mPrincipal, rules); NS_ENSURE_SUCCESS(result, result); PRInt32 rulecount = rules.Count(); if (rulecount == 0) { // Since we know aRule was not an empty string, just throw return NS_ERROR_DOM_SYNTAX_ERR; } PRInt32 counter; nsICSSRule* rule; for (counter = 0; counter < rulecount; counter++) { // Only rulesets are allowed in a group as of CSS2 PRInt32 type = nsICSSRule::UNKNOWN_RULE; rule = rules.ObjectAt(counter); rule->GetType(type); if (type != nsICSSRule::STYLE_RULE) { return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR; } } result = aGroup->InsertStyleRulesAt(aIndex, rules); NS_ENSURE_SUCCESS(result, result); DidDirty(); for (counter = 0; counter < rulecount; counter++) { rule = rules.ObjectAt(counter); if (mDocument) { mDocument->StyleRuleAdded(this, rule); } } if (loader) { loader->RecycleParser(css); } *_retval = aIndex; return NS_OK; } NS_IMETHODIMP nsCSSStyleSheet::ReplaceRuleInGroup(nsICSSGroupRule* aGroup, nsICSSRule* aOld, nsICSSRule* aNew) { nsresult result; NS_PRECONDITION(mInner->mComplete, "No replacing in an incomplete sheet!"); #ifdef DEBUG { nsCOMPtr groupSheet; aGroup->GetStyleSheet(*getter_AddRefs(groupSheet)); NS_ASSERTION(this == groupSheet, "group doesn't belong to this sheet"); } #endif result = WillDirty(); NS_ENSURE_SUCCESS(result, result); result = aGroup->ReplaceStyleRule(aOld, aNew); DidDirty(); return result; } // nsICSSLoaderObserver implementation NS_IMETHODIMP nsCSSStyleSheet::StyleSheetLoaded(nsICSSStyleSheet* aSheet, PRBool aWasAlternate, nsresult aStatus) { #ifdef DEBUG nsCOMPtr styleSheet(do_QueryInterface(aSheet)); NS_ASSERTION(styleSheet, "Sheet not implementing nsIStyleSheet!\n"); nsCOMPtr parentSheet; aSheet->GetParentSheet(*getter_AddRefs(parentSheet)); nsCOMPtr thisSheet; QueryInterface(NS_GET_IID(nsIStyleSheet), getter_AddRefs(thisSheet)); NS_ASSERTION(thisSheet == parentSheet, "We are being notified of a sheet load for a sheet that is not our child!\n"); #endif if (mDocument && NS_SUCCEEDED(aStatus)) { nsCOMPtr ownerRule; aSheet->GetOwnerRule(getter_AddRefs(ownerRule)); mozAutoDocUpdate updateBatch(mDocument, UPDATE_STYLE, PR_TRUE); // XXXldb @import rules shouldn't even implement nsIStyleRule (but // they do)! nsCOMPtr styleRule(do_QueryInterface(ownerRule)); mDocument->StyleRuleAdded(this, styleRule); } return NS_OK; } nsresult NS_NewCSSStyleSheet(nsICSSStyleSheet** aInstancePtrResult) { *aInstancePtrResult = nsnull; nsCSSStyleSheet *it = new nsCSSStyleSheet(); if (!it) { return NS_ERROR_OUT_OF_MEMORY; } NS_ADDREF(it); if (!it->mInner || !it->mInner->mPrincipal) { NS_RELEASE(it); return NS_ERROR_OUT_OF_MEMORY; } *aInstancePtrResult = it; return NS_OK; }