blob: d2964f5b6b64ee6acfffbc27d3577c8e92f776f5 [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 "base/auto_reset.h"
#include "base/optional.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-shared.h"
#include "services/service_manager/public/cpp/interface_provider.h"
#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
#include "third_party/blink/public/mojom/net/ip_address_space.mojom-blink.h"
#include "third_party/blink/public/mojom/page/page_visibility_state.mojom-blink.h"
#include "third_party/blink/public/platform/interface_provider.h"
#include "third_party/blink/public/platform/modules/insecure_input/insecure_input_service.mojom-blink.h"
#include "third_party/blink/public/platform/platform.h"
#include "third_party/blink/public/platform/task_type.h"
#include "third_party/blink/public/platform/ukm.mojom-blink.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/renderer/bindings/core/v8/html_script_element_or_svg_script_element.h"
#include "third_party/blink/renderer/bindings/core/v8/script_controller.h"
#include "third_party/blink/renderer/bindings/core/v8/script_promise.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/usv_string_or_trusted_url.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/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/css_style_sheet_init.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/dom/attr.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/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.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/nth_index_cache.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/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/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/feature_policy/document_policy.h"
#include "third_party/blink/renderer/core/frame/csp/content_security_policy.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/use_counter.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_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/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.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/idleness_detector.h"
#include "third_party/blink/renderer/core/loader/interactive_detector.h"
#include "third_party/blink/renderer/core/loader/navigation_scheduler.h"
#include "third_party/blink/renderer/core/loader/prerenderer_client.h"
#include "third_party/blink/renderer/core/loader/text_resource_decoder_builder.h"
#include "third_party/blink/renderer/core/origin_trials/origin_trials.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/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/top_document_root_scroller_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.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/trustedtypes/trusted_url.h"
#include "third_party/blink/renderer/core/workers/shared_worker_repository_client.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/cross_thread_functional.h"
#include "third_party/blink/renderer/platform/date_components.h"
#include "third_party/blink/renderer/platform/geometry/length_functions.h"
#include "third_party/blink/renderer/platform/histogram.h"
#include "third_party/blink/renderer/platform/instance_counters.h"
#include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
#include "third_party/blink/renderer/platform/language.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/plugins/plugin_script_forbidden_scope.h"
#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
#include "third_party/blink/renderer/platform/scheduler/public/frame_or_worker_scheduler.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/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/date_math.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"
#include "third_party/blink/renderer/platform/wtf/time.h"
#ifndef NDEBUG
using WeakDocumentSet = blink::HeapHashSet<blink::WeakMember<blink::Document>>;
static WeakDocumentSet& liveDocumentSet();
#endif
namespace blink {
using namespace html_names;
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 int kCLayoutScheduleThreshold = 250;
// After a document has been committed for this time, it can create a history
// entry even if the user hasn't interacted with the document.
static const int kElapsedTimeForHistoryEntryWithoutUserGestureMS = 5000;
// 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;
// This doesn't work with non-Document ExecutionContext.
static void RunAutofocusTask(ExecutionContext* context) {
// Document lifecycle check is done in Element::focus()
if (!context)
return;
Document* document = To<Document>(context);
if (Element* element = document->AutofocusElement()) {
document->SetAutofocusElement(nullptr);
element->focus();
}
}
class Document::NetworkStateObserver final
: public GarbageCollectedFinalized<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(blink::Visitor* visitor) override {
ContextLifecycleObserver::Trace(visitor);
}
private:
std::unique_ptr<NetworkStateNotifier::NetworkStateObserverHandle>
online_observer_handle_;
};
Document* Document::CreateForTest() {
return MakeGarbageCollected<Document>(DocumentInit::Create());
}
Document* Document::Create(Document& document) {
Document* new_document = MakeGarbageCollected<Document>(
DocumentInit::Create().WithContextDocument(&document).WithURL(
BlankURL()));
new_document->SetSecurityOrigin(document.GetMutableSecurityOrigin());
new_document->SetContextFeatures(document.GetContextFeatures());
return new_document;
}
Document::Document(const DocumentInit& initializer,
DocumentClassFlags document_classes)
: ContainerNode(nullptr, kCreateDocument),
TreeScope(*this),
has_nodes_with_placeholder_style_(false),
evaluate_media_queries_on_style_recalc_(false),
pending_sheet_layout_(kNoLayoutWithPendingSheets),
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()),
well_formed_(false),
printing_(kNotPrinting),
compatibility_mode_(kNoQuirksMode),
compatibility_mode_locked_(false),
has_autofocused_(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_(VisitedLinkState::Create(*this)),
visually_ordered_(false),
ready_state_(kComplete),
parsing_state_(kFinishedParsing),
contains_validity_style_rules_(false),
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)),
update_focus_appearance_timer_(
GetTaskRunner(TaskType::kInternalUserInteraction),
this,
&Document::UpdateFocusAppearanceTimerFired),
css_target_(nullptr),
was_discarded_(false),
load_event_progress_(kLoadEventCompleted),
is_freezing_in_progress_(false),
start_time_(CurrentTime()),
script_runner_(ScriptRunner::Create(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.ShouldTreatURLAsSrcdocDocument()),
is_mobile_document_(false),
layout_view_(nullptr),
has_fullscreen_supplement_(false),
load_event_delay_count_(0),
load_event_delay_timer_(GetTaskRunner(TaskType::kNetworking),
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),
registration_context_(initializer.RegistrationContext(this)),
element_data_cache_clear_timer_(
GetTaskRunner(TaskType::kInternalUserInteraction),
this,
&Document::ElementDataCacheClearTimerFired),
timeline_(DocumentTimeline::Create(this)),
pending_animations_(new PendingAnimations(*this)),
worklet_animation_controller_(new WorkletAnimationController(this)),
template_document_host_(nullptr),
did_associate_form_controls_timer_(
GetTaskRunner(TaskType::kInternalLoading),
this,
&Document::DidAssociateFormControlsTimerFired),
timers_(GetTaskRunner(TaskType::kJavascriptTimer)),
has_viewport_units_(false),
parser_sync_policy_(kAllowAsynchronousParsing),
node_count_(0),
password_count_(0),
logged_field_edit_(false),
secure_context_state_(SecureContextState::kUnknown),
ukm_source_id_(ukm::UkmRecorder::GetNewSourceID()),
#if DCHECK_IS_ON()
slot_assignment_recalc_forbidden_recursion_depth_(0),
#endif
needs_to_record_ukm_outlive_time_(false),
viewport_data_(new ViewportData(*this)),
agent_cluster_id_(base::UnguessableToken::Create()),
parsed_feature_policies_(
static_cast<int>(mojom::FeaturePolicyFeature::kMaxValue) + 1) {
if (frame_) {
DCHECK(frame_->GetPage());
ProvideContextFeaturesToDocumentFrom(*this, *frame_->GetPage());
fetcher_ = frame_->Loader().GetDocumentLoader()->Fetcher();
FrameFetchContext::ProvideDocumentToContext(fetcher_->Context(), 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_);
} else if (imports_controller_) {
fetcher_ = FrameFetchContext::CreateFetcherFromDocument(this);
} else {
fetcher_ = ResourceFetcher::Create(nullptr);
}
DCHECK(fetcher_);
root_scroller_controller_ = RootScrollerController::Create(*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);
if (frame_)
frame_->Client()->DidSetFramePolicyHeaders(GetSandboxFlags(), {});
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_ = StyleEngine::Create(*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
}
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 Range::Create(tree_scope.GetDocument(), position, position);
Node* const shadow_host = tree_scope.AncestorInThisScope(anchor_node);
return Range::Create(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_ = MediaQueryMatcher::Create(*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_ = DOMImplementation::Create(*this);
return *implementation_;
}
Location* Document::location() const {
if (!GetFrame())
return nullptr;
return domWindow()->location();
}
bool Document::ShouldInstallV8Extensions() const {
return frame_->Client()->AllowScriptExtensions();
}
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/multipage/dom.html#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 = HTMLElement::Create(qname, *this);
else
element = HTMLUnknownElement::Create(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 = SVGUnknownElement::Create(qname, *this);
saw_elements_in_known_namespaces_ = true;
} else {
element = Element::Create(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 HTMLUnknownElement::Create(q_name, *this);
}
return Element::Create(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();
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();
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);
}
ScriptPromise Document::createCSSStyleSheet(ScriptState* script_state,
const String& text,
ExceptionState& exception_state) {
return Document::createCSSStyleSheet(
script_state, text, CSSStyleSheetInit::Create(), exception_state);
}
ScriptPromise Document::createCSSStyleSheet(ScriptState* script_state,
const String& text,
const CSSStyleSheetInit* options,
ExceptionState& exception_state) {
// Even though this function returns a Promise, it actually does all the work
// at once here because CSS parsing is done synchronously on the main thread.
// TODO(rakina): Find a way to improve this.
CSSStyleSheet* sheet = CSSStyleSheet::Create(*this, options, exception_state);
sheet->SetText(text, true /* allow_import_rules */, exception_state);
sheet->SetAssociatedDocument(this);
return ScriptPromise::Cast(script_state, ToV8(sheet, script_state));
}
CSSStyleSheet* Document::createCSSStyleSheetSync(
ScriptState* script_state,
const String& text,
ExceptionState& exception_state) {
return Document::createCSSStyleSheetSync(
script_state, text, CSSStyleSheetInit::Create(), exception_state);
}
CSSStyleSheet* Document::createCSSStyleSheetSync(
ScriptState* script_state,
const String& text,
const CSSStyleSheetInit* options,
ExceptionState& exception_state) {
CSSStyleSheet* sheet = CSSStyleSheet::Create(*this, options, exception_state);
sheet->SetText(text, false /* allow_import_rules */, exception_state);
if (exception_state.HadException())
return nullptr;
sheet->SetAssociatedDocument(this);
return sheet;
}
CSSStyleSheet* Document::createEmptyCSSStyleSheet(
ScriptState* script_state,
const CSSStyleSheetInit* options,
ExceptionState& exception_state) {
CSSStyleSheet* sheet = CSSStyleSheet::Create(*this, options, exception_state);
sheet->SetAssociatedDocument(this);
return sheet;
}
CSSStyleSheet* Document::createEmptyCSSStyleSheet(
ScriptState* script_state,
ExceptionState& exception_state) {
return Document::createEmptyCSSStyleSheet(
script_state, CSSStyleSheetInit::Create(), exception_state);
}
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);
return constructor_builder.BindingsReturnValue();
}
V0CustomElementMicrotaskRunQueue* Document::CustomElementMicrotaskRunQueue() {
if (!custom_element_microtask_run_queue_)
custom_element_microtask_run_queue_ =
V0CustomElementMicrotaskRunQueue::Create();
return custom_element_microtask_run_queue_.Get();
}
void Document::ClearImportsController() {
if (!Loader())
fetcher_->ClearContext();
imports_controller_ = nullptr;
}
HTMLImportsController* Document::EnsureImportsController() {
if (!imports_controller_) {
DCHECK(frame_);
imports_controller_ = HTMLImportsController::Create(*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 ProcessingInstruction::Create(*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: {
Attr* attr = ToAttr(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 (source->IsFrameOwnerElement()) {
HTMLFrameOwnerElement* frame_owner_element =
ToHTMLFrameOwnerElement(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(
kJSMessageSource, kWarningMessageLevel,
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.
SetNeedsStyleRecalc(kSubtreeStyleChange, 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(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 (IsSVGSVGElement(element)) {
if (!title_element_) {
title_element_ = SVGTitleElement::Create(*this);
element->InsertBefore(title_element_.Get(), element->firstChild());
}
if (auto* svg_title = ToSVGTitleElementOrNull(title_element_))
svg_title->SetText(title);
} else if (element && element->IsHTMLElement()) {
if (!title_element_) {
HTMLElement* head_element = head();
if (!head_element)
return;
title_element_ = HTMLTitleElement::Create(*this);
head_element->AppendChild(title_element_.Get());
}
if (auto* html_title = ToHTMLTitleElementOrNull(title_element_))
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 (IsSVGSVGElement(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 (IsSVGTitleElement(title_element_)) {
title_element_ = nullptr;
return;
}
}
if (auto* html_title = ToHTMLTitleElementOrNull(title_element_))
UpdateTitle(html_title->text());
else if (auto* svg_title = ToSVGTitleElementOrNull(title_element_))
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 = ToHTMLHtmlElementOrNull(root_element))
return html->dir();
return g_null_atom;
}
void Document::setDir(const AtomicString& value) {
Element* root_element = documentElement();
if (auto* html = ToHTMLHtmlElementOrNull(root_element))
html->setDir(value);
}
mojom::PageVisibilityState Document::GetPageVisibilityState() 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 mojom::PageVisibilityState::kHidden;
// 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 mojom::PageVisibilityState::kHidden;
return frame_->GetPage()->VisibilityState();
}
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 PageVisibilityStateString(GetPageVisibilityState());
}
bool Document::hidden() const {
return GetPageVisibilityState() != mojom::PageVisibilityState::kVisible;
}
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 (GetPageVisibilityState() == mojom::PageVisibilityState::kVisible)
Timeline().SetAllCompositorPending();
if (hidden() && canvas_font_cache_)
canvas_font_cache_->PruneAll();
InteractiveDetector* interactive_detector = InteractiveDetector::From(*this);
if (interactive_detector) {
interactive_detector->OnPageVisibilityChanged(GetPageVisibilityState());
}
}
String Document::nodeName() const {
return "#document";
}
Node::NodeType Document::getNodeType() const {
return kDocumentNode;
}
FormController& Document::GetFormController() {
if (!form_controller_) {
form_controller_ = FormController::Create();
HistoryItem* history_item = Loader() ? Loader()->GetHistoryItem() : nullptr;
if (history_item)
history_item->SetDocumentState(form_controller_->FormElementsState());
}
return *form_controller_;
}
DocumentState* Document::FormElementsState() const {
if (!form_controller_)
return nullptr;
return form_controller_->FormElementsState();
}
void Document::SetStateForNewFormElements(const Vector<String>& state_vector) {
if (!state_vector.size() && !form_controller_)
return;
GetFormController().SetStateForNewFormElements(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 NodeIterator::Create(root, what_to_show, filter);
}
TreeWalker* Document::createTreeWalker(Node* root,
unsigned what_to_show,
V8NodeFilter* filter) {
DCHECK(root);
return TreeWalker::Create(root, what_to_show, filter);
}
bool Document::NeedsLayoutTreeUpdate() const {
if (!IsActive() || !View())
return false;
if (NeedsFullLayoutTreeUpdate())
return true;
if (ChildNeedsStyleRecalc())
return true;
if (ChildNeedsStyleInvalidation())
return true;
if (ChildNeedsReattachLayoutTree()) {
DCHECK(InStyleRecalc());
return true;
}
if (GetLayoutView() && GetLayoutView()->WasNotifiedOfSubtreeChange())
return true;
return false;
}
bool Document::NeedsFullLayoutTreeUpdate() const {
if (!IsActive() || !View())
return false;
if (style_engine_->NeedsActiveStyleUpdate())
return true;
if (style_engine_->NeedsWhitespaceReattachment())
return true;
if (!use_elements_needing_update_.IsEmpty())
return true;
if (NeedsStyleRecalc())
return true;
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",
InspectorRecalculateStylesEvent::Data(GetFrame()));
++style_version_;
}
bool Document::HasPendingForcedStyleRecalc() const {
return HasPendingVisualUpdate() && !InStyleRecalc() &&
GetStyleChangeType() >= kSubtreeStyleChange;
}
void Document::UpdateStyleInvalidationIfNeeded() {
DCHECK(IsActive());
ScriptForbiddenScope forbid_script;
if (!ChildNeedsStyleInvalidation() && !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);
}
void Document::PropagateStyleToViewport() {
DCHECK(InStyleRecalc());
if (!documentElement())
return;
HTMLElement* body = this->body();
const ComputedStyle* body_style =
body ? body->EnsureComputedStyle() : nullptr;
const ComputedStyle* document_element_style =
documentElement()->EnsureComputedStyle();
TouchAction effective_touch_action =
document_element_style->GetEffectiveTouchAction();
WritingMode root_writing_mode = document_element_style->GetWritingMode();
TextDirection root_direction = document_element_style->Direction();
if (body_style) {
root_writing_mode = body_style->GetWritingMode();
root_direction = body_style->Direction();
}
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 (IsHTMLHtmlElement(documentElement()) &&
document_element_style->Display() != EDisplay::kNone &&
IsHTMLBodyElement(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->Display() != EDisplay::kNone) {
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();
}
const ComputedStyle* overflow_style = nullptr;
Element* viewport_element = ViewportDefiningElement();
DCHECK(viewport_element);
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);
}
DCHECK(overflow_style);
EOverflowAnchor overflow_anchor = overflow_style->OverflowAnchor();
EOverflow overflow_x = overflow_style->OverflowX();
EOverflow overflow_y = overflow_style->OverflowY();
// 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;
// Column-gap is (ab)used by the current paged overflow implementation (in
// lack of other ways to specify gaps between pages), so we have to
// propagate it too.
GapLength column_gap = overflow_style->ColumnGap();
ScrollSnapType snap_type = overflow_style->GetScrollSnapType();
ScrollBehavior scroll_behavior = document_element_style->GetScrollBehavior();
EOverscrollBehavior overscroll_behavior_x =
overflow_style->OverscrollBehaviorX();
EOverscrollBehavior overscroll_behavior_y =
overflow_style->OverscrollBehaviorY();
using OverscrollBehaviorType = cc::OverscrollBehavior::OverscrollBehaviorType;
if (IsInMainFrame()) {
GetPage()->GetOverscrollController().SetOverscrollBehavior(
cc::OverscrollBehavior(
static_cast<OverscrollBehaviorType>(overscroll_behavior_x),
static_cast<OverscrollBehaviorType>(overscroll_behavior_y)));
}
Length scroll_padding_top = overflow_style->ScrollPaddingTop();
Length scroll_padding_right = overflow_style->ScrollPaddingRight();
Length scroll_padding_bottom = overflow_style->ScrollPaddingBottom();
Length scroll_padding_left = overflow_style->ScrollPaddingLeft();
const ComputedStyle& viewport_style = GetLayoutView()->StyleRef();
if (viewport_style.GetWritingMode() != root_writing_mode ||
viewport_style.Direction() != root_direction ||
viewport_style.VisitedDependentColor(GetCSSPropertyBackgroundColor()) !=
background_color ||
viewport_style.BackgroundLayers() != background_layers ||
viewport_style.ImageRendering() != image_rendering ||
viewport_style.OverflowAnchor() != overflow_anchor ||
viewport_style.OverflowX() != overflow_x ||
viewport_style.OverflowY() != overflow_y ||
viewport_style.ColumnGap() != column_gap ||
viewport_style.GetScrollSnapType() != snap_type ||
viewport_style.GetScrollBehavior() != scroll_behavior ||
viewport_style.OverscrollBehaviorX() != overscroll_behavior_x ||
viewport_style.OverscrollBehaviorY() != overscroll_behavior_y ||
viewport_style.ScrollPaddingTop() != scroll_padding_top ||
viewport_style.ScrollPaddingRight() != scroll_padding_right ||
viewport_style.ScrollPaddingBottom() != scroll_padding_bottom ||
viewport_style.ScrollPaddingLeft() != scroll_padding_left ||
viewport_style.GetEffectiveTouchAction() != effective_touch_action) {
scoped_refptr<ComputedStyle> new_style =
ComputedStyle::Clone(viewport_style);
new_style->SetWritingMode(root_writing_mode);
new_style->UpdateFontOrientation();
new_style->SetDirection(root_direction);
new_style->SetBackgroundColor(background_color);
new_style->AccessBackgroundLayers() = background_layers;
new_style->SetImageRendering(image_rendering);
new_style->SetOverflowAnchor(overflow_anchor);
new_style->SetOverflowX(overflow_x);
new_style->SetOverflowY(overflow_y);
new_style->SetColumnGap(column_gap);
new_style->SetScrollSnapType(snap_type);
new_style->SetScrollBehavior(scroll_behavior);
new_style->SetOverscrollBehaviorX(overscroll_behavior_x);
new_style->SetOverscrollBehaviorY(overscroll_behavior_y);
new_style->SetScrollPaddingTop(scroll_padding_top);
new_style->SetScrollPaddingRight(scroll_padding_right);
new_style->SetScrollPaddingBottom(scroll_padding_bottom);
new_style->SetScrollPaddingLeft(scroll_padding_left);
new_style->SetEffectiveTouchAction(effective_touch_action);
GetLayoutView()->SetStyle(new_style);
SetupFontBuilder(*new_style);
if (PaintLayerScrollableArea* scrollable_area =
GetLayoutView()->GetScrollableArea()) {
if (scrollable_area->HorizontalScrollbar() &&
scrollable_area->HorizontalScrollbar()->IsCustomScrollbar())
scrollable_area->HorizontalScrollbar()->StyleChanged();
if (scrollable_area->VerticalScrollbar() &&
scrollable_area->VerticalScrollbar()->IsCustomScrollbar())
scrollable_area->VerticalScrollbar()->StyleChanged();
}
}
}
#if DCHECK_IS_ON()
static void AssertLayoutTreeUpdated(Node& root) {
Node* node = &root;
while (node) {
if (RuntimeEnabledFeatures::DisplayLockingEnabled() &&
node->IsElementNode() &&
ToElement(node)->StyleRecalcBlockedByDisplayLock()) {
node = NodeTraversal::NextSkippingChildren(*node);
continue;
}
DCHECK(!node->NeedsStyleRecalc());
DCHECK(!node->ChildNeedsStyleRecalc());
DCHECK(!node->NeedsReattachLayoutTree());
DCHECK(!node->ChildNeedsReattachLayoutTree());
DCHECK(!node->ChildNeedsDistributionRecalc());
DCHECK(!node->NeedsStyleInvalidation());
DCHECK(!node->ChildNeedsStyleInvalidation());
DCHECK(!node->GetForceReattachLayoutTree());
// Make sure there is no node which has a LayoutObject, but doesn't have a
// parent in a flat tree. If there is such a node, we forgot to detach the
// node. DocumentNode is only an exception.
DCHECK((node->IsDocumentNode() || !node->GetLayoutObject() ||
FlatTreeTraversal::Parent(*node)))
<< *node;
if (ShadowRoot* shadow_root = node->GetShadowRoot())
AssertLayoutTreeUpdated(*shadow_root);
node = NodeTraversal::Next(*node);
}
}
#endif
void Document::UpdateStyleAndLayoutTree() {
DCHECK(IsMainThread());
if (Lifecycle().LifecyclePostponed())
return;
HTMLFrameOwnerElement::PluginDisposeSuspendScope suspend_plugin_dispose;
ScriptForbiddenScope forbid_script;
if (HTMLFrameOwnerElement* owner = LocalOwner()) {
owner->GetDocument().UpdateStyleAndLayoutTree();
}
if (!View() || !IsActive())
return;
if (View()->ShouldThrottleRendering())
return;
// RecalcSlotAssignments should be done before checking
// NeedsLayoutTreeUpdate().
GetSlotAssignmentEngine().RecalcSlotAssignments();
#if DCHECK_IS_ON()
NestingLevelIncrementer slot_assignment_recalc_forbidden_scope(
slot_assignment_recalc_forbidden_recursion_depth_);
#endif
if (!NeedsLayoutTreeUpdate()) {
if (Lifecycle().GetState() < DocumentLifecycle::kStyleClean) {
// needsLayoutTreeUpdate may change to false without any actual layout
// tree update. For example, needsAnimationTimingUpdate may change to
// false when time elapses. Advance lifecycle to StyleClean because style
// is actually clean now.
Lifecycle().AdvanceTo(DocumentLifecycle::kInStyleRecalc);
Lifecycle().AdvanceTo(DocumentLifecycle::kStyleClean);
}
return;
}
if (InStyleRecalc())
return;
// Entering here from inside layout, paint etc. would be catastrophic since
// recalcStyle can tear down the layout tree or (unfortunately) run
// script. Kill the whole layoutObject if someone managed to get into here in
// states not allowing tree mutations.
CHECK(Lifecycle().StateAllowsTreeMutations());
TRACE_EVENT_BEGIN1("blink,devtools.timeline", "UpdateLayoutTree", "beginData",
InspectorRecalculateStylesEvent::Data(GetFrame()));
unsigned start_element_count = GetStyleEngine().StyleForElementCount();
probe::RecalculateStyle recalculate_style_scope(this);
DocumentAnimations::UpdateAnimationTimingIfNeeded(*this);
EvaluateMediaQueryListIfNeeded();
UpdateUseShadowTreesIfNeeded();
UpdateDistributionForLegacyDistributedNodes();
UpdateActiveStyle();
UpdateStyleInvalidationIfNeeded();
// FIXME: We should update style on our ancestor chain before proceeding
// however doing so currently causes several tests to crash, as
// LocalFrame::setDocument calls Document::attach before setting the
// LocalDOMWindow on the LocalFrame, or the SecurityOrigin on the
// document. The attach, in turn resolves style (here) and then when we
// resolve style on the parent chain, we may end up re-attaching our
// containing iframe, which when asked HTMLFrameElementBase::isURLAllowed hits
// a null-dereference due to security code always assuming the document has a
// SecurityOrigin.
UpdateStyle();
NotifyLayoutTreeOfSubtreeChanges();
// As a result of the style recalculation, the currently hovered element might
// have been detached (for example, by setting display:none in the :hover
// style), schedule another mouseMove event to check if any other elements
// ended up under the mouse pointer due to re-layout.
if (HoverElement() && !HoverElement()->GetLayoutObject() && GetFrame()) {
GetFrame()->GetEventHandler().MayUpdateHoverWhenContentUnderMouseChanged(
MouseEventManager::UpdateHoverReason::kLayoutOrStyleChanged);
}
if (focused_element_ && !focused_element_->IsFocusable())
ClearFocusedElementSoon();
GetLayoutView()->ClearHitTestCache();
DCHECK(!DocumentAnimations::NeedsAnimationTimingUpdate(*this));
unsigned element_count =
GetStyleEngine().StyleForElementCount() - start_element_count;
TRACE_EVENT_END1("blink,devtools.timeline", "UpdateLayoutTree",
"elementCount", element_count);
#if DCHECK_IS_ON()
AssertLayoutTreeUpdated(*this);
#endif
}
void Document::UpdateActiveStyle() {
DCHECK(IsActive());
DCHECK(IsMainThread());
TRACE_EVENT0("blink", "Document::updateActiveStyle");
GetStyleEngine().UpdateActiveStyle();
}
void Document::UpdateStyle() {
DCHECK(!View()->ShouldThrottleRendering());
TRACE_EVENT_BEGIN0("blink,blink_style", "Document::updateStyle");
RUNTIME_CALL_TIMER_SCOPE(V8PerIsolateData::MainThreadIsolate(),
RuntimeCallStats::CounterId::kUpdateStyle);
unsigned initial_element_count = GetStyleEngine().StyleForElementCount();
lifecycle_.AdvanceTo(DocumentLifecycle::kInStyleRecalc);
StyleRecalcChange change = kNoChange;
if (GetStyleChangeType() >= kSubtreeStyleChange)
change = kForce;
NthIndexCache nth_index_cache(*this);
// TODO(futhark@chromium.org): Cannot access the EnsureStyleResolver() before
// calling StyleForViewport() below because apparently the StyleResolver's
// constructor has side effects. We should fix it. See
// printing/setPrinting.html, printing/width-overflow.html though they only
// fail on mac when accessing the resolver by what appears to be a viewport
// size difference.
if (change == kForce) {
has_nodes_with_placeholder_style_ = false;
scoped_refptr<ComputedStyle> viewport_style =
StyleResolver::StyleForViewport(*this);
StyleRecalcChange local_change = ComputedStyle::StylePropagationDiff(
viewport_style.get(), GetLayoutView()->Style());
if (local_change != kNoChange)
GetLayoutView()->SetStyle(std::move(viewport_style));
}
ClearNeedsStyleRecalc();
ClearNeedsReattachLayoutTree();
StyleResolver& resolver = EnsureStyleResolver();
bool should_record_stats;
TRACE_EVENT_CATEGORY_GROUP_ENABLED("blink,blink_style", &should_record_stats);
GetStyleEngine().SetStatsEnabled(should_record_stats);
if (Element* document_element = documentElement()) {
if (document_element->ShouldCallRecalcStyle(change)) {
TRACE_EVENT0("blink,blink_style", "Document::recalcStyle");
SCOPED_BLINK_UMA_HISTOGRAM_TIMER_HIGHRES("Style.RecalcTime");
Element* viewport_defining = ViewportDefiningElement();
GetStyleEngine().RecalcStyle(change);
if (viewport_defining != ViewportDefiningElement())
ViewportDefiningElementDidChange();
}
GetStyleEngine().MarkForWhitespaceReattachment();
if (document_element->NeedsReattachLayoutTree() ||
document_element->ChildNeedsReattachLayoutTree()) {
TRACE_EVENT0("blink,blink_style", "Document::rebuildLayoutTree");
SCOPED_BLINK_UMA_HISTOGRAM_TIMER_HIGHRES("Style.RebuildLayoutTreeTime");
ReattachLegacyLayoutObjectList legacy_layout_objects(*this);
GetStyleEngine().RebuildLayoutTree();
legacy_layout_objects.ForceLegacyLayoutIfNeeded();
}
}
GetStyleEngine().ClearWhitespaceReattachSet();
ClearChildNeedsStyleRecalc();
ClearChildNeedsReattachLayoutTree();
PropagateStyleToViewport();
View()->UpdateCountersAfterStyleChange();
GetLayoutView()->RecalcOverflow();
DCHECK(!NeedsStyleRecalc());
DCHECK(!ChildNeedsStyleRecalc());
DCHECK(!NeedsReattachLayoutTree());
DCHECK(!ChildNeedsReattachLayoutTree());
DCHECK(InStyleRecalc());
DCHECK_EQ(GetStyleResolver(), &resolver);
lifecycle_.AdvanceTo(DocumentLifecycle::kStyleClean);
if (should_record_stats) {
TRACE_EVENT_END2(
"blink,blink_style", "Document::updateStyle", "resolverAccessCount",
GetStyleEngine().StyleForElementCount() - initial_element_count,
"counters", GetStyleEngine().Stats()->ToTracedValue());
} else {
TRACE_EVENT_END1(
"blink,blink_style", "Document::updateStyle", "resolverAccessCount",
GetStyleEngine().StyleForElementCount() - initial_element_count);
}
}
void Document::ViewportDefiningElementDidChange() {
HTMLBodyElement* body = FirstBodyElement();
if (!body)
return;
LayoutObject* layout_object = body->GetLayoutObject();
if (layout_object && layout_object->IsLayoutBlock()) {
// When the overflow style for documentElement changes to or from visible,
// it changes whether the body element's box should have scrollable overflow
// on its own box or propagated to the viewport. If the body style did not
// need a recalc, this will not be updated as its done as part of setting
// ComputedStyle on the LayoutObject. Force a SetStyle for body when the
// ViewportDefiningElement changes in order to trigger an update of
// HasOverflowClip() and the PaintLayer in StyleDidChange().
layout_object->SetStyle(ComputedStyle::Clone(*layout_object->Style()));
// CompositingReason::kClipsCompositingDescendants depends on the root
// element having a clip-related style. Since style update due to changes of
// viewport-defining element don't end up as a StyleDifference, we need a
// special dirty bit for this situation.
if (layout_object->HasLayer()) {
ToLayoutBoxModelObject(layout_object)
->Layer()
->SetNeeedsCompositingReasonsUpdate();
}
}
}
void Document::NotifyLayoutTreeOfSubtreeChanges() {
if (!GetLayoutView()->WasNotifiedOfSubtreeChange())
return;
lifecycle_.AdvanceTo(DocumentLifecycle::kInLayoutSubtreeChange);
GetLayoutView()->HandleSubtreeModifications();
DCHECK(!GetLayoutView()->WasNotifiedOfSubtreeChange());
lifecycle_.AdvanceTo(DocumentLifecycle::kLayoutSubtreeChangeClean);
}
bool Document::NeedsLayoutTreeUpdateForNode(const Node& node) const {
if (!node.CanParticipateInFlatTree())
return false;
if (!NeedsLayoutTreeUpdate())
return false;
if (!node.isConnected())
return false;
if (NeedsFullLayoutTreeUpdate() || node.NeedsStyleRecalc() ||
node.NeedsStyleInvalidation())
return true;
for (const ContainerNode* ancestor = LayoutTreeBuilderTraversal::Parent(node);
ancestor; ancestor = LayoutTreeBuilderTraversal::Parent(*ancestor)) {
if (ShadowRoot* root = ancestor->GetShadowRoot()) {
if (root->NeedsStyleRecalc() || root->NeedsStyleInvalidation() ||
root->NeedsAdjacentStyleRecalc()) {
return true;
}
}
if (ancestor->NeedsStyleRecalc() || ancestor->NeedsStyleInvalidation() ||
ancestor->NeedsAdjacentStyleRecalc()) {
return true;
}
}
return false;
}
void Document::UpdateStyleAndLayoutTreeForNode(const Node* node) {
DCHECK(node);
if (!NeedsLayoutTreeUpdateForNode(*node))
return;
UpdateStyleAndLayoutTree();
}
void Document::UpdateStyleAndLayoutIgnorePendingStylesheetsForNode(Node* node) {
DCHECK(node);
if (!node->InActiveDocument())
return;
UpdateStyleAndLayoutIgnorePendingStylesheets();
}
void Document::UpdateStyleAndLayout() {
DCHECK(IsMainThread());
HTMLFrameOwnerElement::PluginDisposeSuspendScope suspend_plugin_dispose;
LocalFrameView* frame_view = View();
DCHECK(!frame_view || !frame_view->IsInPerformLayout())
<< "View layout should not be re-entrant";
if (HTMLFrameOwnerElement* owner = LocalOwner())
owner->GetDocument().UpdateStyleAndLayout();
UpdateStyleAndLayoutTree();
if (!IsActive())
return;
if (frame_view && frame_view->NeedsLayout())
frame_view->UpdateLayout();
if (Lifecycle().GetState() < DocumentLifecycle::kLayoutClean)
Lifecycle().AdvanceTo(DocumentLifecycle::kLayoutClean);
if (AXObjectCache* cache = ExistingAXObjectCache())
cache->ProcessUpdatesAfterLayout(*this);
if (LocalFrameView* frame_view_anchored = View())
frame_view_anchored->PerformScrollAnchoringAdjustments();
}
void Document::LayoutUpdated() {
DCHECK(GetFrame());
DCHECK(View());
// If we're restoring a scroll position from history, that takes precedence
// over scrolling to the anchor in the URL.
View()->ScrollAndFocusFragmentAnchor();
// Script run in the call above may detach the document.
if (GetFrame() && View()) {
GetFrame()->Loader().RestoreScrollPositionAndViewState();
// The focus call above can execute JS which can dirty layout. Ensure
// layout is clean since this is called from UpdateLayout.
if (View()->NeedsLayout())
View()->UpdateLayout();
}
// Plugins can run script inside layout which can detach the page.
// TODO(dcheng): Does it make sense to do any of this work if detached?
if (GetFrame() && GetFrame()->IsMainFrame())
GetFrame()->GetPage()->GetChromeClient().MainFrameLayoutUpdated();
Markers().InvalidateRectsForAllTextMatchMarkers();
// The layout system may perform layouts with pending stylesheets. When
// recording first layout time, we ignore these layouts, since painting is
// suppressed for them. We're interested in tracking the time of the
// first real or 'paintable' layout.
// TODO(esprehn): This doesn't really make sense, why not track the first
// beginFrame? This will catch the first layout in a page that does lots
// of layout thrashing even though that layout might not be followed by
// a paint for many seconds.
if (IsRenderingReady() && body() && HaveRenderBlockingResourcesLoaded()) {
if (document_timing_.FirstLayout().is_null())
document_timing_.MarkFirstLayout();
}
}
void Document::ClearFocusedElementSoon() {
if (!clear_focused_element_timer_.IsActive())
clear_focused_element_timer_.StartOneShot(TimeDelta(), FROM_HERE);
}
void Document::ClearFocusedElementTimerFired(TimerBase*) {
UpdateStyleAndLayoutTree();
if (focused_element_ && !focused_element_->IsFocusable())
focused_element_->blur();
}
// FIXME: This is a bad idea and needs to be removed eventually.
// Other browsers load stylesheets before they continue parsing the web page.
// Since we don't, we can run JavaScript code that needs answers before the
// stylesheets are loaded. Doing a layout ignoring the pending stylesheets
// lets us get reasonable answers. The long term solution to this problem is
// to instead suspend JavaScript execution.
void Document::UpdateStyleAndLayoutTreeIgnorePendingStylesheets() {
if (Lifecycle().LifecyclePostponed())
return;
// See comment for equivalent CHECK in Document::UpdateStyleAndLayoutTree.
// Updating style and layout can dirty state that must remain clean during
// lifecycle updates.
CHECK(Lifecycle().StateAllowsTreeMutations());
StyleEngine::IgnoringPendingStylesheet ignoring(GetStyleEngine());
if (!HaveRenderBlockingResourcesLoaded()) {
// FIXME: We are willing to attempt to suppress painting with outdated style
// info only once. Our assumption is that it would be dangerous to try to
// stop it a second time, after page content has already been loaded and
// displayed with accurate style information. (Our suppression involves
// blanking the whole page at the moment. If it were more refined, we might
// be able to do something better.) It's worth noting though that this
// entire method is a hack, since what we really want to do is suspend JS
// instead of doing a layout with inaccurate information.
HTMLElement* body_element = body();
if (body_element && !body_element->GetLayoutObject() &&
pending_sheet_layout_ == kNoLayoutWithPendingSheets) {
pending_sheet_layout_ = kDidLayoutWithPendingSheets;
GetStyleEngine().MarkAllTreeScopesDirty();
}
if (has_nodes_with_placeholder_style_) {
// If new nodes have been added or style recalc has been done with style
// sheets still pending, some nodes may not have had their real style
// calculated yet. Normally this gets cleaned when style sheets arrive
// but here we need up-to-date style immediately.
SetNeedsStyleRecalc(kSubtreeStyleChange,
StyleChangeReasonForTracing::Create(
style_change_reason::kCleanupPlaceholderStyles));
}
}
UpdateStyleAndLayoutTree();
}
void Document::UpdateStyleAndLayoutIgnorePendingStylesheets(
Document::RunPostLayoutTasks run_post_layout_tasks) {
LocalFrameView* local_view = View();
if (local_view)
local_view->WillStartForcedLayout();
UpdateStyleAndLayoutTreeIgnorePendingStylesheets();
UpdateStyleAndLayout();
if (local_view) {
if (run_post_layout_tasks == kRunPostLayoutTasksSynchronously)
local_view->FlushAnyPendingPostLayoutTasks();
local_view->DidFinishForcedLayout();
}
}
scoped_refptr<ComputedStyle>
Document::StyleForElementIgnoringPendingStylesheets(Element* element) {
DCHECK_EQ(element->GetDocument(), this);
StyleEngine::IgnoringPendingStylesheet ignoring(GetStyleEngine());
if (!element->CanParticipateInFlatTree())
return EnsureStyleResolver().StyleForElement(element, nullptr);
ContainerNode* parent = LayoutTreeBuilderTraversal::Parent(*element);
const ComputedStyle* parent_style =
parent ? parent->EnsureComputedStyle() : nullptr;
ContainerNode* layout_parent =
parent ? LayoutTreeBuilderTraversal::LayoutParent(*element) : nullptr;
const ComputedStyle* layout_parent_style =
layout_parent ? layout_parent->EnsureComputedStyle() : parent_style;
return EnsureStyleResolver().StyleForElement(element, parent_style,
layout_parent_style);
}
scoped_refptr<ComputedStyle> Document::StyleForPage(int page_index) {
UpdateDistributionForUnknownReasons();
return EnsureStyleResolver().StyleForPage(page_index);
}
void Document::EnsurePaintLocationDataValidForNode(const Node* node) {
DCHECK(node);
if (!node->InActiveDocument())
return;
// For all nodes we must have up-to-date style and have performed layout to do
// any location-based calculation.
UpdateStyleAndLayoutIgnorePendingStylesheets();
// The location of elements that are position: sticky is not known until
// compositing inputs are cleaned. Therefore, for any elements that are either
// sticky or are in a sticky sub-tree (e.g. are affected by a sticky element),
// we need to also clean compositing inputs.
if (View() && node->GetLayoutObject() &&
node->GetLayoutObject()->StyleRef().SubtreeIsSticky()) {
bool success = false;
if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) {
// In SPv2, compositing inputs are cleaned as part of PrePaint.
success = View()->UpdateAllLifecyclePhasesExceptPaint();
} else {
success = View()->UpdateLifecycleToCompositingInputsClean();
}
// The lifecycle update should always succeed, because forced lifecycles
// from script are never throttled.
DCHECK(success);
}
}
bool Document::IsPageBoxVisible(int page_index) {
return StyleForPage(page_index)->Visibility() !=
EVisibility::kHidden; // display property doesn't apply to @page.
}
void Document::PageSizeAndMarginsInPixels(int page_index,
DoubleSize& page_size,
int& margin_top,
int& margin_right,
int& margin_bottom,
int& margin_left) {
scoped_refptr<ComputedStyle> style = StyleForPage(page_index);
double width = page_size.Width();
double height = page_size.Height();
switch (style->PageSizeType()) {
case EPageSizeType::kAuto:
break;
case EPageSizeType::kLandscape:
if (width < height)
std::swap(width, height);
break;
case EPageSizeType::kPortrait:
if (width > height)
std::swap(width, height);
break;