blob: d1f548dc45a78304bba7a3ba75804ee81cddbde2 [file] [log] [blame]
// Copyright 2014 The Cobalt Authors. All Rights Reserved.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.
#include <memory>
#include <string>
#include <vector>
#include "base/compiler_specific.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "base/optional.h"
#include "base/strings/string_piece.h"
#include "cobalt/base/token.h"
#include "cobalt/cssom/animation_set.h"
#include "cobalt/cssom/css_computed_style_declaration.h"
#include "cobalt/cssom/css_declared_style_declaration.h"
#include "cobalt/cssom/css_style_declaration.h"
#include "cobalt/cssom/css_style_rule.h"
#include "cobalt/cssom/css_transition_set.h"
#include "cobalt/cssom/mutation_observer.h"
#include "cobalt/cssom/selector_tree.h"
#include "cobalt/cssom/style_sheet_list.h"
#include "cobalt/dom/css_animations_adapter.h"
#include "cobalt/dom/css_transitions_adapter.h"
#include "cobalt/dom/directionality.h"
#include "cobalt/dom/dom_rect_list.h"
#include "cobalt/dom/dom_stat_tracker.h"
#include "cobalt/dom/element.h"
#include "cobalt/dom/layout_boxes.h"
#include "cobalt/dom/pseudo_element.h"
#include "cobalt/loader/image/image_cache.h"
#include "cobalt/ui_navigation/nav_item.h"
namespace cobalt {
namespace dom {
class DOMStringMap;
class HTMLAnchorElement;
class HTMLAudioElement;
class HTMLBodyElement;
class HTMLBRElement;
class HTMLDivElement;
class HTMLElementContext;
class HTMLHeadElement;
class HTMLHeadingElement;
class HTMLHtmlElement;
class HTMLImageElement;
class HTMLLinkElement;
class HTMLMetaElement;
class HTMLParagraphElement;
class HTMLScriptElement;
class HTMLSpanElement;
class HTMLStyleElement;
class HTMLTitleElement;
class HTMLUnknownElement;
class HTMLVideoElement;
// The enum PseudoElementType is used to track the type of pseudo element
enum PseudoElementType {
kNotPseudoElementType = kMaxPseudoElementType,
// The basic interface, from which all the HTML elements' interfaces inherit,
// and which must be used by elements that have no additional requirements.
class HTMLElement : public Element, public cssom::MutationObserver {
typedef cssom::SelectorTree::Nodes SelectorTreeNodes;
// Cached state used with rule matching to minimize the amount of work that
// must occur during UpdateMatchingRules() when little has changed.
struct RuleMatchingState {
: is_set(false),
are_following_sibling_nodes_dirty(true) {}
// Fully clears the state but does not deallocate the vectors. This allows
// the vectors to be reused without additional allocations the next time
// they are needed and speeds up rule matching.
void Clear();
// Whether or not the rule matching state is set. When it is not set, the
// next call to UpdateMatchingRules() always generates new matching rules
// from the rule matching state.
bool is_set;
// The cached node state of the parent and previous sibling. Caching these
// allows the element to know the exact nodes that have changed and
// resultantly need to be checked during calls to UpdateMatchingRules().
SelectorTreeNodes parent_matching_nodes;
SelectorTreeNodes parent_descendant_nodes;
SelectorTreeNodes previous_sibling_matching_nodes;
SelectorTreeNodes previous_sibling_following_sibling_nodes;
// The element's current matching nodes, along with their parent nodes.
// These are kept in sync. This allows matching nodes to be removed when
// their parent nodes are no longer available to the element.
SelectorTreeNodes matching_nodes_parent_nodes;
SelectorTreeNodes matching_nodes;
// The nodes that are to be used by the element's descendants and following
// siblings during their own rule matching. These are generated by combining
// the nodes from the element's parent and previous sibling with the
// element's own matching nodes that contain the required combinator.
bool are_descendant_nodes_dirty;
SelectorTreeNodes descendant_nodes;
bool are_following_sibling_nodes_dirty;
SelectorTreeNodes following_sibling_nodes;
enum AncestorsAreDisplayed {
// NOTE: 'auto' is not supported.
enum DirState {
// Web API: HTMLElement
std::string dir() const;
void set_dir(const std::string& value);
scoped_refptr<DOMStringMap> dataset();
int32 tab_index() const;
void set_tab_index(int32 tab_index);
void Focus();
void Blur();
// Web API: ElementCSSInlineStyle (implements)
const scoped_refptr<cssom::CSSDeclaredStyleDeclaration>& style() {
return style_;
// Web API: CSSOM View Module: Extensions to the Element Interface (partial
// interface)
scoped_refptr<DOMRectList> GetClientRects() override;
float client_top() override;
float client_left() override;
float client_width() override;
float client_height() override;
// Updated version of the CSSOM View Module extensions:
int32 scroll_width() override;
int32 scroll_height() override;
// These attributes are only partially implemented. They will only work with
// elements associated with UI navigation containers.
float scroll_left() override;
float scroll_top() override;
void set_scroll_left(float x) override;
void set_scroll_top(float y) override;
// Web API: CSSOM View Module: Extensions to the HTMLElement Interface
// (partial interface)
Element* offset_parent();
float offset_top();
float offset_left();
float offset_width();
float offset_height();
// Custom, not in any spec: Node.
scoped_refptr<Node> Duplicate() const override;
// Custom, not in any spec: Element.
scoped_refptr<HTMLElement> AsHTMLElement() override { return this; }
base::Optional<std::string> GetStyleAttribute() const override;
void SetStyleAttribute(const std::string& value) override;
void RemoveStyleAttribute() override;
// Custom, not in any spec.
// From cssom::CSSStyleDeclaration::MutationObserver.
void OnCSSMutation() override;
// Safe type conversion methods that will downcast to the required type if
// possible or return NULL otherwise.
virtual scoped_refptr<HTMLAnchorElement> AsHTMLAnchorElement();
virtual scoped_refptr<HTMLAudioElement> AsHTMLAudioElement();
virtual scoped_refptr<HTMLBodyElement> AsHTMLBodyElement();
virtual scoped_refptr<HTMLBRElement> AsHTMLBRElement();
virtual scoped_refptr<HTMLDivElement> AsHTMLDivElement();
virtual scoped_refptr<HTMLHeadElement> AsHTMLHeadElement();
virtual scoped_refptr<HTMLHeadingElement> AsHTMLHeadingElement();
virtual scoped_refptr<HTMLHtmlElement> AsHTMLHtmlElement();
virtual scoped_refptr<HTMLImageElement> AsHTMLImageElement();
virtual scoped_refptr<HTMLLinkElement> AsHTMLLinkElement();
virtual scoped_refptr<HTMLMetaElement> AsHTMLMetaElement();
virtual scoped_refptr<HTMLParagraphElement> AsHTMLParagraphElement();
virtual scoped_refptr<HTMLScriptElement> AsHTMLScriptElement();
virtual scoped_refptr<HTMLSpanElement> AsHTMLSpanElement();
virtual scoped_refptr<HTMLStyleElement> AsHTMLStyleElement();
virtual scoped_refptr<HTMLTitleElement> AsHTMLTitleElement();
virtual scoped_refptr<HTMLUnknownElement> AsHTMLUnknownElement();
virtual scoped_refptr<HTMLVideoElement> AsHTMLVideoElement();
// Returns the directionality of the element, which is based upon the
// element's "dir" attribute if it was set, or that of the parent's if not
// set.
Directionality directionality();
// Retrieve the dir attribute state. This is similar to dir() but returns the
// enumerated state rather than string.
DirState dir_state() const { return dir_; }
// Rule matching related methods.
// Returns the rule matching state of this element.
RuleMatchingState* rule_matching_state() { return &rule_matching_state_; }
void ClearRuleMatchingState();
void ClearRuleMatchingStateOnElementAndAncestors(
bool invalidate_tree_matching_rules);
void ClearRuleMatchingStateOnElementAndDescendants();
void ClearRuleMatchingStateOnElementAndSiblingsAndDescendants();
// Returns the cached matching rules of this element.
cssom::RulesWithCascadePrecedence* matching_rules() {
return &matching_rules_;
void InvalidateMatchingRulesRecursively();
void UpdateMatchingRules();
void UpdateMatchingRulesRecursively();
void OnMatchingRulesModified();
void OnPseudoElementMatchingRulesModified();
// Computed style related methods.
// Used by layout engine to cache the computed values.
// See for the definition of
// computed value.
css_computed_style_declaration() {
return css_computed_style_declaration_;
const scoped_refptr<const cssom::CSSComputedStyleData>& computed_style()
const {
return css_computed_style_declaration_->data();
// Updates the cached computed style of this element and its descendants.
void UpdateComputedStyleRecursively(
const scoped_refptr<cssom::CSSComputedStyleDeclaration>&
const scoped_refptr<const cssom::CSSComputedStyleData>&
const base::TimeDelta& style_change_event_time, bool ancestors_were_valid,
int current_element_depth);
// Updates the cached computed style of this element.
void UpdateComputedStyle(
const scoped_refptr<cssom::CSSComputedStyleDeclaration>&
const scoped_refptr<const cssom::CSSComputedStyleData>&
const base::TimeDelta& style_change_event_time,
AncestorsAreDisplayed ancestor_is_displayed);
void MarkNotDisplayedOnNodeAndDescendants() override;
void PurgeCachedBackgroundImagesOfNodeAndDescendants() override;
void InvalidateComputedStylesOfNodeAndDescendants() override;
void InvalidateLayoutBoxesOfNodeAndAncestors() override;
void InvalidateLayoutBoxesOfNodeAndDescendants() override;
void InvalidateLayoutBoxSizes() override;
void InvalidateLayoutBoxCrossReferences() override;
void InvalidateLayoutBoxRenderTreeNodes() override;
// Layout box related methods.
// The LayoutContainerBox gives the HTML Element an interface to the container
// box that result from it. The BoxList is set when layout is performed for a
// node.
void set_layout_boxes(std::unique_ptr<LayoutBoxes> layout_boxes) {
layout_boxes_ = std::move(layout_boxes);
LayoutBoxes* layout_boxes() const { return layout_boxes_.get(); }
PseudoElement* pseudo_element(PseudoElementType type) const {
DCHECK(type < kMaxPseudoElementType);
return pseudo_elements_[type].get();
void SetPseudoElement(PseudoElementType type,
std::unique_ptr<PseudoElement> pseudo_element);
// Returns true if the element's computed style and all of its pseudo
// element's computed styles are valid.
bool AreComputedStylesValid() const;
bool descendant_computed_styles_valid() const {
return descendant_computed_styles_valid_;
bool matching_rules_valid() const { return matching_rules_valid_; }
void set_matching_rules_valid() { matching_rules_valid_ = true; }
// Returns whether the element has been designated.
bool IsDesignated() const;
// Returns whether the element can be designated by a pointer.
bool CanbeDesignatedByPointerIfDisplayed() const;
// Returns true if this node and all of its ancestors do NOT have display set
// to 'none'.
bool IsDisplayed() const;
// Get the UI navigation item (if any) representing this HTML element.
const scoped_refptr<ui_navigation::NavItem>& GetUiNavItem() const {
return ui_nav_item_;
// Returns true if the element is the root element as defined in
bool IsRootElement();
HTMLElement(Document* document, base::Token local_name);
~HTMLElement() override;
void OnInsertedIntoDocument() override;
void OnRemovedFromDocument() override;
// From Element.
void OnSetAttribute(const std::string& name,
const std::string& value) override;
void OnRemoveAttribute(const std::string& name) override;
// HTMLElement keeps a pointer to the dom stat tracker to ensure that it can
// make stat updates even after its weak pointer to its document has been
// deleted. This is protected because some derived classes need access to it.
DomStatTracker* const dom_stat_tracker_;
// From Node.
void OnMutation() override;
bool IsFocusable();
bool HasTabindexFocusFlag() const;
bool IsBeingRendered();
void RunFocusingSteps();
void RunUnFocusingSteps();
// This both updates the 'dir' attribute based upon the string value and
// invalidates layout box caching if the value has changed.
void SetDir(const std::string& value);
// Update the cached value of tabindex.
void SetTabIndex(const std::string& value);
// Invalidate the matching rules and rule matching state in this element and
// its descendants. In the case where this is the the initial invalidation,
// it will also invalidate the rule matching state of its siblings.
void InvalidateMatchingRulesRecursivelyInternal(bool is_initial_element);
// Fully clear the rule matching state of this element and optionally
// invalidate all of its descendants matching rules.
void ClearRuleMatchingStateInternal(bool invalidate_descendants);
// Update the UI navigation item type for this element.
void UpdateUiNavigationType();
// Clear the list of active background images, and notify the animated image
// tracker to stop the animations.
void ClearActiveBackgroundImages();
void UpdateCachedBackgroundImagesFromComputedStyle();
// This will be called when the image data associated with this element's
// computed style's background-image property is loaded.
void OnBackgroundImageLoaded();
// Purge the cached background images on only this node.
void PurgeCachedBackgroundImages();
// Helper function that hosts the shared code between
// InvalidateLayoutBoxesOfNodeAndAncestors() and
// InvalidateLayoutBoxesOfNodeAndDescendants().
void InvalidateLayoutBoxes();
// Handle UI navigation events.
void OnUiNavBlur();
void OnUiNavFocus();
void OnUiNavScroll();
bool locked_for_focus_;
// This represents the enumerated value of the 'dir' attribute.
DirState dir_;
// This represents the computed directionality for this element.
// NOTE: Cobalt does not support either the CSS 'direction' or 'unicode-bidi'
// properties, and instead relies entirely upon the 'dir' attribute for
// determining directionality. Inheritance of directionality occurs via the
// base direction of the parent element's paragraph.
base::Optional<Directionality> directionality_;
// Cache the tabindex value.
base::Optional<int32> tabindex_;
// The inline style specified via attribute's in the element's HTML tag, or
// through JavaScript (accessed via style() defined above).
scoped_refptr<cssom::CSSDeclaredStyleDeclaration> style_;
// Keeps track of whether the HTML element's current computed style is out
// of date or not.
bool computed_style_valid_;
// Keeps track of whether the HTML element's pseudo element's computed styles
// are out of date or not.
bool pseudo_elements_computed_styles_valid_;
// Keeps track of whether the HTML element's descendants' computed styles are
// out of date or not.
bool descendant_computed_styles_valid_;
// Indicates whether this node has an ancestor which has display set to none
// or not. This value gets updated when computed style is updated.
AncestorsAreDisplayed ancestors_are_displayed_;
dom::CSSTransitionsAdapter transitions_adapter_;
cssom::TransitionSet css_transitions_;
dom::CSSAnimationsAdapter animations_adapter_;
cssom::AnimationSet css_animations_;
// The following fields are used in rule matching.
RuleMatchingState rule_matching_state_;
bool matching_rules_valid_;
cssom::RulesWithCascadePrecedence matching_rules_;
// This contains information about the boxes generated from the element.
std::unique_ptr<LayoutBoxes> layout_boxes_;
std::unique_ptr<PseudoElement> pseudo_elements_[kMaxPseudoElementType];
base::WeakPtr<DOMStringMap> dataset_;
std::vector<GURL> active_background_images_;
// |cached_background_images_| contains a list of CachedImage references for
// all images referenced by the computed value for the background_image CSS
// property and a image loaded handle to add and remove image loaded callback.
// We maintain it here to indicate to the resource caching system
// that the images are currently in-use, and should not be purged.
loader::image::CachedImageReferenceVector cached_background_images_;
// Elements with specific styles or attributes can be animated by the
// platform's UI engine. This is done by attaching special animation nodes to
// the boxes generated for the relevant elements; the rendering pipeline will
// then query starboard for position data each frame, thus animating the
// boxes without requiring a new layout.
scoped_refptr<ui_navigation::NavItem> ui_nav_item_;
// This temporary flag is used to avoid a cycle on focus changes. When the
// HTML element receives focus, it must inform the UI navigation item. When
// the UI navigation item receives focus (either by calling SetFocus or by an
// update from the UI engine), it will tell the HTML element it was focused.
bool ui_nav_focusing_ = false;
// HTMLElement is a friend of Animatable so that animatable can insert and
// remove animations into HTMLElement's set of animations.
friend class DOMAnimatable;
} // namespace dom
} // namespace cobalt