| /* |
| * Copyright (C) 1997 Martin Jones (mjones@kde.org) |
| * (C) 1997 Torben Weis (weis@kde.org) |
| * (C) 1998 Waldo Bastian (bastian@kde.org) |
| * (C) 1999 Lars Knoll (knoll@kde.org) |
| * (C) 1999 Antti Koivisto (koivisto@kde.org) |
| * Copyright (C) 2003, 2004, 2005, 2006, 2008, 2010, 2011 Apple Inc. All rights |
| * reserved. |
| * |
| * 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 "third_party/blink/renderer/core/html/html_table_element.h" |
| |
| #include "third_party/blink/renderer/core/css/css_identifier_value.h" |
| #include "third_party/blink/renderer/core/css/css_image_value.h" |
| #include "third_party/blink/renderer/core/css/css_inherited_value.h" |
| #include "third_party/blink/renderer/core/css/css_property_names.h" |
| #include "third_party/blink/renderer/core/css/css_property_value_set.h" |
| #include "third_party/blink/renderer/core/css/style_change_reason.h" |
| #include "third_party/blink/renderer/core/css_value_keywords.h" |
| #include "third_party/blink/renderer/core/dom/attribute.h" |
| #include "third_party/blink/renderer/core/dom/element_traversal.h" |
| #include "third_party/blink/renderer/core/dom/node_lists_node_data.h" |
| #include "third_party/blink/renderer/core/frame/use_counter.h" |
| #include "third_party/blink/renderer/core/html/html_table_caption_element.h" |
| #include "third_party/blink/renderer/core/html/html_table_cell_element.h" |
| #include "third_party/blink/renderer/core/html/html_table_row_element.h" |
| #include "third_party/blink/renderer/core/html/html_table_rows_collection.h" |
| #include "third_party/blink/renderer/core/html/html_table_section_element.h" |
| #include "third_party/blink/renderer/core/html/parser/html_parser_idioms.h" |
| #include "third_party/blink/renderer/core/html_names.h" |
| #include "third_party/blink/renderer/platform/bindings/exception_state.h" |
| #include "third_party/blink/renderer/platform/heap/heap.h" |
| #include "third_party/blink/renderer/platform/weborigin/referrer.h" |
| #include "third_party/blink/renderer/platform/wtf/std_lib_extras.h" |
| |
| namespace blink { |
| |
| using namespace html_names; |
| |
| HTMLTableElement::HTMLTableElement(Document& document) |
| : HTMLElement(kTableTag, document), |
| border_attr_(false), |
| border_color_attr_(false), |
| frame_attr_(false), |
| rules_attr_(kUnsetRules), |
| padding_(1) {} |
| |
| // An explicit empty destructor should be in html_table_element.cc, because |
| // if an implicit destructor is used or an empty destructor is defined in |
| // html_table_element.h, when including html_table_element.h, msvc tries to |
| // expand the destructor and causes a compile error because of lack of |
| // CSSPropertyValueSet definition. |
| HTMLTableElement::~HTMLTableElement() = default; |
| |
| HTMLTableCaptionElement* HTMLTableElement::caption() const { |
| return Traversal<HTMLTableCaptionElement>::FirstChild(*this); |
| } |
| |
| void HTMLTableElement::setCaption(HTMLTableCaptionElement* new_caption, |
| ExceptionState& exception_state) { |
| deleteCaption(); |
| if (new_caption) |
| InsertBefore(new_caption, firstChild(), exception_state); |
| } |
| |
| HTMLTableSectionElement* HTMLTableElement::tHead() const { |
| return ToHTMLTableSectionElement( |
| Traversal<HTMLElement>::FirstChild(*this, HasHTMLTagName(kTheadTag))); |
| } |
| |
| void HTMLTableElement::setTHead(HTMLTableSectionElement* new_head, |
| ExceptionState& exception_state) { |
| if (new_head && !new_head->HasTagName(kTheadTag)) { |
| exception_state.ThrowDOMException(DOMExceptionCode::kHierarchyRequestError, |
| "Not a thead element."); |
| return; |
| } |
| |
| deleteTHead(); |
| if (!new_head) |
| return; |
| |
| HTMLElement* child; |
| for (child = Traversal<HTMLElement>::FirstChild(*this); child; |
| child = Traversal<HTMLElement>::NextSibling(*child)) { |
| if (!child->HasTagName(kCaptionTag) && !child->HasTagName(kColgroupTag)) |
| break; |
| } |
| |
| InsertBefore(new_head, child, exception_state); |
| } |
| |
| HTMLTableSectionElement* HTMLTableElement::tFoot() const { |
| return ToHTMLTableSectionElement( |
| Traversal<HTMLElement>::FirstChild(*this, HasHTMLTagName(kTfootTag))); |
| } |
| |
| void HTMLTableElement::setTFoot(HTMLTableSectionElement* new_foot, |
| ExceptionState& exception_state) { |
| if (new_foot && !new_foot->HasTagName(kTfootTag)) { |
| exception_state.ThrowDOMException(DOMExceptionCode::kHierarchyRequestError, |
| "Not a tfoot element."); |
| return; |
| } |
| |
| deleteTFoot(); |
| |
| if (new_foot) |
| AppendChild(new_foot, exception_state); |
| } |
| |
| HTMLTableSectionElement* HTMLTableElement::createTHead() { |
| if (HTMLTableSectionElement* existing_head = tHead()) |
| return existing_head; |
| auto* head = |
| MakeGarbageCollected<HTMLTableSectionElement>(kTheadTag, GetDocument()); |
| setTHead(head, IGNORE_EXCEPTION_FOR_TESTING); |
| return head; |
| } |
| |
| void HTMLTableElement::deleteTHead() { |
| RemoveChild(tHead(), IGNORE_EXCEPTION_FOR_TESTING); |
| } |
| |
| HTMLTableSectionElement* HTMLTableElement::createTFoot() { |
| if (HTMLTableSectionElement* existing_foot = tFoot()) |
| return existing_foot; |
| auto* foot = |
| MakeGarbageCollected<HTMLTableSectionElement>(kTfootTag, GetDocument()); |
| setTFoot(foot, IGNORE_EXCEPTION_FOR_TESTING); |
| return foot; |
| } |
| |
| void HTMLTableElement::deleteTFoot() { |
| RemoveChild(tFoot(), IGNORE_EXCEPTION_FOR_TESTING); |
| } |
| |
| HTMLTableSectionElement* HTMLTableElement::createTBody() { |
| auto* body = |
| MakeGarbageCollected<HTMLTableSectionElement>(kTbodyTag, GetDocument()); |
| Node* reference_element = LastBody() ? LastBody()->nextSibling() : nullptr; |
| |
| InsertBefore(body, reference_element); |
| return body; |
| } |
| |
| HTMLTableCaptionElement* HTMLTableElement::createCaption() { |
| if (HTMLTableCaptionElement* existing_caption = caption()) |
| return existing_caption; |
| auto* caption = MakeGarbageCollected<HTMLTableCaptionElement>(GetDocument()); |
| setCaption(caption, IGNORE_EXCEPTION_FOR_TESTING); |
| return caption; |
| } |
| |
| void HTMLTableElement::deleteCaption() { |
| RemoveChild(caption(), IGNORE_EXCEPTION_FOR_TESTING); |
| } |
| |
| HTMLTableSectionElement* HTMLTableElement::LastBody() const { |
| return ToHTMLTableSectionElement( |
| Traversal<HTMLElement>::LastChild(*this, HasHTMLTagName(kTbodyTag))); |
| } |
| |
| HTMLTableRowElement* HTMLTableElement::insertRow( |
| int index, |
| ExceptionState& exception_state) { |
| if (index < -1) { |
| exception_state.ThrowDOMException( |
| DOMExceptionCode::kIndexSizeError, |
| "The index provided (" + String::Number(index) + ") is less than -1."); |
| return nullptr; |
| } |
| |
| HTMLTableRowElement* last_row = nullptr; |
| HTMLTableRowElement* row = nullptr; |
| if (index == -1) { |
| last_row = HTMLTableRowsCollection::LastRow(*this); |
| } else { |
| for (int i = 0; i <= index; ++i) { |
| row = HTMLTableRowsCollection::RowAfter(*this, last_row); |
| if (!row) { |
| if (i != index) { |
| exception_state.ThrowDOMException( |
| DOMExceptionCode::kIndexSizeError, |
| "The index provided (" + String::Number(index) + |
| ") is greater than the number of rows in the table (" + |
| String::Number(i) + ")."); |
| return nullptr; |
| } |
| break; |
| } |
| last_row = row; |
| } |
| } |
| |
| ContainerNode* parent; |
| if (last_row) { |
| parent = row ? row->parentNode() : last_row->parentNode(); |
| } else { |
| parent = LastBody(); |
| if (!parent) { |
| auto* new_body = MakeGarbageCollected<HTMLTableSectionElement>( |
| kTbodyTag, GetDocument()); |
| auto* new_row = MakeGarbageCollected<HTMLTableRowElement>(GetDocument()); |
| new_body->AppendChild(new_row, exception_state); |
| AppendChild(new_body, exception_state); |
| return new_row; |
| } |
| } |
| |
| auto* new_row = MakeGarbageCollected<HTMLTableRowElement>(GetDocument()); |
| parent->InsertBefore(new_row, row, exception_state); |
| return new_row; |
| } |
| |
| void HTMLTableElement::deleteRow(int index, ExceptionState& exception_state) { |
| if (index < -1) { |
| exception_state.ThrowDOMException( |
| DOMExceptionCode::kIndexSizeError, |
| "The index provided (" + String::Number(index) + ") is less than -1."); |
| return; |
| } |
| |
| HTMLTableRowElement* row = nullptr; |
| int i = 0; |
| if (index == -1) { |
| row = HTMLTableRowsCollection::LastRow(*this); |
| if (!row) |
| return; |
| } else { |
| for (i = 0; i <= index; ++i) { |
| row = HTMLTableRowsCollection::RowAfter(*this, row); |
| if (!row) |
| break; |
| } |
| } |
| if (!row) { |
| exception_state.ThrowDOMException( |
| DOMExceptionCode::kIndexSizeError, |
| "The index provided (" + String::Number(index) + |
| ") is greater than the number of rows in the table (" + |
| String::Number(i) + ")."); |
| return; |
| } |
| row->remove(exception_state); |
| } |
| |
| void HTMLTableElement::SetNeedsTableStyleRecalc() const { |
| Element* element = ElementTraversal::Next(*this, this); |
| while (element) { |
| element->SetNeedsStyleRecalc( |
| kLocalStyleChange, |
| StyleChangeReasonForTracing::FromAttribute(kRulesAttr)); |
| if (IsHTMLTableCellElement(*element)) |
| element = ElementTraversal::NextSkippingChildren(*element, this); |
| else |
| element = ElementTraversal::Next(*element, this); |
| } |
| } |
| |
| static bool GetBordersFromFrameAttributeValue(const AtomicString& value, |
| bool& border_top, |
| bool& border_right, |
| bool& border_bottom, |
| bool& border_left) { |
| border_top = false; |
| border_right = false; |
| border_bottom = false; |
| border_left = false; |
| |
| if (DeprecatedEqualIgnoringCase(value, "above")) |
| border_top = true; |
| else if (DeprecatedEqualIgnoringCase(value, "below")) |
| border_bottom = true; |
| else if (DeprecatedEqualIgnoringCase(value, "hsides")) |
| border_top = border_bottom = true; |
| else if (DeprecatedEqualIgnoringCase(value, "vsides")) |
| border_left = border_right = true; |
| else if (DeprecatedEqualIgnoringCase(value, "lhs")) |
| border_left = true; |
| else if (DeprecatedEqualIgnoringCase(value, "rhs")) |
| border_right = true; |
| else if (DeprecatedEqualIgnoringCase(value, "box") || |
| DeprecatedEqualIgnoringCase(value, "border")) |
| border_top = border_bottom = border_left = border_right = true; |
| else if (!DeprecatedEqualIgnoringCase(value, "void")) |
| return false; |
| return true; |
| } |
| |
| void HTMLTableElement::CollectStyleForPresentationAttribute( |
| const QualifiedName& name, |
| const AtomicString& value, |
| MutableCSSPropertyValueSet* style) { |
| if (name == kWidthAttr) { |
| AddHTMLLengthToStyle(style, CSSPropertyID::kWidth, value); |
| } else if (name == kHeightAttr) { |
| AddHTMLLengthToStyle(style, CSSPropertyID::kHeight, value); |
| } else if (name == kBorderAttr) { |
| AddPropertyToPresentationAttributeStyle( |
| style, CSSPropertyID::kBorderWidth, ParseBorderWidthAttribute(value), |
| CSSPrimitiveValue::UnitType::kPixels); |
| } else if (name == kBordercolorAttr) { |
| if (!value.IsEmpty()) |
| AddHTMLColorToStyle(style, CSSPropertyID::kBorderColor, value); |
| } else if (name == kBgcolorAttr) { |
| AddHTMLColorToStyle(style, CSSPropertyID::kBackgroundColor, value); |
| } else if (name == kBackgroundAttr) { |
| String url = StripLeadingAndTrailingHTMLSpaces(value); |
| if (!url.IsEmpty()) { |
| UseCounter::Count( |
| GetDocument(), |
| WebFeature::kHTMLTableElementPresentationAttributeBackground); |
| CSSImageValue* image_value = |
| CSSImageValue::Create(url, GetDocument().CompleteURL(url), |
| Referrer(GetDocument().OutgoingReferrer(), |
| GetDocument().GetReferrerPolicy())); |
| style->SetProperty( |
| CSSPropertyValue(GetCSSPropertyBackgroundImage(), *image_value)); |
| } |
| } else if (name == kValignAttr) { |
| if (!value.IsEmpty()) { |
| AddPropertyToPresentationAttributeStyle( |
| style, CSSPropertyID::kVerticalAlign, value); |
| } |
| } else if (name == kCellspacingAttr) { |
| if (!value.IsEmpty()) { |
| AddHTMLLengthToStyle(style, CSSPropertyID::kBorderSpacing, value, |
| kDontAllowPercentageValues); |
| } |
| } else if (name == kAlignAttr) { |
| if (!value.IsEmpty()) { |
| if (DeprecatedEqualIgnoringCase(value, "center")) { |
| AddPropertyToPresentationAttributeStyle( |
| style, CSSPropertyID::kMarginInlineStart, CSSValueID::kAuto); |
| AddPropertyToPresentationAttributeStyle( |
| style, CSSPropertyID::kMarginInlineEnd, CSSValueID::kAuto); |
| } else { |
| AddPropertyToPresentationAttributeStyle(style, CSSPropertyID::kFloat, |
| value); |
| } |
| } |
| } else if (name == kRulesAttr) { |
| // The presence of a valid rules attribute causes border collapsing to be |
| // enabled. |
| if (rules_attr_ != kUnsetRules) { |
| AddPropertyToPresentationAttributeStyle( |
| style, CSSPropertyID::kBorderCollapse, CSSValueID::kCollapse); |
| } |
| } else if (name == kFrameAttr) { |
| bool border_top; |
| bool border_right; |
| bool border_bottom; |
| bool border_left; |
| if (GetBordersFromFrameAttributeValue(value, border_top, border_right, |
| border_bottom, border_left)) { |
| AddPropertyToPresentationAttributeStyle( |
| style, CSSPropertyID::kBorderWidth, CSSValueID::kThin); |
| AddPropertyToPresentationAttributeStyle( |
| style, CSSPropertyID::kBorderTopStyle, |
| border_top ? CSSValueID::kSolid : CSSValueID::kHidden); |
| AddPropertyToPresentationAttributeStyle( |
| style, CSSPropertyID::kBorderBottomStyle, |
| border_bottom ? CSSValueID::kSolid : CSSValueID::kHidden); |
| AddPropertyToPresentationAttributeStyle( |
| style, CSSPropertyID::kBorderLeftStyle, |
| border_left ? CSSValueID::kSolid : CSSValueID::kHidden); |
| AddPropertyToPresentationAttributeStyle( |
| style, CSSPropertyID::kBorderRightStyle, |
| border_right ? CSSValueID::kSolid : CSSValueID::kHidden); |
| } |
| } else { |
| HTMLElement::CollectStyleForPresentationAttribute(name, value, style); |
| } |
| } |
| |
| bool HTMLTableElement::IsPresentationAttribute( |
| const QualifiedName& name) const { |
| if (name == kWidthAttr || name == kHeightAttr || name == kBgcolorAttr || |
| name == kBackgroundAttr || name == kValignAttr || name == kVspaceAttr || |
| name == kHspaceAttr || name == kAlignAttr || name == kCellspacingAttr || |
| name == kBorderAttr || name == kBordercolorAttr || name == kFrameAttr || |
| name == kRulesAttr) |
| return true; |
| return HTMLElement::IsPresentationAttribute(name); |
| } |
| |
| void HTMLTableElement::ParseAttribute( |
| const AttributeModificationParams& params) { |
| const QualifiedName& name = params.name; |
| CellBorders borders_before = GetCellBorders(); |
| uint16_t old_padding = padding_; |
| |
| if (name == kBorderAttr) { |
| // FIXME: This attribute is a mess. |
| border_attr_ = ParseBorderWidthAttribute(params.new_value); |
| } else if (name == kBordercolorAttr) { |
| border_color_attr_ = !params.new_value.IsEmpty(); |
| } else if (name == kFrameAttr) { |
| // FIXME: This attribute is a mess. |
| bool border_top; |
| bool border_right; |
| bool border_bottom; |
| bool border_left; |
| frame_attr_ = GetBordersFromFrameAttributeValue( |
| params.new_value, border_top, border_right, border_bottom, border_left); |
| } else if (name == kRulesAttr) { |
| rules_attr_ = kUnsetRules; |
| if (DeprecatedEqualIgnoringCase(params.new_value, "none")) |
| rules_attr_ = kNoneRules; |
| else if (DeprecatedEqualIgnoringCase(params.new_value, "groups")) |
| rules_attr_ = kGroupsRules; |
| else if (DeprecatedEqualIgnoringCase(params.new_value, "rows")) |
| rules_attr_ = kRowsRules; |
| else if (DeprecatedEqualIgnoringCase(params.new_value, "cols")) |
| rules_attr_ = kColsRules; |
| else if (DeprecatedEqualIgnoringCase(params.new_value, "all")) |
| rules_attr_ = kAllRules; |
| } else if (params.name == kCellpaddingAttr) { |
| if (!params.new_value.IsEmpty()) { |
| padding_ = |
| std::max(0, std::min((int32_t)std::numeric_limits<uint16_t>::max(), |
| params.new_value.ToInt())); |
| } else { |
| padding_ = 1; |
| } |
| } else if (params.name == kColsAttr) { |
| // ### |
| } else { |
| HTMLElement::ParseAttribute(params); |
| } |
| |
| if (borders_before != GetCellBorders() || old_padding != padding_) { |
| shared_cell_style_ = nullptr; |
| SetNeedsTableStyleRecalc(); |
| } |
| } |
| |
| static CSSPropertyValueSet* CreateBorderStyle(CSSValueID value) { |
| MutableCSSPropertyValueSet* style = |
| MutableCSSPropertyValueSet::Create(kHTMLQuirksMode); |
| style->SetProperty(CSSPropertyID::kBorderTopStyle, value); |
| style->SetProperty(CSSPropertyID::kBorderBottomStyle, value); |
| style->SetProperty(CSSPropertyID::kBorderLeftStyle, value); |
| style->SetProperty(CSSPropertyID::kBorderRightStyle, value); |
| return style; |
| } |
| |
| const CSSPropertyValueSet* |
| HTMLTableElement::AdditionalPresentationAttributeStyle() { |
| if (frame_attr_) |
| return nullptr; |
| |
| if (!border_attr_ && !border_color_attr_) { |
| // Setting the border to 'hidden' allows it to win over any border |
| // set on the table's cells during border-conflict resolution. |
| if (rules_attr_ != kUnsetRules) { |
| DEFINE_STATIC_LOCAL(Persistent<CSSPropertyValueSet>, solid_border_style, |
| (CreateBorderStyle(CSSValueID::kHidden))); |
| return solid_border_style; |
| } |
| return nullptr; |
| } |
| |
| if (border_color_attr_) { |
| DEFINE_STATIC_LOCAL(Persistent<CSSPropertyValueSet>, solid_border_style, |
| (CreateBorderStyle(CSSValueID::kSolid))); |
| return solid_border_style; |
| } |
| DEFINE_STATIC_LOCAL(Persistent<CSSPropertyValueSet>, outset_border_style, |
| (CreateBorderStyle(CSSValueID::kOutset))); |
| return outset_border_style; |
| } |
| |
| HTMLTableElement::CellBorders HTMLTableElement::GetCellBorders() const { |
| switch (rules_attr_) { |
| case kNoneRules: |
| case kGroupsRules: |
| return kNoBorders; |
| case kAllRules: |
| return kSolidBorders; |
| case kColsRules: |
| return kSolidBordersColsOnly; |
| case kRowsRules: |
| return kSolidBordersRowsOnly; |
| case kUnsetRules: |
| if (!border_attr_) |
| return kNoBorders; |
| if (border_color_attr_) |
| return kSolidBorders; |
| return kInsetBorders; |
| } |
| NOTREACHED(); |
| return kNoBorders; |
| } |
| |
| CSSPropertyValueSet* HTMLTableElement::CreateSharedCellStyle() { |
| MutableCSSPropertyValueSet* style = |
| MutableCSSPropertyValueSet::Create(kHTMLQuirksMode); |
| |
| switch (GetCellBorders()) { |
| case kSolidBordersColsOnly: |
| style->SetProperty(CSSPropertyID::kBorderLeftWidth, CSSValueID::kThin); |
| style->SetProperty(CSSPropertyID::kBorderRightWidth, CSSValueID::kThin); |
| style->SetProperty(CSSPropertyID::kBorderLeftStyle, CSSValueID::kSolid); |
| style->SetProperty(CSSPropertyID::kBorderRightStyle, CSSValueID::kSolid); |
| style->SetProperty(CSSPropertyID::kBorderColor, |
| *CSSInheritedValue::Create()); |
| break; |
| case kSolidBordersRowsOnly: |
| style->SetProperty(CSSPropertyID::kBorderTopWidth, CSSValueID::kThin); |
| style->SetProperty(CSSPropertyID::kBorderBottomWidth, CSSValueID::kThin); |
| style->SetProperty(CSSPropertyID::kBorderTopStyle, CSSValueID::kSolid); |
| style->SetProperty(CSSPropertyID::kBorderBottomStyle, CSSValueID::kSolid); |
| style->SetProperty(CSSPropertyID::kBorderColor, |
| *CSSInheritedValue::Create()); |
| break; |
| case kSolidBorders: |
| style->SetProperty( |
| CSSPropertyID::kBorderWidth, |
| *CSSPrimitiveValue::Create(1, CSSPrimitiveValue::UnitType::kPixels)); |
| style->SetProperty(CSSPropertyID::kBorderStyle, |
| *CSSIdentifierValue::Create(CSSValueID::kSolid)); |
| style->SetProperty(CSSPropertyID::kBorderColor, |
| *CSSInheritedValue::Create()); |
| break; |
| case kInsetBorders: |
| style->SetProperty( |
| CSSPropertyID::kBorderWidth, |
| *CSSPrimitiveValue::Create(1, CSSPrimitiveValue::UnitType::kPixels)); |
| style->SetProperty(CSSPropertyID::kBorderStyle, |
| *CSSIdentifierValue::Create(CSSValueID::kInset)); |
| style->SetProperty(CSSPropertyID::kBorderColor, |
| *CSSInheritedValue::Create()); |
| break; |
| case kNoBorders: |
| // If 'rules=none' then allow any borders set at cell level to take |
| // effect. |
| break; |
| } |
| |
| if (padding_) |
| style->SetProperty(CSSPropertyID::kPadding, |
| *CSSPrimitiveValue::Create( |
| padding_, CSSPrimitiveValue::UnitType::kPixels)); |
| |
| return style; |
| } |
| |
| const CSSPropertyValueSet* HTMLTableElement::AdditionalCellStyle() { |
| if (!shared_cell_style_) |
| shared_cell_style_ = CreateSharedCellStyle(); |
| return shared_cell_style_.Get(); |
| } |
| |
| static CSSPropertyValueSet* CreateGroupBorderStyle(int rows) { |
| MutableCSSPropertyValueSet* style = |
| MutableCSSPropertyValueSet::Create(kHTMLQuirksMode); |
| if (rows) { |
| style->SetProperty(CSSPropertyID::kBorderTopWidth, CSSValueID::kThin); |
| style->SetProperty(CSSPropertyID::kBorderBottomWidth, CSSValueID::kThin); |
| style->SetProperty(CSSPropertyID::kBorderTopStyle, CSSValueID::kSolid); |
| style->SetProperty(CSSPropertyID::kBorderBottomStyle, CSSValueID::kSolid); |
| } else { |
| style->SetProperty(CSSPropertyID::kBorderLeftWidth, CSSValueID::kThin); |
| style->SetProperty(CSSPropertyID::kBorderRightWidth, CSSValueID::kThin); |
| style->SetProperty(CSSPropertyID::kBorderLeftStyle, CSSValueID::kSolid); |
| style->SetProperty(CSSPropertyID::kBorderRightStyle, CSSValueID::kSolid); |
| } |
| return style; |
| } |
| |
| const CSSPropertyValueSet* HTMLTableElement::AdditionalGroupStyle(bool rows) { |
| if (rules_attr_ != kGroupsRules) |
| return nullptr; |
| |
| if (rows) { |
| DEFINE_STATIC_LOCAL(Persistent<CSSPropertyValueSet>, row_border_style, |
| (CreateGroupBorderStyle(true))); |
| return row_border_style; |
| } |
| DEFINE_STATIC_LOCAL(Persistent<CSSPropertyValueSet>, column_border_style, |
| (CreateGroupBorderStyle(false))); |
| return column_border_style; |
| } |
| |
| bool HTMLTableElement::IsURLAttribute(const Attribute& attribute) const { |
| return attribute.GetName() == kBackgroundAttr || |
| HTMLElement::IsURLAttribute(attribute); |
| } |
| |
| bool HTMLTableElement::HasLegalLinkAttribute(const QualifiedName& name) const { |
| return name == kBackgroundAttr || HTMLElement::HasLegalLinkAttribute(name); |
| } |
| |
| const QualifiedName& HTMLTableElement::SubResourceAttributeName() const { |
| return kBackgroundAttr; |
| } |
| |
| HTMLTableRowsCollection* HTMLTableElement::rows() { |
| return EnsureCachedCollection<HTMLTableRowsCollection>(kTableRows); |
| } |
| |
| HTMLCollection* HTMLTableElement::tBodies() { |
| return EnsureCachedCollection<HTMLCollection>(kTableTBodies); |
| } |
| |
| const AtomicString& HTMLTableElement::Rules() const { |
| return getAttribute(kRulesAttr); |
| } |
| |
| const AtomicString& HTMLTableElement::Summary() const { |
| return getAttribute(kSummaryAttr); |
| } |
| |
| void HTMLTableElement::Trace(Visitor* visitor) { |
| visitor->Trace(shared_cell_style_); |
| HTMLElement::Trace(visitor); |
| } |
| |
| } // namespace blink |