mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-12-14 10:43:24 +00:00
234 lines
6.1 KiB
C
234 lines
6.1 KiB
C
/* -*- Mode: C; tab-width: 4; 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 "xp.h"
|
|
#include "libstyle.h"
|
|
#include "jsapi.h"
|
|
#include "xp_mcom.h"
|
|
#include "jsspriv.h"
|
|
#include "jssrules.h"
|
|
|
|
/*
|
|
* Helper routine to determine whether the array of simple selectors matches
|
|
* the selectors for the specified rule
|
|
*/
|
|
static JSBool
|
|
jss_IsSelectorEqual(JSContext *mc,
|
|
StyleRule *rule,
|
|
unsigned nSelectors,
|
|
jsval *selectors)
|
|
{
|
|
unsigned i;
|
|
|
|
if (rule->nSelectors == nSelectors) {
|
|
for (i = 0; i < rule->nSelectors; i++) {
|
|
/* We expect each simple selector to be a JavaScript object */
|
|
XP_ASSERT(JSVAL_IS_OBJECT(selectors[i]));
|
|
|
|
/* The simple selectors match if they point to the same StyleTag structure */
|
|
if (rule->selectors[i] != JS_GetPrivate(mc, (JSObject *)selectors[i]))
|
|
return JS_FALSE;
|
|
}
|
|
|
|
return JS_TRUE;
|
|
}
|
|
|
|
return JS_FALSE;
|
|
}
|
|
|
|
/*
|
|
* Given a list of rules and a contextual selector (array of simple
|
|
* selectors), looks to see if the contextual selector matches an
|
|
* existing rule
|
|
*
|
|
* Returns the rule if successful, and 0 otherwise
|
|
*/
|
|
StyleRule *
|
|
jss_LookupRule(JSContext *mc,
|
|
StyleRule *rules,
|
|
unsigned nSelectors,
|
|
jsval *selectors)
|
|
{
|
|
while (rules) {
|
|
if (jss_IsSelectorEqual(mc, rules, nSelectors, selectors))
|
|
return rules;
|
|
|
|
rules = rules->next;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* This routine creates and returns a new rule */
|
|
StyleRule *
|
|
jss_NewRule(JSContext *mc, unsigned argc, jsval *argv)
|
|
{
|
|
StyleRule *rule;
|
|
unsigned i;
|
|
|
|
/* Create a new rule */
|
|
rule = (StyleRule *)XP_CALLOC(1, sizeof(StyleRule));
|
|
if (!rule) {
|
|
JS_ReportOutOfMemory(mc);
|
|
return 0;
|
|
}
|
|
|
|
/* Initialize the list of selectors. While we're at it compute the specificity */
|
|
rule->nSelectors = argc;
|
|
rule->selectors = XP_CALLOC((size_t)rule->nSelectors, sizeof(StyleTag *));
|
|
rule->tag.specificity = 0;
|
|
|
|
for (i = 0; i < rule->nSelectors; i++) {
|
|
/* We expect each simple selector to be a JavaScript object */
|
|
XP_ASSERT(JSVAL_IS_OBJECT(argv[i]));
|
|
|
|
rule->selectors[i] = JS_GetPrivate(mc, (JSObject *)argv[i]);
|
|
if (!rule->selectors[i]) {
|
|
XP_ASSERT(FALSE);
|
|
XP_FREE(rule);
|
|
return 0;
|
|
}
|
|
|
|
/* The specificity of the rule is just the sum of the specificity of each selector */
|
|
rule->tag.specificity += rule->selectors[i]->specificity;
|
|
}
|
|
|
|
return rule;
|
|
}
|
|
|
|
/* Destroys a list of rules */
|
|
void
|
|
jss_DestroyRules(StyleRule *rules)
|
|
{
|
|
StyleRule *next;
|
|
|
|
while (rules) {
|
|
next = rules->next;
|
|
|
|
/* Destroy any properties associated with the tag structure */
|
|
XP_ASSERT(rules->tag.name == 0);
|
|
XP_ASSERT(rules->tag.rules == 0);
|
|
jss_DestroyProperties(rules->tag.properties);
|
|
|
|
/* Destroy the array of selectors */
|
|
if (rules->selectors)
|
|
XP_FREE(rules->selectors);
|
|
|
|
XP_FREE(rules);
|
|
rules = next;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Helper function to determine if a simple selector applies. index
|
|
* represents where in the tag stack the search should begin
|
|
*
|
|
* Returns JS_TRUE if selector applies. In this case index will be set
|
|
* to the tag stack entry where the match occured. Returns JS_FALSE if
|
|
* the selector doesn't apply
|
|
*/
|
|
static JSBool
|
|
jss_SelectorApplies(JSSContext *jc,
|
|
StyleTag *jsstag,
|
|
StyleAndTagStack *styleStack,
|
|
int32 *index)
|
|
{
|
|
TagStruct *tag;
|
|
|
|
for (tag = STYLESTACK_GetTagByIndex(styleStack, *index);
|
|
tag;
|
|
tag = STYLESTACK_GetTagByIndex(styleStack, ++(*index))) {
|
|
|
|
/* We determine if the selector applies by comparing StyleTag pointers */
|
|
if (tag->id && jc->ids && jc->ids->table) {
|
|
if ((StyleTag *)PR_HashTableLookup(jc->ids->table, tag->id) == jsstag)
|
|
return JS_TRUE;
|
|
}
|
|
|
|
if (tag->class_name && jc->classes && jc->classes->table) {
|
|
StyleObject *classes = (StyleObject *)PR_HashTableLookup(jc->classes->table, tag->class_name);
|
|
|
|
if (classes && classes->table) {
|
|
/* Check against all elements of the class, e.g. classes.punk.all */
|
|
if ((StyleTag *)PR_HashTableLookup(classes->table, "all") == jsstag)
|
|
return JS_TRUE;
|
|
|
|
/* Now check for the specified tag, e.g. classes.punk.H1 */
|
|
XP_ASSERT(tag->name);
|
|
if (tag->name && ((StyleTag *)PR_HashTableLookup(classes->table, tag->name) == jsstag))
|
|
return JS_TRUE;
|
|
}
|
|
}
|
|
|
|
XP_ASSERT(tag->name);
|
|
if (tag->name && jc->tags && jc->tags->table) {
|
|
if ((StyleTag *)PR_HashTableLookup(jc->tags->table, tag->name) == jsstag)
|
|
return JS_TRUE;
|
|
}
|
|
}
|
|
|
|
/* The selector didn't match against any element of the tag stack */
|
|
return JS_FALSE;
|
|
}
|
|
|
|
/*
|
|
* Helper function to determine if a contextual selector applies. Returns
|
|
* JS_TRUE if the selector applies and JS_FALSE otherwise
|
|
*/
|
|
static JSBool
|
|
jss_RuleApplies(JSSContext *jc, StyleRule *rule, StyleAndTagStack *styleStack)
|
|
{
|
|
int i;
|
|
int32 index = 0;
|
|
|
|
/*
|
|
* Starting with the right-most selector, check each selector to see
|
|
* if it applies
|
|
*/
|
|
for (i = (int)rule->nSelectors - 1; i >= 0; i--) {
|
|
if (!jss_SelectorApplies(jc, rule->selectors[i], styleStack, &index))
|
|
return JS_FALSE;
|
|
|
|
index++; /* continue with the next HTML element */
|
|
}
|
|
|
|
return JS_TRUE;
|
|
}
|
|
|
|
/*
|
|
* This routine takes as an argument a list of rules, a stack of open HTML
|
|
* elements, and a callback function. The callback function is called once for
|
|
* each rule that applies
|
|
*/
|
|
JSBool
|
|
jss_EnumApplicableRules(JSSContext *jc,
|
|
StyleRule *rules,
|
|
StyleAndTagStack *styleStack,
|
|
RULECALLBACK callback,
|
|
void *data)
|
|
{
|
|
while (rules) {
|
|
if (jss_RuleApplies(jc, rules, styleStack))
|
|
callback(&rules->tag, data);
|
|
|
|
rules = rules->next;
|
|
}
|
|
|
|
return JS_TRUE;
|
|
}
|