blob: 851679dc3bb8012af65e6c87f154a8b4e1c319e7 [file] [log] [blame]
/*
* Copyright (C) 1999 Lars Knoll (knoll@kde.org)
* (C) 1999 Antti Koivisto (koivisto@kde.org)
* (C) 2001 Dirk Mueller (mueller@kde.org)
* (C) 2006 Alexey Proskuryakov (ap@webkit.org)
* Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2011, 2012 Apple Inc. All
* rights reserved.
* Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved.
* (http://www.torchmobile.com/)
* Copyright (C) 2008, 2009, 2011, 2012 Google Inc. All rights reserved.
* Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies)
* Copyright (C) Research In Motion Limited 2010-2011. 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/dom/document.h"
#include <memory>
#include <utility>
#include "base/auto_reset.h"
#include "base/macros.h"
#include "base/optional.h"
#include "base/time/time.h"
#include "cc/input/overscroll_behavior.h"
#include "cc/input/scroll_snap_data.h"
#include "components/performance_manager/public/mojom/coordination_unit.mojom-blink.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "mojo/public/mojom/base/text_direction.mojom-blink.h"
#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
#include "services/metrics/public/cpp/mojo_ukm_recorder.h"
#include "services/metrics/public/cpp/ukm_builders.h"
#include "services/metrics/public/cpp/ukm_source_id.h"
#include "services/metrics/public/mojom/ukm_interface.mojom-blink.h"
#include "services/network/public/mojom/ip_address_space.mojom-blink.h"
#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
#include "third_party/blink/public/common/browser_interface_broker_proxy.h"
#include "third_party/blink/public/common/feature_policy/document_policy_features.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/public/common/thread_safe_browser_interface_broker_proxy.h"
#include "third_party/blink/public/mojom/feature_policy/feature_policy.mojom-blink.h"
#include "third_party/blink/public/mojom/input/focus_type.mojom-blink.h"
#include "third_party/blink/public/mojom/insecure_input/insecure_input_service.mojom-blink.h"
#include "third_party/blink/public/mojom/ukm/ukm.mojom-blink.h"
#include "third_party/blink/public/platform/platform.h"
#include "third_party/blink/public/platform/task_type.h"
#include "third_party/blink/public/platform/web_content_settings_client.h"
#include "third_party/blink/public/platform/web_theme_engine.h"
#include "third_party/blink/renderer/bindings/core/v8/html_script_element_or_svg_script_element.h"
#include "third_party/blink/renderer/bindings/core/v8/isolated_world_csp.h"
#include "third_party/blink/renderer/bindings/core/v8/script_controller.h"
#include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
#include "third_party/blink/renderer/bindings/core/v8/script_value.h"
#include "third_party/blink/renderer/bindings/core/v8/source_location.h"
#include "third_party/blink/renderer/bindings/core/v8/string_or_element_creation_options.h"
#include "third_party/blink/renderer/bindings/core/v8/v0_custom_element_constructor_builder.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_element_creation_options.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_element_registration_options.h"
#include "third_party/blink/renderer/bindings/core/v8/window_proxy.h"
#include "third_party/blink/renderer/core/accessibility/ax_context.h"
#include "third_party/blink/renderer/core/accessibility/ax_object_cache.h"
#include "third_party/blink/renderer/core/animation/document_animations.h"
#include "third_party/blink/renderer/core/animation/document_timeline.h"
#include "third_party/blink/renderer/core/animation/pending_animations.h"
#include "third_party/blink/renderer/core/animation/worklet_animation_controller.h"
#include "third_party/blink/renderer/core/aom/computed_accessible_node.h"
#include "third_party/blink/renderer/core/css/css_font_selector.h"
#include "third_party/blink/renderer/core/css/css_property_value_set.h"
#include "third_party/blink/renderer/core/css/css_style_declaration.h"
#include "third_party/blink/renderer/core/css/css_style_sheet.h"
#include "third_party/blink/renderer/core/css/cssom/computed_style_property_map.h"
#include "third_party/blink/renderer/core/css/font_face_set_document.h"
#include "third_party/blink/renderer/core/css/invalidation/style_invalidator.h"
#include "third_party/blink/renderer/core/css/media_query_matcher.h"
#include "third_party/blink/renderer/core/css/parser/css_parser.h"
#include "third_party/blink/renderer/core/css/properties/css_property.h"
#include "third_party/blink/renderer/core/css/property_registry.h"
#include "third_party/blink/renderer/core/css/resolver/font_builder.h"
#include "third_party/blink/renderer/core/css/resolver/style_resolver.h"
#include "third_party/blink/renderer/core/css/resolver/style_resolver_stats.h"
#include "third_party/blink/renderer/core/css/selector_query.h"
#include "third_party/blink/renderer/core/css/style_change_reason.h"
#include "third_party/blink/renderer/core/css/style_engine.h"
#include "third_party/blink/renderer/core/css/style_sheet_contents.h"
#include "third_party/blink/renderer/core/css/style_sheet_list.h"
#include "third_party/blink/renderer/core/display_lock/display_lock_context.h"
#include "third_party/blink/renderer/core/display_lock/display_lock_utilities.h"
#include "third_party/blink/renderer/core/dom/attr.h"
#include "third_party/blink/renderer/core/dom/beforeunload_event_listener.h"
#include "third_party/blink/renderer/core/dom/cdata_section.h"
#include "third_party/blink/renderer/core/dom/comment.h"
#include "third_party/blink/renderer/core/dom/context_features.h"
#include "third_party/blink/renderer/core/dom/document_fragment.h"
#include "third_party/blink/renderer/core/dom/document_init.h"
#include "third_party/blink/renderer/core/dom/document_parser_timing.h"
#include "third_party/blink/renderer/core/dom/document_type.h"
#include "third_party/blink/renderer/core/dom/dom_implementation.h"
#include "third_party/blink/renderer/core/dom/element.h"
#include "third_party/blink/renderer/core/dom/element_data_cache.h"
#include "third_party/blink/renderer/core/dom/element_traversal.h"
#include "third_party/blink/renderer/core/dom/events/event.h"
#include "third_party/blink/renderer/core/dom/events/event_dispatch_forbidden_scope.h"
#include "third_party/blink/renderer/core/dom/events/event_listener.h"
#include "third_party/blink/renderer/core/dom/events/native_event_listener.h"
#include "third_party/blink/renderer/core/dom/events/scoped_event_queue.h"
#include "third_party/blink/renderer/core/dom/flat_tree_traversal.h"
#include "third_party/blink/renderer/core/dom/layout_tree_builder_traversal.h"
#include "third_party/blink/renderer/core/dom/live_node_list.h"
#include "third_party/blink/renderer/core/dom/mutation_observer.h"
#include "third_party/blink/renderer/core/dom/node_child_removal_tracker.h"
#include "third_party/blink/renderer/core/dom/node_computed_style.h"
#include "third_party/blink/renderer/core/dom/node_iterator.h"
#include "third_party/blink/renderer/core/dom/node_lists_node_data.h"
#include "third_party/blink/renderer/core/dom/node_rare_data.h"
#include "third_party/blink/renderer/core/dom/node_traversal.h"
#include "third_party/blink/renderer/core/dom/node_with_index.h"
#include "third_party/blink/renderer/core/dom/processing_instruction.h"
#include "third_party/blink/renderer/core/dom/scripted_animation_controller.h"
#include "third_party/blink/renderer/core/dom/shadow_root.h"
#include "third_party/blink/renderer/core/dom/slot_assignment.h"
#include "third_party/blink/renderer/core/dom/slot_assignment_engine.h"
#include "third_party/blink/renderer/core/dom/slot_assignment_recalc_forbidden_scope.h"
#include "third_party/blink/renderer/core/dom/static_node_list.h"
#include "third_party/blink/renderer/core/dom/transform_source.h"
#include "third_party/blink/renderer/core/dom/tree_walker.h"
#include "third_party/blink/renderer/core/dom/visited_link_state.h"
#include "third_party/blink/renderer/core/dom/whitespace_attacher.h"
#include "third_party/blink/renderer/core/dom/xml_document.h"
#include "third_party/blink/renderer/core/editing/editing_utilities.h"
#include "third_party/blink/renderer/core/editing/frame_selection.h"
#include "third_party/blink/renderer/core/editing/markers/document_marker_controller.h"
#include "third_party/blink/renderer/core/editing/serializers/serialization.h"
#include "third_party/blink/renderer/core/events/before_unload_event.h"
#include "third_party/blink/renderer/core/events/event_factory.h"
#include "third_party/blink/renderer/core/events/hash_change_event.h"
#include "third_party/blink/renderer/core/events/overscroll_event.h"
#include "third_party/blink/renderer/core/events/page_transition_event.h"
#include "third_party/blink/renderer/core/events/visual_viewport_resize_event.h"
#include "third_party/blink/renderer/core/events/visual_viewport_scroll_event.h"
#include "third_party/blink/renderer/core/execution_context/agent_metrics_collector.h"
#include "third_party/blink/renderer/core/execution_context/security_context_init.h"
#include "third_party/blink/renderer/core/execution_context/window_agent.h"
#include "third_party/blink/renderer/core/execution_context/window_agent_factory.h"
#include "third_party/blink/renderer/core/feature_policy/dom_document_policy.h"
#include "third_party/blink/renderer/core/feature_policy/feature_policy_parser.h"
#include "third_party/blink/renderer/core/frame/csp/content_security_policy.h"
#include "third_party/blink/renderer/core/frame/csp/navigation_initiator_impl.h"
#include "third_party/blink/renderer/core/frame/dom_timer.h"
#include "third_party/blink/renderer/core/frame/dom_visual_viewport.h"
#include "third_party/blink/renderer/core/frame/event_handler_registry.h"
#include "third_party/blink/renderer/core/frame/feature_policy_violation_report_body.h"
#include "third_party/blink/renderer/core/frame/frame_console.h"
#include "third_party/blink/renderer/core/frame/history.h"
#include "third_party/blink/renderer/core/frame/intervention.h"
#include "third_party/blink/renderer/core/frame/local_dom_window.h"
#include "third_party/blink/renderer/core/frame/local_frame.h"
#include "third_party/blink/renderer/core/frame/local_frame_client.h"
#include "third_party/blink/renderer/core/frame/local_frame_view.h"
#include "third_party/blink/renderer/core/frame/performance_monitor.h"
#include "third_party/blink/renderer/core/frame/report.h"
#include "third_party/blink/renderer/core/frame/reporting_context.h"
#include "third_party/blink/renderer/core/frame/settings.h"
#include "third_party/blink/renderer/core/frame/viewport_data.h"
#include "third_party/blink/renderer/core/frame/visual_viewport.h"
#include "third_party/blink/renderer/core/html/anchor_element_metrics.h"
#include "third_party/blink/renderer/core/html/canvas/canvas_font_cache.h"
#include "third_party/blink/renderer/core/html/canvas/canvas_rendering_context.h"
#include "third_party/blink/renderer/core/html/canvas/html_canvas_element.h"
#include "third_party/blink/renderer/core/html/custom/custom_element.h"
#include "third_party/blink/renderer/core/html/custom/custom_element_definition.h"
#include "third_party/blink/renderer/core/html/custom/custom_element_descriptor.h"
#include "third_party/blink/renderer/core/html/custom/custom_element_registry.h"
#include "third_party/blink/renderer/core/html/custom/v0_custom_element_microtask_run_queue.h"
#include "third_party/blink/renderer/core/html/custom/v0_custom_element_registration_context.h"
#include "third_party/blink/renderer/core/html/document_all_name_collection.h"
#include "third_party/blink/renderer/core/html/document_name_collection.h"
#include "third_party/blink/renderer/core/html/forms/form_controller.h"
#include "third_party/blink/renderer/core/html/forms/html_form_element.h"
#include "third_party/blink/renderer/core/html/forms/html_input_element.h"
#include "third_party/blink/renderer/core/html/html_all_collection.h"
#include "third_party/blink/renderer/core/html/html_anchor_element.h"
#include "third_party/blink/renderer/core/html/html_base_element.h"
#include "third_party/blink/renderer/core/html/html_body_element.h"
#include "third_party/blink/renderer/core/html/html_collection.h"
#include "third_party/blink/renderer/core/html/html_dialog_element.h"
#include "third_party/blink/renderer/core/html/html_document.h"
#include "third_party/blink/renderer/core/html/html_frame_owner_element.h"
#include "third_party/blink/renderer/core/html/html_head_element.h"
#include "third_party/blink/renderer/core/html/html_html_element.h"
#include "third_party/blink/renderer/core/html/html_link_element.h"
#include "third_party/blink/renderer/core/html/html_meta_element.h"
#include "third_party/blink/renderer/core/html/html_object_element.h"
#include "third_party/blink/renderer/core/html/html_plugin_element.h"
#include "third_party/blink/renderer/core/html/html_script_element.h"
#include "third_party/blink/renderer/core/html/html_title_element.h"
#include "third_party/blink/renderer/core/html/html_unknown_element.h"
#include "third_party/blink/renderer/core/html/imports/html_import_loader.h"
#include "third_party/blink/renderer/core/html/imports/html_imports_controller.h"
#include "third_party/blink/renderer/core/html/lazy_load_image_observer.h"
#include "third_party/blink/renderer/core/html/parser/html_document_parser.h"
#include "third_party/blink/renderer/core/html/parser/html_parser_idioms.h"
#include "third_party/blink/renderer/core/html/parser/nesting_level_incrementer.h"
#include "third_party/blink/renderer/core/html/parser/text_resource_decoder.h"
#include "third_party/blink/renderer/core/html/plugin_document.h"
#include "third_party/blink/renderer/core/html/portal/document_portals.h"
#include "third_party/blink/renderer/core/html/portal/portal_contents.h"
#include "third_party/blink/renderer/core/html/window_name_collection.h"
#include "third_party/blink/renderer/core/html_element_factory.h"
#include "third_party/blink/renderer/core/html_element_type_helpers.h"
#include "third_party/blink/renderer/core/html_names.h"
#include "third_party/blink/renderer/core/input/event_handler.h"
#include "third_party/blink/renderer/core/input/touch_list.h"
#include "third_party/blink/renderer/core/inspector/console_message.h"
#include "third_party/blink/renderer/core/inspector/inspector_issue.h"
#include "third_party/blink/renderer/core/inspector/inspector_issue_storage.h"
#include "third_party/blink/renderer/core/inspector/inspector_trace_events.h"
#include "third_party/blink/renderer/core/intersection_observer/element_intersection_observer_data.h"
#include "third_party/blink/renderer/core/intersection_observer/intersection_observer_controller.h"
#include "third_party/blink/renderer/core/intersection_observer/intersection_observer_entry.h"
#include "third_party/blink/renderer/core/layout/adjust_for_absolute_zoom.h"
#include "third_party/blink/renderer/core/layout/hit_test_canvas_result.h"
#include "third_party/blink/renderer/core/layout/hit_test_result.h"
#include "third_party/blink/renderer/core/layout/layout_embedded_content.h"
#include "third_party/blink/renderer/core/layout/layout_view.h"
#include "third_party/blink/renderer/core/layout/text_autosizer.h"
#include "third_party/blink/renderer/core/loader/appcache/application_cache_host_for_frame.h"
#include "third_party/blink/renderer/core/loader/cookie_jar.h"
#include "third_party/blink/renderer/core/loader/document_loader.h"
#include "third_party/blink/renderer/core/loader/frame_fetch_context.h"
#include "third_party/blink/renderer/core/loader/frame_loader.h"
#include "third_party/blink/renderer/core/loader/http_refresh_scheduler.h"
#include "third_party/blink/renderer/core/loader/idleness_detector.h"
#include "third_party/blink/renderer/core/loader/interactive_detector.h"
#include "third_party/blink/renderer/core/loader/prerenderer_client.h"
#include "third_party/blink/renderer/core/loader/progress_tracker.h"
#include "third_party/blink/renderer/core/loader/text_resource_decoder_builder.h"
#include "third_party/blink/renderer/core/mathml/mathml_element.h"
#include "third_party/blink/renderer/core/mathml/mathml_row_element.h"
#include "third_party/blink/renderer/core/mathml_element_factory.h"
#include "third_party/blink/renderer/core/mathml_names.h"
#include "third_party/blink/renderer/core/origin_trials/origin_trial_context.h"
#include "third_party/blink/renderer/core/page/chrome_client.h"
#include "third_party/blink/renderer/core/page/event_with_hit_test_results.h"
#include "third_party/blink/renderer/core/page/focus_controller.h"
#include "third_party/blink/renderer/core/page/frame_tree.h"
#include "third_party/blink/renderer/core/page/page.h"
#include "third_party/blink/renderer/core/page/plugin_script_forbidden_scope.h"
#include "third_party/blink/renderer/core/page/pointer_lock_controller.h"
#include "third_party/blink/renderer/core/page/scrolling/overscroll_controller.h"
#include "third_party/blink/renderer/core/page/scrolling/root_scroller_controller.h"
#include "third_party/blink/renderer/core/page/scrolling/scroll_state_callback.h"
#include "third_party/blink/renderer/core/page/scrolling/scrolling_coordinator.h"
#include "third_party/blink/renderer/core/page/scrolling/snap_coordinator.h"
#include "third_party/blink/renderer/core/page/scrolling/text_fragment_anchor.h"
#include "third_party/blink/renderer/core/page/scrolling/top_document_root_scroller_controller.h"
#include "third_party/blink/renderer/core/page/spatial_navigation_controller.h"
#include "third_party/blink/renderer/core/page/validation_message_client.h"
#include "third_party/blink/renderer/core/paint/compositing/paint_layer_compositor.h"
#include "third_party/blink/renderer/core/paint/first_meaningful_paint_detector.h"
#include "third_party/blink/renderer/core/paint/paint_layer.h"
#include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h"
#include "third_party/blink/renderer/core/probe/core_probes.h"
#include "third_party/blink/renderer/core/resize_observer/resize_observer_controller.h"
#include "third_party/blink/renderer/core/script/script_runner.h"
#include "third_party/blink/renderer/core/scroll/scrollbar_theme.h"
#include "third_party/blink/renderer/core/svg/svg_document_extensions.h"
#include "third_party/blink/renderer/core/svg/svg_script_element.h"
#include "third_party/blink/renderer/core/svg/svg_title_element.h"
#include "third_party/blink/renderer/core/svg/svg_unknown_element.h"
#include "third_party/blink/renderer/core/svg/svg_use_element.h"
#include "third_party/blink/renderer/core/svg_element_factory.h"
#include "third_party/blink/renderer/core/svg_names.h"
#include "third_party/blink/renderer/core/trustedtypes/trusted_html.h"
#include "third_party/blink/renderer/core/xml/parser/xml_document_parser.h"
#include "third_party/blink/renderer/core/xml_names.h"
#include "third_party/blink/renderer/core/xmlhttprequest/main_thread_disallow_synchronous_xhr_scope.h"
#include "third_party/blink/renderer/core/xmlns_names.h"
#include "third_party/blink/renderer/platform/bindings/dom_data_store.h"
#include "third_party/blink/renderer/platform/bindings/exception_messages.h"
#include "third_party/blink/renderer/platform/bindings/exception_state.h"
#include "third_party/blink/renderer/platform/bindings/microtask.h"
#include "third_party/blink/renderer/platform/bindings/script_forbidden_scope.h"
#include "third_party/blink/renderer/platform/bindings/v8_dom_wrapper.h"
#include "third_party/blink/renderer/platform/bindings/v8_per_isolate_data.h"
#include "third_party/blink/renderer/platform/fonts/font_matching_metrics.h"
#include "third_party/blink/renderer/platform/geometry/length_functions.h"
#include "third_party/blink/renderer/platform/heap/heap.h"
#include "third_party/blink/renderer/platform/instrumentation/histogram.h"
#include "third_party/blink/renderer/platform/instrumentation/instance_counters.h"
#include "third_party/blink/renderer/platform/instrumentation/resource_coordinator/document_resource_coordinator.h"
#include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
#include "third_party/blink/renderer/platform/instrumentation/use_counter.h"
#include "third_party/blink/renderer/platform/language.h"
#include "third_party/blink/renderer/platform/loader/fetch/null_resource_fetcher_properties.h"
#include "third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h"
#include "third_party/blink/renderer/platform/network/content_security_policy_parsers.h"
#include "third_party/blink/renderer/platform/network/http_parsers.h"
#include "third_party/blink/renderer/platform/network/network_state_notifier.h"
#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
#include "third_party/blink/renderer/platform/scheduler/public/dummy_schedulers.h"
#include "third_party/blink/renderer/platform/scheduler/public/event_loop.h"
#include "third_party/blink/renderer/platform/scheduler/public/frame_or_worker_scheduler.h"
#include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h"
#include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h"
#include "third_party/blink/renderer/platform/text/platform_locale.h"
#include "third_party/blink/renderer/platform/web_test_support.h"
#include "third_party/blink/renderer/platform/weborigin/origin_access_entry.h"
#include "third_party/blink/renderer/platform/weborigin/scheme_registry.h"
#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
#include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h"
#include "third_party/blink/renderer/platform/wtf/functional.h"
#include "third_party/blink/renderer/platform/wtf/hash_functions.h"
#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
#include "third_party/blink/renderer/platform/wtf/text/character_names.h"
#include "third_party/blink/renderer/platform/wtf/text/string_buffer.h"
#include "third_party/blink/renderer/platform/wtf/text/text_encoding_registry.h"
#ifndef NDEBUG
using WeakDocumentSet = blink::HeapHashSet<blink::WeakMember<blink::Document>>;
static WeakDocumentSet& liveDocumentSet();
#endif
namespace blink {
namespace {
// Returns true if any of <object> ancestors don't start loading or are loading
// plugins/frames/images. If there are no <object> ancestors, this function
// returns false.
bool IsInIndeterminateObjectAncestor(const Element* element) {
if (!element->isConnected())
return false;
for (; element; element = element->ParentOrShadowHostElement()) {
if (const auto* object = DynamicTo<HTMLObjectElement>(element)) {
if (!object->DidFinishLoading())
return true;
}
}
return false;
}
} // namespace
class DocumentOutliveTimeReporter : public BlinkGCObserver {
public:
explicit DocumentOutliveTimeReporter(Document* document)
: BlinkGCObserver(ThreadState::Current()), document_(document) {}
~DocumentOutliveTimeReporter() override {
// As not all documents are destroyed before the process dies, this might
// miss some long-lived documents or leaked documents.
UMA_HISTOGRAM_EXACT_LINEAR(
"Document.OutliveTimeAfterShutdown.DestroyedBeforeProcessDies",
GetOutliveTimeCount() + 1, 101);
}
void OnCompleteSweepDone() override {
enum GCCount {
kGCCount5,
kGCCount10,
kGCCountMax,
};
// There are some cases that a document can live after shutting down because
// the document can still be referenced (e.g. a document opened via
// window.open can be referenced by the opener even after shutting down). To
// avoid such cases as much as possible, outlive time count is started after
// all DomWrapper of the document have disappeared.
if (!gc_age_when_document_detached_) {
if (document_->domWindow() &&
DOMWrapperWorld::HasWrapperInAnyWorldInMainThread(
document_->domWindow())) {
return;
}
gc_age_when_document_detached_ = ThreadState::Current()->GcAge();
}
int outlive_time_count = GetOutliveTimeCount();
if (outlive_time_count == 5 || outlive_time_count == 10) {
const char* kUMAString = "Document.OutliveTimeAfterShutdown.GCCount";
if (outlive_time_count == 5)
UMA_HISTOGRAM_ENUMERATION(kUMAString, kGCCount5, kGCCountMax);
else if (outlive_time_count == 10)
UMA_HISTOGRAM_ENUMERATION(kUMAString, kGCCount10, kGCCountMax);
else
NOTREACHED();
}
if (outlive_time_count == 5 || outlive_time_count == 10 ||
outlive_time_count == 20 || outlive_time_count == 50) {
document_->RecordUkmOutliveTimeAfterShutdown(outlive_time_count);
}
}
private:
int GetOutliveTimeCount() const {
if (!gc_age_when_document_detached_)
return 0;
return ThreadState::Current()->GcAge() - gc_age_when_document_detached_;
}
WeakPersistent<Document> document_;
int gc_age_when_document_detached_ = 0;
};
static const unsigned kCMaxWriteRecursionDepth = 21;
// This amount of time must have elapsed before we will even consider scheduling
// a layout without a delay.
// FIXME: For faster machines this value can really be lowered to 200. 250 is
// adequate, but a little high for dual G5s. :)
static const base::TimeDelta kCLayoutScheduleThreshold =
base::TimeDelta::FromMilliseconds(250);
// DOM Level 2 says (letters added):
//
// a) Name start characters must have one of the categories Ll, Lu, Lo, Lt, Nl.
// b) Name characters other than Name-start characters must have one of the
// categories Mc, Me, Mn, Lm, or Nd.
// c) Characters in the compatibility area (i.e. with character code greater
// than #xF900 and less than #xFFFE) are not allowed in XML names.
// d) Characters which have a font or compatibility decomposition (i.e. those
// with a "compatibility formatting tag" in field 5 of the database -- marked
// by field 5 beginning with a "<") are not allowed.
// e) The following characters are treated as name-start characters rather than
// name characters, because the property file classifies them as Alphabetic:
// [#x02BB-#x02C1], #x0559, #x06E5, #x06E6.
// f) Characters #x20DD-#x20E0 are excluded (in accordance with Unicode, section
// 5.14).
// g) Character #x00B7 is classified as an extender, because the property list
// so identifies it.
// h) Character #x0387 is added as a name character, because #x00B7 is its
// canonical equivalent.
// i) Characters ':' and '_' are allowed as name-start characters.
// j) Characters '-' and '.' are allowed as name characters.
//
// It also contains complete tables. If we decide it's better, we could include
// those instead of the following code.
static inline bool IsValidNameStart(UChar32 c) {
// rule (e) above
if ((c >= 0x02BB && c <= 0x02C1) || c == 0x559 || c == 0x6E5 || c == 0x6E6)
return true;
// rule (i) above
if (c == ':' || c == '_')
return true;
// rules (a) and (f) above
const uint32_t kNameStartMask =
WTF::unicode::kLetter_Lowercase | WTF::unicode::kLetter_Uppercase |
WTF::unicode::kLetter_Other | WTF::unicode::kLetter_Titlecase |
WTF::unicode::kNumber_Letter;
if (!(WTF::unicode::Category(c) & kNameStartMask))
return false;
// rule (c) above
if (c >= 0xF900 && c < 0xFFFE)
return false;
// rule (d) above
WTF::unicode::CharDecompositionType decomp_type =
WTF::unicode::DecompositionType(c);
if (decomp_type == WTF::unicode::kDecompositionFont ||
decomp_type == WTF::unicode::kDecompositionCompat)
return false;
return true;
}
static inline bool IsValidNamePart(UChar32 c) {
// rules (a), (e), and (i) above
if (IsValidNameStart(c))
return true;
// rules (g) and (h) above
if (c == 0x00B7 || c == 0x0387)
return true;
// rule (j) above
if (c == '-' || c == '.')
return true;
// rules (b) and (f) above
const uint32_t kOtherNamePartMask =
WTF::unicode::kMark_NonSpacing | WTF::unicode::kMark_Enclosing |
WTF::unicode::kMark_SpacingCombining | WTF::unicode::kLetter_Modifier |
WTF::unicode::kNumber_DecimalDigit;
if (!(WTF::unicode::Category(c) & kOtherNamePartMask))
return false;
// rule (c) above
if (c >= 0xF900 && c < 0xFFFE)
return false;
// rule (d) above
WTF::unicode::CharDecompositionType decomp_type =
WTF::unicode::DecompositionType(c);
if (decomp_type == WTF::unicode::kDecompositionFont ||
decomp_type == WTF::unicode::kDecompositionCompat)
return false;
return true;
}
// Tests whether |name| is something the HTML parser would accept as a
// tag name.
template <typename CharType>
static inline bool IsValidElementNamePerHTMLParser(const CharType* characters,
unsigned length) {
CharType c = characters[0] | 0x20;
if (!('a' <= c && c <= 'z'))
return false;
for (unsigned i = 1; i < length; ++i) {
c = characters[i];
if (c == '\t' || c == '\n' || c == '\f' || c == '\r' || c == ' ' ||
c == '/' || c == '>')
return false;
}
return true;
}
static bool IsValidElementNamePerHTMLParser(const String& name) {
unsigned length = name.length();
if (!length)
return false;
if (name.Is8Bit()) {
const LChar* characters = name.Characters8();
return IsValidElementNamePerHTMLParser(characters, length);
}
const UChar* characters = name.Characters16();
return IsValidElementNamePerHTMLParser(characters, length);
}
// Tests whether |name| is a valid name per DOM spec. Also checks
// whether the HTML parser would accept this element name and counts
// cases of mismatches.
static bool IsValidElementName(Document* document, const String& name) {
bool is_valid_dom_name = Document::IsValidName(name);
bool is_valid_html_name = IsValidElementNamePerHTMLParser(name);
if (UNLIKELY(is_valid_html_name != is_valid_dom_name)) {
// This is inaccurate because it will not report activity in
// detached documents. However retrieving the frame from the
// bindings is too slow.
UseCounter::Count(document,
is_valid_dom_name
? WebFeature::kElementNameDOMValidHTMLParserInvalid
: WebFeature::kElementNameDOMInvalidHTMLParserValid);
}
return is_valid_dom_name;
}
static bool AcceptsEditingFocus(const Element& element) {
DCHECK(HasEditableStyle(element));
return element.GetDocument().GetFrame() && RootEditableElement(element);
}
uint64_t Document::global_tree_version_ = 0;
static bool g_threaded_parsing_enabled_for_testing = true;
class Document::NetworkStateObserver final
: public GarbageCollected<Document::NetworkStateObserver>,
public NetworkStateNotifier::NetworkStateObserver,
public ExecutionContextLifecycleObserver {
USING_GARBAGE_COLLECTED_MIXIN(Document::NetworkStateObserver);
public:
explicit NetworkStateObserver(Document& document)
: ExecutionContextLifecycleObserver(&document) {
online_observer_handle_ = GetNetworkStateNotifier().AddOnLineObserver(
this, GetExecutionContext()->GetTaskRunner(TaskType::kNetworking));
}
void OnLineStateChange(bool on_line) override {
AtomicString event_name =
on_line ? event_type_names::kOnline : event_type_names::kOffline;
Document* document = Document::From(GetExecutionContext());
if (!document->domWindow())
return;
document->domWindow()->DispatchEvent(*Event::Create(event_name));
probe::NetworkStateChanged(document->GetFrame(), on_line);
}
void ContextDestroyed() override {
UnregisterAsObserver(GetExecutionContext());
}
void UnregisterAsObserver(ExecutionContext* context) {
DCHECK(context);
online_observer_handle_ = nullptr;
}
void Trace(Visitor* visitor) override {
ExecutionContextLifecycleObserver::Trace(visitor);
}
private:
std::unique_ptr<NetworkStateNotifier::NetworkStateObserverHandle>
online_observer_handle_;
};
ExplicitlySetAttrElementsMap* Document::GetExplicitlySetAttrElementsMap(
Element* element) {
DCHECK(element);
DCHECK(element->GetDocument() == this);
auto add_result =
element_explicitly_set_attr_elements_map_.insert(element, nullptr);
if (add_result.is_new_entry) {
add_result.stored_value->value =
MakeGarbageCollected<ExplicitlySetAttrElementsMap>();
}
return add_result.stored_value->value;
}
Document* Document::Create(Document& document) {
Document* new_document =
MakeGarbageCollected<Document>(DocumentInit::Create()
.WithContextDocument(&document)
.WithURL(BlankURL())
.WithOwnerDocument(&document));
new_document->SetContextFeatures(document.GetContextFeatures());
return new_document;
}
Document::Document() : Document(DocumentInit::Create()) {}
Document::Document(const DocumentInit& initializer,
DocumentClassFlags document_classes)
: Document(initializer,
SecurityContextInit(initializer),
document_classes) {}
Document::Document(const DocumentInit& initializer,
const SecurityContextInit& security_initializer,
DocumentClassFlags document_classes)
: ContainerNode(nullptr, kCreateDocument),
TreeScope(*this),
evaluate_media_queries_on_style_recalc_(false),
pending_sheet_layout_(kNoLayoutWithPendingSheets),
window_agent_factory_(initializer.GetWindowAgentFactory()),
frame_(initializer.GetFrame()),
// TODO(dcheng): Why does this need both a LocalFrame and LocalDOMWindow
// pointer?
dom_window_(frame_ ? frame_->DomWindow() : nullptr),
imports_controller_(initializer.ImportsController()),
security_context_(security_initializer, SecurityContext::kLocal),
use_counter_during_construction_(initializer.GetUseCounter()),
context_document_(initializer.ContextDocument()),
context_features_(ContextFeatures::DefaultSwitch()),
http_refresh_scheduler_(MakeGarbageCollected<HttpRefreshScheduler>(this)),
well_formed_(false),
printing_(kNotPrinting),
is_painting_preview_(false),
compatibility_mode_(kNoQuirksMode),
compatibility_mode_locked_(false),
last_focus_type_(mojom::blink::FocusType::kNone),
had_keyboard_event_(false),
clear_focused_element_timer_(
GetTaskRunner(TaskType::kInternalUserInteraction),
this,
&Document::ClearFocusedElementTimerFired),
dom_tree_version_(++global_tree_version_),
style_version_(0),
listener_types_(0),
mutation_observer_types_(0),
visited_link_state_(MakeGarbageCollected<VisitedLinkState>(*this)),
visually_ordered_(false),
ready_state_(kComplete),
parsing_state_(kFinishedParsing),
contains_plugins_(false),
ignore_destructive_write_count_(0),
throw_on_dynamic_markup_insertion_count_(0),
ignore_opens_during_unload_count_(0),
markers_(MakeGarbageCollected<DocumentMarkerController>(*this)),
css_target_(nullptr),
was_discarded_(false),
load_event_progress_(kLoadEventCompleted),
is_freezing_in_progress_(false),
script_runner_(MakeGarbageCollected<ScriptRunner>(this)),
xml_version_("1.0"),
xml_standalone_(kStandaloneUnspecified),
has_xml_declaration_(0),
design_mode_(false),
is_running_exec_command_(false),
has_annotated_regions_(false),
annotated_regions_dirty_(false),
document_classes_(document_classes),
is_view_source_(false),
saw_elements_in_known_namespaces_(false),
is_srcdoc_document_(initializer.IsSrcdocDocument()),
is_mobile_document_(false),
layout_view_(nullptr),
has_fullscreen_supplement_(false),
load_event_delay_count_(0),
// We already intentionally fire load event asynchronously and here we use
// kDOMManipulation to ensure that we run onload() in order with other
// callbacks (e.g. onloadstart()) per the spec.
// See: https://html.spec.whatwg.org/#delay-the-load-event
load_event_delay_timer_(GetTaskRunner(TaskType::kDOMManipulation),
this,
&Document::LoadEventDelayTimerFired),
plugin_loading_timer_(GetTaskRunner(TaskType::kInternalLoading),
this,
&Document::PluginLoadingTimerFired),
document_timing_(*this),
write_recursion_is_too_deep_(false),
write_recursion_depth_(0),
scripted_animation_controller_(
MakeGarbageCollected<ScriptedAnimationController>(this)),
current_frame_is_throttled_(false),
registration_context_(initializer.RegistrationContext(this)),
element_data_cache_clear_timer_(
GetTaskRunner(TaskType::kInternalUserInteraction),
this,
&Document::ElementDataCacheClearTimerFired),
document_animations_(MakeGarbageCollected<DocumentAnimations>(this)),
timeline_(MakeGarbageCollected<DocumentTimeline>(this)),
pending_animations_(MakeGarbageCollected<PendingAnimations>(*this)),
worklet_animation_controller_(
MakeGarbageCollected<WorkletAnimationController>(this)),
template_document_host_(nullptr),
did_associate_form_controls_timer_(
GetTaskRunner(TaskType::kInternalLoading),
this,
&Document::DidAssociateFormControlsTimerFired),
has_viewport_units_(false),
parser_sync_policy_(kAllowAsynchronousParsing),
node_count_(0),
logged_field_edit_(false),
ukm_source_id_(ukm::UkmRecorder::GetNewSourceID()),
needs_to_record_ukm_outlive_time_(false),
viewport_data_(MakeGarbageCollected<ViewportData>(*this)),
is_for_external_handler_(initializer.IsForExternalHandler()),
isolated_world_csp_map_(
MakeGarbageCollected<
HeapHashMap<int, Member<ContentSecurityPolicy>>>()),
permission_service_(this->ToExecutionContext()),
font_preload_manager_(*this) {
security_initializer.ApplyPendingDataToDocument(*this);
GetOriginTrialContext()->BindExecutionContext(GetExecutionContext());
if (frame_) {
pending_fp_headers_ = security_initializer.FeaturePolicyHeader();
pending_dp_headers_ = initializer.GetDocumentPolicy();
}
if (frame_) {
DCHECK(frame_->GetPage());
ProvideContextFeaturesToDocumentFrom(*this, *frame_->GetPage());
fetcher_ = FrameFetchContext::CreateFetcherForCommittedDocument(
*frame_->Loader().GetDocumentLoader(), *this);
// TODO(dcheng): Why does this need to check that DOMWindow is non-null?
CustomElementRegistry* registry =
frame_->DomWindow() ? frame_->DomWindow()->MaybeCustomElements()
: nullptr;
if (registry && registration_context_)
registry->Entangle(registration_context_);
cookie_jar_ = MakeGarbageCollected<CookieJar>(this);
} else if (imports_controller_ &&
!base::FeatureList::IsEnabled(
features::kHtmlImportsRequestInitiatorLock)) {
fetcher_ = FrameFetchContext::CreateFetcherForImportedDocument(this);
} else {
// We disable fetches for frame-less Documents, including HTML-imported
// Documents (if kHtmlImportsRequestInitiatorLock is enabled). Subresources
// of HTML-imported Documents are fetched via the context document's
// ResourceFetcher. See https://crbug.com/961614 for details.
auto& properties =
*MakeGarbageCollected<DetachableResourceFetcherProperties>(
*MakeGarbageCollected<NullResourceFetcherProperties>());
fetcher_ = MakeGarbageCollected<ResourceFetcher>(ResourceFetcherInit(
properties, &FetchContext::NullInstance(),
GetTaskRunner(TaskType::kNetworking), nullptr /* loader_factory */));
if (imports_controller_) {
// We don't expect the fetcher to be used, so count such unexpected use.
fetcher_->SetShouldLogRequestAsInvalidInImportedDocument();
}
}
DCHECK(fetcher_);
root_scroller_controller_ =
MakeGarbageCollected<RootScrollerController>(*this);
// We depend on the url getting immediately set in subframes, but we
// also depend on the url NOT getting immediately set in opened windows.
// See fast/dom/early-frame-url.html
// and fast/dom/location-new-window-no-crash.html, respectively.
// FIXME: Can/should we unify this behavior?
if (initializer.ShouldSetURL()) {
SetURL(initializer.Url());
} else {
// Even if this document has no URL, we need to initialize base URL with
// fallback base URL.
UpdateBaseURL();
}
if (initializer.GetWebBundleClaimedUrl().IsValid()) {
web_bundle_claimed_url_ = initializer.GetWebBundleClaimedUrl();
SetBaseURLOverride(initializer.GetWebBundleClaimedUrl());
}
InitSecurityContext(initializer);
PoliciesInitialized(initializer);
InitDNSPrefetch();
InstanceCounters::IncrementCounter(InstanceCounters::kDocumentCounter);
lifecycle_.AdvanceTo(DocumentLifecycle::kInactive);
UpdateForcedColors();
// Since CSSFontSelector requires Document::fetcher_ and StyleEngine owns
// CSSFontSelector, need to initialize |style_engine_| after initializing
// |fetcher_|.
style_engine_ = MakeGarbageCollected<StyleEngine>(*this);
// The parent's parser should be suspended together with all the other
// objects, else this new Document would have a new ExecutionContext which
// suspended state would not match the one from the parent, and could start
// loading resources ignoring the defersLoading flag.
DCHECK(!ParentDocument() || !ParentDocument()->IsContextPaused());
#ifndef NDEBUG
liveDocumentSet().insert(this);
#endif
if (frame_ && frame_->GetPage()->GetAgentMetricsCollector())
frame_->GetPage()->GetAgentMetricsCollector()->DidAttachDocument(*this);
// We will use Loader() as UseCounter after initialization.
use_counter_during_construction_ = nullptr;
}
Document::~Document() {
DCHECK(!GetLayoutView());
DCHECK(!ParentTreeScope());
// If a top document with a cache, verify that it was comprehensively
// cleared during detach.
DCHECK(!ax_object_cache_);
InstanceCounters::DecrementCounter(InstanceCounters::kDocumentCounter);
}
Range* Document::CreateRangeAdjustedToTreeScope(const TreeScope& tree_scope,
const Position& position) {
DCHECK(position.IsNotNull());
// Note: Since |Position::ComputeContainerNode()| returns |nullptr| if
// |position| is |BeforeAnchor| or |AfterAnchor|.
Node* const anchor_node = position.AnchorNode();
if (anchor_node->GetTreeScope() == tree_scope) {
return MakeGarbageCollected<Range>(tree_scope.GetDocument(), position,
position);
}
Node* const shadow_host = tree_scope.AncestorInThisScope(anchor_node);
return MakeGarbageCollected<Range>(tree_scope.GetDocument(),
Position::BeforeNode(*shadow_host),
Position::BeforeNode(*shadow_host));
}
SelectorQueryCache& Document::GetSelectorQueryCache() {
if (!selector_query_cache_)
selector_query_cache_ = std::make_unique<SelectorQueryCache>();
return *selector_query_cache_;
}
MediaQueryMatcher& Document::GetMediaQueryMatcher() {
if (!media_query_matcher_)
media_query_matcher_ = MakeGarbageCollected<MediaQueryMatcher>(*this);
return *media_query_matcher_;
}
void Document::MediaQueryAffectingValueChanged() {
GetStyleEngine().MediaQueryAffectingValueChanged();
if (NeedsLayoutTreeUpdate())
evaluate_media_queries_on_style_recalc_ = true;
else
EvaluateMediaQueryList();
probe::MediaQueryResultChanged(this);
}
void Document::SetCompatibilityMode(CompatibilityMode mode) {
if (compatibility_mode_locked_ || mode == compatibility_mode_)
return;
if (compatibility_mode_ == kQuirksMode)
UseCounter::Count(*this, WebFeature::kQuirksModeDocument);
else if (compatibility_mode_ == kLimitedQuirksMode)
UseCounter::Count(*this, WebFeature::kLimitedQuirksModeDocument);
compatibility_mode_ = mode;
GetSelectorQueryCache().Invalidate();
}
String Document::compatMode() const {
return InQuirksMode() ? "BackCompat" : "CSS1Compat";
}
void Document::SetDoctype(DocumentType* doc_type) {
// This should never be called more than once.
DCHECK(!doc_type_ || !doc_type);
doc_type_ = doc_type;
if (doc_type_) {
AdoptIfNeeded(*doc_type_);
if (doc_type_->publicId().StartsWithIgnoringASCIICase(
"-//wapforum//dtd xhtml mobile 1.")) {
is_mobile_document_ = true;
style_engine_->ViewportRulesChanged();
}
}
}
DOMImplementation& Document::implementation() {
if (!implementation_)
implementation_ = MakeGarbageCollected<DOMImplementation>(*this);
return *implementation_;
}
Location* Document::location() const {
if (!GetFrame())
return nullptr;
return domWindow()->location();
}
ContentSecurityPolicy* Document::GetContentSecurityPolicyForWorld() {
v8::Isolate* isolate = GetIsolate();
if (!isolate)
return GetContentSecurityPolicy();
v8::HandleScope handle_scope(isolate);
v8::Local<v8::Context> v8_context = isolate->GetCurrentContext();
// This can be called before we enter v8, hence the context might be empty,
// which implies we are not in an isolated world.
if (v8_context.IsEmpty())
return GetContentSecurityPolicy();
DOMWrapperWorld& world = DOMWrapperWorld::Current(isolate);
if (!world.IsIsolatedWorld())
return GetContentSecurityPolicy();
int32_t world_id = world.GetWorldId();
auto it = isolated_world_csp_map_->find(world_id);
if (it != isolated_world_csp_map_->end())
return it->value;
ContentSecurityPolicy* policy =
IsolatedWorldCSP::Get().CreateIsolatedWorldCSP(*this, world_id);
if (!policy)
return GetContentSecurityPolicy();
isolated_world_csp_map_->insert(world_id, policy);
return policy;
}
// static
Document& Document::From(ExecutionContext& context) {
SECURITY_DCHECK(context.IsDocument());
return *static_cast<LocalDOMWindow&>(context).document();
}
// static
const Document& Document::From(const ExecutionContext& context) {
SECURITY_DCHECK(context.IsDocument());
return *static_cast<const LocalDOMWindow&>(context).document();
}
ExecutionContext* Document::ToExecutionContext() {
return domWindow();
}
const ExecutionContext* Document::ToExecutionContext() const {
return domWindow();
}
bool Document::FeatureEnabled(OriginTrialFeature feature) const {
return GetOriginTrialContext()->IsFeatureEnabled(feature);
}
void Document::CountFeaturePolicyUsage(mojom::WebFeature feature) {
UseCounter::Count(*this, feature);
}
bool Document::FeaturePolicyFeatureObserved(
mojom::blink::FeaturePolicyFeature feature) {
wtf_size_t feature_index = static_cast<wtf_size_t>(feature);
if (parsed_feature_policies_.size() == 0) {
parsed_feature_policies_.resize(
static_cast<wtf_size_t>(mojom::blink::FeaturePolicyFeature::kMaxValue) +
1);
} else if (parsed_feature_policies_[feature_index]) {
return true;
}
parsed_feature_policies_[feature_index] = true;
return false;
}
const SecurityOrigin* Document::GetSecurityOrigin() const {
return GetSecurityContext().GetSecurityOrigin();
}
SecurityOrigin* Document::GetMutableSecurityOrigin() {
return GetSecurityContext().GetMutableSecurityOrigin();
}
ContentSecurityPolicy* Document::GetContentSecurityPolicy() const {
return GetSecurityContext().GetContentSecurityPolicy();
}
mojom::blink::WebSandboxFlags Document::GetSandboxFlags() const {
return GetSecurityContext().GetSandboxFlags();
}
bool Document::IsSandboxed(mojom::blink::WebSandboxFlags mask) const {
return GetSecurityContext().IsSandboxed(mask);
}
PublicURLManager& Document::GetPublicURLManager() {
DCHECK(GetExecutionContext());
return GetExecutionContext()->GetPublicURLManager();
}
bool Document::IsContextPaused() const {
return GetExecutionContext() ? GetExecutionContext()->IsContextPaused()
: false;
}
bool Document::IsContextDestroyed() const {
return GetExecutionContext() ? GetExecutionContext()->IsContextDestroyed()
: true;
}
ContentSecurityPolicyDelegate& Document::GetContentSecurityPolicyDelegate() {
return GetExecutionContext()->GetContentSecurityPolicyDelegate();
}
SecureContextMode Document::GetSecureContextMode() const {
return GetSecurityContext().GetSecureContextMode();
}
bool Document::IsSecureContext() const {
return GetExecutionContext()->IsSecureContext();
}
bool Document::IsSecureContext(String& error_message) const {
return GetExecutionContext()->IsSecureContext(error_message);
}
void Document::SetReferrerPolicy(network::mojom::ReferrerPolicy policy) {
GetExecutionContext()->SetReferrerPolicy(policy);
}
v8::Isolate* Document::GetIsolate() const {
return GetExecutionContext() ? GetExecutionContext()->GetIsolate() : nullptr;
}
Agent* Document::GetAgent() const {
return GetSecurityContext().GetAgent();
}
OriginTrialContext* Document::GetOriginTrialContext() const {
return MasterDocument().GetSecurityContext().GetOriginTrialContext();
}
void Document::SetSecureContextModeForTesting(SecureContextMode mode) {
GetSecurityContext().SetSecureContextModeForTesting(mode);
}
bool Document::IsFeatureEnabled(mojom::blink::FeaturePolicyFeature feature,
ReportOptions report_on_failure,
const String& message,
const String& source_file) const {
return GetExecutionContext() &&
GetExecutionContext()->IsFeatureEnabled(feature, report_on_failure,
message, source_file);
}
bool Document::IsFeatureEnabled(mojom::blink::FeaturePolicyFeature feature,
PolicyValue threshold_value,
ReportOptions report_on_failure,
const String& message,
const String& source_file) const {
return GetExecutionContext() &&
GetExecutionContext()->IsFeatureEnabled(
feature, threshold_value, report_on_failure, message, source_file);
}
bool Document::IsFeatureEnabled(mojom::blink::DocumentPolicyFeature feature,
ReportOptions report_option,
const String& message,
const String& source_file) {
return GetExecutionContext() &&
GetExecutionContext()->IsFeatureEnabled(feature, report_option,
message, source_file);
}
bool Document::IsFeatureEnabled(mojom::blink::DocumentPolicyFeature feature,
PolicyValue threshold_value,
ReportOptions report_option,
const String& message,
const String& source_file) {
return GetExecutionContext() &&
GetExecutionContext()->IsFeatureEnabled(
feature, threshold_value, report_option, message, source_file);
}
String Document::addressSpaceForBindings() const {
return GetExecutionContext()->addressSpaceForBindings();
}
void Document::ChildrenChanged(const ChildrenChange& change) {
ContainerNode::ChildrenChanged(change);
document_element_ = ElementTraversal::FirstWithin(*this);
// For non-HTML documents the willInsertBody notification won't happen
// so we resume as soon as we have a document element. Even for XHTML
// documents there may never be a <body> (since the parser won't always
// insert one), so we resume here too. That does mean XHTML documents make
// frames when there's only a <head>, but such documents are pretty rare.
if (document_element_ && !IsA<HTMLDocument>(this))
BeginLifecycleUpdatesIfRenderingReady();
}
void Document::setRootScroller(Element* new_scroller, ExceptionState&) {
root_scroller_controller_->Set(new_scroller);
}
Element* Document::rootScroller() const {
return root_scroller_controller_->Get();
}
bool Document::IsInMainFrame() const {
return GetFrame() && GetFrame()->IsMainFrame();
}
AtomicString Document::ConvertLocalName(const AtomicString& name) {
return IsA<HTMLDocument>(this) ? name.LowerASCII() : name;
}
// Just creates an element with specified qualified name without any
// custom element processing.
// This is a common code for step 5.2 and 7.2 of "create an element"
// <https://dom.spec.whatwg.org/#concept-create-element>
// Functions other than this one should not use HTMLElementFactory and
// SVGElementFactory because they don't support prefixes correctly.
Element* Document::CreateRawElement(const QualifiedName& qname,
CreateElementFlags flags) {
Element* element = nullptr;
if (qname.NamespaceURI() == html_names::xhtmlNamespaceURI) {
// https://html.spec.whatwg.org/C/#elements-in-the-dom:element-interface
element = HTMLElementFactory::Create(qname.LocalName(), *this, flags);
if (!element) {
// 6. If name is a valid custom element name, then return
// HTMLElement.
// 7. Return HTMLUnknownElement.
if (CustomElement::IsValidName(qname.LocalName()))
element = MakeGarbageCollected<HTMLElement>(qname, *this);
else
element = MakeGarbageCollected<HTMLUnknownElement>(qname, *this);
}
saw_elements_in_known_namespaces_ = true;
} else if (qname.NamespaceURI() == svg_names::kNamespaceURI) {
element = SVGElementFactory::Create(qname.LocalName(), *this, flags);
if (!element)
element = MakeGarbageCollected<SVGUnknownElement>(qname, *this);
saw_elements_in_known_namespaces_ = true;
} else if (RuntimeEnabledFeatures::MathMLCoreEnabled() &&
qname.NamespaceURI() == mathml_names::kNamespaceURI) {
element = MathMLElementFactory::Create(qname.LocalName(), *this, flags);
// An unknown MathML element is treated like an <mrow> element.
// TODO(crbug.com/1021837): Determine if we need to introduce a
// MathMLUnknownElement IDL.
if (!element)
element = MakeGarbageCollected<MathMLRowElement>(qname, *this);
saw_elements_in_known_namespaces_ = true;
} else {
element = MakeGarbageCollected<Element>(qname, this);
}
if (element->prefix() != qname.Prefix())
element->SetTagNameForCreateElementNS(qname);
DCHECK(qname == element->TagQName());
return element;
}
// https://dom.spec.whatwg.org/#dom-document-createelement
Element* Document::CreateElementForBinding(const AtomicString& name,
ExceptionState& exception_state) {
if (!IsValidElementName(this, name)) {
exception_state.ThrowDOMException(
DOMExceptionCode::kInvalidCharacterError,
"The tag name provided ('" + name + "') is not a valid name.");
return nullptr;
}
if (IsXHTMLDocument() || IsA<HTMLDocument>(this)) {
// 2. If the context object is an HTML document, let localName be
// converted to ASCII lowercase.
AtomicString local_name = ConvertLocalName(name);
if (CustomElement::ShouldCreateCustomElement(local_name)) {
return CustomElement::CreateCustomElement(
*this,
QualifiedName(g_null_atom, local_name, html_names::xhtmlNamespaceURI),
CreateElementFlags::ByCreateElement());
}
if (auto* element = HTMLElementFactory::Create(
local_name, *this, CreateElementFlags::ByCreateElement()))
return element;
QualifiedName q_name(g_null_atom, local_name,
html_names::xhtmlNamespaceURI);
if (RegistrationContext() && V0CustomElement::IsValidName(local_name))
return RegistrationContext()->CreateCustomTagElement(*this, q_name);
return MakeGarbageCollected<HTMLUnknownElement>(q_name, *this);
}
return MakeGarbageCollected<Element>(
QualifiedName(g_null_atom, name, g_null_atom), this);
}
AtomicString GetTypeExtension(
Document* document,
const StringOrElementCreationOptions& string_or_options) {
if (string_or_options.IsNull())
return AtomicString();
if (string_or_options.IsString()) {
UseCounter::Count(document,
WebFeature::kDocumentCreateElement2ndArgStringHandling);
return AtomicString(string_or_options.GetAsString());
}
if (string_or_options.IsElementCreationOptions()) {
const ElementCreationOptions& options =
*string_or_options.GetAsElementCreationOptions();
if (options.hasIs())
return AtomicString(options.is());
}
return AtomicString();
}
// https://dom.spec.whatwg.org/#dom-document-createelement
Element* Document::CreateElementForBinding(
const AtomicString& local_name,
const StringOrElementCreationOptions& string_or_options,
ExceptionState& exception_state) {
if (string_or_options.IsNull()) {
return CreateElementForBinding(local_name, exception_state);
}
// 1. If localName does not match Name production, throw InvalidCharacterError
if (!IsValidElementName(this, local_name)) {
exception_state.ThrowDOMException(
DOMExceptionCode::kInvalidCharacterError,
"The tag name provided ('" + local_name + "') is not a valid name.");
return nullptr;
}
// 2. localName converted to ASCII lowercase
const AtomicString& converted_local_name = ConvertLocalName(local_name);
QualifiedName q_name(g_null_atom, converted_local_name,
IsXHTMLDocument() || IsA<HTMLDocument>(this)
? html_names::xhtmlNamespaceURI
: g_null_atom);
bool is_v1 =
string_or_options.IsElementCreationOptions() || !RegistrationContext();
// V0 is only allowed with the flag.
DCHECK(is_v1 || RuntimeEnabledFeatures::CustomElementsV0Enabled(this));
bool create_v1_builtin = string_or_options.IsElementCreationOptions();
bool should_create_builtin =
create_v1_builtin || string_or_options.IsString();
// 3.
const AtomicString& is = GetTypeExtension(this, string_or_options);
// 5. Let element be the result of creating an element given ...
Element* element =
CreateElement(q_name,
is_v1 ? CreateElementFlags::ByCreateElementV1()
: CreateElementFlags::ByCreateElementV0(),
should_create_builtin ? is : g_null_atom);
// 8. If 'is' is non-null, set 'is' attribute
if (!is_v1 && !is.IsEmpty())
element->setAttribute(html_names::kIsAttr, is);
return element;
}
static inline QualifiedName CreateQualifiedName(
const AtomicString& namespace_uri,
const AtomicString& qualified_name,
ExceptionState& exception_state) {
AtomicString prefix, local_name;
if (!Document::ParseQualifiedName(qualified_name, prefix, local_name,
exception_state))
return QualifiedName::Null();
QualifiedName q_name(prefix, local_name, namespace_uri);
if (!Document::HasValidNamespaceForElements(q_name)) {
exception_state.ThrowDOMException(
DOMExceptionCode::kNamespaceError,
"The namespace URI provided ('" + namespace_uri +
"') is not valid for the qualified name provided ('" +
qualified_name + "').");
return QualifiedName::Null();
}
return q_name;
}
Element* Document::createElementNS(const AtomicString& namespace_uri,
const AtomicString& qualified_name,
ExceptionState& exception_state) {
QualifiedName q_name(
CreateQualifiedName(namespace_uri, qualified_name, exception_state));
if (q_name == QualifiedName::Null())
return nullptr;
CreateElementFlags flags = CreateElementFlags::ByCreateElement();
if (CustomElement::ShouldCreateCustomElement(q_name))
return CustomElement::CreateCustomElement(*this, q_name, flags);
if (RegistrationContext() && V0CustomElement::IsValidName(q_name.LocalName()))
return RegistrationContext()->CreateCustomTagElement(*this, q_name);
return CreateRawElement(q_name, flags);
}
// https://dom.spec.whatwg.org/#internal-createelementns-steps
Element* Document::createElementNS(
const AtomicString& namespace_uri,
const AtomicString& qualified_name,
const StringOrElementCreationOptions& string_or_options,
ExceptionState& exception_state) {
if (string_or_options.IsNull())
return createElementNS(namespace_uri, qualified_name, exception_state);
// 1. Validate and extract
QualifiedName q_name(
CreateQualifiedName(namespace_uri, qualified_name, exception_state));
if (q_name == QualifiedName::Null())
return nullptr;
bool is_v1 =
string_or_options.IsElementCreationOptions() || !RegistrationContext();
// V0 is only allowed with the flag.
DCHECK(is_v1 || RuntimeEnabledFeatures::CustomElementsV0Enabled(this));
bool create_v1_builtin = string_or_options.IsElementCreationOptions();
bool should_create_builtin =
create_v1_builtin || string_or_options.IsString();
// 2.
const AtomicString& is = GetTypeExtension(this, string_or_options);
if (!IsValidElementName(this, qualified_name)) {
exception_state.ThrowDOMException(DOMExceptionCode::kInvalidCharacterError,
"The tag name provided ('" +
qualified_name +
"') is not a valid name.");
return nullptr;
}
// 3. Let element be the result of creating an element
Element* element =
CreateElement(q_name,
is_v1 ? CreateElementFlags::ByCreateElementV1()
: CreateElementFlags::ByCreateElementV0(),
should_create_builtin ? is : g_null_atom);
// 4. If 'is' is non-null, set 'is' attribute
if (!is_v1 && !is.IsEmpty())
element->setAttribute(html_names::kIsAttr, is);
return element;
}
// Entry point of "create an element".
// https://dom.spec.whatwg.org/#concept-create-element
Element* Document::CreateElement(const QualifiedName& q_name,
const CreateElementFlags flags,
const AtomicString& is) {
CustomElementDefinition* definition = nullptr;
if (flags.IsCustomElementsV1() &&
q_name.NamespaceURI() == html_names::xhtmlNamespaceURI) {
const CustomElementDescriptor desc(is.IsNull() ? q_name.LocalName() : is,
q_name.LocalName());
if (CustomElementRegistry* registry = CustomElement::Registry(*this))
definition = registry->DefinitionFor(desc);
}
if (definition)
return definition->CreateElement(*this, q_name, flags);
return CustomElement::CreateUncustomizedOrUndefinedElement(*this, q_name,
flags, is);
}
ScriptValue Document::registerElement(ScriptState* script_state,
const AtomicString& name,
const ElementRegistrationOptions* options,
ExceptionState& exception_state) {
if (!RegistrationContext()) {
exception_state.ThrowDOMException(
DOMExceptionCode::kNotSupportedError,
"No element registration context is available.");
return ScriptValue();
}
// Polymer V1 uses Custom Elements V0. <dom-module> is defined in its base
// library and is a strong signal that this is a Polymer V1.
// This counter is used to research how much users are affected once Custom
// Element V0 is deprecated.
if (name == "dom-module")
UseCounter::Count(*this, WebFeature::kPolymerV1Detected);
V0CustomElementConstructorBuilder constructor_builder(script_state, options);
RegistrationContext()->RegisterElement(this, &constructor_builder, name,
exception_state);
if (exception_state.HadException())
return ScriptValue();
return constructor_builder.BindingsReturnValue();
}
V0CustomElementRegistrationContext* Document::RegistrationContext() const {
if (RuntimeEnabledFeatures::CustomElementsV0Enabled(this))
return registration_context_.Get();
return nullptr;
}
V0CustomElementMicrotaskRunQueue* Document::CustomElementMicrotaskRunQueue() {
if (!custom_element_microtask_run_queue_) {
custom_element_microtask_run_queue_ =
MakeGarbageCollected<V0CustomElementMicrotaskRunQueue>();
}
return custom_element_microtask_run_queue_.Get();
}
void Document::ClearImportsController() {
fetcher_->ClearContext();
imports_controller_ = nullptr;
}
HTMLImportsController* Document::EnsureImportsController() {
if (!imports_controller_) {
DCHECK(frame_);
imports_controller_ = MakeGarbageCollected<HTMLImportsController>(*this);
}
return imports_controller_;
}
HTMLImportLoader* Document::ImportLoader() const {
if (!imports_controller_)
return nullptr;
return imports_controller_->LoaderFor(*this);
}
bool Document::IsHTMLImport() const {
return imports_controller_ && imports_controller_->Master() != this;
}
Document& Document::MasterDocument() const {
if (!imports_controller_)
return *const_cast<Document*>(this);
Document* master = imports_controller_->Master();
DCHECK(master);
return *master;
}
bool Document::HaveImportsLoaded() const {
if (!imports_controller_)
return true;
return !imports_controller_->ShouldBlockScriptExecution(*this);
}
LocalDOMWindow* Document::ExecutingWindow() const {
if (LocalDOMWindow* owning_window = domWindow())
return owning_window;
if (HTMLImportsController* import = ImportsController())
return import->Master()->domWindow();
return nullptr;
}
LocalFrame* Document::ExecutingFrame() {
LocalDOMWindow* window = ExecutingWindow();
if (!window)
return nullptr;
return window->GetFrame();
}
DocumentFragment* Document::createDocumentFragment() {
return DocumentFragment::Create(*this);
}
Text* Document::createTextNode(const String& data) {
return Text::Create(*this, data);
}
Comment* Document::createComment(const String& data) {
return Comment::Create(*this, data);
}
CDATASection* Document::createCDATASection(const String& data,
ExceptionState& exception_state) {
if (IsA<HTMLDocument>(this)) {
exception_state.ThrowDOMException(
DOMExceptionCode::kNotSupportedError,
"This operation is not supported for HTML documents.");
return nullptr;
}
if (data.Contains("]]>")) {
exception_state.ThrowDOMException(DOMExceptionCode::kInvalidCharacterError,
"String cannot contain ']]>' since that "
"is the end delimiter of a CData "
"section.");
return nullptr;
}
return CDATASection::Create(*this, data);
}
ProcessingInstruction* Document::createProcessingInstruction(
const String& target,
const String& data,
ExceptionState& exception_state) {
if (!IsValidName(target)) {
exception_state.ThrowDOMException(
DOMExceptionCode::kInvalidCharacterError,
"The target provided ('" + target + "') is not a valid name.");
return nullptr;
}
if (data.Contains("?>")) {
exception_state.ThrowDOMException(
DOMExceptionCode::kInvalidCharacterError,
"The data provided ('" + data + "') contains '?>'.");
return nullptr;
}
if (IsA<HTMLDocument>(this)) {
UseCounter::Count(*this,
WebFeature::kHTMLDocumentCreateProcessingInstruction);
}
return MakeGarbageCollected<ProcessingInstruction>(*this, target, data);
}
Text* Document::CreateEditingTextNode(const String& text) {
return Text::CreateEditingText(*this, text);
}
Node* Document::importNode(Node* imported_node,
bool deep,
ExceptionState& exception_state) {
// https://dom.spec.whatwg.org/#dom-document-importnode
// 1. If node is a document or shadow root, then throw a "NotSupportedError"
// DOMException.
if (imported_node->IsDocumentNode()) {
exception_state.ThrowDOMException(
DOMExceptionCode::kNotSupportedError,
"The node provided is a document, which may not be imported.");
return nullptr;
}
if (imported_node->IsShadowRoot()) {
// ShadowRoot nodes should not be explicitly importable. Either they are
// imported along with their host node, or created implicitly.
exception_state.ThrowDOMException(
DOMExceptionCode::kNotSupportedError,
"The node provided is a shadow root, which may not be imported.");
return nullptr;
}
// 2. Return a clone of node, with context object and the clone children flag
// set if deep is true.
return imported_node->Clone(
*this, deep ? CloneChildrenFlag::kClone : CloneChildrenFlag::kSkip);
}
Node* Document::adoptNode(Node* source, ExceptionState& exception_state) {
EventQueueScope scope;
switch (source->getNodeType()) {
case kDocumentNode:
exception_state.ThrowDOMException(DOMExceptionCode::kNotSupportedError,
"The node provided is of type '" +
source->nodeName() +
"', which may not be adopted.");
return nullptr;
case kAttributeNode: {
auto* attr = To<Attr>(source);
if (Element* owner_element = attr->ownerElement())
owner_element->removeAttributeNode(attr, exception_state);
break;
}
default:
if (source->IsShadowRoot()) {
// ShadowRoot cannot disconnect itself from the host node.
exception_state.ThrowDOMException(
DOMExceptionCode::kHierarchyRequestError,
"The node provided is a shadow root, which may not be adopted.");
return nullptr;
}
if (auto* frame_owner_element =
DynamicTo<HTMLFrameOwnerElement>(source)) {
if (GetFrame() && GetFrame()->Tree().IsDescendantOf(
frame_owner_element->ContentFrame())) {
exception_state.ThrowDOMException(
DOMExceptionCode::kHierarchyRequestError,
"The node provided is a frame which contains this document.");
return nullptr;
}
}
if (source->parentNode()) {
source->parentNode()->RemoveChild(source, exception_state);
if (exception_state.HadException())
return nullptr;
// The above removeChild() can execute arbitrary JavaScript code.
if (source->parentNode()) {
AddConsoleMessage(MakeGarbageCollected<ConsoleMessage>(
mojom::ConsoleMessageSource::kJavaScript,
mojom::ConsoleMessageLevel::kWarning,
ExceptionMessages::FailedToExecute("adoptNode", "Document",
"Unable to remove the "
"specified node from the "
"original parent.")));
return nullptr;
}
}
}
AdoptIfNeeded(*source);
return source;
}
bool Document::HasValidNamespaceForElements(const QualifiedName& q_name) {
// These checks are from DOM Core Level 2, createElementNS
// http://www.w3.org/TR/DOM-Level-2-Core/core.html#ID-DocCrElNS
// createElementNS(null, "html:div")
if (!q_name.Prefix().IsEmpty() && q_name.NamespaceURI().IsNull())
return false;
// createElementNS("http://www.example.com", "xml:lang")
if (q_name.Prefix() == g_xml_atom &&
q_name.NamespaceURI() != xml_names::kNamespaceURI)
return false;
// Required by DOM Level 3 Core and unspecified by DOM Level 2 Core:
// http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/core.html#ID-DocCrElNS
// createElementNS("http://www.w3.org/2000/xmlns/", "foo:bar"),
// createElementNS(null, "xmlns:bar"), createElementNS(null, "xmlns")
if (q_name.Prefix() == g_xmlns_atom ||
(q_name.Prefix().IsEmpty() && q_name.LocalName() == g_xmlns_atom))
return q_name.NamespaceURI() == xmlns_names::kNamespaceURI;
return q_name.NamespaceURI() != xmlns_names::kNamespaceURI;
}
bool Document::HasValidNamespaceForAttributes(const QualifiedName& q_name) {
return HasValidNamespaceForElements(q_name);
}
String Document::readyState() const {
DEFINE_STATIC_LOCAL(const String, loading, ("loading"));
DEFINE_STATIC_LOCAL(const String, interactive, ("interactive"));
DEFINE_STATIC_LOCAL(const String, complete, ("complete"));
switch (ready_state_) {
case kLoading:
return loading;
case kInteractive:
return interactive;
case kComplete:
return complete;
}
NOTREACHED();
return String();
}
void Document::SetReadyState(DocumentReadyState ready_state) {
if (ready_state == ready_state_)
return;
switch (ready_state) {
case kLoading:
if (document_timing_.DomLoading().is_null()) {
document_timing_.MarkDomLoading();
}
break;
case kInteractive:
if (document_timing_.DomInteractive().is_null())
document_timing_.MarkDomInteractive();
break;
case kComplete:
if (document_timing_.DomComplete().is_null())
document_timing_.MarkDomComplete();
break;
}
ready_state_ = ready_state;
DispatchEvent(*Event::Create(event_type_names::kReadystatechange));
}
bool Document::IsLoadCompleted() const {
return ready_state_ == kComplete;
}
AtomicString Document::EncodingName() const {
// TextEncoding::name() returns a char*, no need to allocate a new
// String for it each time.
// FIXME: We should fix TextEncoding to speak AtomicString anyway.
return AtomicString(Encoding().GetName());
}
void Document::SetContentLanguage(const AtomicString& language) {
if (content_language_ == language)
return;
content_language_ = language;
// Document's style depends on the content language.
GetStyleEngine().MarkViewportStyleDirty();
GetStyleEngine().MarkAllElementsForStyleRecalc(
StyleChangeReasonForTracing::Create(style_change_reason::kLanguage));
}
void Document::setXMLVersion(const String& version,
ExceptionState& exception_state) {
if (!XMLDocumentParser::SupportsXMLVersion(version)) {
exception_state.ThrowDOMException(
DOMExceptionCode::kNotSupportedError,
"This document does not support the XML version '" + version + "'.");
return;
}
xml_version_ = version;
}
void Document::setXMLStandalone(bool standalone,
ExceptionState& exception_state) {
xml_standalone_ = standalone ? kStandalone : kNotStandalone;
}
void Document::SetContent(const String& content) {
// Only set the content of the document if it is ready to be set. This method
// could be called at any time.
if (ScriptableDocumentParser* parser = GetScriptableDocumentParser()) {
if (parser->IsParsing() && parser->IsExecutingScript())
return;
}
if (ignore_opens_during_unload_count_)
return;
open();
parser_->Append(content);
close();
}
String Document::SuggestedMIMEType() const {
if (IsA<XMLDocument>(this)) {
if (IsXHTMLDocument())
return "application/xhtml+xml";
if (IsSVGDocument())
return "image/svg+xml";
return "application/xml";
}
if (xmlStandalone())
return "text/xml";
if (IsA<HTMLDocument>(this))
return "text/html";
if (DocumentLoader* document_loader = Loader())
return document_loader->MimeType();
return String();
}
void Document::SetMimeType(const AtomicString& mime_type) {
mime_type_ = mime_type;
}
AtomicString Document::contentType() const {
if (!mime_type_.IsEmpty())
return mime_type_;
if (DocumentLoader* document_loader = Loader())
return document_loader->MimeType();
String mime_type = SuggestedMIMEType();
if (!mime_type.IsEmpty())
return AtomicString(mime_type);
return AtomicString("application/xml");
}
Element* Document::ElementFromPoint(double x, double y) const {
if (!GetLayoutView())
return nullptr;
return TreeScope::ElementFromPoint(x, y);
}
HeapVector<Member<Element>> Document::ElementsFromPoint(double x,
double y) const {
if (!GetLayoutView())
return HeapVector<Member<Element>>();
return TreeScope::ElementsFromPoint(x, y);
}
Range* Document::caretRangeFromPoint(int x, int y) {
if (!GetLayoutView())
return nullptr;
HitTestResult result = HitTestInDocument(this, x, y);
PositionWithAffinity position_with_affinity = result.GetPosition();
if (position_with_affinity.IsNull())
return nullptr;
Position range_compliant_position =
position_with_affinity.GetPosition().ParentAnchoredEquivalent();
return CreateRangeAdjustedToTreeScope(*this, range_compliant_position);
}
Element* Document::scrollingElement() {
if (RuntimeEnabledFeatures::ScrollTopLeftInteropEnabled() && InQuirksMode())
UpdateStyleAndLayoutTree();
return ScrollingElementNoLayout();
}
Element* Document::ScrollingElementNoLayout() {
if (RuntimeEnabledFeatures::ScrollTopLeftInteropEnabled()) {
if (InQuirksMode()) {
DCHECK(!IsActive() ||
lifecycle_.GetState() >= DocumentLifecycle::kStyleClean);
HTMLBodyElement* body = FirstBodyElement();
if (body && body->GetLayoutObject() &&
body->GetLayoutObject()->HasOverflowClip())
return nullptr;
return body;
}
return documentElement();
}
return body();
}
/*
* Performs three operations:
* 1. Convert control characters to spaces
* 2. Trim leading and trailing spaces
* 3. Collapse internal whitespace.
*/
template <typename CharacterType>
static inline String CanonicalizedTitle(Document* document,
const String& title) {
unsigned length = title.length();
unsigned builder_index = 0;
const CharacterType* characters = title.GetCharacters<CharacterType>();
StringBuffer<CharacterType> buffer(length);
// Replace control characters with spaces and collapse whitespace.
bool pending_whitespace = false;
for (unsigned i = 0; i < length; ++i) {
UChar32 c = characters[i];
if ((c <= WTF::unicode::kSpaceCharacter &&
c != WTF::unicode::kLineTabulationCharacter) ||
c == WTF::unicode::kDeleteCharacter) {
if (builder_index != 0)
pending_whitespace = true;
} else {
if (pending_whitespace) {
buffer[builder_index++] = ' ';
pending_whitespace = false;
}
buffer[builder_index++] = c;
}
}
buffer.Shrink(builder_index);
return String::Adopt(buffer);
}
void Document::UpdateTitle(const String& title) {
if (raw_title_ == title)
return;
raw_title_ = title;
String old_title = title_;
if (raw_title_.IsEmpty())
title_ = String();
else if (raw_title_.Is8Bit())
title_ = CanonicalizedTitle<LChar>(this, raw_title_);
else
title_ = CanonicalizedTitle<UChar>(this, raw_title_);
if (!frame_ || old_title == title_)
return;
DispatchDidReceiveTitle();
if (AXObjectCache* cache = ExistingAXObjectCache())
cache->DocumentTitleChanged();
}
void Document::DispatchDidReceiveTitle() {
if (GetFrame() && !GetFrame()->Tree().Parent()) {
String shortened_title = title_.Substring(0, mojom::blink::kMaxTitleChars);
GetFrame()->GetLocalFrameHostRemote().UpdateTitle(
shortened_title, mojo_base::mojom::blink::TextDirection::LEFT_TO_RIGHT);
}
frame_->Client()->DispatchDidReceiveTitle(title_);
}
void Document::setTitle(const String& title) {
// Title set by JavaScript -- overrides any title elements.
Element* element = documentElement();
if (IsA<SVGSVGElement>(element)) {
if (!title_element_) {
title_element_ = MakeGarbageCollected<SVGTitleElement>(*this);
element->InsertBefore(title_element_.Get(), element->firstChild());
}
if (auto* svg_title = DynamicTo<SVGTitleElement>(title_element_.Get()))
svg_title->SetText(title);
} else if (element && element->IsHTMLElement()) {
if (!title_element_) {
HTMLElement* head_element = head();
if (!head_element)
return;
title_element_ = MakeGarbageCollected<HTMLTitleElement>(*this);
head_element->AppendChild(title_element_.Get());
}
if (auto* html_title = DynamicTo<HTMLTitleElement>(title_element_.Get()))
html_title->setText(title);
}
}
void Document::SetTitleElement(Element* title_element) {
// If the root element is an svg element in the SVG namespace, then let value
// be the child text content of the first title element in the SVG namespace
// that is a child of the root element.
if (IsA<SVGSVGElement>(documentElement())) {
title_element_ = Traversal<SVGTitleElement>::FirstChild(*documentElement());
} else {
if (title_element_ && title_element_ != title_element)
title_element_ = Traversal<HTMLTitleElement>::FirstWithin(*this);
else
title_element_ = title_element;
// If the root element isn't an svg element in the SVG namespace and the
// title element is in the SVG namespace, it is ignored.
if (IsA<SVGTitleElement>(*title_element_)) {
title_element_ = nullptr;
return;
}
}
if (auto* html_title = DynamicTo<HTMLTitleElement>(title_element_.Get()))
UpdateTitle(html_title->text());
else if (auto* svg_title = DynamicTo<SVGTitleElement>(title_element_.Get()))
UpdateTitle(svg_title->textContent());
}
void Document::RemoveTitle(Element* title_element) {
if (title_element_ != title_element)
return;
title_element_ = nullptr;
// Update title based on first title element in the document, if one exists.
if (IsA<HTMLDocument>(this) || IsXHTMLDocument()) {
if (HTMLTitleElement* title =
Traversal<HTMLTitleElement>::FirstWithin(*this))
SetTitleElement(title);
} else if (IsSVGDocument()) {
if (SVGTitleElement* title = Traversal<SVGTitleElement>::FirstWithin(*this))
SetTitleElement(title);
}
if (!title_element_)
UpdateTitle(String());
}
const AtomicString& Document::dir() {
Element* root_element = documentElement();
if (auto* html = DynamicTo<HTMLHtmlElement>(root_element))
return html->dir();
return g_null_atom;
}
void Document::setDir(const AtomicString& value) {
Element* root_element = documentElement();
if (auto* html = DynamicTo<HTMLHtmlElement>(root_element))
html->setDir(value);
}
bool Document::IsPageVisible() const {
// The visibility of the document is inherited from the visibility of the
// page. If there is no page associated with the document, we will assume
// that the page is hidden, as specified by the spec:
// https://w3c.github.io/page-visibility/#hidden-attribute
if (!frame_ || !frame_->GetPage())
return false;
// While visibilitychange is being dispatched during unloading it is
// expected that the visibility is hidden regardless of the page's
// visibility.
if (load_event_progress_ >= kUnloadVisibilityChangeInProgress)
return false;
return frame_->GetPage()->IsPageVisible();
}
bool Document::IsPrefetchOnly() const {
if (!frame_ || !frame_->GetPage())
return false;
PrerendererClient* prerenderer_client =
PrerendererClient::From(frame_->GetPage());
return prerenderer_client && prerenderer_client->IsPrefetchOnly();
}
String Document::visibilityState() const {
return PageHiddenStateString(hidden());
}
bool Document::hidden() const {
return !IsPageVisible();
}
bool Document::wasDiscarded() const {
return was_discarded_;
}
void Document::SetWasDiscarded(bool was_discarded) {
was_discarded_ = was_discarded;
}
void Document::DidChangeVisibilityState() {
DispatchEvent(*Event::CreateBubble(event_type_names::kVisibilitychange));
// Also send out the deprecated version until it can be removed.
DispatchEvent(
*Event::CreateBubble(event_type_names::kWebkitvisibilitychange));
if (IsPageVisible())
Timeline().SetAllCompositorPending();
if (hidden() && canvas_font_cache_)
canvas_font_cache_->PruneAll();
InteractiveDetector* interactive_detector = InteractiveDetector::From(*this);
if (interactive_detector) {
interactive_detector->OnPageHiddenChanged(hidden());
}
}
String Document::nodeName() const {
return "#document";
}
Node::NodeType Document::getNodeType() const {
return kDocumentNode;
}
FormController& Document::GetFormController() {
if (!form_controller_) {
form_controller_ = MakeGarbageCollected<FormController>(*this);
HistoryItem* history_item = Loader() ? Loader()->GetHistoryItem() : nullptr;
if (history_item)
history_item->SetDocumentState(form_controller_->ControlStates());
}
return *form_controller_;
}
DocumentState* Document::GetDocumentState() const {
if (!form_controller_)
return nullptr;
return form_controller_->ControlStates();
}
void Document::SetStateForNewControls(const Vector<String>& state_vector) {
if (!state_vector.size() && !form_controller_)
return;
GetFormController().SetStateForNewControls(state_vector);
}
LocalFrameView* Document::View() const {
return frame_ ? frame_->View() : nullptr;
}
Page* Document::GetPage() const {
return frame_ ? frame_->GetPage() : nullptr;
}
LocalFrame* Document::GetFrameOfMasterDocument() const {
if (frame_)
return frame_;
if (imports_controller_)
return imports_controller_->Master()->GetFrame();
return nullptr;
}
Settings* Document::GetSettings() const {
return frame_ ? frame_->GetSettings() : nullptr;
}
Range* Document::createRange() {
return Range::Create(*this);
}
NodeIterator* Document::createNodeIterator(Node* root,
unsigned what_to_show,
V8NodeFilter* filter) {
DCHECK(root);
return MakeGarbageCollected<NodeIterator>(root, what_to_show, filter);
}
TreeWalker* Document::createTreeWalker(Node* root,
unsigned what_to_show,
V8NodeFilter* filter) {
DCHECK(root);
return MakeGarbageCollected<TreeWalker>(root, what_to_show, filter);
}
bool Document::NeedsLayoutTreeUpdate() const {
if (!IsActive() || !View())
return false;
if (NeedsFullLayoutTreeUpdate())
return true;
if (style_engine_->NeedsStyleRecalc())
return true;
if (style_engine_->NeedsStyleInvalidation())
return true;
if (GetLayoutView() && GetLayoutView()->WasNotifiedOfSubtreeChange())
return true;
if (style_engine_->NeedsLayoutTreeRebuild()) {
// TODO(futhark): there a couple of places where call back into the top
// frame while recursively doing a lifecycle update. One of them are for the
// RootScrollerController. These should probably be post layout tasks and
// make this test unnecessary since the layout tree rebuild dirtiness is
// internal to StyleEngine::UpdateStyleAndLayoutTree().
DCHECK(InStyleRecalc());
return true;
}
return false;
}
bool Document::NeedsFullLayoutTreeUpdate() const {
// This method returns true if we cannot decide which specific elements need
// to have its style or layout tree updated on the next lifecycle update. If
// this method returns false, we typically use that to walk up the ancestor
// chain to decide if we can let getComputedStyle() use the current
// ComputedStyle without doing the lifecycle update (implemented in
// Document::NeedsLayoutTreeUpdateForNodeIncludingDisplayLocked()).
if (!IsActive() || !View())
return false;
if (style_engine_->NeedsFullStyleUpdate())
return true;
if (!use_elements_needing_update_.IsEmpty())
return true;
// We have scheduled an invalidation set on the document node which means any
// element may need a style recalc.
if (NeedsStyleInvalidation())
return true;
if (IsSlotAssignmentOrLegacyDistributionDirty())
return true;
if (document_animations_->NeedsAnimationTimingUpdate())
return true;
return false;
}
bool Document::ShouldScheduleLayoutTreeUpdate() const {
if (!IsActive())
return false;
if (InStyleRecalc())
return false;
// InPreLayout will recalc style itself. There's no reason to schedule another
// recalc.
if (lifecycle_.GetState() == DocumentLifecycle::kInPreLayout)
return false;
if (!ShouldScheduleLayout())
return false;
return true;
}
void Document::ScheduleLayoutTreeUpdate() {
DCHECK(!HasPendingVisualUpdate());
DCHECK(ShouldScheduleLayoutTreeUpdate());
DCHECK(NeedsLayoutTreeUpdate());
if (!View()->CanThrottleRendering())
GetPage()->Animator().ScheduleVisualUpdate(GetFrame());
// FrameSelection caches visual selection information, which must be
// invalidated on dirty layout tree.
GetFrame()->Selection().MarkCacheDirty();
lifecycle_.EnsureStateAtMost(DocumentLifecycle::kVisualUpdatePending);
TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"),
"ScheduleStyleRecalculation", TRACE_EVENT_SCOPE_THREAD,
"data",
inspector_recalculate_styles_event::Data(GetFrame()));
++style_version_;
}
bool Document::HasPendingForcedStyleRecalc() const {
return HasPendingVisualUpdate() && !InStyleRecalc() &&
GetStyleChangeType() == kSubtreeStyleChange;
}
void Document::UpdateStyleInvalidationIfNeeded() {
DCHECK(IsActive());
ScriptForbiddenScope forbid_script;
if (!GetStyleEngine().NeedsStyleInvalidation())
return;
TRACE_EVENT0("blink", "Document::updateStyleInvalidationIfNeeded");
SCOPED_BLINK_UMA_HISTOGRAM_TIMER_HIGHRES("Style.InvalidationTime");
GetStyleEngine().InvalidateStyle();
}
void Document::SetupFontBuilder(ComputedStyle& document_style) {
FontBuilder font_builder(this);
CSSFontSelector* selector = GetStyleEngine().GetFontSelector();
font_builder.CreateFontForDocument(selector, document_style);
}
#define PROPAGATE_FROM(source, getter, setter, initial) \
PROPAGATE_VALUE(source ? source->getter() : initial, getter, setter);
#define PROPAGATE_VALUE(value, getter, setter) \
if ((new_viewport_style->getter()) != (value)) { \
new_viewport_style->setter(value); \
changed = true; \
}
bool PropagateScrollSnapStyleToViewport(
Document& document,
const ComputedStyle* document_element_style,
scoped_refptr<ComputedStyle> new_viewport_style) {
bool changed = false;
// We only propagate the properties related to snap container since viewport
// defining element cannot be a snap area.
PROPAGATE_FROM(document_element_style, GetScrollSnapType, SetScrollSnapType,
cc::ScrollSnapType());
PROPAGATE_FROM(document_element_style, ScrollPaddingTop, SetScrollPaddingTop,
Length());
PROPAGATE_FROM(document_element_style, ScrollPaddingRight,
SetScrollPaddingRight, Length());
PROPAGATE_FROM(document_element_style, ScrollPaddingBottom,
SetScrollPaddingBottom, Length());
PROPAGATE_FROM(document_element_style, ScrollPaddingLeft,
SetScrollPaddingLeft, Length());
if (changed) {
document.GetSnapCoordinator().SnapContainerDidChange(
*document.GetLayoutView());
}
return changed;
}
void Document::PropagateStyleToViewport() {
DCHECK(InStyleRecalc());
HTMLElement* body = this->body();
Element* document_element = this->documentElement();
const ComputedStyle* document_element_style =
document_element && documentElement()->GetLayoutObject()
? documentElement()->GetComputedStyle()
: nullptr;
const ComputedStyle* body_style =
body && body->GetLayoutObject() ? body->GetComputedStyle() : nullptr;
const ComputedStyle& viewport_style = GetLayoutView()->StyleRef();
scoped_refptr<ComputedStyle> new_viewport_style =
ComputedStyle::Clone(viewport_style);
bool changed = false;
bool update_scrollbar_style = false;
// Writing mode and direction
{
const ComputedStyle* direction_style =
body_style ? body_style : document_element_style;
PROPAGATE_FROM(direction_style, GetWritingMode, SetWritingMode,
WritingMode::kHorizontalTb);
PROPAGATE_FROM(direction_style, Direction, SetDirection,
TextDirection::kLtr);
}
// Background
{
const ComputedStyle* background_style = document_element_style;
// http://www.w3.org/TR/css3-background/#body-background
// <html> root element with no background steals background from its first
// <body> child.
// Also see LayoutBoxModelObject::BackgroundTransfersToView()
if (body_style && IsA<HTMLHtmlElement>(documentElement()) &&
IsA<HTMLBodyElement>(body) && !background_style->HasBackground()) {
background_style = body_style;
}
Color background_color = Color::kTransparent;
FillLayer background_layers(EFillLayerType::kBackground, true);
EImageRendering image_rendering = EImageRendering::kAuto;
if (background_style) {
background_color = background_style->VisitedDependentColor(
GetCSSPropertyBackgroundColor());
background_layers = background_style->BackgroundLayers();
for (auto* current_layer = &background_layers; current_layer;
current_layer = current_layer->Next()) {
// http://www.w3.org/TR/css3-background/#root-background
// The root element background always have painting area of the whole
// canvas.
current_layer->SetClip(EFillBox::kBorder);
// The root element doesn't scroll. It always propagates its layout
// overflow to the viewport. Positioning background against either box
// is equivalent to positioning against the scrolled box of the
// viewport.
if (current_layer->Attachment() == EFillAttachment::kScroll)
current_layer->SetAttachment(EFillAttachment::kLocal);
}
image_rendering = background_style->ImageRendering();
}
if (viewport_style.VisitedDependentColor(GetCSSPropertyBackgroundColor()) !=
background_color ||
viewport_style.BackgroundLayers() != background_layers ||
viewport_style.ImageRendering() != image_rendering) {
changed = true;
new_viewport_style->SetBackgroundColor(background_color);
new_viewport_style->AccessBackgroundLayers() = background_layers;
new_viewport_style->SetImageRendering(image_rendering);
}
}
// Overflow
{
const ComputedStyle* overflow_style = nullptr;
if (Element* viewport_element = ViewportDefiningElement()) {
if (viewport_element == body) {
overflow_style = body_style;
} else {
DCHECK_EQ(viewport_element, documentElement());
overflow_style = document_element_style;
// The body element has its own scrolling box, independent from the
// viewport. This is a bit of a weird edge case in the CSS spec that we
// might want to try to eliminate some day (eg. for ScrollTopLeftInterop
// - see http://crbug.com/157855).
if (body_style && !body_style->IsOverflowVisible()) {
UseCounter::Count(*this,
WebFeature::kBodyScrollsInAdditionToViewport);
}
}
}
// TODO(954423, 952711): overscroll-behavior (and most likely
// overflow-anchor) should be propagated from the document element and not
// the viewport defining element.
PROPAGATE_FROM(overflow_style, OverscrollBehaviorX, SetOverscrollBehaviorX,
EOverscrollBehavior::kAuto);
PROPAGATE_FROM(overflow_style, OverscrollBehaviorY, SetOverscrollBehaviorY,
EOverscrollBehavior::kAuto);
// Counts any time scroll snapping and scroll padding break if we change its
// viewport propagation logic. Scroll snapping only breaks if body has
// non-none snap type that is different from the document one.
// TODO(952711): Remove once propagation logic change is complete.
if (document_element_style && body_style) {
bool snap_type_is_different =
!body_style->GetScrollSnapType().is_none &&
(body_style->GetScrollSnapType() !=
document_element_style->GetScrollSnapType());
bool scroll_padding_is_different =
body_style->ScrollPaddingTop() !=
document_element_style->ScrollPaddingTop() ||
body_style->ScrollPaddingBottom() !=
document_element_style->ScrollPaddingBottom() ||
body_style->ScrollPaddingLeft() !=
document_element_style->ScrollPaddingLeft() ||
body_style->ScrollPaddingRight() !=
document_element_style->ScrollPaddingRight();
if (snap_type_is_different) {
UseCounter::Count(*this, WebFeature::kScrollSnapOnViewportBreaks);
}
if (scroll_padding_is_different) {
UseCounter::Count(*this, WebFeature::kScrollPaddingOnViewportBreaks);
}
}
EOverflow overflow_x = EOverflow::kAuto;
EOverflow overflow_y = EOverflow::kAuto;
EOverflowAnchor overflow_anchor = EOverflowAnchor::kAuto;
if (overflow_style) {
overflow_x = overflow_style->OverflowX();
overflow_y = overflow_style->OverflowY();
overflow_anchor = overflow_style->OverflowAnchor();
// Visible overflow on the viewport is meaningless, and the spec says to
// treat it as 'auto':
if (overflow_x == EOverflow::kVisible)
overflow_x = EOverflow::kAuto;
if (overflow_y == EOverflow::kVisible)
overflow_y = EOverflow::kAuto;
if (overflow_anchor == EOverflowAnchor::kVisible)
overflow_anchor = EOverflowAnchor::kAuto;
if (IsInMainFrame()) {
using OverscrollBehaviorType =
cc::OverscrollBehavior::OverscrollBehaviorType;
GetPage()->GetChromeClient().SetOverscrollBehavior(
*GetFrame(),
cc::OverscrollBehavior(static_cast<OverscrollBehaviorType>(
overflow_style->OverscrollBehaviorX()),
static_cast<OverscrollBehaviorType>(
overflow_style->OverscrollBehaviorY())));
}
if (overflow_style->HasPseudoElementStyle(kPseudoIdScrollbar))
update_scrollbar_style = true;
}
PROPAGATE_VALUE(overflow_x, OverflowX, SetOverflowX)
PROPAGATE_VALUE(overflow_y, OverflowY, SetOverflowY)
PROPAGATE_VALUE(overflow_anchor, OverflowAnchor, SetOverflowAnchor);
}
// Misc
{
PROPAGATE_FROM(document_element_style, GetEffectiveTouchAction,
SetEffectiveTouchAction, TouchAction::kAuto);
PROPAGATE_FROM(document_element_style, GetScrollBehavior, SetScrollBehavior,
mojom::blink::ScrollBehavior::kAuto);
PROPAGATE_FROM(document_element_style, DarkColorScheme, SetDarkColorScheme,
false);
}
changed |= PropagateScrollSnapStyleToViewport(*this, document_element_style,
new_viewport_style);
if (changed) {
new_viewport_style->UpdateFontOrientation();
GetLayoutView()->SetStyle(new_viewport_style);
SetupFontBuilder(*new_viewport_style);
}
if (changed || update_scrollbar_style) {
if (PaintLayerScrollableArea* scrollable_area =
GetLayoutView()->GetScrollableArea()) {
if (scrollable_area->HorizontalScrollbar() &&
scrollable_area->HorizontalScrollbar()->IsCustomScrollbar())
scrollable_area->HorizontalScrollbar()->StyleChanged();
if (scrollable_area->VerticalScrollbar() &&
scrollable_area->VerticalScrollbar()->IsCustomScrollbar())
scrollable_area->VerticalScrollbar()->StyleChanged();
}
}
}
#undef PROPAGATE_VALUE
#undef PROPAGATE_FROM
#if DCHECK_IS_ON()
static void AssertNodeClean(const Node& node) {
DCHECK(!node.NeedsStyleRecalc());
DCHECK(!node.ChildNeedsStyleRecalc());
DCHECK(!node.NeedsReattachLayoutTree());
DCHECK(!node.ChildNeedsReattachLayoutTree());
DCHECK(!node.ChildNeedsDistributionRecalc());
DCHECK(!node.NeedsStyleInvalidation());
DCHECK(!node.ChildNeedsStyleInvalidation());
DCHECK(!node.GetForceReattachLayoutTree());
}
static void AssertLayoutTreeUpdatedForPseudoElements(const Element& element) {
WTF::Vector<PseudoId> pseudo_ids = {kPseudoIdFirstLetter, kPseudoIdBefore,
kPseudoIdAfter, kPseudoIdMarker,
kPseudoIdBackdrop};
for (auto pseudo_id : pseudo_ids) {
if (auto* pseudo_element = element.GetPseudoElement(pseudo_id))
AssertNodeClean(*pseudo_element);
}
}
static void AssertLayoutTreeUpdated(Node& root) {
Node* node = &root;
while (node) {
if (auto* element = DynamicTo<Element>(node)) {
if (RuntimeEnabledFeatures::CSSSubtreeVisibilityEnabled() &&
element->StyleRecalcBlockedByDisplayLock(
DisplayLockLifecycleTarget::kChildren)) {
node = FlatTreeTraversal::NextSkippingChildren(*node);
continue;
}
// Check pseudo elements.
AssertLayoutTreeUpdatedForPseudoElements(*element);
}
AssertNodeClean(*node);
// Make sure there is no node which has a LayoutObject, but doesn't have a
// parent in a flat tree. If there is such a node, we forgot to detach the
// node. DocumentNode is only an exception.
DCHECK((node->IsDocumentNode() || !node->GetLayoutObject() ||
FlatTreeTraversal::Parent(*node)))
<< *node;
node = FlatTreeTraversal::Next(*node);
}
}
#endif
void Document::UpdateStyleAndLayoutTree() {
DCHECK(IsMainThread());
if (Lifecycle().LifecyclePostponed())
return;
HTMLFrameOwnerElement::PluginDisposeSuspendScope suspend_plugin_dispose;
ScriptForbiddenScope forbid_script;
if (HTMLFrameOwnerElement* owner = LocalOwner()) {
owner->GetDocument().UpdateStyleAndLayoutTree();
}
if (!View() || !IsActive())
return;
if (View()->ShouldThrottleRendering())
return;
// RecalcSlotAssignments should be done before checking
// NeedsLayoutTreeUpdate().
GetSlotAssignmentEngine().RecalcSlotAssignments();
// We can call FlatTreeTraversal::AssertFlatTreeNodeDataUpdated just after
// calling RecalcSlotAssignments(), however, it would be better to call it at
// least after InStyleRecalc() check below in order to avoid superfluous
// check, which would be the cause of web tests timeout when dcheck is on.
SlotAssignmentRecalcForbiddenScope forbid_slot_recalc(*this);
if (!NeedsLayoutTreeUpdate()) {
if (Lifecycle().GetState() < DocumentLifecycle::kStyleClean) {
// needsLayoutTreeUpdate may change to false without any actual layout
// tree update. For example, needsAnimationTimingUpdate may change to
// false when time elapses. Advance lifecycle to StyleClean because style
// is actually clean now.
Lifecycle().AdvanceTo(DocumentLifecycle::kInStyleRecalc);
Lifecycle().AdvanceTo(DocumentLifecycle::kStyleClean);
}
return;
}
if (InStyleRecalc())
return;
#if DCHECK_IS_ON()
int assigned_nodes_in_slot_count = 0;
int nodes_which_have_assigned_slot_count = 0;
FlatTreeTraversal::AssertFlatTreeNodeDataUpdated(
*this, assigned_nodes_in_slot_count,
nodes_which_have_assigned_slot_count);
DCHECK_EQ(assigned_nodes_in_slot_count, nodes_which_have_assigned_slot_count);
#endif
// Entering here from inside layout, paint etc. would be catastrophic since
// recalcStyle can tear down the layout tree or (unfortunately) run
// script. Kill the whole layoutObject if someone managed to get into here in
// states not allowing tree mutations.
CHECK(Lifecycle().StateAllowsTreeMutations());
TRACE_EVENT_BEGIN1("blink,devtools.timeline", "UpdateLayoutTree", "beginData",
inspector_recalculate_styles_event::Data(GetFrame()));
unsigned start_element_count = GetStyleEngine().StyleForElementCount();
probe::RecalculateStyle recalculate_style_scope(this);
document_animations_->UpdateAnimationTimingIfNeeded();
EvaluateMediaQueryListIfNeeded();
UpdateUseShadowTreesIfNeeded();
UpdateDistributionForLegacyDistributedNodes();
UpdateActiveStyle();
InvalidateStyleAndLayoutForFontUpdates();
UpdateStyleInvalidationIfNeeded();
UpdateStyle();
NotifyLayoutTreeOfSubtreeChanges();
if (focused_element_ && !focused_element_->IsFocusable())
ClearFocusedElementSoon();
GetLayoutView()->ClearHitTestCache();
DCHECK(!document_animations_->NeedsAnimationTimingUpdate());
unsigned element_count =
GetStyleEngine().StyleForElementCount() - start_element_count;
TRACE_EVENT_END1("blink,devtools.timeline", "UpdateLayoutTree",
"elementCount", element_count);
#if DCHECK_IS_ON()
AssertLayoutTreeUpdated(*this);
#endif
}
void Document::UpdateActiveStyle() {
DCHECK(IsActive());
DCHECK(IsMainThread());
TRACE_EVENT0("blink", "Document::updateActiveStyle");
GetStyleEngine().UpdateActiveStyle();
}
void Document::InvalidateStyleAndLayoutForFontUpdates() {
DCHECK(IsActive());
DCHECK(IsMainThread());
GetStyleEngine().InvalidateStyleAndLayoutForFontUpdates();
}
void Document::UpdateStyle() {
DCHECK(!View()->ShouldThrottleRendering());
TRACE_EVENT_BEGIN0("blink,blink_style", "Document::updateStyle");
RUNTIME_CALL_TIMER_SCOPE(V8PerIsolateData::MainThreadIsolate(),
RuntimeCallStats::CounterId::kUpdateStyle);
unsigned initial_element_count = GetStyleEngine().StyleForElementCount();
lifecycle_.AdvanceTo(DocumentLifecycle::kInStyleRecalc);
// SetNeedsStyleRecalc should only happen on Element and Text nodes.
DCHECK(!NeedsStyleRecalc());
StyleResolver& resolver = EnsureStyleResolver();
bool should_record_stats;
TRACE_EVENT_CATEGORY_GROUP_ENABLED("blink,blink_style", &should_record_stats);
GetStyleEngine().SetStatsEnabled(should_record_stats);
GetStyleEngine().UpdateStyleAndLayoutTree();
ClearChildNeedsStyleRecalc();
PropagateStyleToViewport();
View()->UpdateCountersAfterStyleChange();
GetLayoutView()->RecalcLayoutOverflow();
DCHECK(!NeedsStyleRecalc());