blob: f83857eab977008fb5d1f8714aafceadbf8a7dcf [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 "core/dom/Document.h"
#include "bindings/core/v8/ExceptionMessages.h"
#include "bindings/core/v8/ExceptionState.h"
#include "bindings/core/v8/HTMLScriptElementOrSVGScriptElement.h"
#include "bindings/core/v8/ScriptController.h"
#include "bindings/core/v8/SourceLocation.h"
#include "bindings/core/v8/StringOrDictionary.h"
#include "bindings/core/v8/V0CustomElementConstructorBuilder.h"
#include "bindings/core/v8/V8ElementCreationOptions.h"
#include "bindings/core/v8/WindowProxy.h"
#include "core/HTMLElementFactory.h"
#include "core/HTMLElementTypeHelpers.h"
#include "core/HTMLNames.h"
#include "core/SVGElementFactory.h"
#include "core/SVGNames.h"
#include "core/XMLNSNames.h"
#include "core/XMLNames.h"
#include "core/animation/CompositorPendingAnimations.h"
#include "core/animation/DocumentAnimations.h"
#include "core/animation/DocumentTimeline.h"
#include "core/css/CSSFontSelector.h"
#include "core/css/CSSStyleDeclaration.h"
#include "core/css/CSSStyleSheet.h"
#include "core/css/CSSTiming.h"
#include "core/css/FontFaceSet.h"
#include "core/css/MediaQueryMatcher.h"
#include "core/css/PropertyRegistry.h"
#include "core/css/StylePropertySet.h"
#include "core/css/StyleSheetContents.h"
#include "core/css/StyleSheetList.h"
#include "core/css/invalidation/StyleInvalidator.h"
#include "core/css/parser/CSSParser.h"
#include "core/css/resolver/FontBuilder.h"
#include "core/css/resolver/StyleResolver.h"
#include "core/css/resolver/StyleResolverStats.h"
#include "core/dom/AXObjectCache.h"
#include "core/dom/Attr.h"
#include "core/dom/CDATASection.h"
#include "core/dom/ClientRect.h"
#include "core/dom/Comment.h"
#include "core/dom/ContextFeatures.h"
#include "core/dom/DOMImplementation.h"
#include "core/dom/DocumentFragment.h"
#include "core/dom/DocumentParserTiming.h"
#include "core/dom/DocumentType.h"
#include "core/dom/Element.h"
#include "core/dom/ElementCreationOptions.h"
#include "core/dom/ElementDataCache.h"
#include "core/dom/ElementRegistrationOptions.h"
#include "core/dom/ElementTraversal.h"
#include "core/dom/ExceptionCode.h"
#include "core/dom/ExecutionContextTask.h"
#include "core/dom/FrameRequestCallback.h"
#include "core/dom/IntersectionObserverController.h"
#include "core/dom/LayoutTreeBuilderTraversal.h"
#include "core/dom/LiveNodeList.h"
#include "core/dom/MutationObserver.h"
#include "core/dom/NodeChildRemovalTracker.h"
#include "core/dom/NodeComputedStyle.h"
#include "core/dom/NodeFilter.h"
#include "core/dom/NodeIterator.h"
#include "core/dom/NodeRareData.h"
#include "core/dom/NodeTraversal.h"
#include "core/dom/NodeWithIndex.h"
#include "core/dom/NthIndexCache.h"
#include "core/dom/ProcessingInstruction.h"
#include "core/dom/ResizeObserverController.h"
#include "core/dom/ScriptRunner.h"
#include "core/dom/ScriptedAnimationController.h"
#include "core/dom/ScriptedIdleTaskController.h"
#include "core/dom/SelectorQuery.h"
#include "core/dom/StaticNodeList.h"
#include "core/dom/StyleChangeReason.h"
#include "core/dom/StyleEngine.h"
#include "core/dom/TaskRunnerHelper.h"
#include "core/dom/TouchList.h"
#include "core/dom/TransformSource.h"
#include "core/dom/TreeWalker.h"
#include "core/dom/VisitedLinkState.h"
#include "core/dom/XMLDocument.h"
#include "core/dom/custom/CustomElement.h"
#include "core/dom/custom/CustomElementDefinition.h"
#include "core/dom/custom/CustomElementDescriptor.h"
#include "core/dom/custom/CustomElementRegistry.h"
#include "core/dom/custom/V0CustomElementMicrotaskRunQueue.h"
#include "core/dom/custom/V0CustomElementRegistrationContext.h"
#include "core/dom/shadow/ElementShadow.h"
#include "core/dom/shadow/FlatTreeTraversal.h"
#include "core/dom/shadow/ShadowRoot.h"
#include "core/editing/EditingUtilities.h"
#include "core/editing/FrameSelection.h"
#include "core/editing/markers/DocumentMarkerController.h"
#include "core/editing/serializers/Serialization.h"
#include "core/editing/spellcheck/SpellChecker.h"
#include "core/events/BeforeUnloadEvent.h"
#include "core/events/Event.h"
#include "core/events/EventFactory.h"
#include "core/events/EventListener.h"
#include "core/events/HashChangeEvent.h"
#include "core/events/PageTransitionEvent.h"
#include "core/events/ScopedEventQueue.h"
#include "core/events/VisualViewportResizeEvent.h"
#include "core/events/VisualViewportScrollEvent.h"
#include "core/frame/ContentSettingsClient.h"
#include "core/frame/DOMTimer.h"
#include "core/frame/DOMVisualViewport.h"
#include "core/frame/EventHandlerRegistry.h"
#include "core/frame/FrameConsole.h"
#include "core/frame/FrameView.h"
#include "core/frame/History.h"
#include "core/frame/HostsUsingFeatures.h"
#include "core/frame/LocalDOMWindow.h"
#include "core/frame/LocalFrame.h"
#include "core/frame/LocalFrameClient.h"
#include "core/frame/PerformanceMonitor.h"
#include "core/frame/Settings.h"
#include "core/frame/csp/ContentSecurityPolicy.h"
#include "core/html/DocumentNameCollection.h"
#include "core/html/HTMLAllCollection.h"
#include "core/html/HTMLAnchorElement.h"
#include "core/html/HTMLBaseElement.h"
#include "core/html/HTMLBodyElement.h"
#include "core/html/HTMLCanvasElement.h"
#include "core/html/HTMLCollection.h"
#include "core/html/HTMLDialogElement.h"
#include "core/html/HTMLDocument.h"
#include "core/html/HTMLFrameOwnerElement.h"
#include "core/html/HTMLHeadElement.h"
#include "core/html/HTMLHtmlElement.h"
#include "core/html/HTMLIFrameElement.h"
#include "core/html/HTMLInputElement.h"
#include "core/html/HTMLLinkElement.h"
#include "core/html/HTMLMetaElement.h"
#include "core/html/HTMLPlugInElement.h"
#include "core/html/HTMLScriptElement.h"
#include "core/html/HTMLTemplateElement.h"
#include "core/html/HTMLTitleElement.h"
#include "core/html/PluginDocument.h"
#include "core/html/WindowNameCollection.h"
#include "core/html/canvas/CanvasContextCreationAttributes.h"
#include "core/html/canvas/CanvasFontCache.h"
#include "core/html/canvas/CanvasRenderingContext.h"
#include "core/html/forms/FormController.h"
#include "core/html/imports/HTMLImportLoader.h"
#include "core/html/imports/HTMLImportsController.h"
#include "core/html/parser/HTMLDocumentParser.h"
#include "core/html/parser/HTMLParserIdioms.h"
#include "core/html/parser/NestingLevelIncrementer.h"
#include "core/html/parser/TextResourceDecoder.h"
#include "core/input/EventHandler.h"
#include "core/inspector/ConsoleMessage.h"
#include "core/inspector/InspectorTraceEvents.h"
#include "core/inspector/MainThreadDebugger.h"
#include "core/layout/HitTestCanvasResult.h"
#include "core/layout/HitTestResult.h"
#include "core/layout/LayoutPart.h"
#include "core/layout/LayoutView.h"
#include "core/layout/TextAutosizer.h"
#include "core/layout/api/LayoutPartItem.h"
#include "core/layout/api/LayoutViewItem.h"
#include "core/layout/compositing/PaintLayerCompositor.h"
#include "core/loader/CookieJar.h"
#include "core/loader/DocumentLoader.h"
#include "core/loader/FrameFetchContext.h"
#include "core/loader/FrameLoader.h"
#include "core/loader/ImageLoader.h"
#include "core/loader/NavigationScheduler.h"
#include "core/loader/PrerendererClient.h"
#include "core/loader/appcache/ApplicationCacheHost.h"
#include "core/page/ChromeClient.h"
#include "core/page/EventWithHitTestResults.h"
#include "core/page/FocusController.h"
#include "core/page/FrameTree.h"
#include "core/page/Page.h"
#include "core/page/PointerLockController.h"
#include "core/page/scrolling/RootScrollerController.h"
#include "core/page/scrolling/ScrollStateCallback.h"
#include "core/page/scrolling/ScrollingCoordinator.h"
#include "core/page/scrolling/SnapCoordinator.h"
#include "core/page/scrolling/TopDocumentRootScrollerController.h"
#include "core/probe/CoreProbes.h"
#include "core/svg/SVGDocumentExtensions.h"
#include "core/svg/SVGScriptElement.h"
#include "core/svg/SVGTitleElement.h"
#include "core/svg/SVGUseElement.h"
#include "core/timing/DOMWindowPerformance.h"
#include "core/timing/Performance.h"
#include "core/workers/SharedWorkerRepositoryClient.h"
#include "core/xml/parser/XMLDocumentParser.h"
#include "platform/DateComponents.h"
#include "platform/EventDispatchForbiddenScope.h"
#include "platform/Histogram.h"
#include "platform/InstanceCounters.h"
#include "platform/Language.h"
#include "platform/LengthFunctions.h"
#include "platform/PluginScriptForbiddenScope.h"
#include "platform/RuntimeEnabledFeatures.h"
#include "platform/ScriptForbiddenScope.h"
#include "platform/WebFrameScheduler.h"
#include "platform/bindings/DOMDataStore.h"
#include "platform/bindings/Microtask.h"
#include "platform/bindings/V8DOMWrapper.h"
#include "platform/bindings/V8PerIsolateData.h"
#include "platform/instrumentation/tracing/TraceEvent.h"
#include "platform/loader/fetch/ResourceFetcher.h"
#include "platform/network/ContentSecurityPolicyParsers.h"
#include "platform/network/HTTPParsers.h"
#include "platform/network/NetworkStateNotifier.h"
#include "platform/scheduler/child/web_scheduler.h"
#include "platform/scroll/ScrollbarTheme.h"
#include "platform/text/PlatformLocale.h"
#include "platform/text/SegmentedString.h"
#include "platform/weborigin/OriginAccessEntry.h"
#include "platform/weborigin/SchemeRegistry.h"
#include "platform/weborigin/SecurityOrigin.h"
#include "platform/wtf/AutoReset.h"
#include "platform/wtf/CurrentTime.h"
#include "platform/wtf/DateMath.h"
#include "platform/wtf/Functional.h"
#include "platform/wtf/HashFunctions.h"
#include "platform/wtf/PtrUtil.h"
#include "platform/wtf/StdLibExtras.h"
#include "platform/wtf/text/CharacterNames.h"
#include "platform/wtf/text/StringBuffer.h"
#include "platform/wtf/text/TextEncodingRegistry.h"
#include "public/platform/InterfaceProvider.h"
#include "public/platform/Platform.h"
#include "public/platform/WebAddressSpace.h"
#include "public/platform/WebPrerenderingSupport.h"
#include "public/platform/modules/sensitive_input_visibility/sensitive_input_visibility_service.mojom-blink.h"
#include "public/platform/site_engagement.mojom-blink.h"
#include <memory>
#ifndef NDEBUG
using WeakDocumentSet =
blink::PersistentHeapHashSet<blink::WeakMember<blink::Document>>;
static WeakDocumentSet& liveDocumentSet();
#endif
namespace blink {
using namespace HTMLNames;
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(const LocalDOMWindow* window,
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 && window)) {
UseCounter::Count(window->GetFrame(),
is_valid_dom_name
? UseCounter::kElementNameDOMValidHTMLParserInvalid
: UseCounter::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 = ToDocument(context);
if (Element* element = document->AutofocusElement()) {
document->SetAutofocusElement(0);
element->focus();
}
}
static void RecordLoadReasonToHistogram(WouldLoadReason reason) {
// TODO(dcheng): Make EnumerationHistogram work with scoped enums.
DEFINE_STATIC_LOCAL(EnumerationHistogram, unseen_frame_histogram,
("Navigation.DeferredDocumentLoading.StatesV4",
static_cast<int>(WouldLoadReason::kCount)));
unseen_frame_histogram.Count(static_cast<int>(reason));
}
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) {
GetNetworkStateNotifier().AddOnLineObserver(
this,
TaskRunnerHelper::Get(TaskType::kNetworking, GetExecutionContext()));
}
void OnLineStateChange(bool on_line) override {
AtomicString event_name =
on_line ? EventTypeNames::online : EventTypeNames::offline;
Document* document = ToDocument(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);
GetNetworkStateNotifier().RemoveOnLineObserver(
this, TaskRunnerHelper::Get(TaskType::kNetworking, context));
}
DEFINE_INLINE_VIRTUAL_TRACE() { ContextLifecycleObserver::Trace(visitor); }
};
Document::Document(const DocumentInit& initializer,
DocumentClassFlags document_classes)
: ContainerNode(0, 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_(this, initializer.ImportsController()),
context_features_(ContextFeatures::DefaultSwitch()),
well_formed_(false),
implementation_(this, nullptr),
printing_(kNotPrinting),
paginated_for_screen_(false),
compatibility_mode_(kNoQuirksMode),
compatibility_mode_locked_(false),
has_autofocused_(false),
clear_focused_element_timer_(
TaskRunnerHelper::Get(TaskType::kUnspecedTimer, this),
this,
&Document::ClearFocusedElementTimerFired),
dom_tree_version_(++global_tree_version_),
style_version_(0),
listener_types_(0),
mutation_observer_types_(0),
style_engine_(this, nullptr),
style_sheet_list_(this, nullptr),
visited_link_state_(VisitedLinkState::Create(*this)),
visually_ordered_(false),
ready_state_(kComplete),
parsing_state_(kFinishedParsing),
goto_anchor_needed_after_stylesheets_load_(false),
contains_validity_style_rules_(false),
contains_plugins_(false),
ignore_destructive_write_count_(0),
throw_on_dynamic_markup_insertion_count_(0),
markers_(new DocumentMarkerController(*this)),
update_focus_appearance_timer_(
TaskRunnerHelper::Get(TaskType::kUnspecedTimer, this),
this,
&Document::UpdateFocusAppearanceTimerFired),
css_target_(nullptr),
load_event_progress_(kLoadEventCompleted),
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_(false),
is_mobile_document_(false),
layout_view_(0),
context_document_(initializer.ContextDocument()),
has_fullscreen_supplement_(false),
load_event_delay_count_(0),
load_event_delay_timer_(
TaskRunnerHelper::Get(TaskType::kNetworking, this),
this,
&Document::LoadEventDelayTimerFired),
plugin_loading_timer_(
TaskRunnerHelper::Get(TaskType::kUnspecedLoading, this),
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_(
TaskRunnerHelper::Get(TaskType::kUnspecedTimer, this),
this,
&Document::ElementDataCacheClearTimerFired),
timeline_(DocumentTimeline::Create(this)),
compositor_pending_animations_(new CompositorPendingAnimations(*this)),
template_document_host_(nullptr),
did_associate_form_controls_timer_(
TaskRunnerHelper::Get(TaskType::kUnspecedLoading, this),
this,
&Document::DidAssociateFormControlsTimerFired),
timers_(TaskRunnerHelper::Get(TaskType::kTimer, this)),
has_viewport_units_(false),
parser_sync_policy_(kAllowAsynchronousParsing),
node_count_(0),
would_load_reason_(WouldLoadReason::kInvalid),
password_count_(0),
engagement_level_(mojom::blink::EngagementLevel::NONE) {
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());
InitSecurityContext(initializer);
InitDNSPrefetch();
InstanceCounters::IncrementCounter(InstanceCounters::kDocumentCounter);
lifecycle_.AdvanceTo(DocumentLifecycle::kInactive);
// Since CSSFontSelector requires Document::m_fetcher and StyleEngine owns
// CSSFontSelector, need to initialize m_styleEngine after initializing
// m_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()->IsContextSuspended());
#ifndef NDEBUG
liveDocumentSet().insert(this);
#endif
}
Document::~Document() {
DCHECK(GetLayoutViewItem().IsNull());
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);
}
SelectorQueryCache& Document::GetSelectorQueryCache() {
if (!selector_query_cache_)
selector_query_cache_ = WTF::MakeUnique<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;
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_) {
this->AdoptIfNeeded(*doc_type_);
if (doc_type_->publicId().StartsWith("-//wapforum//dtd xhtml mobile 1.",
kTextCaseASCIIInsensitive)) {
is_mobile_document_ = true;
style_engine_->ViewportRulesChanged();
}
}
}
DOMImplementation& Document::implementation() {
if (!implementation_)
implementation_ = DOMImplementation::Create(*this);
return *implementation_;
}
bool Document::HasAppCacheManifest() const {
return isHTMLHtmlElement(documentElement()) &&
documentElement()->hasAttribute(manifestAttr);
}
Location* Document::location() const {
if (!GetFrame())
return 0;
return domWindow()->location();
}
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;
}
// https://dom.spec.whatwg.org/#dom-document-createelement
Element* Document::createElement(const LocalDOMWindow* window,
const AtomicString& name,
ExceptionState& exception_state) {
if (!IsValidElementName(window, name)) {
exception_state.ThrowDOMException(
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::CreateCustomElementSync(
*this,
QualifiedName(g_null_atom, local_name, HTMLNames::xhtmlNamespaceURI));
}
return HTMLElementFactory::createHTMLElement(local_name, *this,
kCreatedByCreateElement);
}
return Element::Create(QualifiedName(g_null_atom, name, g_null_atom), this);
}
String GetTypeExtension(Document* document,
const StringOrDictionary& string_or_options,
ExceptionState& exception_state) {
if (string_or_options.isNull())
return g_empty_string;
if (string_or_options.isString()) {
UseCounter::Count(document,
UseCounter::kDocumentCreateElement2ndArgStringHandling);
return string_or_options.getAsString();
}
if (string_or_options.isDictionary()) {
Dictionary dict = string_or_options.getAsDictionary();
ElementCreationOptions impl;
V8ElementCreationOptions::toImpl(dict.GetIsolate(), dict.V8Value(), impl,
exception_state);
if (exception_state.HadException())
return g_empty_string;
if (impl.hasIs())
return impl.is();
}
return g_empty_string;
}
// https://dom.spec.whatwg.org/#dom-document-createelement
Element* Document::createElement(const LocalDOMWindow* window,
const AtomicString& local_name,
const StringOrDictionary& string_or_options,
ExceptionState& exception_state) {
// 1. If localName does not match Name production, throw InvalidCharacterError
if (!IsValidElementName(window, local_name)) {
exception_state.ThrowDOMException(
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);
bool is_v1 = string_or_options.isDictionary() || !RegistrationContext();
bool create_v1_builtin =
string_or_options.isDictionary() &&
RuntimeEnabledFeatures::customElementsBuiltinEnabled();
bool should_create_builtin =
create_v1_builtin || string_or_options.isString();
// 3.
const AtomicString& is =
AtomicString(GetTypeExtension(this, string_or_options, exception_state));
const AtomicString& name = should_create_builtin ? is : converted_local_name;
// 4. Let definition be result of lookup up custom element definition
CustomElementDefinition* definition = nullptr;
if (is_v1) {
// Is the runtime flag enabled for customized builtin elements?
const CustomElementDescriptor desc =
RuntimeEnabledFeatures::customElementsBuiltinEnabled()
? CustomElementDescriptor(name, converted_local_name)
: CustomElementDescriptor(converted_local_name,
converted_local_name);
if (CustomElementRegistry* registry = CustomElement::Registry(*this))
definition = registry->DefinitionFor(desc);
// 5. If 'is' is non-null and definition is null, throw NotFoundError
// TODO(yurak): update when https://github.com/w3c/webcomponents/issues/608
// is resolved
if (!definition && create_v1_builtin) {
exception_state.ThrowDOMException(kNotFoundError,
"Custom element definition not found.");
return nullptr;
}
}
// 7. Let element be the result of creating an element
Element* element;
if (definition) {
element = CustomElement::CreateCustomElementSync(
*this, converted_local_name, definition);
} else if (V0CustomElement::IsValidName(local_name) &&
RegistrationContext()) {
element = RegistrationContext()->CreateCustomTagElement(
*this,
QualifiedName(g_null_atom, converted_local_name, xhtmlNamespaceURI));
} else {
element = createElement(window, local_name, exception_state);
if (exception_state.HadException())
return nullptr;
}
// 8. If 'is' is non-null, set 'is' attribute
if (!is.IsEmpty()) {
if (string_or_options.isString()) {
V0CustomElementRegistrationContext::SetIsAttributeAndTypeExtension(
element, is);
} else if (string_or_options.isDictionary()) {
element->setAttribute(HTMLNames::isAttr, 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(
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 LocalDOMWindow* window,
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;
if (CustomElement::ShouldCreateCustomElement(q_name))
return CustomElement::CreateCustomElementSync(*this, q_name);
return createElement(q_name, kCreatedByCreateElement);
}
// https://dom.spec.whatwg.org/#internal-createelementns-steps
Element* Document::createElementNS(const LocalDOMWindow* window,
const AtomicString& namespace_uri,
const AtomicString& qualified_name,
const StringOrDictionary& string_or_options,
ExceptionState& 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.isDictionary() || !RegistrationContext();
bool create_v1_builtin =
string_or_options.isDictionary() &&
RuntimeEnabledFeatures::customElementsBuiltinEnabled();
bool should_create_builtin =
create_v1_builtin || string_or_options.isString();
// 2.
const AtomicString& is =
AtomicString(GetTypeExtension(this, string_or_options, exception_state));
const AtomicString& name = should_create_builtin ? is : qualified_name;
if (!IsValidElementName(window, qualified_name)) {
exception_state.ThrowDOMException(
kInvalidCharacterError, "The tag name provided ('" + qualified_name +
"') is not a valid name.");
return nullptr;
}
// 3. Let definition be result of lookup up custom element definition
CustomElementDefinition* definition = nullptr;
if (is_v1) {
const CustomElementDescriptor desc =
RuntimeEnabledFeatures::customElementsBuiltinEnabled()
? CustomElementDescriptor(name, qualified_name)
: CustomElementDescriptor(qualified_name, qualified_name);
if (CustomElementRegistry* registry = CustomElement::Registry(*this))
definition = registry->DefinitionFor(desc);
// 4. If 'is' is non-null and definition is null, throw NotFoundError
if (!definition && create_v1_builtin) {
exception_state.ThrowDOMException(kNotFoundError,
"Custom element definition not found.");
return nullptr;
}
}
// 5. Let element be the result of creating an element
Element* element;
if (CustomElement::ShouldCreateCustomElement(q_name) || create_v1_builtin) {
element = CustomElement::CreateCustomElementSync(*this, q_name, definition);
} else if (V0CustomElement::IsValidName(q_name.LocalName()) &&
RegistrationContext()) {
element = RegistrationContext()->CreateCustomTagElement(*this, q_name);
} else {
element = createElement(q_name, kCreatedByCreateElement);
}
// 6. If 'is' is non-null, set 'is' attribute
if (!is.IsEmpty()) {
if (element->GetCustomElementState() != CustomElementState::kCustom) {
V0CustomElementRegistrationContext::SetIsAttributeAndTypeExtension(
element, is);
} else if (string_or_options.isDictionary()) {
element->setAttribute(HTMLNames::isAttr, is);
}
}
return element;
}
ScriptValue Document::registerElement(ScriptState* script_state,
const AtomicString& name,
const ElementRegistrationOptions& options,
ExceptionState& exception_state,
V0CustomElement::NameSet valid_names) {
HostsUsingFeatures::CountMainWorldOnly(
script_state, *this,
HostsUsingFeatures::Feature::kDocumentRegisterElement);
if (!RegistrationContext()) {
exception_state.ThrowDOMException(
kNotSupportedError, "No element registration context is available.");
return ScriptValue();
}
V0CustomElementConstructorBuilder constructor_builder(script_state, options);
RegistrationContext()->RegisterElement(this, &constructor_builder, name,
valid_names, 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() {
imports_controller_ = nullptr;
if (!Loader())
fetcher_->ClearContext();
}
void Document::CreateImportsController() {
DCHECK(!imports_controller_);
imports_controller_ = HTMLImportsController::Create(*this);
}
HTMLImportLoader* Document::ImportLoader() const {
if (!imports_controller_)
return 0;
return imports_controller_->LoaderFor(*this);
}
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 = this->ImportsController())
return import->Master()->domWindow();
return 0;
}
LocalFrame* Document::ExecutingFrame() {
LocalDOMWindow* window = ExecutingWindow();
if (!window)
return 0;
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(
kNotSupportedError,
"This operation is not supported for HTML documents.");
return nullptr;
}
if (data.Contains("]]>")) {
exception_state.ThrowDOMException(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(
kInvalidCharacterError,
"The target provided ('" + target + "') is not a valid name.");
return nullptr;
}
if (data.Contains("?>")) {
exception_state.ThrowDOMException(
kInvalidCharacterError,
"The data provided ('" + data + "') contains '?>'.");
return nullptr;
}
if (IsHTMLDocument()) {
UseCounter::Count(*this,
UseCounter::kHTMLDocumentCreateProcessingInstruction);
}
return ProcessingInstruction::Create(*this, target, data);
}
Text* Document::CreateEditingTextNode(const String& text) {
return Text::CreateEditingText(*this, text);
}
bool Document::ImportContainerNodeChildren(ContainerNode* old_container_node,
ContainerNode* new_container_node,
ExceptionState& exception_state) {
for (Node& old_child : NodeTraversal::ChildrenOf(*old_container_node)) {
Node* new_child = importNode(&old_child, true, exception_state);
if (exception_state.HadException())
return false;
new_container_node->AppendChild(new_child, exception_state);
if (exception_state.HadException())
return false;
}
return true;
}
Node* Document::importNode(Node* imported_node,
bool deep,
ExceptionState& exception_state) {
switch (imported_node->getNodeType()) {
case kTextNode:
return createTextNode(imported_node->nodeValue());
case kCdataSectionNode:
return CDATASection::Create(*this, imported_node->nodeValue());
case kProcessingInstructionNode:
return createProcessingInstruction(imported_node->nodeName(),
imported_node->nodeValue(),
exception_state);
case kCommentNode:
return createComment(imported_node->nodeValue());
case kDocumentTypeNode: {
DocumentType* doctype = ToDocumentType(imported_node);
return DocumentType::Create(this, doctype->name(), doctype->publicId(),
doctype->systemId());
}
case kElementNode: {
Element* old_element = ToElement(imported_node);
// FIXME: The following check might be unnecessary. Is it possible that
// oldElement has mismatched prefix/namespace?
if (!HasValidNamespaceForElements(old_element->TagQName())) {
exception_state.ThrowDOMException(
kNamespaceError, "The imported node has an invalid namespace.");
return nullptr;
}
Element* new_element =
createElement(old_element->TagQName(), kCreatedByImportNode);
new_element->CloneDataFromElement(*old_element);
if (deep) {
if (!ImportContainerNodeChildren(old_element, new_element,
exception_state))
return nullptr;
if (isHTMLTemplateElement(*old_element) &&
!EnsureTemplateDocument().ImportContainerNodeChildren(
toHTMLTemplateElement(old_element)->content(),
toHTMLTemplateElement(new_element)->content(), exception_state))
return nullptr;
}
return new_element;
}
case kAttributeNode:
return Attr::Create(
*this,
QualifiedName(g_null_atom,
AtomicString(ToAttr(imported_node)->name()),
g_null_atom),
ToAttr(imported_node)->value());
case kDocumentFragmentNode: {
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(
kNotSupportedError,
"The node provided is a shadow root, which may not be imported.");
return nullptr;
}
DocumentFragment* old_fragment = ToDocumentFragment(imported_node);
DocumentFragment* new_fragment = createDocumentFragment();
if (deep && !ImportContainerNodeChildren(old_fragment, new_fragment,
exception_state))
return nullptr;
return new_fragment;
}
case kDocumentNode:
exception_state.ThrowDOMException(
kNotSupportedError,
"The node provided is a document, which may not be imported.");
return nullptr;
}
NOTREACHED();
return nullptr;
}
Node* Document::adoptNode(Node* source, ExceptionState& exception_state) {
EventQueueScope scope;
switch (source->getNodeType()) {
case kDocumentNode:
exception_state.ThrowDOMException(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(
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(
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;
}
}
}
this->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() != XMLNames::xmlNamespaceURI)
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() == XMLNSNames::xmlnsNamespaceURI;
return q_name.NamespaceURI() != XMLNSNames::xmlnsNamespaceURI;
}
bool Document::HasValidNamespaceForAttributes(const QualifiedName& q_name) {
return HasValidNamespaceForElements(q_name);
}
// FIXME: This should really be in a possible ElementFactory class
Element* Document::createElement(const QualifiedName& q_name,
CreateElementFlags flags) {
Element* e = nullptr;
// FIXME: Use registered namespaces and look up in a hash to find the right
// factory.
if (q_name.NamespaceURI() == xhtmlNamespaceURI)
e = HTMLElementFactory::createHTMLElement(q_name.LocalName(), *this, flags);
else if (q_name.NamespaceURI() == SVGNames::svgNamespaceURI)
e = SVGElementFactory::createSVGElement(q_name.LocalName(), *this, flags);
if (e)
saw_elements_in_known_namespaces_ = true;
else
e = Element::Create(q_name, this);
if (e->prefix() != q_name.Prefix())
e->SetTagNameForCreateElementNS(q_name);
DCHECK(q_name == e->TagQName());
return e;
}
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()) {
document_timing_.MarkDomLoading();
}
break;
case kInteractive:
if (!document_timing_.DomInteractive())
document_timing_.MarkDomInteractive();
break;
case kComplete:
if (!document_timing_.DomComplete())
document_timing_.MarkDomComplete();
break;
}
ready_state_ = ready_state;
DispatchEvent(Event::Create(EventTypeNames::readystatechange));
}
bool Document::IsLoadCompleted() {
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(
StyleChangeReason::kLanguage));
}
void Document::setXMLVersion(const String& version,
ExceptionState& exception_state) {
if (!XMLDocumentParser::SupportsXMLVersion(version)) {
exception_state.ThrowDOMException(
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) {
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->ResponseMIMEType();
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(int x, int y) const {
if (GetLayoutViewItem().IsNull())
return 0;
return TreeScope::ElementFromPoint(x, y);
}
HeapVector<Member<Element>> Document::ElementsFromPoint(int x, int y) const {
if (GetLayoutViewItem().IsNull())
return HeapVector<Member<Element>>();
return TreeScope::ElementsFromPoint(x, y);
}
Range* Document::caretRangeFromPoint(int x, int y) {
if (GetLayoutViewItem().IsNull())
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 Range::CreateAdjustedToTreeScope(*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;
frame_->Loader().Client()->DispatchDidReceiveTitle(title_);
}
void Document::setTitle(const String& title) {
// Title set by JavaScript -- overrides any title elements.
if (!title_element_) {
if (IsHTMLDocument() || IsXHTMLDocument()) {
HTMLElement* head_element = head();
if (!head_element)
return;
title_element_ = HTMLTitleElement::Create(*this);
head_element->AppendChild(title_element_.Get());
} else if (IsSVGDocument()) {
Element* element = documentElement();
if (!isSVGSVGElement(element))
return;
title_element_ = SVGTitleElement::Create(*this);
element->InsertBefore(title_element_.Get(), element->firstChild());
}
} else {
if (!IsHTMLDocument() && !IsXHTMLDocument() && !IsSVGDocument())
title_element_ = nullptr;
}
if (isHTMLTitleElement(title_element_))
toHTMLTitleElement(title_element_)->setText(title);
else if (isSVGTitleElement(title_element_))
toSVGTitleElement(title_element_)->SetText(title);
else
UpdateTitle(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 (isHTMLTitleElement(title_element_))
UpdateTitle(toHTMLTitleElement(title_element_)->text());
else if (isSVGTitleElement(title_element_))
UpdateTitle(toSVGTitleElement(title_element_)->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 (isHTMLHtmlElement(root_element))
return toHTMLHtmlElement(root_element)->dir();
return g_null_atom;
}
void Document::setDir(const AtomicString& value) {
Element* root_element = documentElement();
if (isHTMLHtmlElement(root_element))
toHTMLHtmlElement(root_element)->setDir(value);
}
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 kPageVisibilityStateHidden;
// 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 kPageVisibilityStateHidden;
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() != kPageVisibilityStateVisible;
}
void Document::DidChangeVisibilityState() {
DispatchEvent(Event::CreateBubble(EventTypeNames::visibilitychange));
// Also send out the deprecated version until it can be removed.
DispatchEvent(Event::CreateBubble(EventTypeNames::webkitvisibilitychange));
if (GetPageVisibilityState() == kPageVisibilityStateVisible)
Timeline().SetAllCompositorPending();
if (hidden() && canvas_font_cache_)
canvas_font_cache_->PruneAll();
}
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 0;
return form_controller_->FormElementsState();
}
void Document::SetStateForNewFormElements(const Vector<String>& state_vector) {
if (!state_vector.size() && !form_controller_)
return;
GetFormController().SetStateForNewFormElements(state_vector);
}
FrameView* Document::View() const {
return frame_ ? frame_->View() : nullptr;
}
Page* Document::GetPage() const {
return frame_ ? frame_->GetPage() : 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,
V8NodeFilterCondition* filter) {
DCHECK(root);
return NodeIterator::Create(root, what_to_show, filter);
}
TreeWalker* Document::createTreeWalker(Node* root,
unsigned what_to_show,
V8NodeFilterCondition* 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 (GetLayoutViewItem().WasNotifiedOfSubtreeChange())
return true;
return false;
}
bool Document::NeedsFullLayoutTreeUpdate() const {
if (!IsActive() || !View())
return false;
if (style_engine_->NeedsActiveStyleUpdate())
return true;
if (!use_elements_needing_update_.IsEmpty())
return true;
if (NeedsStyleRecalc())
return true;
if (NeedsStyleInvalidation())
return true;
// FIXME: The childNeedsDistributionRecalc bit means either self or children,
// we should fix that.
if (ChildNeedsDistributionRecalc())
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");
GetStyleEngine().GetStyleInvalidator().Invalidate(*this);
}
void Document::SetupFontBuilder(ComputedStyle& document_style) {
FontBuilder font_builder(*this);
CSSFontSelector* selector = GetStyleEngine().FontSelector();
font_builder.CreateFontForDocument(selector, document_style);
}
void Document::InheritHtmlAndBodyElementStyles(StyleRecalcChange change) {
DCHECK(InStyleRecalc());
DCHECK(documentElement());
bool did_recalc_document_element = false;
RefPtr<ComputedStyle> document_element_style =
documentElement()->MutableComputedStyle();
if (change == kForce)
documentElement()->ClearAnimationStyleChange();
if (!document_element_style || documentElement()->NeedsStyleRecalc() ||
change == kForce) {
document_element_style =
EnsureStyleResolver().StyleForElement(documentElement());
did_recalc_document_element = true;
}
WritingMode root_writing_mode = document_element_style->GetWritingMode();
TextDirection root_direction = document_element_style->Direction();
HTMLElement* body = this->body();
RefPtr<ComputedStyle> body_style;
if (body) {
body_style = body->MutableComputedStyle();
if (did_recalc_document_element)
body->ClearAnimationStyleChange();
if (!body_style || body->NeedsStyleRecalc() ||
did_recalc_document_element) {
body_style = EnsureStyleResolver().StyleForElement(
body, document_element_style.Get(), document_element_style.Get());
}
root_writing_mode = body_style->GetWritingMode();
root_direction = body_style->Direction();
}
const ComputedStyle* background_style = document_element_style.Get();
// 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::backgroundStolenForBeingBody()
if (isHTMLHtmlElement(documentElement()) && isHTMLBodyElement(body) &&
!background_style->HasBackground())
background_style = body_style.Get();
Color background_color =
background_style->VisitedDependentColor(CSSPropertyBackgroundColor);
FillLayer 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(kBorderFillBox);
// 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() == kScrollBackgroundAttachment)
current_layer->SetAttachment(kLocalBackgroundAttachment);
}
EImageRendering image_rendering = background_style->ImageRendering();
const ComputedStyle* overflow_style = nullptr;
if (Element* element =
ViewportDefiningElement(document_element_style.Get())) {
if (element == body) {
overflow_style = body_style.Get();
} else {
DCHECK_EQ(element, documentElement());
overflow_style = document_element_style.Get();
// 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, UseCounter::kBodyScrollsInAdditionToViewport);
}
}
// Resolved rem units are stored in the matched properties cache so we need to
// make sure to invalidate the cache if the documentElement needed to reattach
// or the font size changed and then trigger a full document recalc. We also
// need to clear it here since the call to styleForElement on the body above
// can cache bad values for rem units if the documentElement's style was
// dirty. We could keep track of which elements depend on rem units like we do
// for viewport styles, but we assume root font size changes are rare and just
// invalidate the cache for now.
if (GetStyleEngine().UsesRemUnits() &&
(documentElement()->NeedsAttach() ||
!documentElement()->GetComputedStyle() ||
documentElement()->GetComputedStyle()->FontSize() !=
document_element_style->FontSize())) {
EnsureStyleResolver().InvalidateMatchedPropertiesCache();
documentElement()->SetNeedsStyleRecalc(
kSubtreeStyleChange, StyleChangeReasonForTracing::Create(
StyleChangeReason::kFontSizeChange));
}
EOverflowAnchor overflow_anchor = EOverflowAnchor::kAuto;
EOverflow overflow_x = EOverflow::kAuto;
EOverflow overflow_y = EOverflow::kAuto;
float column_gap = 0;
if (overflow_style) {
overflow_anchor = overflow_style->OverflowAnchor();
overflow_x = overflow_style->OverflowX();
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.
column_gap = overflow_style->ColumnGap();
}
ScrollSnapType snap_type = overflow_style->GetScrollSnapType();
const LengthPoint& snap_destination = overflow_style->ScrollSnapDestination();
RefPtr<ComputedStyle> document_style = GetLayoutViewItem().MutableStyle();
if (document_style->GetWritingMode() != root_writing_mode ||
document_style->Direction() != root_direction ||
document_style->VisitedDependentColor(CSSPropertyBackgroundColor) !=
background_color ||
document_style->BackgroundLayers() != background_layers ||
document_style->ImageRendering() != image_rendering ||
document_style->OverflowAnchor() != overflow_anchor ||
document_style->OverflowX() != overflow_x ||
document_style->OverflowY() != overflow_y ||
document_style->ColumnGap() != column_gap ||
document_style->GetScrollSnapType() != snap_type ||
document_style->ScrollSnapDestination() != snap_destination) {
RefPtr<ComputedStyle> new_style = ComputedStyle::Clone(*document_style);
new_style->SetWritingMode(root_writing_mode);
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->SetScrollSnapDestination(snap_destination);
GetLayoutViewItem().SetStyle(new_style);
SetupFontBuilder(*new_style);
}
if (body) {
if (const ComputedStyle* style = body->GetComputedStyle()) {
if (style->Direction() != root_direction ||
style->GetWritingMode() != root_writing_mode)
body->SetNeedsStyleRecalc(kSubtreeStyleChange,
StyleChangeReasonForTracing::Create(
StyleChangeReason::kWritingModeChange));
}
}
if (const ComputedStyle* style = documentElement()->GetComputedStyle()) {
if (style->Direction() != root_direction ||
style->GetWritingMode() != root_writing_mode)
documentElement()->SetNeedsStyleRecalc(
kSubtreeStyleChange, StyleChangeReasonForTracing::Create(
StyleChangeReason::kWritingModeChange));
}
}
#if DCHECK_IS_ON()
static void AssertLayoutTreeUpdated(Node& root) {
for (Node& node : NodeTraversal::InclusiveDescendantsOf(root)) {
// We leave some nodes with dirty bits in the tree because they don't
// matter like Comment and ProcessingInstruction nodes.
// TODO(esprehn): Don't even mark those nodes as needing recalcs in the
// first place.
if (!node.IsElementNode() && !node.IsTextNode() && !node.IsShadowRoot() &&
!node.IsDocumentNode())
continue;
DCHECK(!node.NeedsStyleRecalc());
DCHECK(!node.ChildNeedsStyleRecalc());
DCHECK(!node.NeedsReattachLayoutTree());
DCHECK(!node.ChildNeedsReattachLayoutTree());
DCHECK(!node.ChildNeedsDistributionRecalc());
DCHECK(!node.NeedsStyleInvalidation());
DCHECK(!node.ChildNeedsStyleInvalidation());
for (ShadowRoot* shadow_root = node.YoungestShadowRoot(); shadow_root;
shadow_root = shadow_root->OlderShadowRoot())
AssertLayoutTreeUpdated(*shadow_root);
}
}
#endif
void Document::UpdateStyleAndLayoutTree() {
DCHECK(IsMainThread());
ScriptForbiddenScope forbid_script;
// We should forbid script execution for plugins here because update while
// layout is changing, HTMLPlugin element can be reattached and plugin can be
// destroyed. Plugin can execute scripts on destroy. It produces crash without
// PluginScriptForbiddenScope: crbug.com/550427.
PluginScriptForbiddenScope plugin_forbid_script;
if (!View() || !IsActive())
return;
if (View()->ShouldThrottleRendering())
return;
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();
UpdateDistribution();
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().DispatchFakeMouseMoveEventSoon();
if (focused_element_ && !focused_element_->IsFocusable())
ClearFocusedElementSoon();
GetLayoutViewItem().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");
double start_time = MonotonicallyIncreasingTime();
unsigned initial_element_count = GetStyleEngine().StyleForElementCount();
HTMLFrameOwnerElement::UpdateSuspendScope
suspend_frame_view_base_hierarchy_updates;
lifecycle_.AdvanceTo(DocumentLifecycle::kInStyleRecalc);
StyleRecalcChange change = kNoChange;
if (GetStyleChangeType() >= kSubtreeStyleChange)
change = kForce;
NthIndexCache nth_index_cache(*this);
// FIXME: Cannot access the ensureStyleResolver() before calling
// styleForDocument 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;
RefPtr<ComputedStyle> document_style =
StyleResolver::StyleForDocument(*this);
StyleRecalcChange local_change = ComputedStyle::StylePropagationDiff(
document_style.Get(), GetLayoutViewItem().Style());
if (local_change != kNoChange)
GetLayoutViewItem().SetStyle(std::move(document_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 = this->documentElement()) {
InheritHtmlAndBodyElementStyles(change);
if (document_element->ShouldCallRecalcStyle(change)) {
TRACE_EVENT0("blink,blink_style", "Document::recalcStyle");
document_element->RecalcStyle(change);
}
if (document_element->NeedsReattachLayoutTree() ||
document_element->ChildNeedsReattachLayoutTree()) {
TRACE_EVENT0("blink,blink_style", "Document::rebuildLayoutTree");
document_element->RebuildLayoutTree();
}
}
View()->RecalcOverflowAfterStyleChange();
ClearChildNeedsStyleRecalc();
ClearChildNeedsReattachLayoutTree();
resolver.ClearStyleSharingList();
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);
}
double update_duration_seconds = MonotonicallyIncreasingTime() - start_time;
DEFINE_STATIC_LOCAL(CustomCountHistogram, update_histogram,
("Style.UpdateTime", 0, 10000000, 50));
update_histogram.Count(update_duration_seconds * 1000 * 1000);
CSSTiming::From(*this).RecordUpdateDuration(update_duration_seconds);
}
void Document::NotifyLayoutTreeOfSubtreeChanges() {
if (!GetLayoutViewItem().WasNotifiedOfSubtreeChange())
return;
lifecycle_.AdvanceTo(DocumentLifecycle::kInLayoutSubtreeChange);
GetLayoutViewItem().HandleSubtreeModifications();
DCHECK(!GetLayoutViewItem().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 (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());
ScriptForbiddenScope forbid_script;
FrameView* frame_view = View();
if (frame_view && frame_view->IsInPerformLayout()) {
// View layout should not be re-entrant.
NOTREACHED();
return;
}
if (HTMLFrameOwnerElement* owner = LocalOwner())
owner->GetDocument().UpdateStyleAndLayout();
UpdateStyleAndLayoutTree();
if (!IsActive())
return;
if (frame_view->NeedsLayout())
frame_view->UpdateLayout();
if (Lifecycle().GetState() < DocumentLifecycle::kLayoutClean)
Lifecycle().AdvanceTo(DocumentLifecycle::kLayoutClean);
if (FrameView* frame_view = View())
frame_view->PerformScrollAnchoringAdjustments();
}
void Document::LayoutUpdated() {
// Plugins can run script inside layout which can detach the page.
// TODO(esprehn): Can this still happen now that all plugins are out of
// process?
if (GetFrame() && GetFrame()->GetPage())
GetFrame()->GetPage()->GetChromeClient().LayoutUpdated(GetFrame());
Markers().InvalidateRectsForAllMarkers();
// 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() &&
!GetStyleEngine().HasPendingScriptBlockingSheets()) {
if (!document_timing_.FirstLayout())
document_timing_.MarkFirstLayout();
}
root_scroller_controller_->DidUpdateLayout();
}
void Document::ClearFocusedElementSoon() {
if (!clear_focused_element_timer_.IsActive())
clear_focused_element_timer_.StartOneShot(0, BLINK_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() {
StyleEngine::IgnoringPendingStylesheet ignoring(GetStyleEngine());
if (GetStyleEngine().HasPendingScriptBlockingSheets()) {
// 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(
StyleChangeReason::kCleanupPlaceholderStyles));
}
}
UpdateStyleAndLayoutTree();
}
void Document::UpdateStyleAndLayoutIgnorePendingStylesheets(
Document::RunPostLayoutTasks run_post_layout_tasks) {
UpdateStyleAndLayoutTreeIgnorePendingStylesheets();
UpdateStyleAndLayout();
if (run_post_layout_tasks == kRunPostLayoutTasksSynchronously && View())
View()->FlushAnyPendingPostLayoutTasks();
}
PassRefPtr<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);
}
PassRefPtr<ComputedStyle> Document::StyleForPage(int page_index) {
UpdateDistribution();
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()) {
if (RuntimeEnabledFeatures::slimmingPaintV2Enabled()) {
// In SPv2, compositing inputs are cleaned as part of PrePaint.
View()->UpdateAllLifecyclePhasesExceptPaint();
} else {
View()->UpdateLifecycleToCompositingInputsClean();
}
}
}
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) {
RefPtr<ComputedStyle> style = StyleForPage(page_index);
double width = page_size.Width();
double height = page_size.Height();
switch (style->GetPageSizeType()) {
case PAGE_SIZE_AUTO:
break;
case PAGE_SIZE_AUTO_LANDSCAPE:
if (width < height)
std::swap(width, height);
break;
case PAGE_SIZE_AUTO_PORTRAIT:
if (width > height)
std::swap(width, height);
break;
case PAGE_SIZE_RESOLVED: {
FloatSize size = style->PageSize();
width = size.Width();
height = size.Height();
break;
}
default:
NOTREACHED();
}
page_size = DoubleSize(width, height);
// The percentage is calculated with respect to the width even for margin top
// and bottom.
// http://www.w3.org/TR/CSS2/box.html#margin-properties
margin_top = style->MarginTop().IsAuto()
? margin_top
: IntValueForLength(style->MarginTop(), width);
margin_right = style->MarginRight().IsAuto()
? margin_right
: IntValueForLength(style->MarginRight(), width);
margin_bottom = style->MarginBottom().IsAuto()
? margin_bottom
: IntValueForLength(style->MarginBottom(), width);
margin_left = style->MarginLeft().IsAuto()
? margin_left
: IntValueForLength(style->MarginLeft(), width);
}
void Document::SetIsViewSource(bool is_view_source) {
is_view_source_ = is_view_source;
if (!is_view_source_)
return;
}
void Document::ScheduleUseShadowTreeUpdate(SVGUseElement& element) {
use_elements_needing_update_.insert(&element);
ScheduleLayoutTreeUpdateIfNeeded();
}
void Document::UnscheduleUseShadowTreeUpdate(SVGUseElement& element) {
use_elements_needing_update_.erase(&element);
}
void Document::UpdateUseShadowTreesIfNeeded() {
ScriptForbiddenScope forbid_script;
if (use_elements_needing_update_.IsEmpty())
return;
HeapHashSet<Member<SVGUseElement>> elements;
use_elements_needing_update_.swap(elements);
for (SVGUseElement* element : elements)
element->BuildPendingResource();
}
StyleResolver* Document::GetStyleResolver() const {
return style_engine_->Resolver();
}
StyleResolver& Document::EnsureStyleResolver() const {
return style_engine_->EnsureResolver();
}
void Document::Initialize() {
DCHECK_EQ(lifecycle_.GetState(), DocumentLifecycle::kInactive);
DCHECK(!ax_object_cache_ || this != &AxObjectCacheOwner());
layout_view_ = new LayoutView(this);
SetLayoutObject(layout_view_);
layout_view_->SetIsInWindow(true);
layout_view_->SetStyle(StyleResolver::StyleForDocument(*this));
layout_view_->Compositor()->SetNeedsCompositingUpdate(
kCompositingUpdateAfterCompositingInputChange);
ContainerNode::AttachLayoutTree();
// The TextAutosizer can't update layout view info while the Document is
// detached, so update now in case anything changed.
if (TextAutosizer* autosizer = GetTextAutosizer())
autosizer->UpdatePageInfo();
frame_->DocumentAttached();
lifecycle_.AdvanceTo(DocumentLifecycle::kStyleClean);
if (View())
View()->DidAttachDocument();
// Observer(s) should not be initialized until the document is initialized /
// attached to a frame. Otherwise ContextLifecycleObserver::contextDestroyed
// wouldn't be fired.
network_state_observer_ = new NetworkStateObserver(*this);
}
void Document::Shutdown() {
TRACE_EVENT0("blink", "Document::shutdown");
CHECK(!frame_ || frame_->Tree().ChildCount() == 0);
if (!IsActive())
return;
// Frame navigation can cause a new Document to be attached. Don't allow that,
// since that will cause a situation where LocalFrame still has a Document
// attached after this finishes! Normally, it shouldn't actually be possible
// to trigger navigation here. However, plugins (see below) can cause lots of
// crazy things to happen, since plugin detach involves nested run loops.
FrameNavigationDisabler navigation_disabler(*frame_);
// Defer FrameViewBase updates to avoid plugins trying to run script inside
// ScriptForbiddenScope, which will crash the renderer after
// https://crrev.com/200984
HTMLFrameOwnerElement::UpdateSuspendScope
suspend_frame_view_base_hierarchy_updates;
// Don't allow script to run in the middle of detachLayoutTree() because a
// detaching Document is not in a consistent state.
ScriptForbiddenScope forbid_script;
lifecycle_.AdvanceTo(DocumentLifecycle::kStopping);
View()->Dispose();
// If the FrameViewBase of the document's frame owner doesn't match view()
// then FrameView::dispose() didn't clear the owner's FrameViewBase. If we
// don't clear it here, it may be clobbered later in LocalFrame::createView().
// See also https://crbug.com/673170 and the comment in FrameView::dispose().
HTMLFrameOwnerElement* owner_element = frame_->DeprecatedLocalOwner();
if (owner_element)
owner_element->SetWidget(nullptr);
markers_->PrepareForDestruction();
if (GetPage())
GetPage()->DocumentDetached(this);
probe::documentDetached(this);
if (frame_->Loader().Client()->GetSharedWorkerRepositoryClient())
frame_->Loader()
.Client()
->GetSharedWorkerRepositoryClient()
->DocumentDetached(this);
// FIXME: consider using SuspendableObject.
if (scripted_animation_controller_)
scripted_animation_controller_->ClearDocumentPointer();
scripted_animation_controller_.Clear();
scripted_idle_task_controller_.Clear();
if (SvgExtensions())
AccessSVGExtensions().PauseAnimations();
// FIXME: This shouldn't be needed once LocalDOMWindow becomes
// ExecutionContext.
if (dom_window_)
dom_window_->ClearEventQueue();
if (layout_view_)
layout_view_->SetIsInWindow(false);
if (RegistrationContext())
RegistrationContext()->DocumentWasDetached();
MutationObserver::CleanSlotChangeList(*this);
hover_element_ = nullptr;
active_hover_element_ = nullptr;
autofocus_element_ = nullptr;
if (focused_element_.Get()) {
Element* old_focused_element = focused_element_;
focused_element_ = nullptr;
if (GetPage())
GetPage()->GetChromeClient().FocusedNodeChanged(old_focused_element,
nullptr);
}
sequential_focus_navigation_starting_point_ = nullptr;
if (this == &AxObjectCacheOwner())
ClearAXObjectCache();
layout_view_ = nullptr;
ContainerNode::DetachLayoutTree();
if (this != &AxObjectCacheOwner()) {
if (AXObjectCache* cache = ExistingAXObjectCache()) {
// Documents that are not a root document use the AXObjectCache in
// their root document. Node::removedFrom is called after the
// document has been detached so it can't find the root document.
// We do the removals here instead.
for (Node& node : NodeTraversal::DescendantsOf(*this)) {
cache->Remove(&node);
}
}
}
GetStyleEngine().DidDetach();
GetPage()->GetEventHandlerRegistry().DocumentDetached(*this);
// Signal destruction to mutation observers.
SynchronousMutationNotifier::NotifyContextDestroyed();
// If this Document is associated with a live DocumentLoader, the
// DocumentLoader will take care of clearing the FetchContext. Deferring
// to the DocumentLoader when possible also prevents prematurely clearing
// the context in the case where multiple Documents end up associated with
// a single DocumentLoader (e.g., navigating to a javascript: url).
if (!Loader())
fetcher_->ClearContext();
// If this document is the master for an HTMLImportsController, sever that
// relationship. This ensures that we don't leave import loads in flight,
// thinking they should have access to a valid frame when they don't.
if (imports_controller_) {
imports_controller_->Dispose();
ClearImportsController();
}
timers_.SetTimerTaskRunner(
Platform::Current()->CurrentThread()->Scheduler()->TimerTaskRunner());
if (media_query_matcher_)
media_query_matcher_->DocumentDetached();
lifecycle_.AdvanceTo(DocumentLifecycle::kStopped);
// TODO(haraken): Call contextDestroyed() before we start any disruptive
// operations.
// TODO(haraken): Currently we call notifyContextDestroyed() only in
// Document::detachLayoutTree(), which means that we don't call
// notifyContextDestroyed() for a document that doesn't get detached.
// If such a document has any observer, the observer won't get
// a contextDestroyed() notification. This can happen for a document
// created by DOMImplementation::createDocument().
ExecutionContext::NotifyContextDestroyed();
// This is required, as our LocalFrame might delete itself as soon as it
// detaches us. However, this violates Node::detachLayoutTree() semantics, as
// it's never possible to re-attach. Eventually Document::detachLayoutTree()
// should be renamed, or this setting of the frame to 0 could be made
// explicit in each of the callers of Document::detachLayoutTree().
frame_ = nullptr;
}
void Document::RemoveAllEventListeners() {
ContainerNode::RemoveAllEventListeners();
if (LocalDOMWindow* dom_window = this->domWindow())
dom_window->RemoveAllEventListeners();
}
Document& Document::AxObjectCacheOwner() const {
// Every document has its own axObjectCache if accessibility is enabled,
// except for page popups, which share the axObjectCache of their owner.
Document* doc = const_cast<Document*>(this);
if (doc->GetFrame() && doc->GetFrame()->PagePopupOwner()) {
DCHECK(!doc->ax_object_cache_);
return doc->GetFrame()
->PagePopupOwner()
->GetDocument()
.AxObjectCacheOwner();
}
return *doc;
}
void Document::ClearAXObjectCache() {
DCHECK_EQ(&AxObjectCacheOwner(), this);
// Clear the cache member variable before calling delete because attempts
// are made to access it during destruction.
if (ax_object_cache_)
ax_object_cache_->Dispose();
ax_object_cache_.Clear();
}
AXObjectCache* Document::ExistingAXObjectCache() const {
// If the layoutObject is gone then we are in the process of destruction.
// This method will be called before m_frame = nullptr.
if (!AxObjectCacheOwner().GetLayoutView())
return 0;
return AxObjectCacheOwner().ax_object_cache_.Get();
}