| // 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_ |