/* * This file is part of the DOM implementation for KDE. * * Copyright (C) 1999 Antti Koivisto (koivisto@kde.org) * Copyright (C) 1999-2003 Lars Knoll (knoll@kde.org) * Copyright (C) 2002-2003 Dirk Mueller (mueller@kde.org) * Copyright (C) 2002-2005 Apple Computer, Inc. * Copyright (C) 2005 Allan Sandfeld Jensen (kde@carewolf.com) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. * */ #include "xml/dom_stringimpl.h" #include "css/cssstyleselector.h" #include "css/css_valueimpl.h" #include "render_style.h" #include "kdebug.h" using namespace khtml; using namespace DOM; /* CSS says Fixed for the default padding value, but we treat variable as 0 padding anyways, and like * this is works fine for table paddings aswell */ StyleSurroundData::StyleSurroundData() : margin( Fixed ), padding( Variable ) { } StyleSurroundData::StyleSurroundData(const StyleSurroundData& o ) : Shared(), offset( o.offset ), margin( o.margin ), padding( o.padding ), border( o.border ) { } bool StyleSurroundData::operator==(const StyleSurroundData& o) const { return offset==o.offset && margin==o.margin && padding==o.padding && border==o.border; } StyleBoxData::StyleBoxData() : z_index( 0 ), z_auto( true ) { min_width = min_height = RenderStyle::initialMinSize(); max_width = max_height = RenderStyle::initialMaxSize(); box_sizing = RenderStyle::initialBoxSizing(); } StyleBoxData::StyleBoxData(const StyleBoxData& o ) : Shared(), width( o.width ), height( o.height ), min_width( o.min_width ), max_width( o.max_width ), min_height ( o.min_height ), max_height( o.max_height ), box_sizing( o.box_sizing), z_index( o.z_index ), z_auto( o.z_auto ) { } bool StyleBoxData::operator==(const StyleBoxData& o) const { return width == o.width && height == o.height && min_width == o.min_width && max_width == o.max_width && min_height == o.min_height && max_height == o.max_height && box_sizing == o.box_sizing && z_index == o.z_index && z_auto == o.z_auto; } StyleVisualData::StyleVisualData() : textDecoration(RenderStyle::initialTextDecoration()), palette( TQApplication::palette() ) { } StyleVisualData::~StyleVisualData() { } StyleVisualData::StyleVisualData(const StyleVisualData& o ) : Shared(), clip( o.clip ), textDecoration(o.textDecoration), palette( o.palette ) { } BackgroundLayer::BackgroundLayer() :m_image(RenderStyle::initialBackgroundImage()), m_bgAttachment(RenderStyle::initialBackgroundAttachment()), m_bgClip(RenderStyle::initialBackgroundClip()), m_bgOrigin(RenderStyle::initialBackgroundOrigin()), m_bgRepeat(RenderStyle::initialBackgroundRepeat()), m_backgroundSize(RenderStyle::initialBackgroundSize()), m_next(0) { m_imageSet = m_attachmentSet = m_clipSet = m_originSet = m_repeatSet = m_xPosSet = m_yPosSet = m_backgroundSizeSet = false; } BackgroundLayer::BackgroundLayer(const BackgroundLayer& o) { m_next = o.m_next ? new BackgroundLayer(*o.m_next) : 0; m_image = o.m_image; m_xPosition = o.m_xPosition; m_yPosition = o.m_yPosition; m_bgAttachment = o.m_bgAttachment; m_bgClip = o.m_bgClip; m_bgOrigin = o.m_bgOrigin; m_bgRepeat = o.m_bgRepeat; m_backgroundSize = o.m_backgroundSize; m_imageSet = o.m_imageSet; m_attachmentSet = o.m_attachmentSet; m_clipSet = o.m_clipSet; m_originSet = o.m_originSet; m_repeatSet = o.m_repeatSet; m_xPosSet = o.m_xPosSet; m_yPosSet = o.m_yPosSet; m_backgroundSizeSet = o.m_backgroundSizeSet; } BackgroundLayer::~BackgroundLayer() { delete m_next; } BackgroundLayer& BackgroundLayer::operator=(const BackgroundLayer& o) { if (m_next != o.m_next) { delete m_next; m_next = o.m_next ? new BackgroundLayer(*o.m_next) : 0; } m_image = o.m_image; m_xPosition = o.m_xPosition; m_yPosition = o.m_yPosition; m_bgAttachment = o.m_bgAttachment; m_bgClip = o.m_bgClip; m_bgOrigin = o.m_bgOrigin; m_bgRepeat = o.m_bgRepeat; m_backgroundSize = o.m_backgroundSize; m_imageSet = o.m_imageSet; m_attachmentSet = o.m_attachmentSet; m_originSet = o.m_originSet; m_repeatSet = o.m_repeatSet; m_xPosSet = o.m_xPosSet; m_yPosSet = o.m_yPosSet; m_backgroundSizeSet = o.m_backgroundSizeSet; return *this; } bool BackgroundLayer::operator==(const BackgroundLayer& o) const { return m_image == o.m_image && m_xPosition == o.m_xPosition && m_yPosition == o.m_yPosition && m_bgAttachment == o.m_bgAttachment && m_bgClip == o.m_bgClip && m_bgOrigin == o.m_bgOrigin && m_bgRepeat == o.m_bgRepeat && m_backgroundSize.width == o.m_backgroundSize.width && m_backgroundSize.height == o.m_backgroundSize.height && m_imageSet == o.m_imageSet && m_attachmentSet == o.m_attachmentSet && m_repeatSet == o.m_repeatSet && m_xPosSet == o.m_xPosSet && m_yPosSet == o.m_yPosSet && m_backgroundSizeSet == o.m_backgroundSizeSet && ((m_next && o.m_next) ? *m_next == *o.m_next : m_next == o.m_next); } void BackgroundLayer::fillUnsetProperties() { BackgroundLayer* curr; for (curr = this; curr && curr->isBackgroundImageSet(); curr = curr->next()); if (curr && curr != this) { // We need to fill in the remaining values with the pattern specified. for (BackgroundLayer* pattern = this; curr; curr = curr->next()) { curr->m_image = pattern->m_image; pattern = pattern->next(); if (pattern == curr || !pattern) pattern = this; } } for (curr = this; curr && curr->isBackgroundXPositionSet(); curr = curr->next()); if (curr && curr != this) { // We need to fill in the remaining values with the pattern specified. for (BackgroundLayer* pattern = this; curr; curr = curr->next()) { curr->m_xPosition = pattern->m_xPosition; pattern = pattern->next(); if (pattern == curr || !pattern) pattern = this; } } for (curr = this; curr && curr->isBackgroundYPositionSet(); curr = curr->next()); if (curr && curr != this) { // We need to fill in the remaining values with the pattern specified. for (BackgroundLayer* pattern = this; curr; curr = curr->next()) { curr->m_yPosition = pattern->m_yPosition; pattern = pattern->next(); if (pattern == curr || !pattern) pattern = this; } } for (curr = this; curr && curr->isBackgroundAttachmentSet(); curr = curr->next()); if (curr && curr != this) { // We need to fill in the remaining values with the pattern specified. for (BackgroundLayer* pattern = this; curr; curr = curr->next()) { curr->m_bgAttachment = pattern->m_bgAttachment; pattern = pattern->next(); if (pattern == curr || !pattern) pattern = this; } } for (curr = this; curr && curr->isBackgroundClipSet(); curr = curr->next()); if (curr && curr != this) { // We need to fill in the remaining values with the pattern specified. for (BackgroundLayer* pattern = this; curr; curr = curr->next()) { curr->m_bgClip = pattern->m_bgClip; pattern = pattern->next(); if (pattern == curr || !pattern) pattern = this; } } for (curr = this; curr && curr->isBackgroundOriginSet(); curr = curr->next()); if (curr && curr != this) { // We need to fill in the remaining values with the pattern specified. for (BackgroundLayer* pattern = this; curr; curr = curr->next()) { curr->m_bgOrigin = pattern->m_bgOrigin; pattern = pattern->next(); if (pattern == curr || !pattern) pattern = this; } } for (curr = this; curr && curr->isBackgroundRepeatSet(); curr = curr->next()); if (curr && curr != this) { // We need to fill in the remaining values with the pattern specified. for (BackgroundLayer* pattern = this; curr; curr = curr->next()) { curr->m_bgRepeat = pattern->m_bgRepeat; pattern = pattern->next(); if (pattern == curr || !pattern) pattern = this; } } for (curr = this; curr && curr->isBackgroundSizeSet(); curr = curr->next()); if (curr && curr != this) { // We need to fill in the remaining values with the pattern specified. for (BackgroundLayer* pattern = this; curr; curr = curr->next()) { curr->m_backgroundSize = pattern->m_backgroundSize; pattern = pattern->next(); if (pattern == curr || !pattern) pattern = this; } } } void BackgroundLayer::cullEmptyLayers() { BackgroundLayer *next; for (BackgroundLayer *p = this; p; p = next) { next = p->m_next; if (next && !next->isBackgroundImageSet() && !next->isBackgroundXPositionSet() && !next->isBackgroundYPositionSet() && !next->isBackgroundAttachmentSet() && !next->isBackgroundClipSet() && !next->isBackgroundOriginSet() && !next->isBackgroundRepeatSet() && !next->isBackgroundSizeSet()) { delete next; p->m_next = 0; break; } } } StyleBackgroundData::StyleBackgroundData() {} StyleBackgroundData::StyleBackgroundData(const StyleBackgroundData& o) : Shared(), m_background(o.m_background), m_outline(o.m_outline) {} bool StyleBackgroundData::operator==(const StyleBackgroundData& o) const { return m_background == o.m_background && m_color == o.m_color && m_outline == o.m_outline; } StyleGeneratedData::StyleGeneratedData() : Shared(), content(0), counter_reset(0), counter_increment(0) {} StyleGeneratedData::~StyleGeneratedData() { if (counter_reset) counter_reset->deref(); if (counter_increment) counter_increment->deref(); delete content; } StyleGeneratedData::StyleGeneratedData(const StyleGeneratedData& o) : Shared(), content(0), counter_reset(o.counter_reset), counter_increment(o.counter_increment) { if (o.content) content = new ContentData(*o.content); if (counter_reset) counter_reset->ref(); if (counter_increment) counter_increment->ref(); } bool StyleGeneratedData::contentDataEquivalent(const StyleGeneratedData* otherStyle) const { ContentData* c1 = content; ContentData* c2 = otherStyle->content; while (c1 && c2) { if (c1->_contentType != c2->_contentType) return false; if (c1->_contentType == CONTENT_TEXT) { DOM::DOMString c1Str(c1->_content.text); DOM::DOMString c2Str(c2->_content.text); if (c1Str != c2Str) return false; } else if (c1->_contentType == CONTENT_OBJECT) { if (c1->_content.object != c2->_content.object) return false; } else if (c1->_contentType == CONTENT_COUNTER) { if (c1->_content.counter != c2->_content.counter) return false; } else if (c1->_contentType == CONTENT_QUOTE) { if (c1->_content.quote != c2->_content.quote) return false; } c1 = c1->_nextContent; c2 = c2->_nextContent; } return !c1 && !c2; } static bool compareCounterActList(const CSSValueListImpl* ca, const CSSValueListImpl* cb) { // weeee.... CSSValueListImpl* a = const_cast(ca); CSSValueListImpl* b = const_cast(cb); if (!a && !b) return true; if (!a || !b) return false; if (a->length() != b->length()) return false; for(uint i=0; i< a->length(); i++) { CSSValueImpl *ai = a->item(i); CSSValueImpl *bi = b->item(i); assert(ai && ai->cssValueType() == CSSValue::CSS_CUSTOM); assert(bi && bi->cssValueType() == CSSValue::CSS_CUSTOM); CounterActImpl* caa = static_cast(ai); CounterActImpl* cab = static_cast(bi); if (caa->value() != cab->value()) return false; if (caa->counter() != cab->counter()) return false; } return true; } bool StyleGeneratedData::counterDataEquivalent(const StyleGeneratedData* otherStyle) const { return compareCounterActList(counter_reset, otherStyle->counter_reset) && compareCounterActList(counter_increment, otherStyle->counter_increment); } bool StyleGeneratedData::operator==(const StyleGeneratedData& o) const { return contentDataEquivalent(&o) && counterDataEquivalent(&o); } StyleMarqueeData::StyleMarqueeData() { increment = RenderStyle::initialMarqueeIncrement(); speed = RenderStyle::initialMarqueeSpeed(); direction = RenderStyle::initialMarqueeDirection(); behavior = RenderStyle::initialMarqueeBehavior(); loops = RenderStyle::initialMarqueeLoopCount(); } StyleMarqueeData::StyleMarqueeData(const StyleMarqueeData& o) :Shared(), increment(o.increment), speed(o.speed), loops(o.loops), behavior(o.behavior), direction(o.direction) {} bool StyleMarqueeData::operator==(const StyleMarqueeData& o) const { return (increment == o.increment && speed == o.speed && direction == o.direction && behavior == o.behavior && loops == o.loops); } StyleCSS3NonInheritedData::StyleCSS3NonInheritedData() :Shared() , opacity(RenderStyle::initialOpacity()) { } StyleCSS3NonInheritedData::StyleCSS3NonInheritedData(const StyleCSS3NonInheritedData& o) :Shared(), opacity(o.opacity), #ifdef APPLE_CHANGES flexibleBox(o.flexibleBox), #endif marquee(o.marquee) { } bool StyleCSS3NonInheritedData::operator==(const StyleCSS3NonInheritedData& o) const { return opacity == o.opacity && #ifdef APPLE_CHANGES flexibleBox == o.flexibleBox && #endif marquee == o.marquee; } StyleCSS3InheritedData::StyleCSS3InheritedData() :Shared(), textShadow(0), wordWrap(RenderStyle::initialWordWrap()) #ifdef APPLE_CHANGES , userModify(READ_ONLY), textSizeAdjust(RenderStyle::initialTextSizeAdjust()) #endif { } StyleCSS3InheritedData::StyleCSS3InheritedData(const StyleCSS3InheritedData& o) :Shared() { textShadow = o.textShadow ? new ShadowData(*o.textShadow) : 0; wordWrap = o.wordWrap; #ifdef APPLE_CHANGES userModify = o.userModify; textSizeAdjust = o.textSizeAdjust; #endif } StyleCSS3InheritedData::~StyleCSS3InheritedData() { delete textShadow; } bool StyleCSS3InheritedData::operator==(const StyleCSS3InheritedData& o) const { return shadowDataEquivalent(o) && (wordWrap == o.wordWrap) #ifdef APPLE_CHANGES && (userModify == o.userModify) && (textSizeAdjust == o.textSizeAdjust) #endif ; } bool StyleCSS3InheritedData::shadowDataEquivalent(const StyleCSS3InheritedData& o) const { if (!textShadow && o.textShadow || textShadow && !o.textShadow) return false; if (textShadow && o.textShadow && (*textShadow != *o.textShadow)) return false; return true; } StyleInheritedData::StyleInheritedData() : indent( RenderStyle::initialTextIndent() ), line_height( RenderStyle::initialLineHeight() ), style_image( RenderStyle::initialListStyleImage() ), font(), color( RenderStyle::initialColor() ), border_hspacing( RenderStyle::initialBorderHorizontalSpacing() ), border_vspacing( RenderStyle::initialBorderVerticalSpacing() ), widows( RenderStyle::initialWidows() ), orphans( RenderStyle::initialOrphans() ), quotes(0) { } StyleInheritedData::~StyleInheritedData() { if (quotes) quotes->deref(); } StyleInheritedData::StyleInheritedData(const StyleInheritedData& o ) : Shared(), indent( o.indent ), line_height( o.line_height ), style_image( o.style_image ), font( o.font ), color( o.color ), border_hspacing( o.border_hspacing ), border_vspacing( o.border_vspacing ), widows(o.widows), orphans(o.orphans) { quotes = o.quotes; if (quotes) quotes->ref(); } bool StyleInheritedData::operator==(const StyleInheritedData& o) const { return indent == o.indent && line_height == o.line_height && border_hspacing == o.border_hspacing && border_vspacing == o.border_vspacing && style_image == o.style_image && font == o.font && color == o.color && border_hspacing == o.border_hspacing && border_vspacing == o.border_vspacing && quotes == o.quotes && widows == o.widows && orphans == o.orphans ; // doesn't work because structs are not packed //return memcmp(this, &o, sizeof(*this))==0; } RenderStyle::RenderStyle() { // counter++; if (!_default) _default = new RenderStyle(true); box = _default->box; visual = _default->visual; background = _default->background; surround = _default->surround; generated = _default->generated; css3NonInheritedData = _default->css3NonInheritedData; css3InheritedData = _default->css3InheritedData; inherited = _default->inherited; setBitDefaults(); pseudoStyle = 0; } RenderStyle::RenderStyle(bool) { setBitDefaults(); box.init(); visual.init(); background.init(); surround.init(); generated.init(); css3NonInheritedData.init(); #ifdef APPLE_CHANGES // ### yet to be merged css3NonInheritedData.access()->flexibleBox.init(); #endif css3NonInheritedData.access()->marquee.init(); css3InheritedData.init(); inherited.init(); pseudoStyle = 0; } RenderStyle::RenderStyle(const RenderStyle& o) : Shared(), inherited_flags( o.inherited_flags ), noninherited_flags( o.noninherited_flags ), box( o.box ), visual( o.visual ), background( o.background ), surround( o.surround ), generated(o.generated), css3NonInheritedData( o.css3NonInheritedData ), css3InheritedData( o.css3InheritedData ), inherited( o.inherited ), pseudoStyle( 0 ) {} void RenderStyle::inheritFrom(const RenderStyle* inheritParent) { css3InheritedData = inheritParent->css3InheritedData; inherited = inheritParent->inherited; inherited_flags = inheritParent->inherited_flags; // Simulate ":after,:before { white-space: pre-line }" if (styleType() == AFTER || styleType() == BEFORE) setWhiteSpace(PRE_LINE); } RenderStyle::~RenderStyle() { RenderStyle *ps = pseudoStyle; RenderStyle *prev = 0; while (ps) { prev = ps; ps = ps->pseudoStyle; // to prevent a double deletion. // this works only because the styles below aren't really shared // Dirk said we need another construct as soon as these are shared prev->pseudoStyle = 0; prev->deref(); } } bool RenderStyle::operator==(const RenderStyle& o) const { // compare everything except the pseudoStyle pointer return (inherited_flags == o.inherited_flags && noninherited_flags == o.noninherited_flags && box == o.box && visual == o.visual && background == o.background && surround == o.surround && generated == o.generated && css3NonInheritedData == o.css3NonInheritedData && css3InheritedData == o.css3InheritedData && inherited == o.inherited); } enum EPseudoBit { NO_BIT = 0x0, FIRST_LINE_BIT = 0x1, FIRST_LETTER_BIT = 0x2, SELECTION_BIT = 0x4, BEFORE_BIT = 0x8, AFTER_BIT = 0x10, MARKER_BIT = 0x20, REPLACED_BIT = 0x40 }; static int pseudoBit(RenderStyle::PseudoId pseudo) { switch (pseudo) { case RenderStyle::BEFORE: return BEFORE_BIT; case RenderStyle::AFTER: return AFTER_BIT; case RenderStyle::MARKER: return MARKER_BIT; case RenderStyle::REPLACED: return REPLACED_BIT; case RenderStyle::FIRST_LINE: return FIRST_LINE_BIT; case RenderStyle::FIRST_LETTER: return FIRST_LETTER_BIT; case RenderStyle::SELECTION: return SELECTION_BIT; default: return NO_BIT; } } bool RenderStyle::hasPseudoStyle(PseudoId pseudo) const { return (pseudoBit(pseudo) & noninherited_flags.f._pseudoBits) != 0; } void RenderStyle::setHasPseudoStyle(PseudoId pseudo, bool b) { if (b) noninherited_flags.f._pseudoBits |= pseudoBit(pseudo); else noninherited_flags.f._pseudoBits &= ~(pseudoBit(pseudo)); } RenderStyle* RenderStyle::getPseudoStyle(PseudoId pid) const { if (!hasPseudoStyle(pid)) return 0; RenderStyle *ps = 0; if (noninherited_flags.f._styleType==NOPSEUDO) for (ps = pseudoStyle; ps; ps = ps->pseudoStyle) if (ps->noninherited_flags.f._styleType==pid) break; return ps; } RenderStyle* RenderStyle::addPseudoStyle(PseudoId pid) { if (hasPseudoStyle(pid)) return getPseudoStyle(pid); RenderStyle *ps = 0; switch (pid) { case FIRST_LETTER: // pseudo-elements (FIRST_LINE has a special handling) case SELECTION: case BEFORE: case AFTER: ps = new RenderStyle(); break; default: ps = new RenderStyle(*this); // use the real copy constructor to get an identical copy } ps->ref(); ps->noninherited_flags.f._styleType = pid; ps->pseudoStyle = pseudoStyle; pseudoStyle = ps; setHasPseudoStyle(pid, true); return ps; } void RenderStyle::removePseudoStyle(PseudoId pid) { RenderStyle *ps = pseudoStyle; RenderStyle *prev = this; while (ps) { if (ps->noninherited_flags.f._styleType==pid) { prev->pseudoStyle = ps->pseudoStyle; ps->deref(); return; } prev = ps; ps = ps->pseudoStyle; } setHasPseudoStyle(pid, false); } bool RenderStyle::inheritedNotEqual( RenderStyle *other ) const { return ( inherited_flags != other->inherited_flags || inherited != other->inherited || css3InheritedData != other->css3InheritedData ); } /* compares two styles. The result gives an idea of the action that needs to be taken when replacing the old style with a new one. CbLayout: The containing block of the object needs a relayout. Layout: the RenderObject needs a relayout after the style change Visible: The change is visible, but no relayout is needed NonVisible: The object does need neither repaint nor relayout after the change. ### TODO: A lot can be optimised here based on the display type, lots of optimisations are unimplemented, and currently result in the worst case result causing a relayout of the containing block. */ RenderStyle::Diff RenderStyle::diff( const RenderStyle *other ) const { // we anyway assume they are the same // EDisplay _display : 5; // NonVisible: // ECursor _cursor_style : 4; // EUserInput _user_input : 2; as long as :enabled is not impl'd // ### this needs work to know more exactly if we need a relayout // or just a repaint // non-inherited attributes // DataRef box; // DataRef visual; // DataRef surround; // inherited attributes // DataRef inherited; if ( *box.get() != *other->box.get() || *visual.get() != *other->visual.get() || (*surround.get() != *other->surround.get() && (other->position() == STATIC || other->position() != position())) || !(inherited->indent == other->inherited->indent) || !(inherited->line_height == other->inherited->line_height) || !(inherited->style_image == other->inherited->style_image) || !(inherited->font == other->inherited->font) || !(inherited->border_hspacing == other->inherited->border_hspacing) || !(inherited->border_vspacing == other->inherited->border_vspacing) || !(inherited_flags.f._visuallyOrdered == other->inherited_flags.f._visuallyOrdered) || !(inherited_flags.f._htmlHacks == other->inherited_flags.f._htmlHacks) || !(noninherited_flags.f._textOverflow == other->noninherited_flags.f._textOverflow) ) return CbLayout; // changes causing Layout changes: // only for tables: // _border_collapse // EEmptyCell _empty_cells : 2 ; // ECaptionSide _caption_side : 2; // ETableLayout _table_layout : 1; // EPosition _position : 2; // EFloat _floating : 2; if ( ((int)noninherited_flags.f._display) >= TABLE ) { if ( !(inherited_flags.f._empty_cells == other->inherited_flags.f._empty_cells) || !(inherited_flags.f._caption_side == other->inherited_flags.f._caption_side) || !(inherited_flags.f._border_collapse == other->inherited_flags.f._border_collapse) || !(noninherited_flags.f._table_layout == other->noninherited_flags.f._table_layout) || !(noninherited_flags.f._position == other->noninherited_flags.f._position) || !(noninherited_flags.f._floating == other->noninherited_flags.f._floating) || !(noninherited_flags.f._flowAroundFloats == other->noninherited_flags.f._flowAroundFloats) || !(noninherited_flags.f._unicodeBidi == other->noninherited_flags.f._unicodeBidi) ) return CbLayout; } // only for lists: // EListStyleType _list_style_type : 5 ; // EListStylePosition _list_style_position :1; if (noninherited_flags.f._display == LIST_ITEM ) { if ( !(inherited_flags.f._list_style_type == other->inherited_flags.f._list_style_type) || !(inherited_flags.f._list_style_position == other->inherited_flags.f._list_style_position) ) return Layout; } // ### These could be better optimised // ETextAlign _text_align : 3; // ETextTransform _text_transform : 4; // EDirection _direction : 1; // EWhiteSpace _white_space : 2; // EClear _clear : 2; if ( !(inherited_flags.f._text_align == other->inherited_flags.f._text_align) || !(inherited_flags.f._text_transform == other->inherited_flags.f._text_transform) || !(inherited_flags.f._direction == other->inherited_flags.f._direction) || !(inherited_flags.f._white_space == other->inherited_flags.f._white_space) || !(noninherited_flags.f._clear == other->noninherited_flags.f._clear) ) return Layout; // Overflow returns a layout hint. if (noninherited_flags.f._overflowX != other->noninherited_flags.f._overflowX || noninherited_flags.f._overflowY != other->noninherited_flags.f._overflowY) return Layout; // only for inline: // EVerticalAlign _vertical_align : 4; if ( !(noninherited_flags.f._display == INLINE) && !(noninherited_flags.f._vertical_align == other->noninherited_flags.f._vertical_align) ) return Layout; if (*surround.get() != *other->surround.get()) { assert( other->position() != STATIC ); // this style is positioned or relatively positioned if ( surround->hasSamePBMData(*other->surround.get()) && // padding/border/margin are identical (other->position() == RELATIVE || !(other->left().isVariable() && other->right().isVariable()) && // X isn't static !(other->top().isVariable() && other->bottom().isVariable()) )) // neither is Y // therefore only the offset is different return Position; return Layout; } // Visible: // EVisibility _visibility : 2; // int _text_decorations : 4; // DataRef background; if (inherited->color != other->inherited->color || !(inherited_flags.f._visibility == other->inherited_flags.f._visibility) || !(inherited_flags.f._text_decorations == other->inherited_flags.f._text_decorations) || !(noninherited_flags.f._hasClip == other->noninherited_flags.f._hasClip) || visual->textDecoration != other->visual->textDecoration || *background.get() != *other->background.get() || css3NonInheritedData->opacity != other->css3NonInheritedData->opacity || !css3InheritedData->shadowDataEquivalent(*other->css3InheritedData.get()) ) return Visible; RenderStyle::Diff ch = Equal; // Check for visible pseudo-changes: if (hasPseudoStyle(FIRST_LINE) != other->hasPseudoStyle(FIRST_LINE)) ch = Visible; else if (hasPseudoStyle(FIRST_LINE) && other->hasPseudoStyle(FIRST_LINE)) ch = getPseudoStyle(FIRST_LINE)->diff(other->getPseudoStyle(FIRST_LINE)); if (ch != Equal) return ch; // Check for visible pseudo-changes: if (hasPseudoStyle(SELECTION) != other->hasPseudoStyle(SELECTION)) ch = Visible; else if (hasPseudoStyle(SELECTION) && other->hasPseudoStyle(SELECTION)) ch = getPseudoStyle(SELECTION)->diff(other->getPseudoStyle(SELECTION)); return ch; } RenderStyle* RenderStyle::_default = 0; void RenderStyle::cleanup() { delete _default; _default = 0; } void RenderStyle::setPaletteColor(TQPalette::ColorGroup g, TQColorGroup::ColorRole r, const TQColor& c) { visual.access()->palette.setColor(g,r,c); } void RenderStyle::adjustBackgroundLayers() { if (backgroundLayers()->next()) { // First we cull out layers that have no properties set. accessBackgroundLayers()->cullEmptyLayers(); // Next we repeat patterns into layers that don't have some properties set. accessBackgroundLayers()->fillUnsetProperties(); } } void RenderStyle::setClip( Length top, Length right, Length bottom, Length left ) { StyleVisualData *data = visual.access(); data->clip.top = top; data->clip.right = right; data->clip.bottom = bottom; data->clip.left = left; } void RenderStyle::setQuotes(DOM::QuotesValueImpl* q) { DOM::QuotesValueImpl *t = inherited->quotes; inherited.access()->quotes = q; if (q) q->ref(); if (t) t->deref(); } TQString RenderStyle::openQuote(int level) const { if (inherited->quotes) return inherited->quotes->openQuote(level); else return "\""; // 0 is default quotes } TQString RenderStyle::closeQuote(int level) const { if (inherited->quotes) return inherited->quotes->closeQuote(level); else return "\""; // 0 is default quotes } void RenderStyle::addContent(CachedObject* o) { if (!o) return; // The object is null. Nothing to do. Just bail. StyleGeneratedData *t_generated = generated.access(); ContentData* lastContent = t_generated->content; while (lastContent && lastContent->_nextContent) lastContent = lastContent->_nextContent; ContentData* newContentData = new ContentData; if (lastContent) lastContent->_nextContent = newContentData; else t_generated->content = newContentData; // o->ref(); newContentData->_content.object = o; newContentData->_contentType = CONTENT_OBJECT; } void RenderStyle::addContent(DOM::DOMStringImpl* s) { if (!s) return; // The string is null. Nothing to do. Just bail. StyleGeneratedData *t_generated = generated.access(); ContentData* lastContent = t_generated->content; while (lastContent && lastContent->_nextContent) lastContent = lastContent->_nextContent; if (lastContent) { if (lastContent->_contentType == CONTENT_TEXT) { // We can augment the existing string and share this ContentData node. DOMStringImpl* oldStr = lastContent->_content.text; DOMStringImpl* newStr = oldStr->copy(); newStr->ref(); oldStr->deref(); newStr->append(s); lastContent->_content.text = newStr; return; } } ContentData* newContentData = new ContentData; if (lastContent) lastContent->_nextContent = newContentData; else t_generated->content = newContentData; newContentData->_content.text = s; newContentData->_content.text->ref(); newContentData->_contentType = CONTENT_TEXT; } void RenderStyle::addContent(DOM::CounterImpl* c) { if (!c) return; StyleGeneratedData *t_generated = generated.access(); ContentData* lastContent = t_generated->content; while (lastContent && lastContent->_nextContent) lastContent = lastContent->_nextContent; ContentData* newContentData = new ContentData; if (lastContent) lastContent->_nextContent = newContentData; else t_generated->content = newContentData; c->ref(); newContentData->_content.counter = c; newContentData->_contentType = CONTENT_COUNTER; } void RenderStyle::addContent(EQuoteContent q) { if (q == NO_QUOTE) return; StyleGeneratedData *t_generated = generated.access(); ContentData* lastContent = t_generated->content; while (lastContent && lastContent->_nextContent) lastContent = lastContent->_nextContent; ContentData* newContentData = new ContentData; if (lastContent) lastContent->_nextContent = newContentData; else t_generated->content = newContentData; newContentData->_content.quote = q; newContentData->_contentType = CONTENT_QUOTE; } // content: normal is the same as having no content at all void RenderStyle::setContentNormal() { if (generated->content != 0) { delete generated->content; generated.access()->content = 0; } } // content: none, add an empty content node void RenderStyle::setContentNone() { setContentNormal(); generated.access()->content = new ContentData; } void RenderStyle::setContentData(ContentData *data) { if (data != generated->content) { if (data) generated.access()->content = new ContentData(*data); else generated.access()->content = 0; } } ContentData::ContentData(const ContentData& o) : _contentType(o._contentType) { switch (_contentType) { case CONTENT_OBJECT: _content.object = o._content.object; break; case CONTENT_TEXT: _content.text = o._content.text; _content.text->ref(); break; case CONTENT_COUNTER: _content.counter = o._content.counter; _content.counter->ref(); break; case CONTENT_QUOTE: _content.quote = o._content.quote; break; case CONTENT_NONE: default: break; } _nextContent = o._nextContent ? new ContentData(*o._nextContent) : 0; } ContentData::~ContentData() { clearContent(); } void ContentData::clearContent() { delete _nextContent; _nextContent = 0; switch (_contentType) { case CONTENT_OBJECT: _content.object = 0; break; case CONTENT_TEXT: _content.text->deref(); _content.text = 0; break; case CONTENT_COUNTER: _content.counter->deref(); _content.counter = 0; break; case CONTENT_QUOTE: _content.quote = NO_QUOTE; break; default: ; } } void RenderStyle::setTextShadow(ShadowData* val, bool add) { StyleCSS3InheritedData* css3Data = css3InheritedData.access(); if (!add) { delete css3Data->textShadow; css3Data->textShadow = val; return; } ShadowData* last = css3Data->textShadow; while (last->next) last = last->next; last->next = val; } ShadowData::ShadowData(const ShadowData& o) :x(o.x), y(o.y), blur(o.blur), color(o.color) { next = o.next ? new ShadowData(*o.next) : 0; } bool ShadowData::operator==(const ShadowData& o) const { if ((next && !o.next) || (!next && o.next) || (next && o.next && *next != *o.next)) return false; return x == o.x && y == o.y && blur == o.blur && color == o.color; } static bool hasCounter(const DOM::DOMString& c, CSSValueListImpl *l) { int len = l->length(); for(int i=0; i(l->item(i)); Q_ASSERT(ca != 0); if (ca->m_counter == c) return true; } return false; } bool RenderStyle::hasCounterReset(const DOM::DOMString& c) const { if (generated->counter_reset) return hasCounter(c, generated->counter_reset); else return false; } bool RenderStyle::hasCounterIncrement(const DOM::DOMString& c) const { if (generated->counter_increment) return hasCounter(c, generated->counter_increment); else return false; } static short readCounter(const DOM::DOMString& c, CSSValueListImpl *l) { int len = l->length(); for(int i=0; i(l->item(i)); Q_ASSERT(ca != 0); if (ca->m_counter == c) return ca->m_value; } return 0; } short RenderStyle::counterReset(const DOM::DOMString& c) const { if (generated->counter_reset) return readCounter(c, generated->counter_reset); else return 0; } short RenderStyle::counterIncrement(const DOM::DOMString& c) const { if (generated->counter_increment) return readCounter(c, generated->counter_increment); else return 0; } void RenderStyle::setCounterReset(CSSValueListImpl *l) { CSSValueListImpl *t = generated->counter_reset; generated.access()->counter_reset = l; if (l) l->ref(); if (t) t->deref(); } void RenderStyle::setCounterIncrement(CSSValueListImpl *l) { CSSValueListImpl *t = generated->counter_increment; generated.access()->counter_increment = l; if (l) l->ref(); if (t) t->deref(); } #ifdef ENABLE_DUMP static TQString describeFont( const TQFont &f) { TQString res = "'" + f.family() + "' "; if ( f.pointSize() > 0) res += TQString::number( f.pointSize() ) + "pt"; else res += TQString::number( f.pixelSize() ) + "px"; if ( f.bold() ) res += " bold"; if ( f.italic() ) res += " italic"; if ( f.underline() ) res += " underline"; if ( f.overline() ) res += " overline"; if ( f.strikeOut() ) res += " strikeout"; return res; } TQString RenderStyle::createDiff( const RenderStyle &parent ) const { TQString res; if ( color().isValid() && parent.color() != color() ) res += " [color=" + color().name() + "]"; if ( backgroundColor().isValid() && parent.backgroundColor() != backgroundColor() ) res += " [bgcolor=" + backgroundColor().name() + "]"; if ( parent.font() != font() ) res += " [font=" + describeFont( font() ) + "]"; return res; } #endif RenderPageStyle::RenderPageStyle() : next(0), m_pageType(ANY_PAGE) { } RenderPageStyle::~RenderPageStyle() { delete next; } RenderPageStyle* RenderPageStyle::getPageStyle(PageType type) { RenderPageStyle *ps = 0; for (ps = this; ps; ps = ps->next) if (ps->m_pageType==type) break; return ps; } RenderPageStyle* RenderPageStyle::addPageStyle(PageType type) { RenderPageStyle *ps = getPageStyle(type); if (!ps) { ps = new RenderPageStyle(*this); // use the real copy constructor to get an identical copy ps->m_pageType = type; ps->next = next; next = ps; } return ps; } void RenderPageStyle::removePageStyle(PageType type) { RenderPageStyle *ps = next; RenderPageStyle *prev = this; while (ps) { if (ps->m_pageType==type) { prev->next = ps->next; delete ps; return; } prev = ps; ps = ps->next; } }