Mozilla/mozilla/layout/style/nsCSSStyleSheet.cpp
bzbarsky%mit.edu 6e86d03b2e Stylesheets should know what their principal is, and it should be based on the
URI the sheet was loaded from, its certificate (if any) and the loader
principal, not just on the page the sheet is being applied to.  Propagate this
principal as the loading principal to @import loads from stylesheets.  Change
sheet access checks to use this principal instead of the sheet URI.  Bug
221428, r+sr=peterv


git-svn-id: svn://10.0.0.236/trunk@225401 18797224-902f-48f8-a5cc-f745e15eee43
2007-05-01 19:26:15 +00:00

1899 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(NS_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 = NS_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) {
NS_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 =
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<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 = NS_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 =
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<nsIPrincipal> 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<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, 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;
}
// 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, 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;
}