KHtml

css_base.cpp
1 /*
2  * This file is part of the DOM implementation for KDE.
3  *
4  * Copyright 1999-2003 Lars Knoll ([email protected])
5  * Copyright 1999 Waldo Bastian ([email protected])
6  * Copyright 2001 Andreas Schlapbach ([email protected])
7  * Copyright 2001-2003 Dirk Mueller ([email protected])
8  * Copyright 2002 Apple Computer, Inc.
9  * Copyright 2004 Allan Sandfeld Jensen ([email protected])
10  *
11  * This library is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU Library General Public
13  * License as published by the Free Software Foundation; either
14  * version 2 of the License, or (at your option) any later version.
15  *
16  * This library is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19  * Library General Public License for more details.
20  *
21  * You should have received a copy of the GNU Library General Public License
22  * along with this library; see the file COPYING.LIB. If not, write to
23  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
24  * Boston, MA 02110-1301, USA.
25  */
26 
27 //#define CSS_DEBUG
28 
29 #include "css_base.h"
30 
31 #include <assert.h>
32 #include "khtml_debug.h"
33 
34 #ifdef CSS_DEBUG
35 #include "cssproperties.h"
36 #endif
37 
38 #include "css_stylesheetimpl.h"
39 #include <xml/dom_docimpl.h>
40 #include "css_valueimpl.h"
41 using namespace DOM;
42 
43 void StyleBaseImpl::checkLoaded() const
44 {
45  if (m_parent) {
46  m_parent->checkLoaded();
47  }
48 }
49 
50 void StyleBaseImpl::checkPending() const
51 {
52  if (m_parent) {
53  m_parent->checkPending();
54  }
55 }
56 
57 StyleSheetImpl *StyleBaseImpl::stylesheet()
58 {
59  StyleBaseImpl *b = this;
60  while (b && !b->isStyleSheet()) {
61  b = b->m_parent;
62  }
63  return static_cast<StyleSheetImpl *>(b);
64 }
65 
66 QUrl StyleBaseImpl::baseURL()
67 {
68  // try to find the style sheet. If found look for its url.
69  // If it has none, look for the parentsheet, or the parentNode and
70  // try to find out about their url
71 
72  StyleSheetImpl *sheet = stylesheet();
73 
74  if (!sheet) {
75  return QUrl();
76  }
77 
78  if (!sheet->href().isNull()) {
79  return QUrl(sheet->href().string());
80  }
81 
82  // find parent
83  if (sheet->parent()) {
84  return sheet->parent()->baseURL();
85  }
86 
87  if (!sheet->ownerNode()) {
88  return QUrl();
89  }
90 
91  return sheet->ownerNode()->document()->baseURL();
92 }
93 
94 void StyleBaseImpl::setParsedValue(int propId, const CSSValueImpl *parsedValue,
95  bool important, QList<CSSProperty *> *propList)
96 {
97  QMutableListIterator<CSSProperty *> propIt(*propList);
98  propIt.toBack(); // just remove the top one - not sure what should happen if we have multiple instances of the property
99  CSSProperty *p;
100  while (propIt.hasPrevious()) {
101  p = propIt.previous();
102  if (p->m_id == propId && p->m_important == important) {
103  delete propIt.value();
104  propIt.remove();
105  break;
106  }
107  }
108 
109  CSSProperty *prop = new CSSProperty();
110  prop->m_id = propId;
111  prop->setValue(const_cast<CSSValueImpl *>(parsedValue));
112  prop->m_important = important;
113 
114  propList->append(prop);
115 #ifdef CSS_DEBUG
116  qCDebug(KHTML_LOG) << "added property: " << getPropertyName(propId).string()
117  // non implemented yet << ", value: " << parsedValue->cssText().string()
118  << " important: " << prop->m_important;
119 #endif
120 }
121 
122 // ------------------------------------------------------------------------------
123 
124 StyleListImpl::~StyleListImpl()
125 {
126  StyleBaseImpl *n;
127 
128  if (!m_lstChildren) {
129  return;
130  }
131 
132  QListIterator<StyleBaseImpl *> it(*m_lstChildren);
133  while (it.hasNext()) {
134  n = it.next();
135  n->setParent(nullptr);
136  if (!n->refCount()) {
137  delete n;
138  }
139  }
140  delete m_lstChildren;
141 }
142 
143 // --------------------------------------------------------------------------------
144 
145 void CSSSelector::print(void)
146 {
147  // qCDebug(KHTML_LOG) << "[Selector: tag = " << QString::number(makeId(tagNamespace.id(), tagLocalName.id()),16) << ", attr = \"" << makeId(attrNamespace.id(), attrLocalName.id()) << "\", match = \"" << match
148  // << "\" value = \"" << value.string().string().toLatin1().constData() << "\" relation = " << (int)relation
149  // << "]";
150  if (tagHistory) {
151  tagHistory->print();
152  }
153  // qCDebug(KHTML_LOG) << " specificity = " << specificity();
154 }
155 
156 unsigned int CSSSelector::specificity() const
157 {
158 
159  int s = ((tagLocalName.id() == anyLocalName) ? 0 : 1);
160  switch (match) {
161  case Id:
162  s += 0x10000;
163  break;
164  case Exact:
165  case Set:
166  case List:
167  case Class:
168  case Hyphen:
169  case PseudoClass:
170  case PseudoElement:
171  case Contain:
172  case Begin:
173  case End:
174  s += 0x100;
175  case None:
176  break;
177  }
178  if (tagHistory) {
179  s += tagHistory->specificity();
180  }
181  // make sure it doesn't overflow
182  return s & 0xffffff;
183 }
184 
185 void CSSSelector::extractPseudoType() const
186 {
187  if (match != PseudoClass && match != PseudoElement) {
188  return;
189  }
190  _pseudoType = PseudoOther;
191  bool element = false;
192  bool compat = false;
193  if (!value.isEmpty()) {
194  value = value.string().lower();
195  switch (value[0].unicode()) {
196  case '-':
197  if (value == "-khtml-replaced") {
198  _pseudoType = PseudoReplaced;
199  } else if (value == "-khtml-marker") {
200  _pseudoType = PseudoMarker;
201  }
202  element = true;
203  break;
204  case 'a':
205  if (value == "active") {
206  _pseudoType = PseudoActive;
207  } else if (value == "after") {
208  _pseudoType = PseudoAfter;
209  element = compat = true;
210  }
211  break;
212  case 'b':
213  if (value == "before") {
214  _pseudoType = PseudoBefore;
215  element = compat = true;
216  }
217  break;
218  case 'c':
219  if (value == "checked") {
220  _pseudoType = PseudoChecked;
221  } else if (value == "contains(") {
222  _pseudoType = PseudoContains;
223  }
224  break;
225  case 'd':
226  if (value == "disabled") {
227  _pseudoType = PseudoDisabled;
228  }
229  if (value == "default") {
230  _pseudoType = PseudoDefault;
231  }
232  break;
233  case 'e':
234  if (value == "empty") {
235  _pseudoType = PseudoEmpty;
236  } else if (value == "enabled") {
237  _pseudoType = PseudoEnabled;
238  }
239  break;
240  case 'f':
241  if (value == "first-child") {
242  _pseudoType = PseudoFirstChild;
243  } else if (value == "first-letter") {
244  _pseudoType = PseudoFirstLetter;
245  element = compat = true;
246  } else if (value == "first-line") {
247  _pseudoType = PseudoFirstLine;
248  element = compat = true;
249  } else if (value == "first-of-type") {
250  _pseudoType = PseudoFirstOfType;
251  } else if (value == "focus") {
252  _pseudoType = PseudoFocus;
253  }
254  break;
255  case 'h':
256  if (value == "hover") {
257  _pseudoType = PseudoHover;
258  }
259  break;
260  case 'i':
261  if (value == "indeterminate") {
262  _pseudoType = PseudoIndeterminate;
263  }
264  break;
265  case 'l':
266  if (value == "link") {
267  _pseudoType = PseudoLink;
268  } else if (value == "lang(") {
269  _pseudoType = PseudoLang;
270  } else if (value == "last-child") {
271  _pseudoType = PseudoLastChild;
272  } else if (value == "last-of-type") {
273  _pseudoType = PseudoLastOfType;
274  }
275  break;
276  case 'n':
277  if (value == "not(") {
278  _pseudoType = PseudoNot;
279  } else if (value == "nth-child(") {
280  _pseudoType = PseudoNthChild;
281  } else if (value == "nth-last-child(") {
282  _pseudoType = PseudoNthLastChild;
283  } else if (value == "nth-of-type(") {
284  _pseudoType = PseudoNthOfType;
285  } else if (value == "nth-last-of-type(") {
286  _pseudoType = PseudoNthLastOfType;
287  }
288  break;
289  case 'o':
290  if (value == "only-child") {
291  _pseudoType = PseudoOnlyChild;
292  } else if (value == "only-of-type") {
293  _pseudoType = PseudoOnlyOfType;
294  }
295  break;
296  case 'r':
297  if (value == "root") {
298  _pseudoType = PseudoRoot;
299  } else if (value == "read-only") {
300  _pseudoType = PseudoReadOnly;
301  } else if (value == "read-write") {
302  _pseudoType = PseudoReadWrite;
303  }
304  break;
305  case 's':
306  if (value == "selection") {
307  _pseudoType = PseudoSelection;
308  element = true;
309  }
310  break;
311  case 't':
312  if (value == "target") {
313  _pseudoType = PseudoTarget;
314  }
315  break;
316  case 'v':
317  if (value == "visited") {
318  _pseudoType = PseudoVisited;
319  }
320  break;
321  }
322  }
323  if (match == PseudoClass && element)
324  if (!compat) {
325  _pseudoType = PseudoOther;
326  } else {
327  match = PseudoElement;
328  }
329  else if (match == PseudoElement && !element) {
330  _pseudoType = PseudoOther;
331  }
332 }
333 
334 bool CSSSelector::operator == (const CSSSelector &other) const
335 {
336  const CSSSelector *sel1 = this;
337  const CSSSelector *sel2 = &other;
338 
339  while (sel1 && sel2) {
340  //assert(sel1->_pseudoType != PseudoNotParsed);
341  //assert(sel2->_pseudoType != PseudoNotParsed);
342  if (sel1->tagLocalName.id() != sel2->tagLocalName.id() || sel1->attrLocalName.id() != sel2->attrLocalName.id() ||
343  sel1->tagNamespace.id() != sel2->tagNamespace.id() || sel1->attrNamespace.id() != sel2->attrNamespace.id() ||
344  sel1->relation != sel2->relation || sel1->match != sel2->match ||
345  sel1->value != sel2->value ||
346  sel1->pseudoType() != sel2->pseudoType() ||
347  sel1->string_arg != sel2->string_arg) {
348  return false;
349  }
350  sel1 = sel1->tagHistory;
351  sel2 = sel2->tagHistory;
352  }
353  if (sel1 || sel2) {
354  return false;
355  }
356  return true;
357 }
358 
359 DOMString CSSSelector::selectorText() const
360 {
361  // FIXME: Support namespaces when dumping the selector text. This requires preserving
362  // the original namespace prefix used. Ugh. -dwh
363  DOMString str;
364  const CSSSelector *cs = this;
365  quint16 tag = cs->tagLocalName.id();
366  if (tag == anyLocalName && cs->match == CSSSelector::None) {
367  str = "*";
368  } else if (tag != anyLocalName) {
369  str = LocalName::fromId(tag).toString();
370  }
371 
372  const CSSSelector *op = nullptr;
373  while (true) {
374  if (makeId(cs->attrNamespace.id(), cs->attrLocalName.id()) == ATTR_ID && cs->match == CSSSelector::Id) {
375  str += "#";
376  str += cs->value;
377  } else if (cs->match == CSSSelector::Class) {
378  str += ".";
379  str += cs->value;
380  } else if (cs->match == CSSSelector::PseudoClass) {
381  str += ":";
382  str += cs->value;
383  if (!cs->string_arg.isEmpty()) { // e.g :nth-child(...)
384  str += cs->string_arg;
385  str += ")";
386  } else if (cs->simpleSelector && !op) { // :not(...)
387  op = cs;
388  cs = cs->simpleSelector;
389  continue;
390  }
391  } else if (cs->match == CSSSelector::PseudoElement) {
392  str += "::";
393  str += cs->value;
394  }
395  // optional attribute
396  else if (cs->attrLocalName.id()) {
397  DOMString attrName = LocalName::fromId(cs->attrLocalName.id()).toString();
398  str += "[";
399  str += attrName;
400  switch (cs->match) {
401  case CSSSelector::Exact:
402  str += "=";
403  break;
404  case CSSSelector::Set:
405  break;
406  case CSSSelector::List:
407  str += "~=";
408  break;
409  case CSSSelector::Hyphen:
410  str += "|=";
411  break;
412  case CSSSelector::Begin:
413  str += "^=";
414  break;
415  case CSSSelector::End:
416  str += "$=";
417  break;
418  case CSSSelector::Contain:
419  str += "*=";
420  break;
421  default:
422  qCWarning(KHTML_LOG) << "Unhandled case in CSSStyleRuleImpl::selectorText : match=" << cs->match;
423  }
424  if (cs->match != CSSSelector::Set) {
425  str += "\"";
426  str += cs->value;
427  str += "\"";
428  }
429  str += "]";
430  }
431  if (op && !cs->tagHistory) {
432  cs = op;
433  op = nullptr;
434  str += ")";
435  }
436 
437  if ((cs->relation != CSSSelector::SubSelector && !op) || !cs->tagHistory) {
438  break;
439  }
440  cs = cs->tagHistory;
441  }
442 
443  if (cs->tagHistory) {
444  DOMString tagHistoryText = cs->tagHistory->selectorText();
445  if (cs->relation == DirectAdjacent) {
446  str = tagHistoryText + DOMString(" + ") + str;
447  } else if (cs->relation == IndirectAdjacent) {
448  str = tagHistoryText + DOMString(" ~ ") + str;
449  } else if (cs->relation == Child) {
450  str = tagHistoryText + DOMString(" > ") + str;
451  } else { // Descendant
452  str = tagHistoryText + DOMString(" ") + str;
453  }
454  }
455  return str;
456 }
457 
458 // ----------------------------------------------------------------------------
void append(const T &value)
This class implements the basic string we use in the DOM.
Definition: dom_string.h:44
KCOREADDONS_EXPORT Result match(QStringView pattern, QStringView str)
int64_t Id
This library provides a full-featured HTML parser and widget.
char * toString(const T &value)
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Tue Oct 26 2021 22:47:59 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.