pjs/layout/style/nsStyleSet.cpp

531 строка
15 KiB
C++

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
#include "nsIStyleSet.h"
#include "nsIStyleSheet.h"
#include "nsIStyleRule.h"
#include "nsIStyleContext.h"
#include "nsISupportsArray.h"
#include "nsIFrame.h"
#include "nsHashtable.h"
static NS_DEFINE_IID(kIStyleSetIID, NS_ISTYLE_SET_IID);
class ContextKey : public nsHashKey {
public:
ContextKey(nsIStyleContext* aContext);
ContextKey(nsIStyleContext* aParent, nsISupportsArray* aRules);
virtual ~ContextKey(void);
void SetContext(nsIStyleContext* aContext);
PRBool Equals(const nsHashKey* aOther) const;
PRUint32 HashValue(void) const;
nsHashKey* Clone(void) const;
private:
ContextKey(void);
ContextKey(const ContextKey& aCopy);
ContextKey& operator=(const ContextKey& aCopy) const;
protected:
nsIStyleContext* mContext;
nsIStyleContext* mParent;
nsISupportsArray* mRules;
};
ContextKey::ContextKey(nsIStyleContext* aContext)
: mContext(aContext),
mParent(nsnull),
mRules(nsnull)
{
NS_IF_ADDREF(mContext);
}
ContextKey::ContextKey(nsIStyleContext* aParent, nsISupportsArray* aRules)
: mContext(nsnull),
mParent(aParent),
mRules(aRules)
{
NS_IF_ADDREF(mParent);
NS_IF_ADDREF(mRules);
}
ContextKey::ContextKey(const ContextKey& aCopy)
: mContext(aCopy.mContext),
mParent(aCopy.mParent),
mRules(aCopy.mRules)
{
NS_IF_ADDREF(mContext);
NS_IF_ADDREF(mParent);
NS_IF_ADDREF(mRules);
}
ContextKey::~ContextKey(void)
{
NS_IF_RELEASE(mContext);
NS_IF_RELEASE(mParent);
NS_IF_RELEASE(mRules);
}
void ContextKey::SetContext(nsIStyleContext* aContext)
{
if (aContext != mContext) {
NS_IF_RELEASE(mContext);
mContext = aContext;
NS_IF_ADDREF(mContext);
}
NS_IF_RELEASE(mParent);
NS_IF_RELEASE(mRules);
}
PRBool ContextKey::Equals(const nsHashKey* aOther) const
{
PRBool result = PR_TRUE;
const ContextKey* other = (const ContextKey*)aOther;
if (other != this) {
if ((nsnull == mContext) || (nsnull == other->mContext) || (mContext != other->mContext)) {
nsIStyleContext* otherParent = other->mParent;
if ((nsnull == otherParent) && (nsnull != other->mContext)) {
otherParent = other->mContext->GetParent();
}
else {
NS_IF_ADDREF(otherParent); // simulate the above addref
}
if (mParent == otherParent) {
nsISupportsArray* otherRules = other->mRules;
if ((nsnull == otherRules) && (nsnull != other->mContext)) {
otherRules = other->mContext->GetStyleRules();
}
else {
NS_IF_ADDREF(otherRules); // simulate the above addref
}
if ((nsnull != mRules) && (nsnull != otherRules)) {
result = mRules->Equals(otherRules);
}
else {
result = PRBool((nsnull == mRules) && (nsnull == otherRules));
}
NS_IF_RELEASE(otherRules);
}
else {
result = PR_FALSE;
}
NS_IF_RELEASE(otherParent);
}
}
return result;
}
PRUint32 ContextKey::HashValue(void) const
{
if (nsnull != mContext) {
return mContext->HashValue();
}
// we don't have a context yet, compute it like it would
PRUint32 hashValue = ((nsnull != mParent) ? mParent->HashValue() : 0);
if (nsnull != mRules) {
PRInt32 index = mRules->Count();
while (0 <= --index) {
nsIStyleRule* rule = (nsIStyleRule*)mRules->ElementAt(index);
PRUint32 hash = rule->HashValue();
hashValue ^= (hash & 0x7FFFFFFF);
NS_RELEASE(rule);
}
}
return hashValue;
}
nsHashKey* ContextKey::Clone(void) const
{
return new ContextKey(*this);
}
class StyleSetImpl : public nsIStyleSet {
public:
StyleSetImpl();
NS_DECL_ISUPPORTS
virtual void AppendOverrideStyleSheet(nsIStyleSheet* aSheet);
virtual void InsertOverrideStyleSheetAfter(nsIStyleSheet* aSheet,
nsIStyleSheet* aAfterSheet);
virtual void InsertOverrideStyleSheetBefore(nsIStyleSheet* aSheet,
nsIStyleSheet* aBeforeSheet);
virtual void RemoveOverrideStyleSheet(nsIStyleSheet* aSheet);
virtual PRInt32 GetNumberOfOverrideStyleSheets();
virtual nsIStyleSheet* GetOverrideStyleSheetAt(PRInt32 aIndex);
virtual void AppendDocStyleSheet(nsIStyleSheet* aSheet);
virtual void InsertDocStyleSheetAfter(nsIStyleSheet* aSheet,
nsIStyleSheet* aAfterSheet);
virtual void InsertDocStyleSheetBefore(nsIStyleSheet* aSheet,
nsIStyleSheet* aBeforeSheet);
virtual void RemoveDocStyleSheet(nsIStyleSheet* aSheet);
virtual PRInt32 GetNumberOfDocStyleSheets();
virtual nsIStyleSheet* GetDocStyleSheetAt(PRInt32 aIndex);
virtual void AppendBackstopStyleSheet(nsIStyleSheet* aSheet);
virtual void InsertBackstopStyleSheetAfter(nsIStyleSheet* aSheet,
nsIStyleSheet* aAfterSheet);
virtual void InsertBackstopStyleSheetBefore(nsIStyleSheet* aSheet,
nsIStyleSheet* aBeforeSheet);
virtual void RemoveBackstopStyleSheet(nsIStyleSheet* aSheet);
virtual PRInt32 GetNumberOfBackstopStyleSheets();
virtual nsIStyleSheet* GetBackstopStyleSheetAt(PRInt32 aIndex);
virtual nsIStyleContext* ResolveStyleFor(nsIPresContext* aPresContext,
nsIContent* aContent,
nsIFrame* aParentFrame);
// xxx style rules enumeration
virtual void List(FILE* out = stdout, PRInt32 aIndent = 0);
private:
// These are not supported and are not implemented!
StyleSetImpl(const StyleSetImpl& aCopy);
StyleSetImpl& operator=(const StyleSetImpl& aCopy);
protected:
virtual ~StyleSetImpl();
PRBool EnsureArray(nsISupportsArray** aArray);
PRInt32 RulesMatching(nsISupportsArray* aSheets,
nsIPresContext* aPresContext,
nsIContent* aContent,
nsIFrame* aParentFrame,
nsISupportsArray* aResults);
void List(FILE* out, PRInt32 aIndent, nsISupportsArray* aSheets);
nsISupportsArray* mOverrideSheets;
nsISupportsArray* mDocSheets;
nsISupportsArray* mBackstopSheets;
nsHashtable mStyleContexts;
};
StyleSetImpl::StyleSetImpl()
: mOverrideSheets(nsnull),
mDocSheets(nsnull),
mBackstopSheets(nsnull)
{
NS_INIT_REFCNT();
}
PRBool ReleaseContext(nsHashKey *aKey, void *aData)
{
((nsIStyleContext*)aData)->Release();
return PR_TRUE;
}
StyleSetImpl::~StyleSetImpl()
{
NS_IF_RELEASE(mOverrideSheets);
NS_IF_RELEASE(mDocSheets);
NS_IF_RELEASE(mBackstopSheets);
mStyleContexts.Enumerate(ReleaseContext);
}
NS_IMPL_ISUPPORTS(StyleSetImpl, kIStyleSetIID)
PRBool StyleSetImpl::EnsureArray(nsISupportsArray** aArray)
{
if (nsnull == *aArray) {
if (NS_OK != NS_NewISupportsArray(aArray)) {
return PR_FALSE;
}
}
return PR_TRUE;
}
// ----- Override sheets
void StyleSetImpl::AppendOverrideStyleSheet(nsIStyleSheet* aSheet)
{
NS_PRECONDITION(nsnull != aSheet, "null arg");
if (EnsureArray(&mOverrideSheets)) {
mOverrideSheets->AppendElement(aSheet);
}
}
void StyleSetImpl::InsertOverrideStyleSheetAfter(nsIStyleSheet* aSheet,
nsIStyleSheet* aAfterSheet)
{
NS_PRECONDITION(nsnull != aSheet, "null arg");
if (EnsureArray(&mOverrideSheets)) {
PRInt32 index = mOverrideSheets->IndexOf(aAfterSheet);
mOverrideSheets->InsertElementAt(aSheet, ++index);
}
}
void StyleSetImpl::InsertOverrideStyleSheetBefore(nsIStyleSheet* aSheet,
nsIStyleSheet* aBeforeSheet)
{
NS_PRECONDITION(nsnull != aSheet, "null arg");
if (EnsureArray(&mOverrideSheets)) {
PRInt32 index = mOverrideSheets->IndexOf(aBeforeSheet);
mOverrideSheets->InsertElementAt(aSheet, ((-1 < index) ? index : 0));
}
}
void StyleSetImpl::RemoveOverrideStyleSheet(nsIStyleSheet* aSheet)
{
NS_PRECONDITION(nsnull != aSheet, "null arg");
if (nsnull != mOverrideSheets) {
mOverrideSheets->RemoveElement(aSheet);
}
}
PRInt32 StyleSetImpl::GetNumberOfOverrideStyleSheets()
{
if (nsnull != mOverrideSheets) {
return mOverrideSheets->Count();
}
return 0;
}
nsIStyleSheet* StyleSetImpl::GetOverrideStyleSheetAt(PRInt32 aIndex)
{
nsIStyleSheet* sheet = nsnull;
if (nsnull == mOverrideSheets) {
sheet = (nsIStyleSheet*)mOverrideSheets->ElementAt(aIndex);
}
return sheet;
}
// -------- Doc Sheets
void StyleSetImpl::AppendDocStyleSheet(nsIStyleSheet* aSheet)
{
NS_PRECONDITION(nsnull != aSheet, "null arg");
if (EnsureArray(&mDocSheets)) {
mDocSheets->AppendElement(aSheet);
}
}
void StyleSetImpl::InsertDocStyleSheetAfter(nsIStyleSheet* aSheet,
nsIStyleSheet* aAfterSheet)
{
NS_PRECONDITION(nsnull != aSheet, "null arg");
if (EnsureArray(&mDocSheets)) {
PRInt32 index = mDocSheets->IndexOf(aAfterSheet);
mDocSheets->InsertElementAt(aSheet, ++index);
}
}
void StyleSetImpl::InsertDocStyleSheetBefore(nsIStyleSheet* aSheet,
nsIStyleSheet* aBeforeSheet)
{
NS_PRECONDITION(nsnull != aSheet, "null arg");
if (EnsureArray(&mDocSheets)) {
PRInt32 index = mDocSheets->IndexOf(aBeforeSheet);
mDocSheets->InsertElementAt(aSheet, ((-1 < index) ? index : 0));
}
}
void StyleSetImpl::RemoveDocStyleSheet(nsIStyleSheet* aSheet)
{
NS_PRECONDITION(nsnull != aSheet, "null arg");
if (nsnull != mDocSheets) {
mDocSheets->RemoveElement(aSheet);
}
}
PRInt32 StyleSetImpl::GetNumberOfDocStyleSheets()
{
if (nsnull != mDocSheets) {
return mDocSheets->Count();
}
return 0;
}
nsIStyleSheet* StyleSetImpl::GetDocStyleSheetAt(PRInt32 aIndex)
{
nsIStyleSheet* sheet = nsnull;
if (nsnull == mDocSheets) {
sheet = (nsIStyleSheet*)mDocSheets->ElementAt(aIndex);
}
return sheet;
}
// ------ backstop sheets
void StyleSetImpl::AppendBackstopStyleSheet(nsIStyleSheet* aSheet)
{
NS_PRECONDITION(nsnull != aSheet, "null arg");
if (EnsureArray(&mBackstopSheets)) {
mBackstopSheets->AppendElement(aSheet);
}
}
void StyleSetImpl::InsertBackstopStyleSheetAfter(nsIStyleSheet* aSheet,
nsIStyleSheet* aAfterSheet)
{
NS_PRECONDITION(nsnull != aSheet, "null arg");
if (EnsureArray(&mBackstopSheets)) {
PRInt32 index = mBackstopSheets->IndexOf(aAfterSheet);
mBackstopSheets->InsertElementAt(aSheet, ++index);
}
}
void StyleSetImpl::InsertBackstopStyleSheetBefore(nsIStyleSheet* aSheet,
nsIStyleSheet* aBeforeSheet)
{
NS_PRECONDITION(nsnull != aSheet, "null arg");
if (EnsureArray(&mBackstopSheets)) {
PRInt32 index = mBackstopSheets->IndexOf(aBeforeSheet);
mBackstopSheets->InsertElementAt(aSheet, ((-1 < index) ? index : 0));
}
}
void StyleSetImpl::RemoveBackstopStyleSheet(nsIStyleSheet* aSheet)
{
NS_PRECONDITION(nsnull != aSheet, "null arg");
if (nsnull != mBackstopSheets) {
mBackstopSheets->RemoveElement(aSheet);
}
}
PRInt32 StyleSetImpl::GetNumberOfBackstopStyleSheets()
{
if (nsnull != mBackstopSheets) {
return mBackstopSheets->Count();
}
return 0;
}
nsIStyleSheet* StyleSetImpl::GetBackstopStyleSheetAt(PRInt32 aIndex)
{
nsIStyleSheet* sheet = nsnull;
if (nsnull == mBackstopSheets) {
sheet = (nsIStyleSheet*)mBackstopSheets->ElementAt(aIndex);
}
return sheet;
}
PRInt32 StyleSetImpl::RulesMatching(nsISupportsArray* aSheets,
nsIPresContext* aPresContext,
nsIContent* aContent,
nsIFrame* aParentFrame,
nsISupportsArray* aResults)
{
PRInt32 ruleCount = 0;
if (nsnull != aSheets) {
PRInt32 sheetCount = aSheets->Count();
PRInt32 index;
for (index = 0; index < sheetCount; index++) {
nsIStyleSheet* sheet = (nsIStyleSheet*)aSheets->ElementAt(index);
ruleCount += sheet->RulesMatching(aPresContext, aContent, aParentFrame,
aResults);
NS_RELEASE(sheet);
}
}
return ruleCount;
}
nsIStyleContext* StyleSetImpl::ResolveStyleFor(nsIPresContext* aPresContext,
nsIContent* aContent,
nsIFrame* aParentFrame)
{
nsIStyleContext* result = nsnull;
nsIStyleContext* parentContext = nsnull;
if (nsnull != aParentFrame) {
parentContext = aParentFrame->GetStyleContext(aPresContext);
NS_ASSERTION(nsnull != parentContext, "parent must have style context");
}
// want to check parent frame's context for cached child context first
// then do a brute force rule search
nsISupportsArray* rules = nsnull;
if (NS_OK == NS_NewISupportsArray(&rules)) {
PRInt32 ruleCount = RulesMatching(mOverrideSheets, aPresContext, aContent, aParentFrame, rules);
ruleCount += RulesMatching(mDocSheets, aPresContext, aContent, aParentFrame, rules);
ruleCount += RulesMatching(mBackstopSheets, aPresContext, aContent, aParentFrame, rules);
// then check for cached ruleSet to context or create
ContextKey tempKey(parentContext, rules);
result = (nsIStyleContext*)mStyleContexts.Get(&tempKey);
if (nsnull == result) {
if (NS_OK == NS_NewStyleContext(&result, rules, aPresContext, aContent, aParentFrame)) {
tempKey.SetContext(result);
mStyleContexts.Put(&tempKey, result); // hashtable clones key, so this is OK (table gets first ref)
NS_ADDREF(result); // add ref for the caller
//fprintf(stdout, "+");
}
}
else {
NS_ADDREF(result);
//fprintf(stdout, "-");
}
NS_RELEASE(rules);
}
NS_IF_RELEASE(parentContext);
return result;
}
// xxx style rules enumeration
void StyleSetImpl::List(FILE* out, PRInt32 aIndent, nsISupportsArray* aSheets)
{
PRInt32 count = ((nsnull != aSheets) ? aSheets->Count() : 0);
for (PRInt32 index = 0; index < count; index++) {
nsIStyleSheet* sheet = (nsIStyleSheet*)aSheets->ElementAt(index);
sheet->List(out, aIndent);
fputs("\n", out);
NS_RELEASE(sheet);
}
}
void StyleSetImpl::List(FILE* out, PRInt32 aIndent)
{
List(out, aIndent, mOverrideSheets);
List(out, aIndent, mDocSheets);
List(out, aIndent, mBackstopSheets);
}
NS_LAYOUT nsresult
NS_NewStyleSet(nsIStyleSet** aInstancePtrResult)
{
if (aInstancePtrResult == nsnull) {
return NS_ERROR_NULL_POINTER;
}
StyleSetImpl *it = new StyleSetImpl();
if (nsnull == it) {
return NS_ERROR_OUT_OF_MEMORY;
}
return it->QueryInterface(kIStyleSetIID, (void **) aInstancePtrResult);
}