forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			1921 lines
		
	
	
	
		
			48 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			1921 lines
		
	
	
	
		
			48 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*- 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 <dbaron@dbaron.org>
 | |
|  *   Pierre Phaneuf <pp@ludusdesign.com>
 | |
|  *   Daniel Glazman <glazman@netscape.com>
 | |
|  *
 | |
|  * 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<nsICSSRule> 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<nsICSSParser> parser;
 | |
|   nsresult rv = NS_NewCSSParser(getter_AddRefs(parser));
 | |
|   NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|   PRBool htmlMode = PR_FALSE;
 | |
|   nsCOMPtr<nsIDOMStyleSheet> domSheet =
 | |
|     do_QueryInterface(static_cast<nsICSSStyleSheet*>(mStyleSheet));
 | |
|   if (domSheet) {
 | |
|     nsCOMPtr<nsIDOMNode> 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 = static_cast<nsCSSStyleSheet*>(aSheet);
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| nsresult
 | |
| nsMediaList::Clone(nsMediaList** aResult)
 | |
| {
 | |
|   nsRefPtr<nsMediaList> 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<nsIDocument>
 | |
| #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<nsIDocument> 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<nsIDocument> 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<nsIDocument> 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<nsIAtom> 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<nsIAtom> 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<nsICSSStyleSheet> 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) {
 | |
|     static_cast<nsCOMArray<nsICSSRule>*>(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 =
 | |
|       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* aOriginalSheetURI,
 | |
|                          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->mOriginalSheetURI = aOriginalSheetURI;
 | |
|   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<nsICSSNameSpaceRule> nameSpaceRule(do_QueryInterface(aRule));
 | |
| 
 | |
|       nsCOMPtr<nsIAtom> 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 = static_cast<nsICSSStyleSheet*>(clone);
 | |
|     NS_ADDREF(*aClone);
 | |
|   }
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| #ifdef DEBUG
 | |
| static void
 | |
| ListRules(const nsCOMArray<nsICSSRule>& 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 =
 | |
|     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;
 | |
| }
 | |
| 
 | |
| nsresult
 | |
| nsCSSStyleSheet::SubjectSubsumesInnerPrincipal() const
 | |
| {
 | |
|   // Get the security manager and do the subsumes check
 | |
|   nsIScriptSecurityManager *securityManager =
 | |
|     nsContentUtils::GetSecurityManager();
 | |
| 
 | |
|   nsCOMPtr<nsIPrincipal> subjectPrincipal;
 | |
|   securityManager->GetSubjectPrincipal(getter_AddRefs(subjectPrincipal));
 | |
| 
 | |
|   if (!subjectPrincipal) {
 | |
|     return NS_OK;
 | |
|   }
 | |
| 
 | |
|   PRBool subsumes;
 | |
|   nsresult rv = subjectPrincipal->Subsumes(mInner->mPrincipal, &subsumes);
 | |
|   NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|   if (subsumes) {
 | |
|     return NS_OK;
 | |
|   }
 | |
|   
 | |
|   if (!nsContentUtils::IsCallerTrustedForWrite()) {
 | |
|     return NS_ERROR_DOM_SECURITY_ERR;
 | |
|   }
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| 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)
 | |
| {
 | |
|   if (mInner->mOriginalSheetURI) {
 | |
|     nsCAutoString str;
 | |
|     mInner->mOriginalSheetURI->GetSpec(str);
 | |
|     CopyUTF8toUTF16(str, aHref);
 | |
|   } else {
 | |
|     SetDOMStringToNull(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.
 | |
|   nsresult rv = SubjectSubsumesInnerPrincipal();
 | |
|   NS_ENSURE_SUCCESS(rv, 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;
 | |
|   }
 | |
| 
 | |
|   //-- Security check: Only scripts whose principal subsumes that of the
 | |
|   //   style sheet can modify rule collections.
 | |
|   nsresult rv = SubjectSubsumesInnerPrincipal();
 | |
|   NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|   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<nsICSSLoader> loader;
 | |
|   if (mDocument) {
 | |
|     loader = mDocument->CSSLoader();
 | |
|     NS_ASSERTION(loader, "Document with no CSS loader!");
 | |
|   }
 | |
|   
 | |
|   nsCOMPtr<nsICSSParser> 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<nsICSSRule> 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<nsICSSNameSpaceRule> nameSpaceRule(do_QueryInterface(cssRule));
 | |
|     
 | |
|       nsCOMPtr<nsIAtom> 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<nsIDOMCSSImportRule> importRule(do_QueryInterface(cssRule));
 | |
|       NS_ASSERTION(importRule, "Rule which has type IMPORT_RULE and does not implement nsIDOMCSSImportRule!");
 | |
|       nsCOMPtr<nsIDOMCSSStyleSheet> 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;
 | |
|   }
 | |
| 
 | |
|   //-- Security check: Only scripts whose principal subsumes that of the
 | |
|   //   style sheet can modify rule collections.
 | |
|   nsresult rv = SubjectSubsumesInnerPrincipal();
 | |
|   NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|   // 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<nsICSSRule> 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<nsICSSRule> rule;
 | |
|   result = aGroup->GetStyleRuleAt(aIndex, *getter_AddRefs(rule));
 | |
|   NS_ENSURE_SUCCESS(result, result);
 | |
|   
 | |
|   // check that the rule actually belongs to this sheet!
 | |
|   nsCOMPtr<nsIStyleSheet> 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<nsIStyleSheet> 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<nsICSSLoader> loader;
 | |
|   if (mDocument) {
 | |
|     loader = mDocument->CSSLoader();
 | |
|     NS_ASSERTION(loader, "Document with no CSS loader!");
 | |
|   }
 | |
| 
 | |
|   nsCOMPtr<nsICSSParser> 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<nsICSSRule> 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<nsIStyleSheet> 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<nsIStyleSheet> styleSheet(do_QueryInterface(aSheet));
 | |
|   NS_ASSERTION(styleSheet, "Sheet not implementing nsIStyleSheet!\n");
 | |
|   nsCOMPtr<nsIStyleSheet> parentSheet;
 | |
|   aSheet->GetParentSheet(*getter_AddRefs(parentSheet));
 | |
|   nsCOMPtr<nsIStyleSheet> 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<nsICSSImportRule> 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<nsIStyleRule> 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;
 | |
| }
 | 
