blob: 8f8cdcf3b70b1abe67d79b2a27fbe875eb4d7b15 [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
#include "CSSSheet.h"
#include "CSS.h"
#include "CSSProperties.h"
#include "CSSParser.h"
#include "CSSSelector.h"
#include "CSSLength.h"
#include "DFString.h"
#include "DFHashTable.h"
#include "DFBuffer.h"
#include "DFCommon.h"
#include "DFPlatform.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
// //
// CSSSheet //
// //
////////////////////////////////////////////////////////////////////////////////////////////////////
struct CSSSheet {
size_t retainCount;
DFHashTable *_styles; // const char * -> CSSStyle
DFHashTable *_defaultStyles; // int(StyleFamily) -> CSSStyle
};
CSSSheet *CSSSheetNew(void)
{
CSSSheet *sheet = (CSSSheet *)xcalloc(1,sizeof(CSSSheet));
sheet->retainCount = 1;
sheet->_styles = DFHashTableNew((DFCopyFunction)CSSStyleRetain,(DFFreeFunction)CSSStyleRelease);
sheet->_defaultStyles = DFHashTableNew((DFCopyFunction)CSSStyleRetain,(DFFreeFunction)CSSStyleRelease);
return sheet;
}
CSSSheet *CSSSheetRetain(CSSSheet *sheet)
{
if (sheet != NULL)
sheet->retainCount++;
return sheet;
}
void CSSSheetRelease(CSSSheet *sheet)
{
if ((sheet == NULL) || (--sheet->retainCount > 0))
return;
DFHashTableRelease(sheet->_styles);
DFHashTableRelease(sheet->_defaultStyles);
free(sheet);
}
const char **CSSSheetCopySelectors(CSSSheet *sheet)
{
return DFHashTableCopyKeys(sheet->_styles);
}
void CSSSheetAddStyle(CSSSheet *sheet, CSSStyle *style)
{
assert(style->selector != NULL);
DFHashTableAdd(sheet->_styles,style->selector,style);
}
void CSSSheetRemoveStyle(CSSSheet *sheet, CSSStyle *style)
{
DFHashTableRemove(sheet->_styles,style->selector);
}
CSSStyle *CSSSheetLookupSelector(CSSSheet *sheet, const char *selector, int add, int latent)
{
CSSStyle *style = DFHashTableLookup(sheet->_styles,selector);
if ((style == NULL) && add) {
style = CSSStyleNew(selector);
style->latent = latent;
CSSSheetAddStyle(sheet,style);
CSSStyleRelease(style); // The hash table keeps a reference, so this is safe
}
if ((style != NULL) && add && style->latent && !latent)
style->latent = 0;
return style;
}
CSSStyle *CSSSheetLookupElement(CSSSheet *sheet, const char *elementName, const char *className, int add, int latent)
{
char *selector = CSSMakeSelector(elementName,className);
CSSStyle *result = CSSSheetLookupSelector(sheet,selector,add,latent);
free(selector);
return result;
}
CSSStyle *CSSSheetFlattenedStyle(CSSSheet *sheet, CSSStyle *orig)
{
// FIXME: Need tests for parent cycles
CSSStyle *ancestor = orig;
CSSStyle *result = CSSStyleNew("temp");
DFHashTable *visited = DFHashTableNew((DFCopyFunction)xstrdup,(DFFreeFunction)free);
const char **allSuffixes = NULL;
while (1) {
free(allSuffixes);
allSuffixes = CSSStyleCopySuffixes(ancestor);
for (int suffixIndex = 0; allSuffixes[suffixIndex]; suffixIndex++) {
const char *suffix = allSuffixes[suffixIndex];
CSSProperties *origProperties = CSSStyleRuleForSuffix(ancestor,suffix);
CSSProperties *collapsedProperties = CSSStyleRuleForSuffix(result,suffix);
const char **allNames = CSSPropertiesCopyNames(origProperties);
for (int nameIndex = 0; allNames[nameIndex]; nameIndex++) {
const char *name = allNames[nameIndex];
if (!strcmp(name,"-uxwrite-default") && (ancestor != orig))
continue;
if (CSSGet(collapsedProperties,name) == NULL)
CSSPut(collapsedProperties,name,CSSGet(origProperties,name));
}
free(allNames);
}
DFHashTableAdd(visited,ancestor->selector,"");
ancestor = CSSSheetGetStyleParent(sheet,ancestor);
if ((ancestor == NULL) || (DFHashTableLookup(visited,ancestor->selector) != NULL))
break;
}
free(allSuffixes);
DFHashTableRelease(visited);
return result;
}
DFHashTable *CSSSheetRules(CSSSheet *sheet)
{
DFHashTable *result = DFHashTableNew((DFCopyFunction)DFHashTableRetain,(DFFreeFunction)DFHashTableRelease);
const char **allSelectors = CSSSheetCopySelectors(sheet);
for (int selIndex = 0; allSelectors[selIndex]; selIndex++) {
const char *selector = allSelectors[selIndex];
CSSStyle *origStyle = CSSSheetLookupSelector(sheet,selector,0,0);
if ((origStyle == NULL) || origStyle->latent)
continue;
char *elementName = CSSSelectorCopyElementName(selector);
char *className = CSSSelectorCopyClassName(selector);
char *baseSelector;
if (className != NULL) {
char *escapedClassName = CSSEscapeIdent(className);
baseSelector = DFFormatString("%s.%s",elementName,escapedClassName);
free(escapedClassName);
}
else {
baseSelector = xstrdup(elementName);
}
CSSStyle *flattenedStyle = CSSSheetFlattenedStyle(sheet,origStyle);
const char **allSuffixes = CSSStyleCopySuffixes(flattenedStyle);
for (int suffixIndex = 0; allSuffixes[suffixIndex]; suffixIndex++) {
const char *suffix = allSuffixes[suffixIndex];
char *fullSelector = DFFormatString("%s%s",baseSelector,suffix);
CSSProperties *properties = CSSStyleRuleForSuffix(flattenedStyle,suffix);
DFHashTable *collapsed = CSSCollapseProperties(properties);
if (!((DFHashTableCount(collapsed) == 0) && ((strlen(suffix) > 0) || CSSIsBuiltinSelector(selector))))
DFHashTableAdd(result,fullSelector,collapsed);
free(fullSelector);
DFHashTableRelease(collapsed);
}
free(allSuffixes);
CSSStyleRelease(flattenedStyle);
free(elementName);
free(className);
free(baseSelector);
}
free(allSelectors);
return result;
}
char *CSSSheetCopyCSSText(CSSSheet *sheet)
{
DFHashTable *hashRules = CSSSheetRules(sheet);
char *cssText = CSSCopyStylesheetTextFromRules(hashRules);
DFHashTableRelease(hashRules);
return cssText;
}
char *CSSSheetCopyText(CSSSheet *sheet)
{
DFBuffer *result = DFBufferNew();
const char **allSelectors = CSSSheetCopySelectors(sheet);
DFSortStringsCaseInsensitive(allSelectors);
for (int selIndex = 0; allSelectors[selIndex]; selIndex++) {
CSSStyle *style = CSSSheetLookupSelector(sheet,allSelectors[selIndex],0,0);
DFBufferFormat(result,"%s\n",style->selector);
const char **sortedSuffixes = CSSStyleCopySuffixes(style);
DFSortStringsCaseInsensitive(sortedSuffixes);
for (int suffixIndex = 0; sortedSuffixes[suffixIndex]; suffixIndex++) {
const char *suffix = sortedSuffixes[suffixIndex];
char *quotedSuffix = DFQuote(suffix);
DFBufferFormat(result," %s\n",quotedSuffix);
free(quotedSuffix);
CSSProperties *properties = CSSStyleRuleForSuffix(style,suffix);
const char **sortedNames = CSSPropertiesCopyNames(properties);
DFSortStringsCaseInsensitive(sortedNames);
for (int nameIndex = 0; sortedNames[nameIndex]; nameIndex++) {
const char *name = sortedNames[nameIndex];
const char *value = CSSGet(properties,name);
DFBufferFormat(result," %s = %s\n",name,value);
}
free(sortedNames);
}
free(sortedSuffixes);
}
free(allSelectors);
char *str = xstrdup(result->data);
DFBufferRelease(result);
return str;
}
static void breakCycles(CSSSheet *sheet)
{
// FIXME: Not covered by tests
const char **allSelectors = CSSSheetCopySelectors(sheet);
for (int i = 0; allSelectors[i]; i++) {
const char *selector = allSelectors[i];
CSSStyle *style = CSSSheetLookupSelector(sheet,selector,0,0);
DFHashTable *visited = DFHashTableNew((DFCopyFunction)xstrdup,(DFFreeFunction)free);
int depth = 0;
while (style != NULL) {
if (DFHashTableLookup(visited,style->selector) != NULL) {
CSSStyleSetParent(style,NULL);
break;
}
DFHashTableAdd(visited,style->selector,"");
style = CSSSheetGetStyleParent(sheet,style);
depth++;
}
DFHashTableRelease(visited);
}
free(allSelectors);
}
static const char **reverseTopologicalSortedSelectors(CSSSheet *sheet)
{
// FIXME: Not covered by tests
DFArray *selectorsByDepth = DFArrayNew((DFCopyFunction)DFArrayRetain,(DFFreeFunction)DFArrayRelease);
const char **allSelectors = CSSSheetCopySelectors(sheet);
for (int i = 0; allSelectors[i]; i++) {
const char *selector = allSelectors[i];
CSSStyle *style = CSSSheetLookupSelector(sheet,selector,0,0);
unsigned int depth = 0;
while ((style = CSSSheetGetStyleParent(sheet,style)) != NULL)
depth++;
while (DFArrayCount(selectorsByDepth) < depth+1) {
DFArray *array = DFArrayNew((DFCopyFunction)xstrdup,(DFFreeFunction)free);
DFArrayAppend(selectorsByDepth,array);
DFArrayRelease(array);
}
DFArray *selectorsAtCurrentDepth = DFArrayItemAt(selectorsByDepth,depth);
DFArrayAppend(selectorsAtCurrentDepth,(char*)selector);
}
free(allSelectors);
DFArray *sortedSelectors = DFArrayNew((DFCopyFunction)xstrdup,(DFFreeFunction)free);
for (size_t i = DFArrayCount(selectorsByDepth); i > 0; i--) {
DFArray *atDepth = DFArrayItemAt(selectorsByDepth,i-1);
for (size_t j = 0; j < DFArrayCount(atDepth); j++)
DFArrayAppend(sortedSelectors,DFArrayItemAt(atDepth,j));
}
const char **result = DFStringArrayFlatten(sortedSelectors);
DFArrayRelease(selectorsByDepth);
DFArrayRelease(sortedSelectors);
return result;
}
static void removeRedundantProperties(CSSSheet *sheet)
{
// Remove any properties set on a style that have the same value as the corresponding property
// on the parent style. This is necessary because CSS doesn't support style inheritance (in
// the sense of Word & ODF's styles), so when we save out a HTML file, every style has all
// properties of its ancestors. After reading in a HTML file for the purposes of updating the
// original Word or ODF style, we don't want these extra property settings to remain, so that
// we can avoid adding spurious extra redundant property settings to the original file.
breakCycles(sheet);
const char **sortedSelectors = reverseTopologicalSortedSelectors(sheet);
for (size_t selIndex = 0; sortedSelectors[selIndex]; selIndex++) {
const char *selector = sortedSelectors[selIndex];
CSSStyle *child = CSSSheetLookupSelector(sheet,selector,0,0);
CSSStyle *parent = CSSSheetGetStyleParent(sheet,child);
if (parent == NULL)
continue;
const char **allSuffixes = CSSStyleCopySuffixes(child);
for (int suffixIndex = 0; allSuffixes[suffixIndex]; suffixIndex++) {
const char *suffix = allSuffixes[suffixIndex];
int isCell = !strcmp(suffix," > * > tr > td");
CSSProperties *childProperties = CSSStyleRuleForSuffix(child,suffix);
CSSProperties *parentProperties = CSSStyleRuleForSuffix(parent,suffix);
const char **allNames = CSSPropertiesCopyNames(childProperties);
for (int nameIndex = 0; allNames[nameIndex]; nameIndex++) {
const char *name = allNames[nameIndex];
// In docx's styles.xml, the tblCellMar values in table styles are not inherited
// (this seems like a bug in word, as isn't inconsistent with all other properties)
// So keep these ones.
if (isCell && DFStringHasPrefix(name,"padding-"))
continue;
const char *childVal = CSSGet(childProperties,name);
const char *parentVal = CSSGet(parentProperties,name);
if ((childVal != NULL) && (parentVal != NULL) && DFStringEquals(childVal,parentVal))
CSSPut(childProperties,name,NULL);
}
free(allNames);
}
free(allSuffixes);
}
free(sortedSelectors);
}
static void updateFromRawCSSRules(CSSSheet *sheet, DFHashTable *rules)
{
// FIXME: Handle class names containing escape sequences
DFHashTableRelease(sheet->_styles);
sheet->_styles = DFHashTableNew((DFCopyFunction)CSSStyleRetain,(DFFreeFunction)CSSStyleRelease);
const char **sortedSelectors = DFHashTableCopyKeys(rules);
DFSortStringsCaseInsensitive(sortedSelectors);
for (int selIndex = 0; sortedSelectors[selIndex]; selIndex++) {
const char *constSelector = sortedSelectors[selIndex];
// Treat any selectors specifying the class name only as paragraph styles
char *selector;
if (!strncmp(constSelector,".",1))
selector = DFFormatString("p%s",constSelector); // FIXME: Not covered by tests
else
selector = xstrdup(constSelector);
DFHashTable *raw = DFHashTableLookup(rules,constSelector);
char *baseId = NULL;
char *suffix = NULL;
CSSParseSelector(selector,&baseId,&suffix);
CSSStyle *style = CSSSheetLookupSelector(sheet,baseId,0,0);
if (style == NULL) {
style = CSSStyleNew(baseId);
CSSSheetAddStyle(sheet,style);
CSSStyleRelease(style);
}
CSSProperties *properties = CSSStyleRuleForSuffix(style,suffix);
CSSProperties *expanded = CSSPropertiesNewWithRaw(raw);
const char **allNames = CSSPropertiesCopyNames(expanded);
for (int nameIndex = 0; allNames[nameIndex]; nameIndex++) {
const char *name = allNames[nameIndex];
CSSPut(properties,name,CSSGet(expanded,name));
}
free(allNames);
if (!strcmp(suffix,"")) {
const char *defaultVal = CSSGet(properties,"-uxwrite-default");
if ((defaultVal != NULL) && DFStringEqualsCI(defaultVal,"true"))
CSSSheetSetDefaultStyle(sheet,style,StyleFamilyFromHTMLTag(style->tag));
}
CSSPropertiesRelease(expanded);
free(baseId);
free(suffix);
free(selector);
}
free(sortedSelectors);
removeRedundantProperties(sheet);
}
void CSSSheetUpdateFromCSSText(CSSSheet *sheet, const char *cssText)
{
DFHashTable *rules = DFHashTableNew((DFCopyFunction)DFHashTableRetain,(DFFreeFunction)DFHashTableRelease);
CSSParser *parser = CSSParserNew(cssText);
DFHashTable *top = CSSParserRules(parser);
CSSParserFree(parser);
const char **allSelectorsText = DFHashTableCopyKeys(top);
for (int i = 0; allSelectorsText[i]; i++) {
const char *selectorsText = allSelectorsText[i];
const char *propertiesText = DFHashTableLookup(top,selectorsText);
parser = CSSParserNew(selectorsText);
DFArray *selectors = CSSParserSelectors(parser);
CSSParserFree(parser);
if (selectors == NULL)
continue;
parser = CSSParserNew(propertiesText);
DFHashTable *properties = CSSParserProperties(parser);
CSSParserFree(parser);
if (properties == NULL) {
DFArrayRelease(selectors);
continue;
}
for (size_t selIndex = 0; selIndex < DFArrayCount(selectors); selIndex++) {
const char *selector = DFArrayItemAt(selectors,selIndex);
DFHashTableAdd(rules,selector,properties);
}
DFHashTableRelease(properties);
DFArrayRelease(selectors);
}
updateFromRawCSSRules(sheet,rules);
free(allSelectorsText);
DFHashTableRelease(top);
DFHashTableRelease(rules);
}
CSSProperties *CSSSheetPageProperties(CSSSheet *sheet)
{
return CSSStyleRule(CSSSheetLookupElement(sheet,"@page",NULL,1,0));
}
CSSProperties *CSSSheetBodyProperties(CSSSheet *sheet)
{
return CSSStyleRule(CSSSheetLookupElement(sheet,"body",NULL,1,0));
}
CSSStyle *CSSSheetDefaultStyleForFamily(CSSSheet *sheet, StyleFamily family)
{
return DFHashTableLookupInt(sheet->_defaultStyles,(int)family);
}
void CSSSheetSetDefaultStyle(CSSSheet *sheet, CSSStyle *style, StyleFamily family)
{
if (style == NULL)
DFHashTableRemoveInt(sheet->_defaultStyles,(int)family);
else
DFHashTableAddInt(sheet->_defaultStyles,(int)family,style);
}
int CSSSheetHeadingNumbering(CSSSheet *sheet)
{
const char **allSelectors = CSSSheetCopySelectors(sheet);
for (int i = 0; allSelectors[i]; i++) {
CSSStyle *style = CSSSheetLookupSelector(sheet,allSelectors[i],0,0);
if (style->headingLevel == 0)
continue;
if (CSSGet(CSSStyleBefore(style),"content") == NULL)
continue;
DFArray *contentParts = CSSParseContent(CSSGet(CSSStyleBefore(style),"content"));
for (size_t partIndex = 0; partIndex < DFArrayCount(contentParts); partIndex++) {
ContentPart *part = DFArrayItemAt(contentParts,partIndex);
if (part->type == ContentPartCounter) {
free(allSelectors);
DFArrayRelease(contentParts);
return 1;
}
}
DFArrayRelease(contentParts);
}
free(allSelectors);
return 0;
}
typedef struct StyleList StyleList;
struct StyleList {
CSSStyle *style;
StyleList *next;
};
static DFHashTable *getStylesByHeadingLevel(CSSSheet *sheet)
{
DFHashTable *stylesByHeadingLevel = DFHashTableNew(NULL,NULL);
const char **allSelectors = CSSSheetCopySelectors(sheet);
for (int i = 0; allSelectors[i]; i++) {
CSSStyle *style = CSSSheetLookupSelector(sheet,allSelectors[i],0,0);
if (style->headingLevel > 0) {
int headingLevel = style->headingLevel;
StyleList *item = (StyleList *)xcalloc(1,sizeof(StyleList));
item->style = CSSStyleRetain(style);
item->next = DFHashTableLookupInt(stylesByHeadingLevel,headingLevel);
DFHashTableAddInt(stylesByHeadingLevel,headingLevel,item);
}
}
free(allSelectors);
return stylesByHeadingLevel;
}
static void enableNumberingForStyle(CSSStyle *style)
{
int level = style->headingLevel;
DFBuffer *reset = DFBufferNew();
for (int after = level+1; after <= 6; after++) {
if (reset->len == 0)
DFBufferFormat(reset,"h%d",after);
else
DFBufferFormat(reset," h%d",after);
}
DFBuffer *content = DFBufferNew();
for (int upto = 1; upto <= level; upto++) {
if (content->len == 0)
DFBufferFormat(content,"counter(h%d)",upto);
else
DFBufferFormat(content," \".\" counter(h%d)",upto);
}
DFBufferFormat(content," \" \"");
CSSProperties *rule = CSSStyleRule(style);
CSSPut(rule,"counter-increment",style->elementName);
if (reset->len > 0)
CSSPut(rule,"counter-reset",reset->data);
CSSPut(CSSStyleBefore(style),"content",content->data);
style->latent = 0;
DFBufferRelease(reset);
DFBufferRelease(content);
}
static void disableNumberingForStyle(CSSStyle *style, int explicitly)
{
CSSProperties *rule = CSSStyleRule(style);
CSSProperties *before = CSSStyleBefore(style);
if (explicitly) {
// FIXME: Not covered by tests
char *increment = DFFormatString("h%d 0",style->headingLevel);
CSSPut(rule,"counter-increment",increment);
CSSPut(rule,"counter-reset","null");
CSSPut(before,"content","\"\"");
style->latent = 0;
free(increment);
}
else {
CSSPut(rule,"counter-increment",NULL);
CSSPut(rule,"counter-reset",NULL);
CSSPut(before,"content",NULL);
}
}
// FIXME: This won't work now for Word documents, where styles always have class names
// FIXME: Not covered by tests
void CSSSheetSetHeadingNumbering(CSSSheet *sheet, int enabled)
{
CSSStyle *defaultStyle = CSSSheetDefaultStyleForFamily(sheet,StyleFamilyParagraph);
if (enabled) {
DFHashTable *stylesByHeadingLevel = getStylesByHeadingLevel(sheet);
for (int level = 1; level <= 6; level++) {
StyleList *styles = DFHashTableLookupInt(stylesByHeadingLevel,level);
int haveClassless = 0;
for (StyleList *item = styles; item != NULL; item = item->next) {
if (item->style->className == NULL)
haveClassless = 1;
}
// If there exists a style at level n which has no class name, then that is the parent of all other
// heading styles. Set the numbering on that. Alternatively, if there no styles exist at level n, then
// create a new one with no class name, and set the numbering information on that.
if ((styles == NULL) || haveClassless) {
char *elementName = DFFormatString("h%d",level);
CSSStyle *style = CSSSheetLookupElement(sheet,elementName,NULL,1,0);
enableNumberingForStyle(style);
free(elementName);
}
else {
// There are multiple heading styles at level n, all of which have a class name associated with them.
// Set the numbering information on those which either have no parentId, or whose parentId is the
// default paragraph style. This caters for situations like the "HeadingXUnnumbered" styles we used
// to have, which were based on the corresponding "HeadingX" style.
for (StyleList *item = styles; item != NULL; item = item->next) {
char *parentSelector = CSSStyleCopyParent(item->style);
if ((parentSelector == NULL) || !strcmp(parentSelector,defaultStyle->selector)) {
enableNumberingForStyle(item->style);
}
else {
disableNumberingForStyle(item->style,1);
}
free(parentSelector);
}
}
StyleList *next;
for (; styles != NULL; styles = next) {
next = styles->next;
CSSStyleRelease(styles->style);
free(styles);
}
}
DFHashTableRelease(stylesByHeadingLevel);
}
else {
// Clear all numbering information on all heading styles
const char **allSelectors = CSSSheetCopySelectors(sheet);
for (int i = 0; allSelectors[i]; i++) {
CSSStyle *style = CSSSheetLookupSelector(sheet,allSelectors[i],0,0);
if (style->headingLevel > 0) {
CSSPut(CSSStyleRule(style),"counter-increment",NULL);
CSSPut(CSSStyleRule(style),"counter-reset",NULL);
CSSPut(CSSStyleBefore(style),"content",NULL);
}
}
free(allSelectors);
}
}
// FIXME: This won't work now for Word documents, where styles always have class names
// FIXME: Not covered by tests
int CSSSheetIsNumberingUsed(CSSSheet *sheet)
{
CSSStyle *style = CSSSheetLookupElement(sheet,"body",NULL,0,0);
return (CSSGet(CSSStyleRule(style),"counter-reset") != NULL);
}
int CSSStyleIsNumbered(CSSStyle *style)
{
const char *beforeContent = CSSGet(CSSStyleBefore(style),"content");
return ((beforeContent != NULL) && !DFStringEquals(beforeContent,"\"\""));
}
CSSStyle *CSSSheetGetStyleParent(CSSSheet *sheet, CSSStyle *style)
{
char *parentSelector = CSSStyleCopyParent(style);
if (parentSelector == NULL)
return NULL;;
CSSStyle *parent = CSSSheetLookupSelector(sheet,parentSelector,0,0);
free(parentSelector);
return parent;
}
CSSStyle *CSSSheetParentOfStyle(CSSSheet *sheet, CSSStyle *style)
{
// FIXME: Not covered by tests
if (style == NULL)
return NULL;;
CSSStyle *parent = CSSSheetGetStyleParent(sheet,style);
if (parent != NULL)
return parent;
if (CSSSelectorHasClassName(style->selector)) {
char *elementName = CSSSelectorCopyElementName(style->selector);
parent = CSSSheetLookupSelector(sheet,elementName,0,0);
free(elementName);
if (parent != NULL)
return parent;
}
return NULL;
}
static int CSSIsBuiltinSelector2(Tag tag, const char *elementName, const char *className)
{
switch (tag) {
case HTML_P:
case HTML_H1:
case HTML_H2:
case HTML_H3:
case HTML_H4:
case HTML_H5:
case HTML_H6:
case HTML_BLOCKQUOTE:
case HTML_PRE:
case HTML_BODY:
case HTML_FIGURE:
case HTML_FIGCAPTION:
case HTML_TABLE:
case HTML_CAPTION:
return (className == NULL);
case HTML_NAV:
if ((className != NULL) &&
(!strcmp(className,"tableofcontents") ||
!strcmp(className,"listoffigures") ||
!strcmp(className,"listoftables")))
return 1;
break;
default:
break;
}
if ((elementName != NULL) && !strcmp(elementName,"@page"))
return 1;
return 0;
}
int CSSIsBuiltinSelector(const char *selector)
{
Tag tag = CSSSelectorGetTag(selector);
char *elementName = CSSSelectorCopyElementName(selector);
char *className = CSSSelectorCopyClassName(selector);
int result = CSSIsBuiltinSelector2(tag,elementName,className);
free(elementName);
free(className);
return result;
}