A major lift.

Quite a few uses of raw memory management had to be
struck down, in a batch.  It could have been a bit
more precise to do some of the lower dependencies
first, but I didn't start there with my analysis.
This commit is contained in:
2025-04-09 03:47:30 -04:00
parent 885a5ba629
commit 228da913c4
6 changed files with 106 additions and 112 deletions

View File

@ -15,6 +15,8 @@
#include "html_common.hh"
#include "css.hh"
#include <iostream>
using namespace dw::core::style;
void CssProperty::print () {
@ -235,8 +237,6 @@ CssSimpleSelector::CssSimpleSelector () {
}
CssSimpleSelector::~CssSimpleSelector () {
for (int i = 0; i < klass.size (); i++)
dFree (klass.get (i));
dFree (id);
dFree (pseudo);
}
@ -244,8 +244,7 @@ CssSimpleSelector::~CssSimpleSelector () {
void CssSimpleSelector::setSelect (SelectType t, const char *v) {
switch (t) {
case SELECT_CLASS:
klass.increase ();
klass.set (klass.size () - 1, dStrdup (v));
klass.push_back(v);
break;
case SELECT_PSEUDO_CLASS:
if (pseudo == NULL)
@ -275,9 +274,9 @@ bool CssSimpleSelector::match (const DoctreeNode *n) {
return false;
for (int i = 0; i < klass.size (); i++) {
bool found = false;
if (n->klass != NULL) {
for (int j = 0; j < n->klass->size (); j++) {
if (dStrAsciiCasecmp (klass.get(i), n->klass->get(j)) == 0) {
if (n->klass.has_value()) {
for (int j = 0; j < n->klass.value().size (); j++) {
if (dStrAsciiCasecmp (klass.at(i).c_str(), n->klass.value().at(j).c_str()) == 0) {
found = true;
break;
}
@ -314,14 +313,13 @@ void CssSimpleSelector::print () {
element, pseudo, id);
fprintf (stderr, "class ");
for (int i = 0; i < klass.size (); i++)
fprintf (stderr, ".%s", klass.get (i));
fprintf (stderr, ".%s", klass.at (i).c_str());
}
CssRule::CssRule (CssSelector *selector, CssPropertyList *props, int pos) {
CssRule::CssRule (std::shared_ptr< CssSelector > selector, CssPropertyList *props, int pos) {
assert (selector->size () > 0);
this->selector= selector;
this->selector->ref ();
this->props = props;
this->props->ref ();
this->pos = pos;
@ -329,7 +327,6 @@ CssRule::CssRule (CssSelector *selector, CssPropertyList *props, int pos) {
}
CssRule::~CssRule () {
selector->unref ();
props->unref ();
}
@ -351,16 +348,13 @@ void CssRule::print () {
* will be added behind the others.
* This gives later added rules more weight.
*/
void CssStyleSheet::RuleList::insert (CssRule *rule) {
increase ();
int i = size () - 1;
void
CssStyleSheet::RuleList::insert( std::shared_ptr< CssRule > rule )
{
auto where= std::upper_bound( begin( rules ), end( rules ), rule,
[&]( const auto &l, const auto &r ) { return l->specificity() < r->specificity(); } );
while (i > 0 && rule->specificity () < get (i - 1)->specificity ()) {
*getRef (i) = get (i - 1);
i--;
}
*getRef (i) = rule;
rules.insert( where, rule );
}
/**
@ -369,33 +363,35 @@ void CssStyleSheet::RuleList::insert (CssRule *rule) {
* To improve matching performance the rules are organized into
* rule lists based on the topmost simple selector of their selector.
*/
void CssStyleSheet::addRule (CssRule *rule) {
void CssStyleSheet::addRule (std::shared_ptr< CssRule > rule) {
CssSimpleSelector *top = rule->selector->top ();
RuleList *ruleList = NULL;
lout::object::ConstString *string;
std::shared_ptr< RuleList > ruleList;
std::string string;
if (top->getId ()) {
string = new lout::object::ConstString (top->getId ());
ruleList = idTable.get (string);
if (ruleList == NULL) {
ruleList = new RuleList ();
idTable.put (string, ruleList);
string = top->getId ();
if (not idTable.contains(string)) {
ruleList= std::make_shared< RuleList > ();
idTable.emplace (string, ruleList);
} else {
delete string;
ruleList= idTable.at(string);
}
} else if (top->getClass () && top->getClass ()->size () > 0) {
string = new lout::object::ConstString (top->getClass ()->get (0));
ruleList = classTable.get (string);
if (ruleList == NULL) {
ruleList = new RuleList;
classTable.put (string, ruleList);
assert( ruleList );
} else if (top->getClass ().size () > 0) {
string = top->getClass ().at (0);
if (not classTable.contains(string)) {
ruleList = std::make_shared< RuleList >();
classTable.emplace (string, ruleList);
} else {
delete string;
ruleList= classTable.at(string);
}
assert( ruleList );
} else if (top->getElement () >= 0 && top->getElement () < ntags) {
ruleList = &elementTable[top->getElement ()];
ruleList = elementTable[top->getElement ()];
assert( ruleList );
} else if (top->getElement () == CssSimpleSelector::ELEMENT_ANY) {
ruleList = &anyTable;
ruleList = anyTable;
assert( ruleList );
}
if (ruleList) {
@ -404,7 +400,6 @@ void CssStyleSheet::addRule (CssRule *rule) {
requiredMatchCache = rule->selector->getRequiredMatchCache ();
} else {
assert (top->getElement () == CssSimpleSelector::ELEMENT_NONE);
delete rule;
}
}
@ -417,13 +412,11 @@ void CssStyleSheet::addRule (CssRule *rule) {
void CssStyleSheet::apply (CssPropertyList *props, Doctree *docTree,
const DoctreeNode *node, MatchCache *matchCache) const {
static const int maxLists = 32;
const RuleList *ruleList[maxLists];
std::array< std::shared_ptr< const RuleList >, maxLists > ruleList;
int numLists = 0, index[maxLists] = {0};
if (node->id) {
lout::object::ConstString idString (node->id);
ruleList[numLists] = idTable.get (&idString);
ruleList[numLists] = idTable.contains( node->id ) ? idTable.at (node->id) : nullptr;
if (ruleList[numLists])
numLists++;
}
@ -435,19 +428,19 @@ void CssStyleSheet::apply (CssPropertyList *props, Doctree *docTree,
break;
}
lout::object::ConstString classString (node->klass->get (i));
const std::string classString= node->klass.value().at( i );
ruleList[numLists] = classTable.get (&classString);
ruleList[numLists] = classTable.contains( classString ) ? classTable.at (classString) : nullptr;
if (ruleList[numLists])
numLists++;
}
}
ruleList[numLists] = &elementTable[node->element];
ruleList[numLists] = elementTable[node->element];
if (ruleList[numLists])
numLists++;
ruleList[numLists] = &anyTable;
ruleList[numLists] = anyTable;
if (ruleList[numLists])
numLists++;
@ -461,21 +454,21 @@ void CssStyleSheet::apply (CssPropertyList *props, Doctree *docTree,
int minSpecIndex = -1;
for (int i = 0; i < numLists; i++) {
const RuleList *rl = ruleList[i];
std::shared_ptr< const RuleList > rl = ruleList[i];
if (rl && rl->size () > index[i] &&
(rl->get(index[i])->specificity () < minSpec ||
(rl->get(index[i])->specificity () == minSpec &&
rl->get(index[i])->position () < minPos))) {
(rl->at(index[i])->specificity () < minSpec ||
(rl->at(index[i])->specificity () == minSpec &&
rl->at(index[i])->position () < minPos))) {
minSpec = rl->get(index[i])->specificity ();
minPos = rl->get(index[i])->position ();
minSpec = rl->at(index[i])->specificity ();
minPos = rl->at(index[i])->position ();
minSpecIndex = i;
}
}
if (minSpecIndex >= 0) {
CssRule *rule = ruleList[minSpecIndex]->get (index[minSpecIndex]);
auto &rule = ruleList[minSpecIndex]->at (index[minSpecIndex]);
rule->apply(props, docTree, node, matchCache);
index[minSpecIndex]++;
} else {
@ -526,26 +519,25 @@ void CssContext::apply (CssPropertyList *props, Doctree *docTree,
sheet[CSS_PRIMARY_USER_IMPORTANT].apply (props, docTree, node, &matchCache);
}
void CssContext::addRule (CssSelector *sel, CssPropertyList *props,
void CssContext::addRule (std::shared_ptr< CssSelector > sel, CssPropertyList *props,
CssPrimaryOrder order) {
if (props->size () > 0) {
CssRule *rule = new CssRule (sel, props, pos++);
auto rule = std::make_unique< CssRule >(sel, props, pos++);
if ((order == CSS_PRIMARY_AUTHOR ||
order == CSS_PRIMARY_AUTHOR_IMPORTANT) &&
!rule->isSafe ()) {
_MSG_WARN ("Ignoring unsafe author style that might reveal browsing history\n");
delete rule;
} else {
rule->selector->setMatchCacheOffset(matchCache.size ());
if (rule->selector->getRequiredMatchCache () > matchCache.size ())
matchCache.setSize (rule->selector->getRequiredMatchCache (), -1);
if (order == CSS_PRIMARY_USER_AGENT) {
userAgentSheet.addRule (rule);
userAgentSheet.addRule (std::move(rule));
} else {
sheet[order].addRule (rule);
sheet[order].addRule (std::move(rule));
}
}
}

View File

@ -13,10 +13,18 @@
#ifndef __CSS_HH__
#define __CSS_HH__
#include <string>
#include <memory>
#include <vector>
#include <array>
#include <map>
#include "dw/core.hh"
#include "doctree.hh"
#include "html.hh"
#include <Alepha/Utility/evaluate.h>
/* Origin and weight. Used only internally.*/
typedef enum {
CSS_PRIMARY_USER_AGENT,
@ -356,7 +364,7 @@ class CssSimpleSelector {
private:
int element;
char *pseudo, *id;
lout::misc::SimpleVector <char *> klass;
std::vector< std::string > klass;
public:
enum {
@ -375,7 +383,7 @@ class CssSimpleSelector {
~CssSimpleSelector ();
inline void setElement (int e) { element = e; };
void setSelect (SelectType t, const char *v);
inline lout::misc::SimpleVector <char *> *getClass () { return &klass; };
inline std::vector< std::string > &getClass () { return klass; };
inline const char *getPseudoClass () { return pseudo; };
inline const char *getId () { return id; };
inline int getElement () { return element; };
@ -438,8 +446,6 @@ class CssSelector {
int specificity ();
bool checksPseudoClass ();
void print ();
inline void ref () { refCount++; }
inline void unref () { if (--refCount == 0) delete this; }
};
/**
@ -453,9 +459,9 @@ class CssRule {
int spec, pos;
public:
CssSelector *selector;
std::shared_ptr< CssSelector > selector;
CssRule (CssSelector *selector, CssPropertyList *props, int pos);
CssRule (std::shared_ptr< CssSelector > selector, CssPropertyList *props, int pos);
~CssRule ();
void apply (CssPropertyList *props, Doctree *docTree,
@ -475,20 +481,19 @@ class CssRule {
*/
class CssStyleSheet {
private:
class RuleList : public lout::misc::SimpleVector <CssRule*>,
public lout::object::Object {
public:
RuleList () : lout::misc::SimpleVector <CssRule*> (1) {};
~RuleList () {
for (int i = 0; i < size (); i++)
delete get (i);
};
class RuleList
{
private:
std::vector< std::shared_ptr< CssRule > > rules;
void insert (CssRule *rule);
inline bool equals (lout::object::Object *other) {
return this == other;
};
inline int hashValue () { return (intptr_t) this; };
public:
void insert( std::shared_ptr< CssRule > rule );
constexpr std::size_t size() const noexcept { return rules.size(); }
constexpr bool empty() const noexcept { return rules.empty(); }
template< typename Self >
auto &&at( this Self &&self, const std::size_t index ) { return self.rules.at( index ); }
};
class RuleMap : public lout::container::typed::HashTable
@ -500,13 +505,21 @@ class CssStyleSheet {
static const int ntags = HTML_NTAGS;
RuleList elementTable[ntags], anyTable;
RuleMap idTable, classTable;
using ElementTable= std::array< std::shared_ptr< RuleList >, ntags >;
ElementTable elementTable= Alepha::Utility::evaluate <=[]
{
ElementTable rv;
std::generate( begin( rv ), end( rv ), &std::make_unique< RuleList > );
return rv;
};
std::shared_ptr< RuleList > anyTable= std::make_unique< RuleList >();
std::map< std::string, std::shared_ptr< RuleList > > idTable;
std::map< std::string, std::shared_ptr< RuleList > > classTable;
int requiredMatchCache;
public:
CssStyleSheet () { requiredMatchCache = 0; }
void addRule (CssRule *rule);
void addRule (std::shared_ptr< CssRule > rule);
void apply (CssPropertyList *props, Doctree *docTree,
const DoctreeNode *node, MatchCache *matchCache) const;
int getRequiredMatchCache () { return requiredMatchCache; }
@ -525,7 +538,7 @@ class CssContext {
public:
CssContext ();
void addRule (CssSelector *sel, CssPropertyList *props,
void addRule (std::shared_ptr< CssSelector > sel, CssPropertyList *props,
CssPrimaryOrder order);
void apply (CssPropertyList *props,
Doctree *docTree, DoctreeNode *node,

View File

@ -1476,14 +1476,13 @@ bool CssParser::parseSimpleSelector(CssSimpleSelector *selector)
return true;
}
CssSelector *CssParser::parseSelector()
std::unique_ptr< CssSelector > CssParser::parseSelector()
{
CssSelector *selector = new CssSelector ();
auto selector = std::make_unique< CssSelector >();
while (true) {
if (! parseSimpleSelector (selector->top ())) {
delete selector;
selector = NULL;
selector = nullptr;
break;
}
@ -1499,8 +1498,7 @@ CssSelector *CssParser::parseSelector()
} else if (ttype != CSS_TK_END && spaceSeparated) {
selector->addSimpleSelector (CssSelector::COMB_DESCENDANT);
} else {
delete selector;
selector = NULL;
selector = nullptr;
break;
}
}
@ -1515,17 +1513,15 @@ CssSelector *CssParser::parseSelector()
void CssParser::parseRuleset()
{
std::vector < CssSelector * > list;
std::vector < std::shared_ptr< CssSelector > > list;
CssPropertyList *props, *importantProps;
CssSelector *selector;
while (true) {
selector = parseSelector();
auto selector= parseSelector();
if (selector) {
selector->ref();
list.push_back(selector);
list.push_back(std::move( selector ));
}
// \todo dump whole ruleset in case of parse error as required by CSS 2.1
@ -1559,7 +1555,7 @@ void CssParser::parseRuleset()
}
for (std::size_t i = 0; i < list.size(); i++) {
CssSelector *s = list.at(i);
const auto &s = list.at(i);
if (origin == CSS_ORIGIN_USER_AGENT) {
context->addRule(s, props, CSS_PRIMARY_USER_AGENT);
@ -1570,8 +1566,6 @@ void CssParser::parseRuleset()
context->addRule(s, props, CSS_PRIMARY_AUTHOR);
context->addRule(s, importantProps, CSS_PRIMARY_AUTHOR_IMPORTANT);
}
s->unref();
}
props->unref();

View File

@ -43,7 +43,7 @@ class CssParser {
char *parseUrl();
void parseImport(DilloHtml *html);
void parseMedia();
CssSelector *parseSelector();
std::unique_ptr< CssSelector > parseSelector();
void parseRuleset();
void ignoreBlock();
void ignoreStatement();

View File

@ -3,6 +3,9 @@
#include "lout/misc.hh"
#include <string>
#include <vector>
class DoctreeNode {
public:
DoctreeNode *parent;
@ -10,7 +13,7 @@ class DoctreeNode {
DoctreeNode *lastChild;
int num; // unique ascending id
int element;
lout::misc::SimpleVector<char*> *klass;
std::optional< std::vector< std::string > > klass;
const char *pseudo;
const char *id;
@ -18,7 +21,6 @@ class DoctreeNode {
parent = NULL;
sibling = NULL;
lastChild = NULL;
klass = NULL;
pseudo = NULL;
id = NULL;
element = 0;
@ -31,11 +33,6 @@ class DoctreeNode {
lastChild = lastChild->sibling;
delete n;
}
if (klass) {
for (int i = 0; i < klass->size (); i++)
dFree (klass->get(i));
delete klass;
}
}
};

View File

@ -165,18 +165,16 @@ void StyleEngine::setId (const char *id) {
/**
* \brief split a string at sep chars and return a SimpleVector of strings
*/
static lout::misc::SimpleVector<char *> *splitStr (const char *str, char sep) {
static std::vector< std::string > splitStr (const char *str, char sep) {
const char *p1 = NULL;
lout::misc::SimpleVector<char *> *list =
new lout::misc::SimpleVector<char *> (1);
std::vector< std::string > list;
for (;; str++) {
if (*str != '\0' && *str != sep) {
if (!p1)
p1 = str;
} else if (p1) {
list->increase ();
list->set (list->size () - 1, dStrndup (p1, str - p1));
list.emplace_back( p1, str - p1 );
p1 = NULL;
}
@ -189,7 +187,7 @@ static lout::misc::SimpleVector<char *> *splitStr (const char *str, char sep) {
void StyleEngine::setClass (const char *klass) {
DoctreeNode *dn = doctree->top ();
assert (dn->klass == NULL);
assert (not dn->klass.has_value());
dn->klass = splitStr (klass, ' ');
}