blob: 18bcaaacd6ff22962853def20c153a12d3fecd83 [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
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef COBALT_DOM_DOCUMENT_H_
#define COBALT_DOM_DOCUMENT_H_
#include <deque>
#include <map>
#include <memory>
#include <queue>
#include <string>
#include <vector>
#include "base/callback.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "base/observer_list_types.h"
#include "base/optional.h"
#include "base/strings/string_piece.h"
#include "base/synchronization/waitable_event.h"
#include "cobalt/base/clock.h"
#include "cobalt/cssom/css_computed_style_declaration.h"
#include "cobalt/cssom/css_keyframes_rule.h"
#include "cobalt/cssom/css_style_sheet.h"
#include "cobalt/cssom/mutation_observer.h"
#include "cobalt/cssom/selector_tree.h"
#include "cobalt/cssom/style_sheet_list.h"
#include "cobalt/cssom/viewport_size.h"
#include "cobalt/dom/application_lifecycle_state.h"
#include "cobalt/dom/csp_delegate_type.h"
#include "cobalt/dom/document_ready_state.h"
#include "cobalt/dom/document_timeline.h"
#include "cobalt/dom/event.h"
#include "cobalt/dom/html_element_context.h"
#include "cobalt/dom/intersection_observer_task_manager.h"
#include "cobalt/dom/location.h"
#include "cobalt/dom/node.h"
#include "cobalt/dom/pointer_state.h"
#include "cobalt/dom/visibility_state.h"
#include "cobalt/math/size.h"
#include "cobalt/network_bridge/cookie_jar.h"
#include "cobalt/network_bridge/net_poster.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/wrappable.h"
#include "url/gurl.h"
namespace cobalt {
namespace dom {
class Comment;
class CspDelegate;
class DOMImplementation;
class Element;
class FontCache;
class HTMLBodyElement;
class HTMLCollection;
class HTMLElement;
class HTMLElementContext;
class HTMLHeadElement;
class HTMLHtmlElement;
class HTMLMediaElement;
class HTMLScriptElement;
class Location;
class Text;
class Window;
class DocumentObserver : public base::CheckedObserver {
public:
// Called at most once, when document and all referred resources are loaded.
virtual void OnLoad() = 0;
// Called each time when the document or one of its descendants is changed.
virtual void OnMutation() = 0;
// Called when document.activeElement changes.
virtual void OnFocusChanged() = 0;
protected:
virtual ~DocumentObserver() {}
};
// The Document interface serves as an entry point into the web page's content
// (the DOM tree, including elements such as <head> and <body>) and provides
// functionality which is global to the document.
// https://www.w3.org/TR/dom/#document
class Document : public Node,
public cssom::MutationObserver,
public ApplicationLifecycleState::Observer {
public:
struct Options {
Options()
: window(NULL),
cookie_jar(NULL),
csp_enforcement_mode(kCspEnforcementEnable) {}
explicit Options(const GURL& url_value)
: url(url_value),
window(NULL),
cookie_jar(NULL),
csp_enforcement_mode(kCspEnforcementEnable) {}
Options(const GURL& url_value, Window* window,
const base::Closure& hashchange_callback,
const scoped_refptr<base::BasicClock>& navigation_start_clock_value,
const base::Callback<void(const GURL&)>& navigation_callback,
const scoped_refptr<cssom::CSSStyleSheet> user_agent_style_sheet,
const base::Optional<cssom::ViewportSize>& viewport_size,
network_bridge::CookieJar* cookie_jar,
const network_bridge::PostSender& post_sender,
csp::CSPHeaderPolicy require_csp,
CspEnforcementType csp_enforcement_mode,
const base::Closure& csp_policy_changed_callback,
int csp_insecure_allowed_token = 0, int dom_max_element_depth = 0)
: url(url_value),
window(window),
hashchange_callback(hashchange_callback),
navigation_start_clock(navigation_start_clock_value),
navigation_callback(navigation_callback),
user_agent_style_sheet(user_agent_style_sheet),
viewport_size(viewport_size),
cookie_jar(cookie_jar),
post_sender(post_sender),
require_csp(require_csp),
csp_enforcement_mode(csp_enforcement_mode),
csp_policy_changed_callback(csp_policy_changed_callback),
csp_insecure_allowed_token(csp_insecure_allowed_token),
dom_max_element_depth(dom_max_element_depth) {}
GURL url;
Window* window;
base::Closure hashchange_callback;
scoped_refptr<base::BasicClock> navigation_start_clock;
base::Callback<void(const GURL&)> navigation_callback;
scoped_refptr<cssom::CSSStyleSheet> user_agent_style_sheet;
base::Optional<cssom::ViewportSize> viewport_size;
network_bridge::CookieJar* cookie_jar;
network_bridge::PostSender post_sender;
csp::CSPHeaderPolicy require_csp;
CspEnforcementType csp_enforcement_mode;
base::Closure csp_policy_changed_callback;
int csp_insecure_allowed_token;
int dom_max_element_depth;
};
Document(HTMLElementContext* html_element_context,
const Options& options = Options());
// Web API: Node
//
NodeType node_type() const override { return Node::kDocumentNode; }
base::Token node_name() const override;
// Web API: Document
//
scoped_refptr<DOMImplementation> implementation();
const std::string& url() const { return location_->url().spec(); }
const std::string& document_uri() const { return location_->url().spec(); }
scoped_refptr<Element> document_element() const;
std::string title() const;
scoped_refptr<Window> default_view() const;
scoped_refptr<HTMLCollection> GetElementsByTagName(
const std::string& local_name) const;
scoped_refptr<HTMLCollection> GetElementsByClassName(
const std::string& class_names) const;
scoped_refptr<Element> CreateElement(const std::string& local_name);
scoped_refptr<Element> CreateElementNS(const std::string& namespace_uri,
const std::string& local_name);
scoped_refptr<Text> CreateTextNode(const std::string& data);
scoped_refptr<Comment> CreateComment(const std::string& data);
scoped_refptr<Event> CreateEvent(const std::string& interface_name,
script::ExceptionState* exception_state);
// Web API: NonElementParentNode (implements)
// https://www.w3.org/TR/2014/WD-dom-20140710/#interface-nonelementparentnode
//
scoped_refptr<Element> GetElementById(const std::string& id) const;
// Web API: HTML5 (partial interface)
// https://www.w3.org/TR/html50/dom.html#the-document-object
//
const scoped_refptr<Location>& location() const;
std::string dir() const;
void set_dir(const std::string& value);
scoped_refptr<HTMLBodyElement> body() const;
void set_body(const scoped_refptr<HTMLBodyElement>& body);
scoped_refptr<HTMLHeadElement> head() const;
// https://www.w3.org/TR/html50/editing.html#dom-document-hasfocus
bool HasFocus() const;
scoped_refptr<Element> active_element() const;
scoped_refptr<HTMLElement> indicated_element() const;
const EventListenerScriptValue* onreadystatechange() const {
return GetAttributeEventListener(base::Tokens::readystatechange());
}
void set_onreadystatechange(const EventListenerScriptValue& event_listener) {
SetAttributeEventListener(base::Tokens::readystatechange(), event_listener);
}
// Web API: CSS Object Model (partial interface)
// http://dev.w3.org/csswg/cssom/#extensions-to-the-document-interface
const scoped_refptr<cssom::StyleSheetList>& style_sheets();
// Web Animations API
// https://www.w3.org/TR/2015/WD-web-animations-1-20150707/#extensions-to-the-document-interface
const scoped_refptr<DocumentTimeline>& timeline() const {
return default_timeline_;
}
// https://www.w3.org/TR/html50/dom.html#dom-document-cookie
void set_cookie(const std::string& cookie,
script::ExceptionState* exception_state);
std::string cookie(script::ExceptionState* exception_state) const;
// For Cobalt code use only. Logs warnings instead of raising exceptions.
void set_cookie(const std::string& cookie);
std::string cookie() const;
// Returns the document's ready state, i.e. whether the document's 'load'
// event has fired yet or not.
// https://www.w3.org/TR/html50/dom.html#dom-document-readystate
DocumentReadyState ready_state() const { return ready_state_; }
// Custom, not in any spec: Node.
//
Document* AsDocument() override { return this; }
void Accept(NodeVisitor* visitor) override;
void Accept(ConstNodeVisitor* visitor) const override;
scoped_refptr<Node> Duplicate() const override;
// Custom, not in any spec.
//
virtual bool IsXMLDocument() const { return false; }
HTMLElementContext* html_element_context() const {
return html_element_context_;
}
FontCache* font_cache() const { return font_cache_.get(); }
const GURL& url_as_gurl() const { return location_->url(); }
scoped_refptr<HTMLHtmlElement> html() const;
// List of scripts that will execute in order as soon as possible.
// https://www.w3.org/TR/html50/scripting-1.html#list-of-scripts-that-will-execute-in-order-as-soon-as-possible
std::deque<HTMLScriptElement*>* scripts_to_be_executed() {
return &scripts_to_be_executed_;
}
cssom::SelectorTree* selector_tree() { return selector_tree_.get(); }
cssom::RulesWithCascadePrecedence* scratchpad_html_element_matching_rules() {
return &scratchpad_html_element_matching_rules_;
}
cssom::RulesWithCascadePrecedence* scratchpad_pseudo_element_matching_rules(
PseudoElementType element_type) {
return &(scratchpad_pseudo_element_matching_rules_[element_type]);
}
// Returns a mapping from keyframes name to CSSKeyframesRule. This can be
// used to quickly lookup the @keyframes rule given a string identifier.
const cssom::CSSKeyframesRule::NameMap& keyframes_map() const {
return keyframes_map_;
}
// Returns whether the document has browsing context. Having the browsing
// context means the document is shown on the screen.
// https://www.w3.org/TR/html50/browsers.html#browsing-context
bool HasBrowsingContext() const { return !!window_; }
void set_window(Window* window) { window_ = window; }
const scoped_refptr<Window> window();
// Sets the active element of the document.
void SetActiveElement(Element* active_element);
// Sets the indicated element of the document.
void SetIndicatedElement(HTMLElement* indicated_element);
// Count all ongoing loadings, including document itself and its dependent
// resources, and dispatch OnLoad() if necessary.
void IncreaseLoadingCounter();
void DecreaseLoadingCounter();
void DecreaseLoadingCounterAndMaybeDispatchLoadEvent();
// Utilities related to DocumentObserver.
void AddObserver(DocumentObserver* observer);
void RemoveObserver(DocumentObserver* observer);
void SignalOnLoadToObservers();
// Must be called by all descendants of the document on their modification.
// TODO: Provide more granularity, model after mutation observers
// (see https://www.w3.org/TR/dom/#mutation-observers).
void RecordMutation();
// Called when the focus changes. This should be called only once when the
// focus is shifted from one element to another.
void OnFocusChange();
// Called when the DOM style sheets changed.
void OnStyleSheetsModified();
// From cssom::MutationObserver.
void OnCSSMutation() override;
// Called when the DOM is mutated in some way.
void OnDOMMutation();
// Called when a new typeface has been loaded.
void OnTypefaceLoadEvent();
// Called when the inline style of an element is modified.
void OnElementInlineStyleMutation();
// Updates the computed styles of all of this document's HTML elements.
// Matching rules, media rules, font faces and key frames are also updated.
void UpdateComputedStyles();
// Updates the computed styles of the element and all its ancestors.
// Matching rules, media rules, font faces and key frames are also updated.
// Returns whether the computed style is valid after the call.
bool UpdateComputedStyleOnElementAndAncestor(HTMLElement* element);
// Manages the clock used by Web Animations.
// https://www.w3.org/TR/web-animations
// This clock is also used for requestAnimationFrame() callbacks, according
// to the specification above.
void SampleTimelineTime();
const scoped_refptr<base::BasicClock>& navigation_start_clock() const {
return navigation_start_clock_;
}
CspDelegate* csp_delegate() const { return csp_delegate_.get(); }
#if defined(ENABLE_PARTIAL_LAYOUT_CONTROL)
bool partial_layout_is_enabled() { return partial_layout_is_enabled_; }
void SetPartialLayout(bool enabled);
#endif // defined(ENABLE_PARTIAL_LAYOUT_CONTROL)
// Triggers a synchronous layout.
scoped_refptr<render_tree::Node> DoSynchronousLayoutAndGetRenderTree();
void DoSynchronousLayout();
void set_synchronous_layout_callback(
const base::Closure& synchronous_layout_callback) {
synchronous_layout_callback_ = synchronous_layout_callback;
}
void set_synchronous_layout_and_produce_render_tree_callback(
const base::Callback<scoped_refptr<render_tree::Node>()>&
synchronous_layout_and_produce_render_tree_callback) {
synchronous_layout_and_produce_render_tree_callback_ =
synchronous_layout_and_produce_render_tree_callback;
}
cssom::ViewportSize viewport_size();
void SetViewport(const cssom::ViewportSize& viewport_size);
const scoped_refptr<cssom::CSSComputedStyleDeclaration>&
initial_computed_style_declaration() const {
return initial_computed_style_declaration_;
}
const scoped_refptr<const cssom::CSSComputedStyleData>&
initial_computed_style_data() const {
return initial_computed_style_data_;
}
int dom_max_element_depth() const { return dom_max_element_depth_; }
void NotifyUrlChanged(const GURL& url);
// Updates the selector tree using all the style sheets in the document.
// Exposed for test purposes.
void UpdateSelectorTree();
void PurgeCachedResources();
void InvalidateLayoutBoxes();
// Disable just-in-time compilation of JavaScript code.
void DisableJit();
// Page Visibility fields.
bool hidden() const { return visibility_state() == kVisibilityStateHidden; }
VisibilityState visibility_state() const {
return application_lifecycle_state()->GetVisibilityState();
}
const EventListenerScriptValue* onvisibilitychange() const {
return GetAttributeEventListener(base::Tokens::visibilitychange());
}
void set_onvisibilitychange(const EventListenerScriptValue& event_listener) {
SetAttributeEventListener(base::Tokens::visibilitychange(), event_listener);
}
// Page Lifecycle fields.
const EventListenerScriptValue* onfreeze() const {
return GetAttributeEventListener(base::Tokens::freeze());
}
const EventListenerScriptValue* onresume() const {
return GetAttributeEventListener(base::Tokens::resume());
}
void set_onfreeze(const EventListenerScriptValue& event_listener) {
SetAttributeEventListener(base::Tokens::freeze(), event_listener);
}
void set_onresume(const EventListenerScriptValue& event_listener) {
SetAttributeEventListener(base::Tokens::resume(), event_listener);
}
// ApplicationLifecycleState::Observer implementation.
void OnWindowFocusChanged(bool has_focus) override;
void OnVisibilityStateChanged(VisibilityState visibility_state) override;
void OnFrozennessChanged(bool is_frozen) override;
bool was_discarded() const { return false; }
PointerState* pointer_state() { return &pointer_state_; }
// render_postponed is a Cobalt-specific Web API.
bool render_postponed() const { return render_postponed_; }
void set_render_postponed(bool render_postponed);
// Called when the root element has its offset dimensions requested and is
// unable to provide them.
void OnRootElementUnableToProvideOffsetDimensions();
IntersectionObserverTaskManager* intersection_observer_task_manager() const {
return intersection_observer_task_manager_;
}
DEFINE_WRAPPABLE_TYPE(Document);
void TraceMembers(script::Tracer* tracer) override;
protected:
~Document() override;
ApplicationLifecycleState* application_lifecycle_state() {
return html_element_context_->application_lifecycle_state().get();
}
const ApplicationLifecycleState* application_lifecycle_state() const {
return html_element_context_->application_lifecycle_state().get();
}
private:
void DispatchOnLoadEvent();
// Updates the style sheets in the document.
void UpdateStyleSheets();
// Updates the media rules in all the style sheets in the document.
void UpdateMediaRules();
// Updates the font faces in all the style sheets in the document.
void UpdateFontFaces();
// Compiles/updates a set of all declared CSS keyframes used to define CSS
// Animations, using all the style sheets in the document.
void UpdateKeyframes();
bool IsCookieAverseDocument() const;
// Collect HTML media elements for the preparation of changing the
// frozeness of documents.
void CollectHTMLMediaElements(
std::vector<HTMLMediaElement*>* html_media_elements);
// https://wicg.github.io/page-lifecycle/#changing-frozenness
void FreezeSteps();
// https://wicg.github.io/page-lifecycle/#changing-frozenness
void ResumeSteps();
// Reference to HTML element context.
HTMLElementContext* const html_element_context_;
// Explicitly store a weak pointer to the application lifecycle state object.
// It is possible that we destroy the application lifecycle state object
// before Document, during shutdown, so this allows us to handle that
// situation more gracefully than crashing.
base::WeakPtr<ApplicationLifecycleState> application_lifecycle_state_;
// Reference to the associated window object.
Window* window_;
// Associated DOM implementation object.
scoped_refptr<DOMImplementation> implementation_;
// List of CSS style sheets.
scoped_refptr<cssom::StyleSheetList> style_sheets_;
// List of scripts that will execute in order as soon as possible.
std::deque<HTMLScriptElement*> scripts_to_be_executed_;
// A mapping from keyframes declaration names to their parsed structure.
cssom::CSSKeyframesRule::NameMap keyframes_map_;
// The number of ongoing loadings.
int loading_counter_;
// Whether the load event should be dispatched when loading counter hits zero.
bool should_dispatch_load_event_;
// Indicates if the document's style sheets need to be re-collected before
// the next layout.
bool are_style_sheets_dirty_;
// Indicates if rule matching/computed style is dirty and needs to be
// recomputed before the next layout.
bool is_selector_tree_dirty_;
bool is_computed_style_dirty_;
bool are_font_faces_dirty_;
bool are_keyframes_dirty_;
#if defined(ENABLE_PARTIAL_LAYOUT_CONTROL)
bool partial_layout_is_enabled_;
#endif // defined(ENABLE_PARTIAL_LAYOUT_CONTROL)
// Viewport size.
base::Optional<cssom::ViewportSize> viewport_size_;
// Content Security Policy enforcement for this document.
std::unique_ptr<CspDelegate> csp_delegate_;
network_bridge::CookieJar* cookie_jar_;
// Associated location object.
scoped_refptr<Location> location_;
// The font cache for this document.
std::unique_ptr<FontCache> font_cache_;
// Weak reference to the active element.
base::WeakPtr<Element> active_element_;
// Weak reference to the indicated element.
base::WeakPtr<HTMLElement> indicated_element_;
// List of document observers.
base::ObserverList<DocumentObserver> observers_;
// Selector Tree.
std::unique_ptr<cssom::SelectorTree> selector_tree_;
// This is set when the document has a style sheet removed or the order of its
// style sheets changed. In this case, it is more straightforward to simply
// recreate the selector tree than to attempt to manage updating all of its
// internal state.
bool should_recreate_selector_tree_;
// Matching rules that are available for temporary operations, so that the
// vectors don't have to be repeatedly re-allocated during rule matching.
cssom::RulesWithCascadePrecedence scratchpad_html_element_matching_rules_;
cssom::RulesWithCascadePrecedence
scratchpad_pseudo_element_matching_rules_[kMaxPseudoElementType];
// The document's latest sample from the global clock, used for updating
// animations.
const scoped_refptr<base::BasicClock> navigation_start_clock_;
scoped_refptr<DocumentTimeline> default_timeline_;
base::Callback<scoped_refptr<render_tree::Node>()>
synchronous_layout_and_produce_render_tree_callback_;
base::Closure synchronous_layout_callback_;
scoped_refptr<cssom::CSSStyleSheet> user_agent_style_sheet_;
// Computed style of the initial containing block, width and height come from
// the viewport size.
scoped_refptr<cssom::CSSComputedStyleDeclaration>
initial_computed_style_declaration_;
scoped_refptr<const cssom::CSSComputedStyleData> initial_computed_style_data_;
// The document's current ready state (e.g. has the 'load' event been fired
// yet)
DocumentReadyState ready_state_;
// The max depth of elements that are guaranteed to be rendered.
int dom_max_element_depth_;
// Various state related to pointer and mouse support.
PointerState pointer_state_;
// Whether or not rendering is currently postponed.
bool render_postponed_;
// Whether or not page lifecycle is currently frozen.
// https://wicg.github.io/page-lifecycle/#page-lifecycle
bool frozenness_;
scoped_refptr<IntersectionObserverTaskManager>
intersection_observer_task_manager_;
};
} // namespace dom
} // namespace cobalt
#endif // COBALT_DOM_DOCUMENT_H_