blob: 7911001f8ef0efea1ec266f8b136cd909e9090da [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 "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/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/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/interface_provider.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_prerendering_support.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/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_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_creation_options.h"
#include "third_party/blink/renderer/core/dom/element_data_cache.h"
#include "third_party/blink/renderer/core/dom/element_registration_options.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/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_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_trace_events.h"
#include "third_party/blink/renderer/core/inspector/main_thread_debugger.h"
#include "third_party/blink/renderer/core/intersection_observer/intersection_observer_controller.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_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/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_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/timing/dom_window_performance.h"
#include "third_party/blink/renderer/core/timing/window_performance.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/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 ContextLifecycleObserver {
USING_GARBAGE_COLLECTED_MIXIN(Document::NetworkStateObserver);
public:
explicit NetworkStateObserver(Document& document)
: ContextLifecycleObserver(&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 = To<Document>(GetExecutionContext());
if (!document->domWindow())
return;
document->domWindow()->DispatchEvent(*Event::Create(event_name));
probe::NetworkStateChanged(document->GetFrame(), on_line);
}
void ContextDestroyed(ExecutionContext* context) override {
UnregisterAsObserver(context);
}
void UnregisterAsObserver(ExecutionContext* context) {
DCHECK(context);
online_observer_handle_ = nullptr;
}
void Trace(Visitor* visitor) override {
ContextLifecycleObserver::Trace(visitor);
}
private:
std::unique_ptr<NetworkStateNotifier::NetworkStateObserverHandle>
online_observer_handle_;
};
// A helper class that allows the security context be initialized in the
// process of constructing the document.
class Document::SecurityContextInit : public FeaturePolicyParserDelegate {
STACK_ALLOCATED();
public:
SecurityContextInit(const DocumentInit& initializer,
DocumentClassFlags document_classes) {
// Content Security Policy can provide sandbox flags. In CSP
// 'self' will be determined when the policy is bound. That occurs
// once the document is constructed.
InitializeContentSecurityPolicy(initializer, document_classes);
// Sandbox flags can come from initializer, loader or CSP.
InitializeSandboxFlags(initializer);
// The origin can be opaque based on sandbox flags.
InitializeOrigin(initializer);
// Initialize origin trials, requires the post sandbox flags
// security origin.
InitializeOriginTrials(initializer);
// Initialize feature policy, depends on origin trials.
InitializeFeaturePolicy(initializer, document_classes);
// Initialize the agent. Depends on security origin.
InitializeAgent(initializer);
}
const scoped_refptr<SecurityOrigin>& GetSecurityOrigin() const {
return security_origin_;
}
WebSandboxFlags GetSandboxFlags() { return sandbox_flags_; }
ContentSecurityPolicy* GetCSP() const { return csp_; }
std::unique_ptr<FeaturePolicy> TakeFeaturePolicy() {
DCHECK(feature_policy_);
return std::move(feature_policy_);
}
const Vector<String>& FeaturePolicyParseMessages() const {
return feature_policy_parse_messages_;
}
const ParsedFeaturePolicy& ParsedHeader() const { return parsed_header_; }
OriginTrialContext* GetOriginTrialContext() { return origin_trials_; }
WindowAgentFactory* GetWindowAgentFactory() { return window_agent_factory_; }
Agent* GetAgent() { return agent_; }
void CountFeaturePolicyUsage(mojom::WebFeature feature) override {
feature_count_.insert(feature);
}
bool FeaturePolicyFeatureObserved(
mojom::FeaturePolicyFeature feature) override {
if (parsed_feature_policies_.Contains(feature))
return true;
parsed_feature_policies_.insert(feature);
return false;
}
bool FeatureEnabled(OriginTrialFeature feature) const override {
return origin_trials_->IsFeatureEnabled(feature);
}
void ApplyPendingDataToDocument(Document& document) {
for (auto feature : feature_count_)
UseCounter::Count(document, feature);
for (auto feature : parsed_feature_policies_)
document.FeaturePolicyFeatureObserved(feature);
}
bool BindCSPImmediately() const { return bind_csp_immediately_; }
private:
void InitializeContentSecurityPolicy(const DocumentInit& initializer,
DocumentClassFlags document_classes) {
auto* frame = initializer.GetFrame();
ContentSecurityPolicy* last_origin_document_csp =
frame ? frame->Loader().GetLastOriginDocumentCSP() : nullptr;
KURL url;
if (initializer.ShouldSetURL())
url = initializer.Url().IsEmpty() ? BlankURL() : initializer.Url();
// Alias certain security properties from |owner_document|. Used for the
// case of about:blank pages inheriting the security properties of their
// requestor context.
//
// Note that this is currently somewhat broken; Blink always inherits from
// the parent or opener, even though it should actually be inherited from
// the request initiator.
if (url.IsEmpty() && initializer.HasSecurityContext() &&
!initializer.OriginToCommit() && initializer.OwnerDocument()) {
last_origin_document_csp =
initializer.OwnerDocument()->GetContentSecurityPolicy();
}
csp_ = initializer.GetContentSecurityPolicy();
if (!csp_) {
if (initializer.ImportsController()) {
// If this document is an HTML import, grab a reference to its master
// document's Content Security Policy. We don't bind the CSP's delegate
// in 'InitSecurityPolicy' in this case, as we can't rebind the master
// document's policy object: The Content Security Policy's delegate
// needs to remain set to the master document.
csp_ = initializer.ImportsController()
->Master()
->GetContentSecurityPolicy();
return;
}
csp_ = MakeGarbageCollected<ContentSecurityPolicy>();
bind_csp_immediately_ = true;
}
// We should inherit the navigation initiator CSP if the document is loaded
// using a local-scheme url.
//
// Note: about:srcdoc inherits CSP from its parent, not from its initiator.
// In this case, the initializer.GetContentSecurityPolicy() is used.
if (last_origin_document_csp && !url.IsAboutSrcdocURL() &&
(url.IsEmpty() || url.ProtocolIsAbout() || url.ProtocolIsData() ||
url.ProtocolIs("blob") || url.ProtocolIs("filesystem"))) {
csp_->CopyStateFrom(last_origin_document_csp);
}
if (document_classes & kPluginDocumentClass) {
if (last_origin_document_csp) {
csp_->CopyPluginTypesFrom(last_origin_document_csp);
return;
}
// TODO(andypaicu): This should inherit the origin document's plugin types
// but because this could be a OOPIF document it might not have access. In
// this situation we fallback on using the parent/opener:
if (frame) {
Frame* inherit_from = frame->Tree().Parent()
? frame->Tree().Parent()
: frame->Client()->Opener();
if (inherit_from && frame != inherit_from) {
csp_->CopyPluginTypesFrom(
inherit_from->GetSecurityContext()->GetContentSecurityPolicy());
}
}
}
}
void InitializeSandboxFlags(const DocumentInit& initializer) {
sandbox_flags_ = initializer.GetSandboxFlags() | csp_->GetSandboxMask();
auto* frame = initializer.GetFrame();
if (frame && frame->Loader().GetDocumentLoader()->Archive()) {
// The URL of a Document loaded from a MHTML archive is controlled by
// the Content-Location header. This would allow UXSS, since
// Content-Location can be arbitrarily controlled to control the
// Document's URL and origin. Instead, force a Document loaded from a
// MHTML archive to be sandboxed, providing exceptions only for creating
// new windows.
sandbox_flags_ |=
(WebSandboxFlags::kAll &
~(WebSandboxFlags::kPopups |
WebSandboxFlags::kPropagatesToAuxiliaryBrowsingContexts));
}
}
void InitializeOrigin(const DocumentInit& initializer) {
scoped_refptr<SecurityOrigin> document_origin =
initializer.GetDocumentOrigin();
if ((sandbox_flags_ & WebSandboxFlags::kOrigin) != WebSandboxFlags::kNone) {
scoped_refptr<SecurityOrigin> sandboxed_origin =
initializer.OriginToCommit()
? initializer.OriginToCommit()
: document_origin->DeriveNewOpaqueOrigin();
// If we're supposed to inherit our security origin from our
// owner, but we're also sandboxed, the only things we inherit are
// the origin's potential trustworthiness and the ability to
// load local resources. The latter lets about:blank iframes in
// file:// URL documents load images and other resources from
// the file system.
//
// Note: Sandboxed about:srcdoc iframe without "allow-same-origin" aren't
// allowed to load user's file, even if its parent can.
if (initializer.OwnerDocument()) {
if (document_origin->IsPotentiallyTrustworthy())
sandboxed_origin->SetOpaqueOriginIsPotentiallyTrustworthy(true);
if (document_origin->CanLoadLocalResources() &&
!initializer.IsSrcdocDocument())
sandboxed_origin->GrantLoadLocalResources();
}
security_origin_ = sandboxed_origin;
} else {
security_origin_ = document_origin;
}
// If we are a page popup in LayoutTests ensure we use the popup
// owner's security origin so the tests can possibly access the
// document via internals API.
auto* frame = initializer.GetFrame();
if (IsPagePopupRunningInWebTest(frame)) {
security_origin_ = frame->PagePopupOwner()
->GetDocument()
.GetSecurityOrigin()
->IsolatedCopy();
}
if (initializer.HasSecurityContext()) {
if (Settings* settings = initializer.GetSettings()) {
if (!settings->GetWebSecurityEnabled()) {
// Web security is turned off. We should let this document access
// every other document. This is used primary by testing harnesses for
// web sites.
security_origin_->GrantUniversalAccess();
} else if (security_origin_->IsLocal()) {
if (settings->GetAllowUniversalAccessFromFileURLs()) {
// Some clients want local URLs to have universal access, but that
// setting is dangerous for other clients.
security_origin_->GrantUniversalAccess();
} else if (!settings->GetAllowFileAccessFromFileURLs()) {
// Some clients do not want local URLs to have access to other local
// URLs.
security_origin_->BlockLocalAccessFromLocalOrigin();
}
}
}
}
if (initializer.GrantLoadLocalResources())
security_origin_->GrantLoadLocalResources();
}
void InitializeFeaturePolicy(const DocumentInit& initializer,
DocumentClassFlags document_classes) {
auto* frame = initializer.GetFrame();
// For a main frame, get inherited feature policy from the opener if any.
const FeaturePolicy::FeatureState* opener_feature_state = nullptr;
if (frame && frame->IsMainFrame() && !frame->OpenerFeatureState().empty()) {
opener_feature_state = &frame->OpenerFeatureState();
}
parsed_header_ = FeaturePolicyParser::ParseHeader(
initializer.FeaturePolicyHeader(), security_origin_,
&feature_policy_parse_messages_, this);
if (sandbox_flags_ != WebSandboxFlags::kNone &&
RuntimeEnabledFeatures::FeaturePolicyForSandboxEnabled()) {
// The sandbox flags might have come from CSP header or the browser; in
// such cases the sandbox is not part of the container policy. They are
// added to the header policy (which specifically makes sense in the case
// of CSP sandbox).
ApplySandboxFlagsToParsedFeaturePolicy(sandbox_flags_, parsed_header_);
}
ParsedFeaturePolicy container_policy;
if (frame && frame->Owner()) {
// TODO(chenleihu): Due to the data replication mechanism in
// multi-process site-per-process environment, the container_policy
// value in remote frame owner gets lazily updated only when the next
// navigation is initiated on the remote frame. We need a more robust way
// to enforce the validity of container_policy value.
// https://crbug.com/972089
if (frame->Owner()->IsRemote()) {
container_policy = frame->Owner()->GetFramePolicy().container_policy;
} else {
container_policy = initializer.GetFramePolicy()
.value_or(FramePolicy())
.container_policy;
}
}
// TODO(icelland): This is problematic querying sandbox flags before
// feature policy is initialized.
if (RuntimeEnabledFeatures::BlockingFocusWithoutUserActivationEnabled() &&
frame && frame->Tree().Parent() &&
(sandbox_flags_ & WebSandboxFlags::kNavigation) !=
WebSandboxFlags::kNone) {
// Enforcing the policy for sandbox frames (for context see
// https://crbug.com/954349).
DisallowFeatureIfNotPresent(
mojom::FeaturePolicyFeature::kFocusWithoutUserActivation,
container_policy);
}
const FeaturePolicy* parent_feature_policy = nullptr;
if (frame && !frame->IsMainFrame()) {
parent_feature_policy =
frame->Tree().Parent()->GetSecurityContext()->GetFeaturePolicy();
}
// If we are a HTMLViewSourceDocument we use container, header or
// inherited policies. https://crbug.com/898688
if (document_classes & kViewSourceDocumentClass) {
feature_policy_ = FeaturePolicy::CreateFromParentPolicy(
nullptr, {}, security_origin_->ToUrlOrigin());
return;
}
// Feature policy should either come from a parent in the case of an
// embedded child frame, or from an opener if any when a new window is
// created by an opener. A main frame without an opener would not have a
// parent policy nor an opener feature state.
DCHECK(!parent_feature_policy || !opener_feature_state);
if (!opener_feature_state ||
!RuntimeEnabledFeatures::FeaturePolicyForSandboxEnabled()) {
feature_policy_ = FeaturePolicy::CreateFromParentPolicy(
parent_feature_policy, container_policy,
security_origin_->ToUrlOrigin());
} else {
DCHECK(!parent_feature_policy);
feature_policy_ = FeaturePolicy::CreateWithOpenerPolicy(
*opener_feature_state, security_origin_->ToUrlOrigin());
}
feature_policy_->SetHeaderPolicy(parsed_header_);
}
void InitializeOriginTrials(const DocumentInit& initializer) {
origin_trials_ = MakeGarbageCollected<OriginTrialContext>();
const String& header_value = initializer.OriginTrialsHeader();
if (header_value.IsEmpty())
return;
std::unique_ptr<Vector<String>> tokens(
OriginTrialContext::ParseHeaderValue(header_value));
if (!tokens)
return;
origin_trials_->AddTokens(security_origin_.get(), true, *tokens);
}
void InitializeAgent(const DocumentInit& initializer) {
auto* frame = initializer.GetFrame();
// If we are a page popup in LayoutTests ensure we use the popup
// owner's frame for looking up the Agent so the tests can possibly
// access the document via internals API.
if (IsPagePopupRunningInWebTest(frame)) {
frame = frame->PagePopupOwner()->GetDocument().GetFrame();
} else if (!frame) {
if (Document* context_document = initializer.ContextDocument()) {
frame = context_document->GetFrame();
window_agent_factory_ = context_document->window_agent_factory_;
} else if (const Document* owner_document = initializer.OwnerDocument()) {
frame = owner_document->GetFrame();
window_agent_factory_ = owner_document->window_agent_factory_;
}
}
if (!window_agent_factory_ && frame)
window_agent_factory_ = &frame->window_agent_factory();
// If we are allowed to share our document with other windows then we need
// to look at the window agent factory, otherwise we should create our own
// window agent.
if (window_agent_factory_ &&
feature_policy_->IsFeatureEnabled(
mojom::FeaturePolicyFeature::kDocumentAccess)) {
bool has_potential_universal_access_privilege = false;
if (frame) {
if (Settings* settings = frame->GetSettings()) {
// TODO(keishi): Also check if AllowUniversalAccessFromFileURLs might
// dynamically change.
if (!settings->GetWebSecurityEnabled() ||
settings->GetAllowUniversalAccessFromFileURLs())
has_potential_universal_access_privilege = true;
}
}
agent_ = window_agent_factory_->GetAgentForOrigin(
has_potential_universal_access_privilege,
V8PerIsolateData::MainThreadIsolate(), security_origin_.get());
} else {
// ContextDocument is null only for Documents created in unit tests.
// In that case, use a throw away WindowAgent.
agent_ = MakeGarbageCollected<WindowAgent>(
V8PerIsolateData::MainThreadIsolate());
}
// Derive possibly a new security origin that contains the cluster id.
security_origin_ =
security_origin_->GetOriginForAgentCluster(agent_->cluster_id());
}
bool IsPagePopupRunningInWebTest(LocalFrame* frame) {
return frame && frame->GetPage()->GetChromeClient().IsPopup() &&
WebTestSupport::IsRunningWebTest();
}
scoped_refptr<SecurityOrigin> security_origin_;
WebSandboxFlags sandbox_flags_ = WebSandboxFlags::kNone;
std::unique_ptr<FeaturePolicy> feature_policy_;
Vector<String> feature_policy_parse_messages_;
ParsedFeaturePolicy parsed_header_;
Member<ContentSecurityPolicy> csp_;
Member<OriginTrialContext> origin_trials_;
Member<Agent> agent_;
Member<WindowAgentFactory> window_agent_factory_;
HashSet<mojom::FeaturePolicyFeature> parsed_feature_policies_;
HashSet<mojom::WebFeature> feature_count_;
bool bind_csp_immediately_ = false;
};
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_classes) {}
Document::Document(const DocumentInit& initializer,
SecurityContextInit security_initializer,
DocumentClassFlags document_classes)
: ContainerNode(nullptr, kCreateDocument),
TreeScope(*this),
SecurityContext(security_initializer.GetSecurityOrigin(),
security_initializer.GetSandboxFlags(),
security_initializer.TakeFeaturePolicy()),
ExecutionContext(V8PerIsolateData::MainThreadIsolate(),
security_initializer.GetAgent(),
security_initializer.GetOriginTrialContext()),
evaluate_media_queries_on_style_recalc_(false),
pending_sheet_layout_(kNoLayoutWithPendingSheets),
window_agent_factory_(security_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()),
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_(kWebFocusTypeNone),
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),
current_frame_is_throttled_(false),
registration_context_(initializer.RegistrationContext(this)),
element_data_cache_clear_timer_(
GetTaskRunner(TaskType::kInternalUserInteraction),
this,
&Document::ElementDataCacheClearTimerFired),
timeline_(DocumentTimeline::Create(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),
secure_context_state_(SecureContextState::kUnknown),
ukm_source_id_(ukm::UkmRecorder::GetNewSourceID()),
needs_to_record_ukm_outlive_time_(false),
viewport_data_(MakeGarbageCollected<ViewportData>(*this)),
isolated_world_csp_map_(
MakeGarbageCollected<
HeapHashMap<int, Member<ContentSecurityPolicy>>>()) {
security_initializer.ApplyPendingDataToDocument(*this);
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_ = std::make_unique<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();
}
InitSecurityContext(initializer, security_initializer);
FeaturePolicyInitialized(initializer, security_initializer);
InitDNSPrefetch();
InstanceCounters::IncrementCounter(InstanceCounters::kDocumentCounter);
lifecycle_.AdvanceTo(DocumentLifecycle::kInactive);
// 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);
}
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();
}
bool Document::ShouldInstallV8Extensions() const {
return frame_->Client()->AllowScriptExtensions();
}
ContentSecurityPolicy* Document::GetContentSecurityPolicyForWorld() {
v8::Isolate* isolate = GetIsolate();
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;
}
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_ && !IsHTMLDocument())
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 IsHTMLDocument() ? 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);
// TODO(crbug.com/1021837): Determine if we need to introduce a
// MathMLUnknownClass.
if (!element)
element = MakeGarbageCollected<MathMLElement>(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() || IsHTMLDocument()) {
// 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() || IsHTMLDocument()
? 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 (IsHTMLDocument()) {
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 (IsHTMLDocument()) {
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(ConsoleMessage::Create(
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 (IsXMLDocument()) {
if (IsXHTMLDocument())
return "application/xhtml+xml";
if (IsSVGDocument())
return "image/svg+xml";
return "application/xml";
}
if (xmlStandalone())
return "text/xml";
if (IsHTMLDocument())
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() {
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 (IsHTMLDocument() || 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 (DocumentAnimations::NeedsAnimationTimingUpdate(*this))
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());
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)