#include "config.h"
#include "Document.h"
#include "AXObjectCache.h"
#include "AnimationController.h"
#include "Attr.h"
#include "CDATASection.h"
#include "CSSFontSelector.h"
#include "CSSStyleDeclaration.h"
#include "CSSStyleSheet.h"
#include "CachedCSSStyleSheet.h"
#include "CachedResourceLoader.h"
#include "Chrome.h"
#include "ChromeClient.h"
#include "Comment.h"
#include "ContentSecurityPolicy.h"
#include "CookieJar.h"
#include "DOMImplementation.h"
#include "DOMNamedFlowCollection.h"
#include "DOMWindow.h"
#include "DateComponents.h"
#include "DebugPageOverlays.h"
#include "Dictionary.h"
#include "DocumentLoader.h"
#include "DocumentMarkerController.h"
#include "DocumentSharedObjectPool.h"
#include "DocumentType.h"
#include "Editor.h"
#include "ElementIterator.h"
#include "EntityReference.h"
#include "EventFactory.h"
#include "EventHandler.h"
#include "FocusController.h"
#include "FontLoader.h"
#include "FormController.h"
#include "FrameLoader.h"
#include "FrameLoaderClient.h"
#include "FrameView.h"
#include "HTMLAllCollection.h"
#include "HTMLAnchorElement.h"
#include "HTMLBaseElement.h"
#include "HTMLBodyElement.h"
#include "HTMLCanvasElement.h"
#include "HTMLCollection.h"
#include "HTMLDocument.h"
#include "HTMLElementFactory.h"
#include "HTMLFormControlElement.h"
#include "HTMLFrameOwnerElement.h"
#include "HTMLFrameSetElement.h"
#include "HTMLHeadElement.h"
#include "HTMLIFrameElement.h"
#include "HTMLImageElement.h"
#include "HTMLLinkElement.h"
#include "HTMLMediaElement.h"
#include "HTMLNameCollection.h"
#include "HTMLParserIdioms.h"
#include "HTMLPictureElement.h"
#include "HTMLPlugInElement.h"
#include "HTMLScriptElement.h"
#include "HTMLStyleElement.h"
#include "HTMLTitleElement.h"
#include "HTTPHeaderNames.h"
#include "HTTPParsers.h"
#include "HashChangeEvent.h"
#include "History.h"
#include "HitTestResult.h"
#include "IconController.h"
#include "ImageLoader.h"
#include "InspectorInstrumentation.h"
#include "JSLazyEventListener.h"
#include "Language.h"
#include "LoaderStrategy.h"
#include "Logging.h"
#include "MainFrame.h"
#include "MediaCanStartListener.h"
#include "MediaProducer.h"
#include "MediaQueryList.h"
#include "MediaQueryMatcher.h"
#include "MouseEventWithHitTestResults.h"
#include "NameNodeList.h"
#include "NestingLevelIncrementer.h"
#include "NodeIterator.h"
#include "NodeRareData.h"
#include "NodeWithIndex.h"
#include "PageConsoleClient.h"
#include "PageGroup.h"
#include "PageTransitionEvent.h"
#include "PlatformLocale.h"
#include "PlatformMediaSessionManager.h"
#include "PlatformStrategies.h"
#include "PlugInsResources.h"
#include "PluginDocument.h"
#include "PointerLockController.h"
#include "PopStateEvent.h"
#include "ProcessingInstruction.h"
#include "RenderChildIterator.h"
#include "RenderLayerCompositor.h"
#include "RenderView.h"
#include "RenderWidget.h"
#include "ResourceLoadScheduler.h"
#include "ResourceLoader.h"
#include "RuntimeEnabledFeatures.h"
#include "SVGDocumentExtensions.h"
#include "SVGElement.h"
#include "SVGElementFactory.h"
#include "SVGNames.h"
#include "SchemeRegistry.h"
#include "ScopedEventQueue.h"
#include "ScriptController.h"
#include "ScriptRunner.h"
#include "ScriptSourceCode.h"
#include "ScrollingCoordinator.h"
#include "SecurityOrigin.h"
#include "SecurityOriginPolicy.h"
#include "SecurityPolicy.h"
#include "SegmentedString.h"
#include "SelectorQuery.h"
#include "Settings.h"
#include "ShadowRoot.h"
#include "StyleProperties.h"
#include "StyleResolver.h"
#include "StyleSheetContents.h"
#include "StyleSheetList.h"
#include "TextNodeTraversal.h"
#include "TextResourceDecoder.h"
#include "TransformSource.h"
#include "TreeWalker.h"
#include "VisitedLinkState.h"
#include "XMLDocumentParser.h"
#include "XMLNSNames.h"
#include "XMLNames.h"
#include "XPathEvaluator.h"
#include "XPathExpression.h"
#include "XPathNSResolver.h"
#include "XPathResult.h"
#include "htmlediting.h"
#include <JavaScriptCore/Profile.h>
#include <inspector/ScriptCallStack.h>
#include <wtf/CurrentTime.h>
#include <wtf/TemporaryChange.h>
#include <wtf/text/StringBuffer.h>
#include <yarr/RegularExpression.h>
#if ENABLE(XSLT)
#include "XSLTProcessor.h"
#endif
#if ENABLE(TOUCH_EVENTS)
#include "TouchList.h"
#endif
#if PLATFORM(IOS)
#include "CSSFontSelector.h"
#include "DeviceMotionClientIOS.h"
#include "DeviceMotionController.h"
#include "DeviceOrientationClientIOS.h"
#include "DeviceOrientationController.h"
#include "Geolocation.h"
#include "Navigator.h"
#include "NavigatorGeolocation.h"
#include "WKContentObservation.h"
#include "WebCoreSystemInterface.h"
#endif
#if ENABLE(IOS_GESTURE_EVENTS)
#include "GestureEvent.h"
#endif
#if ENABLE(MATHML)
#include "MathMLElement.h"
#include "MathMLElementFactory.h"
#include "MathMLNames.h"
#endif
#if ENABLE(FULLSCREEN_API)
#include "RenderFullScreen.h"
#endif
#if ENABLE(REQUEST_ANIMATION_FRAME)
#include "RequestAnimationFrameCallback.h"
#include "ScriptedAnimationController.h"
#endif
#if ENABLE(IOS_TEXT_AUTOSIZING)
#include "TextAutoSizing.h"
#endif
#if ENABLE(TEXT_AUTOSIZING)
#include "TextAutosizer.h"
#endif
#if ENABLE(CSP_NEXT)
#include "DOMSecurityPolicy.h"
#endif
#if ENABLE(VIDEO_TRACK)
#include "CaptionUserPreferences.h"
#endif
#if ENABLE(WEB_REPLAY)
#include "WebReplayInputs.h"
#include <replay/EmptyInputCursor.h>
#include <replay/InputCursor.h>
#endif
#if ENABLE(WIRELESS_PLAYBACK_TARGET)
#include "MediaPlaybackTargetClient.h"
#endif
#if ENABLE(MEDIA_SESSION)
#include "MediaSession.h"
#endif
using namespace WTF;
using namespace Unicode;
namespace WebCore {
using namespace HTMLNames;
static const unsigned cMaxWriteRecursionDepth = 21;
static inline bool isValidNameStart(UChar32 c)
{
if ((c >= 0x02BB && c <= 0x02C1) || c == 0x559 || c == 0x6E5 || c == 0x6E6)
return true;
if (c == ':' || c == '_')
return true;
if (!(U_GET_GC_MASK(c) & (U_GC_LL_MASK | U_GC_LU_MASK | U_GC_LO_MASK | U_GC_LT_MASK | U_GC_NL_MASK)))
return false;
if (c >= 0xF900 && c < 0xFFFE)
return false;
int type = u_getIntPropertyValue(c, UCHAR_DECOMPOSITION_TYPE);
if (type == U_DT_FONT || type == U_DT_COMPAT)
return false;
return true;
}
static inline bool isValidNamePart(UChar32 c)
{
if (isValidNameStart(c))
return true;
if (c == 0x00B7 || c == 0x0387)
return true;
if (c == '-' || c == '.')
return true;
if (!(U_GET_GC_MASK(c) & (U_GC_M_MASK | U_GC_LM_MASK | U_GC_ND_MASK)))
return false;
if (c >= 0xF900 && c < 0xFFFE)
return false;
int type = u_getIntPropertyValue(c, UCHAR_DECOMPOSITION_TYPE);
if (type == U_DT_FONT || type == U_DT_COMPAT)
return false;
return true;
}
static Widget* widgetForElement(Element* focusedElement)
{
if (!focusedElement)
return nullptr;
auto* renderer = focusedElement->renderer();
if (!is<RenderWidget>(renderer))
return nullptr;
return downcast<RenderWidget>(*renderer).widget();
}
static bool acceptsEditingFocus(Node* node)
{
ASSERT(node);
ASSERT(node->hasEditableStyle());
Node* root = node->rootEditableElement();
Frame* frame = node->document().frame();
if (!frame || !root)
return false;
return frame->editor().shouldBeginEditing(rangeOfContents(*root).ptr());
}
static bool canAccessAncestor(const SecurityOrigin* activeSecurityOrigin, Frame* targetFrame)
{
if (!targetFrame)
return false;
const bool isLocalActiveOrigin = activeSecurityOrigin->isLocal();
for (Frame* ancestorFrame = targetFrame; ancestorFrame; ancestorFrame = ancestorFrame->tree().parent()) {
Document* ancestorDocument = ancestorFrame->document();
if (!ancestorDocument)
return true;
const SecurityOrigin* ancestorSecurityOrigin = ancestorDocument->securityOrigin();
if (activeSecurityOrigin->canAccess(ancestorSecurityOrigin))
return true;
if (isLocalActiveOrigin && ancestorSecurityOrigin->isLocal())
return true;
}
return false;
}
static void printNavigationErrorMessage(Frame* frame, const URL& activeURL, const char* reason)
{
String message = "Unsafe JavaScript attempt to initiate navigation for frame with URL '" + frame->document()->url().string() + "' from frame with URL '" + activeURL.string() + "'. " + reason + "\n";
frame->document()->domWindow()->printErrorMessage(message);
}
uint64_t Document::s_globalTreeVersion = 0;
#if ENABLE(IOS_TEXT_AUTOSIZING)
void TextAutoSizingTraits::constructDeletedValue(TextAutoSizingKey& slot)
{
new (&slot) TextAutoSizingKey(TextAutoSizingKey::deletedKeyStyle(), TextAutoSizingKey::deletedKeyDoc());
}
bool TextAutoSizingTraits::isDeletedValue(const TextAutoSizingKey& value)
{
return value.style() == TextAutoSizingKey::deletedKeyStyle() && value.doc() == TextAutoSizingKey::deletedKeyDoc();
}
#endif
HashSet<Document*>& Document::allDocuments()
{
static NeverDestroyed<HashSet<Document*>> documents;
return documents;
}
Document::Document(Frame* frame, const URL& url, unsigned documentClasses, unsigned constructionFlags)
: ContainerNode(*this, CreateDocument)
, TreeScope(*this)
#if ENABLE(IOS_TOUCH_EVENTS)
, m_handlingTouchEvent(false)
, m_touchEventRegionsDirty(false)
, m_touchEventsChangedTimer(*this, &Document::touchEventsChangedTimerFired)
#endif
, m_referencingNodeCount(0)
, m_didCalculateStyleResolver(false)
, m_hasNodesWithPlaceholderStyle(false)
, m_needsNotifyRemoveAllPendingStylesheet(false)
, m_ignorePendingStylesheets(false)
, m_pendingSheetLayout(NoLayoutWithPendingSheets)
, m_frame(frame)
, m_cachedResourceLoader(m_frame ? Ref<CachedResourceLoader>(m_frame->loader().activeDocumentLoader()->cachedResourceLoader()) : CachedResourceLoader::create(nullptr))
, m_activeParserCount(0)
, m_wellFormed(false)
, m_printing(false)
, m_paginatedForScreen(false)
, m_compatibilityMode(DocumentCompatibilityMode::NoQuirksMode)
, m_compatibilityModeLocked(false)
, m_textColor(Color::black)
, m_domTreeVersion(++s_globalTreeVersion)
, m_listenerTypes(0)
, m_mutationObserverTypes(0)
, m_styleSheetCollection(*this)
, m_visitedLinkState(std::make_unique<VisitedLinkState>(*this))
, m_visuallyOrdered(false)
, m_readyState(Complete)
, m_bParsing(false)
, m_optimizedStyleSheetUpdateTimer(*this, &Document::optimizedStyleSheetUpdateTimerFired)
, m_styleRecalcTimer(*this, &Document::styleRecalcTimerFired)
, m_pendingStyleRecalcShouldForce(false)
, m_inStyleRecalc(false)
, m_closeAfterStyleRecalc(false)
, m_gotoAnchorNeededAfterStylesheetsLoad(false)
, m_frameElementsShouldIgnoreScrolling(false)
, m_updateFocusAppearanceRestoresSelection(false)
, m_ignoreDestructiveWriteCount(0)
, m_titleSetExplicitly(false)
, m_markers(std::make_unique<DocumentMarkerController>())
, m_updateFocusAppearanceTimer(*this, &Document::updateFocusAppearanceTimerFired)
, m_cssTarget(nullptr)
, m_processingLoadEvent(false)
, m_loadEventFinished(false)
, m_startTime(std::chrono::steady_clock::now())
, m_overMinimumLayoutThreshold(false)
, m_scriptRunner(std::make_unique<ScriptRunner>(*this))
, m_xmlVersion(ASCIILiteral("1.0"))
, m_xmlStandalone(StandaloneUnspecified)
, m_hasXMLDeclaration(false)
, m_designMode(inherit)
#if !ASSERT_DISABLED
, m_inInvalidateNodeListAndCollectionCaches(false)
#endif
#if ENABLE(DASHBOARD_SUPPORT)
, m_hasAnnotatedRegions(false)
, m_annotatedRegionsDirty(false)
#endif
, m_createRenderers(true)
, m_inPageCache(false)
, m_accessKeyMapValid(false)
, m_documentClasses(documentClasses)
, m_isSynthesized(constructionFlags & Synthesized)
, m_isNonRenderedPlaceholder(constructionFlags & NonRenderedPlaceholder)
, m_sawElementsInKnownNamespaces(false)
, m_isSrcdocDocument(false)
, m_eventQueue(*this)
, m_weakFactory(this)
#if ENABLE(FULLSCREEN_API)
, m_areKeysEnabledInFullScreen(0)
, m_fullScreenRenderer(nullptr)
, m_fullScreenChangeDelayTimer(*this, &Document::fullScreenChangeDelayTimerFired)
, m_isAnimatingFullScreen(false)
#endif
, m_loadEventDelayCount(0)
, m_loadEventDelayTimer(*this, &Document::loadEventDelayTimerFired)
, m_referrerPolicy(ReferrerPolicyDefault)
, m_directionSetOnDocumentElement(false)
, m_writingModeSetOnDocumentElement(false)
, m_writeRecursionIsTooDeep(false)
, m_writeRecursionDepth(0)
, m_lastHandledUserGestureTimestamp(0)
#if PLATFORM(IOS)
#if ENABLE(DEVICE_ORIENTATION)
, m_deviceMotionClient(std::make_unique<DeviceMotionClientIOS>())
, m_deviceMotionController(std::make_unique<DeviceMotionController>(m_deviceMotionClient.get()))
, m_deviceOrientationClient(std::make_unique<DeviceOrientationClientIOS>())
, m_deviceOrientationController(std::make_unique<DeviceOrientationController>(m_deviceOrientationClient.get()))
#endif
#endif
#if ENABLE(TELEPHONE_NUMBER_DETECTION)
, m_isTelephoneNumberParsingAllowed(true)
#endif
, m_pendingTasksTimer(*this, &Document::pendingTasksTimerFired)
, m_scheduledTasksAreSuspended(false)
, m_visualUpdatesAllowed(true)
, m_visualUpdatesSuppressionTimer(*this, &Document::visualUpdatesSuppressionTimerFired)
, m_sharedObjectPoolClearTimer(*this, &Document::sharedObjectPoolClearTimerFired)
#ifndef NDEBUG
, m_didDispatchViewportPropertiesChanged(false)
#endif
#if ENABLE(TEMPLATE_ELEMENT)
, m_templateDocumentHost(nullptr)
#endif
#if ENABLE(WEB_REPLAY)
, m_inputCursor(EmptyInputCursor::create())
#endif
, m_didAssociateFormControlsTimer(*this, &Document::didAssociateFormControlsTimerFired)
, m_cookieCacheExpiryTimer(*this, &Document::domCookieCacheExpiryTimerFired)
, m_disabledFieldsetElementsCount(0)
, m_hasInjectedPlugInsScript(false)
, m_renderTreeBeingDestroyed(false)
, m_hasPreparedForDestruction(false)
, m_hasStyleWithViewportUnits(false)
{
allDocuments().add(this);
if ((frame && frame->ownerElement()) || !url.isEmpty())
setURL(url);
m_cachedResourceLoader->setDocument(this);
#if ENABLE(TEXT_AUTOSIZING)
m_textAutosizer = std::make_unique<TextAutosizer>(this);
#endif
resetLinkColor();
resetVisitedLinkColor();
resetActiveLinkColor();
initSecurityContext();
initDNSPrefetch();
for (unsigned i = 0; i < WTF_ARRAY_LENGTH(m_nodeListAndCollectionCounts); ++i)
m_nodeListAndCollectionCounts[i] = 0;
}
#if ENABLE(FULLSCREEN_API)
static bool isAttributeOnAllOwners(const WebCore::QualifiedName& attribute, const WebCore::QualifiedName& prefixedAttribute, const HTMLFrameOwnerElement* owner)
{
if (!owner)
return true;
do {
if (!(owner->hasAttribute(attribute) || owner->hasAttribute(prefixedAttribute)))
return false;
} while ((owner = owner->document().ownerElement()));
return true;
}
#endif
Ref<Document> Document::create(ScriptExecutionContext& context)
{
Ref<Document> document = adoptRef(*new Document(nullptr, URL()));
document->setSecurityOriginPolicy(context.securityOriginPolicy());
return document;
}
Document::~Document()
{
allDocuments().remove(this);
ASSERT(!renderView());
ASSERT(!m_inPageCache);
ASSERT(m_ranges.isEmpty());
ASSERT(!m_parentTreeScope);
ASSERT(!m_disabledFieldsetElementsCount);
#if ENABLE(DEVICE_ORIENTATION) && PLATFORM(IOS)
m_deviceMotionClient->deviceMotionControllerDestroyed();
m_deviceOrientationClient->deviceOrientationControllerDestroyed();
#endif
#if ENABLE(TEMPLATE_ELEMENT)
if (m_templateDocument)
m_templateDocument->setTemplateDocumentHost(nullptr); #endif
if (m_domWindow)
m_domWindow->resetUnlessSuspendedForPageCache();
m_scriptRunner = nullptr;
removeAllEventListeners();
ASSERT(!m_parser || m_parser->refCount() == 1);
detachParser();
if (this == &topDocument())
clearAXObjectCache();
m_decoder = nullptr;
if (m_styleSheetList)
m_styleSheetList->detachFromDocument();
if (m_elementSheet)
m_elementSheet->detachFromDocument();
m_styleSheetCollection.detachFromDocument();
clearStyleResolver();
if (m_cachedResourceLoader->document() == this)
m_cachedResourceLoader->setDocument(nullptr);
if (auto* platformMediaSessionManager = PlatformMediaSessionManager::sharedManagerIfExists())
platformMediaSessionManager->stopAllMediaPlaybackForDocument(this);
if (hasRareData())
clearRareData();
ASSERT(!m_listsInvalidatedAtDocument.size());
ASSERT(!m_collectionsInvalidatedAtDocument.size());
for (unsigned i = 0; i < WTF_ARRAY_LENGTH(m_nodeListAndCollectionCounts); ++i)
ASSERT(!m_nodeListAndCollectionCounts[i]);
}
void Document::removedLastRef()
{
ASSERT(!m_deletionHasBegun);
if (m_referencingNodeCount) {
incrementReferencingNodeCount();
m_focusedElement = nullptr;
m_hoveredElement = nullptr;
m_activeElement = nullptr;
m_titleElement = nullptr;
m_documentElement = nullptr;
m_userActionElements.documentDidRemoveLastRef();
#if ENABLE(FULLSCREEN_API)
m_fullScreenElement = nullptr;
m_fullScreenElementStack.clear();
#endif
detachParser();
destroyTreeScopeData();
removeDetachedChildren();
m_formController = nullptr;
m_markers->detach();
m_cssCanvasElements.clear();
commonTeardown();
#ifndef NDEBUG
m_inRemovedLastRefFunction = false;
#endif
decrementReferencingNodeCount();
} else {
#ifndef NDEBUG
m_inRemovedLastRefFunction = false;
m_deletionHasBegun = true;
#endif
delete this;
}
}
void Document::commonTeardown()
{
if (svgExtensions())
accessSVGExtensions().pauseAnimations();
#if ENABLE(REQUEST_ANIMATION_FRAME)
clearScriptedAnimationController();
#endif
}
Element* Document::getElementByAccessKey(const String& key)
{
if (key.isEmpty())
return nullptr;
if (!m_accessKeyMapValid) {
buildAccessKeyMap(this);
m_accessKeyMapValid = true;
}
return m_elementsByAccessKey.get(key.impl());
}
void Document::buildAccessKeyMap(TreeScope* scope)
{
ASSERT(scope);
for (auto& element : descendantsOfType<Element>(scope->rootNode())) {
const AtomicString& accessKey = element.fastGetAttribute(accesskeyAttr);
if (!accessKey.isEmpty())
m_elementsByAccessKey.set(accessKey.impl(), &element);
if (ShadowRoot* root = element.shadowRoot())
buildAccessKeyMap(root);
}
}
void Document::invalidateAccessKeyMap()
{
m_accessKeyMapValid = false;
m_elementsByAccessKey.clear();
}
void Document::addImageElementByLowercasedUsemap(const AtomicStringImpl& name, HTMLImageElement& element)
{
return m_imagesByUsemap.add(name, element, *this);
}
void Document::removeImageElementByLowercasedUsemap(const AtomicStringImpl& name, HTMLImageElement& element)
{
return m_imagesByUsemap.remove(name, element);
}
HTMLImageElement* Document::imageElementByLowercasedUsemap(const AtomicStringImpl& name) const
{
return m_imagesByUsemap.getElementByLowercasedUsemap(name, *this);
}
SelectorQuery* Document::selectorQueryForString(const String& selectorString, ExceptionCode& ec)
{
if (selectorString.isEmpty()) {
ec = SYNTAX_ERR;
return nullptr;
}
if (!m_selectorQueryCache)
m_selectorQueryCache = std::make_unique<SelectorQueryCache>();
return m_selectorQueryCache->add(selectorString, *this, ec);
}
void Document::clearSelectorQueryCache()
{
m_selectorQueryCache = nullptr;
}
MediaQueryMatcher& Document::mediaQueryMatcher()
{
if (!m_mediaQueryMatcher)
m_mediaQueryMatcher = MediaQueryMatcher::create(this);
return *m_mediaQueryMatcher;
}
void Document::setCompatibilityMode(DocumentCompatibilityMode mode)
{
if (m_compatibilityModeLocked || mode == m_compatibilityMode)
return;
bool wasInQuirksMode = inQuirksMode();
m_compatibilityMode = mode;
clearSelectorQueryCache();
if (inQuirksMode() != wasInQuirksMode) {
m_styleSheetCollection.clearPageUserSheet();
m_styleSheetCollection.invalidateInjectedStyleSheetCache();
}
}
String Document::compatMode() const
{
return inQuirksMode() ? "BackCompat" : "CSS1Compat";
}
void Document::resetLinkColor()
{
m_linkColor = Color(0, 0, 238);
}
void Document::resetVisitedLinkColor()
{
m_visitedLinkColor = Color(85, 26, 139);
}
void Document::resetActiveLinkColor()
{
m_activeLinkColor.setNamedColor("red");
}
DOMImplementation& Document::implementation()
{
if (!m_implementation)
m_implementation = std::make_unique<DOMImplementation>(*this);
return *m_implementation;
}
bool Document::hasManifest() const
{
return documentElement() && documentElement()->hasTagName(htmlTag) && documentElement()->fastHasAttribute(manifestAttr);
}
DocumentType* Document::doctype() const
{
for (Node* node = firstChild(); node; node = node->nextSibling()) {
if (node->isDocumentTypeNode())
return static_cast<DocumentType*>(node);
}
return nullptr;
}
void Document::childrenChanged(const ChildChange& change)
{
ContainerNode::childrenChanged(change);
#if PLATFORM(IOS)
if (Page* page = this->page())
page->chrome().didReceiveDocType(frame());
#endif
Element* newDocumentElement = childrenOfType<Element>(*this).first();
if (newDocumentElement == m_documentElement)
return;
m_documentElement = newDocumentElement;
clearStyleResolver();
}
RefPtr<Element> Document::createElement(const AtomicString& name, ExceptionCode& ec)
{
if (!isValidName(name)) {
ec = INVALID_CHARACTER_ERR;
return nullptr;
}
if (isXHTMLDocument())
return HTMLElementFactory::createElement(QualifiedName(nullAtom, name, xhtmlNamespaceURI), *this);
return createElement(QualifiedName(nullAtom, name, nullAtom), false);
}
Ref<DocumentFragment> Document::createDocumentFragment()
{
return DocumentFragment::create(document());
}
Ref<Text> Document::createTextNode(const String& data)
{
return Text::create(*this, data);
}
Ref<Comment> Document::createComment(const String& data)
{
return Comment::create(*this, data);
}
RefPtr<CDATASection> Document::createCDATASection(const String& data, ExceptionCode& ec)
{
if (isHTMLDocument()) {
ec = NOT_SUPPORTED_ERR;
return nullptr;
}
return CDATASection::create(*this, data);
}
RefPtr<ProcessingInstruction> Document::createProcessingInstruction(const String& target, const String& data, ExceptionCode& ec)
{
if (!isValidName(target)) {
ec = INVALID_CHARACTER_ERR;
return nullptr;
}
if (isHTMLDocument()) {
ec = NOT_SUPPORTED_ERR;
return nullptr;
}
return ProcessingInstruction::create(*this, target, data);
}
RefPtr<EntityReference> Document::createEntityReference(const String& name, ExceptionCode& ec)
{
if (!isValidName(name)) {
ec = INVALID_CHARACTER_ERR;
return nullptr;
}
if (isHTMLDocument()) {
ec = NOT_SUPPORTED_ERR;
return nullptr;
}
return EntityReference::create(*this, name);
}
Ref<Text> Document::createEditingTextNode(const String& text)
{
return Text::createEditingText(*this, text);
}
Ref<CSSStyleDeclaration> Document::createCSSStyleDeclaration()
{
Ref<MutableStyleProperties> propertySet(MutableStyleProperties::create());
return *propertySet->ensureCSSStyleDeclaration();
}
RefPtr<Node> Document::importNode(Node* importedNode, bool deep, ExceptionCode& ec)
{
if (!importedNode) {
ec = NOT_SUPPORTED_ERR;
return nullptr;
}
switch (importedNode->nodeType()) {
case ELEMENT_NODE:
case TEXT_NODE:
case CDATA_SECTION_NODE:
case ENTITY_REFERENCE_NODE:
case PROCESSING_INSTRUCTION_NODE:
case COMMENT_NODE:
case DOCUMENT_FRAGMENT_NODE:
return importedNode->cloneNodeInternal(document(), deep ? CloningOperation::Everything : CloningOperation::OnlySelf);
case ATTRIBUTE_NODE:
return Attr::create(*this, QualifiedName(nullAtom, downcast<Attr>(*importedNode).name(), nullAtom), downcast<Attr>(*importedNode).value());
case DOCUMENT_NODE: case DOCUMENT_TYPE_NODE: break;
case ENTITY_NODE:
case XPATH_NAMESPACE_NODE:
ASSERT_NOT_REACHED(); break;
}
ec = NOT_SUPPORTED_ERR;
return nullptr;
}
RefPtr<Node> Document::adoptNode(PassRefPtr<Node> source, ExceptionCode& ec)
{
if (!source) {
ec = NOT_SUPPORTED_ERR;
return nullptr;
}
if (source->isReadOnlyNode()) {
ec = NO_MODIFICATION_ALLOWED_ERR;
return nullptr;
}
EventQueueScope scope;
switch (source->nodeType()) {
case ENTITY_NODE:
case DOCUMENT_NODE:
case DOCUMENT_TYPE_NODE:
case XPATH_NAMESPACE_NODE:
ec = NOT_SUPPORTED_ERR;
return nullptr;
case ATTRIBUTE_NODE: {
Attr& attr = downcast<Attr>(*source);
if (attr.ownerElement())
attr.ownerElement()->removeAttributeNode(&attr, ec);
break;
}
default:
if (source->isShadowRoot()) {
ec = HIERARCHY_REQUEST_ERR;
return nullptr;
}
if (is<HTMLFrameOwnerElement>(*source)) {
HTMLFrameOwnerElement& frameOwnerElement = downcast<HTMLFrameOwnerElement>(*source);
if (frame() && frame()->tree().isDescendantOf(frameOwnerElement.contentFrame())) {
ec = HIERARCHY_REQUEST_ERR;
return nullptr;
}
}
if (source->parentNode()) {
source->parentNode()->removeChild(source.get(), ec);
if (ec)
return nullptr;
}
}
adoptIfNeeded(source.get());
return source;
}
bool Document::hasValidNamespaceForElements(const QualifiedName& qName)
{
if (!qName.prefix().isEmpty() && qName.namespaceURI().isNull()) return false;
if (qName.prefix() == xmlAtom && qName.namespaceURI() != XMLNames::xmlNamespaceURI) return false;
if (qName.prefix() == xmlnsAtom || (qName.prefix().isEmpty() && qName.localName() == xmlnsAtom))
return qName.namespaceURI() == XMLNSNames::xmlnsNamespaceURI;
return qName.namespaceURI() != XMLNSNames::xmlnsNamespaceURI;
}
bool Document::hasValidNamespaceForAttributes(const QualifiedName& qName)
{
return hasValidNamespaceForElements(qName);
}
Ref<Element> Document::createElement(const QualifiedName& name, bool createdByParser)
{
RefPtr<Element> element;
if (name.namespaceURI() == xhtmlNamespaceURI)
element = HTMLElementFactory::createElement(name, *this, nullptr, createdByParser);
else if (name.namespaceURI() == SVGNames::svgNamespaceURI)
element = SVGElementFactory::createElement(name, *this, createdByParser);
#if ENABLE(MATHML)
else if (name.namespaceURI() == MathMLNames::mathmlNamespaceURI)
element = MathMLElementFactory::createElement(name, *this, createdByParser);
#endif
if (element)
m_sawElementsInKnownNamespaces = true;
else
element = Element::create(name, document());
ASSERT((name.matches(imageTag) && element->tagQName().matches(imgTag) && element->tagQName().prefix() == name.prefix()) || name == element->tagQName());
return element.releaseNonNull();
}
bool Document::cssRegionsEnabled() const
{
return RuntimeEnabledFeatures::sharedFeatures().cssRegionsEnabled();
}
bool Document::cssCompositingEnabled() const
{
return RuntimeEnabledFeatures::sharedFeatures().cssCompositingEnabled();
}
#if ENABLE(CSS_REGIONS)
RefPtr<DOMNamedFlowCollection> Document::webkitGetNamedFlows()
{
if (!cssRegionsEnabled() || !renderView())
return nullptr;
updateStyleIfNeeded();
return namedFlows().createCSSOMSnapshot();
}
#endif
NamedFlowCollection& Document::namedFlows()
{
if (!m_namedFlows)
m_namedFlows = NamedFlowCollection::create(this);
return *m_namedFlows;
}
RefPtr<Element> Document::createElementNS(const String& namespaceURI, const String& qualifiedName, ExceptionCode& ec)
{
String prefix, localName;
if (!parseQualifiedName(qualifiedName, prefix, localName, ec))
return nullptr;
QualifiedName qName(prefix, localName, namespaceURI);
if (!hasValidNamespaceForElements(qName)) {
ec = NAMESPACE_ERR;
return nullptr;
}
return createElement(qName, false);
}
String Document::readyState() const
{
DEPRECATED_DEFINE_STATIC_LOCAL(const String, loading, (ASCIILiteral("loading")));
DEPRECATED_DEFINE_STATIC_LOCAL(const String, interactive, (ASCIILiteral("interactive")));
DEPRECATED_DEFINE_STATIC_LOCAL(const String, complete, (ASCIILiteral("complete")));
switch (m_readyState) {
case Loading:
return loading;
case Interactive:
return interactive;
case Complete:
return complete;
}
ASSERT_NOT_REACHED();
return String();
}
void Document::setReadyState(ReadyState readyState)
{
if (readyState == m_readyState)
return;
#if ENABLE(WEB_TIMING)
switch (readyState) {
case Loading:
if (!m_documentTiming.domLoading)
m_documentTiming.domLoading = monotonicallyIncreasingTime();
break;
case Interactive:
if (!m_documentTiming.domInteractive)
m_documentTiming.domInteractive = monotonicallyIncreasingTime();
break;
case Complete:
if (!m_documentTiming.domComplete)
m_documentTiming.domComplete = monotonicallyIncreasingTime();
break;
}
#endif
m_readyState = readyState;
dispatchEvent(Event::create(eventNames().readystatechangeEvent, false, false));
if (settings() && settings()->suppressesIncrementalRendering())
setVisualUpdatesAllowed(readyState);
}
void Document::setVisualUpdatesAllowed(ReadyState readyState)
{
ASSERT(settings() && settings()->suppressesIncrementalRendering());
switch (readyState) {
case Loading:
ASSERT(!m_visualUpdatesSuppressionTimer.isActive());
ASSERT(m_visualUpdatesAllowed);
setVisualUpdatesAllowed(false);
break;
case Interactive:
ASSERT(m_visualUpdatesSuppressionTimer.isActive() || m_visualUpdatesAllowed);
break;
case Complete:
if (m_visualUpdatesSuppressionTimer.isActive()) {
ASSERT(!m_visualUpdatesAllowed);
if (!view()->visualUpdatesAllowedByClient())
return;
setVisualUpdatesAllowed(true);
} else
ASSERT(m_visualUpdatesAllowed);
break;
}
}
void Document::setVisualUpdatesAllowed(bool visualUpdatesAllowed)
{
if (m_visualUpdatesAllowed == visualUpdatesAllowed)
return;
m_visualUpdatesAllowed = visualUpdatesAllowed;
if (visualUpdatesAllowed)
m_visualUpdatesSuppressionTimer.stop();
else
m_visualUpdatesSuppressionTimer.startOneShot(settings()->incrementalRenderingSuppressionTimeoutInSeconds());
if (!visualUpdatesAllowed)
return;
FrameView* frameView = view();
bool needsLayout = frameView && renderView() && (frameView->layoutPending() || renderView()->needsLayout());
if (needsLayout)
updateLayout();
if (Page* page = this->page()) {
if (frame()->isMainFrame()) {
frameView->addPaintPendingMilestones(DidFirstPaintAfterSuppressedIncrementalRendering);
if (page->requestedLayoutMilestones() & DidFirstLayoutAfterSuppressedIncrementalRendering)
frame()->loader().didLayout(DidFirstLayoutAfterSuppressedIncrementalRendering);
}
}
if (view())
view()->updateCompositingLayersAfterLayout();
if (RenderView* renderView = this->renderView())
renderView->repaintViewAndCompositedLayers();
if (Frame* frame = this->frame())
frame->loader().forcePageTransitionIfNeeded();
}
void Document::visualUpdatesSuppressionTimerFired()
{
ASSERT(!m_visualUpdatesAllowed);
if (!view()->visualUpdatesAllowedByClient())
return;
setVisualUpdatesAllowed(true);
}
void Document::setVisualUpdatesAllowedByClient(bool visualUpdatesAllowedByClient)
{
if (visualUpdatesAllowedByClient && !m_visualUpdatesSuppressionTimer.isActive() && !visualUpdatesAllowed())
setVisualUpdatesAllowed(true);
}
AtomicString Document::encoding() const
{
if (TextResourceDecoder* d = decoder())
return d->encoding().domName();
return String();
}
String Document::defaultCharset() const
{
if (Settings* settings = this->settings())
return settings->defaultTextEncodingName();
return String();
}
void Document::setCharset(const String& charset)
{
if (!decoder())
return;
decoder()->setEncoding(charset, TextResourceDecoder::UserChosenEncoding);
}
void Document::setContentLanguage(const String& language)
{
if (m_contentLanguage == language)
return;
m_contentLanguage = language;
styleResolverChanged(DeferRecalcStyle);
}
void Document::setXMLVersion(const String& version, ExceptionCode& ec)
{
if (!implementation().hasFeature("XML", String())) {
ec = NOT_SUPPORTED_ERR;
return;
}
if (!XMLDocumentParser::supportsXMLVersion(version)) {
ec = NOT_SUPPORTED_ERR;
return;
}
m_xmlVersion = version;
}
void Document::setXMLStandalone(bool standalone, ExceptionCode& ec)
{
if (!implementation().hasFeature("XML", String())) {
ec = NOT_SUPPORTED_ERR;
return;
}
m_xmlStandalone = standalone ? Standalone : NotStandalone;
}
void Document::setDocumentURI(const String& uri)
{
m_documentURI = uri;
updateBaseURL();
}
URL Document::baseURI() const
{
return m_baseURL;
}
void Document::setContent(const String& content)
{
open();
m_parser->append(content.impl());
close();
}
String Document::suggestedMIMEType() const
{
if (isXHTMLDocument())
return ASCIILiteral("application/xhtml+xml");
if (isSVGDocument())
return ASCIILiteral("image/svg+xml");
if (xmlStandalone())
return ASCIILiteral("text/xml");
if (isHTMLDocument())
return ASCIILiteral("text/html");
if (DocumentLoader* loader = this->loader())
return loader->responseMIMEType();
return String();
}
void Document::overrideMIMEType(const String& mimeType)
{
m_overriddenMIMEType = mimeType;
}
String Document::contentType() const
{
if (!m_overriddenMIMEType.isNull())
return m_overriddenMIMEType;
if (DocumentLoader* documentLoader = loader())
return documentLoader->currentContentType();
String mimeType = suggestedMIMEType();
if (!mimeType.isNull())
return mimeType;
return ASCIILiteral("application/xml");
}
Node* Document::nodeFromPoint(const LayoutPoint& clientPoint, LayoutPoint* localPoint)
{
if (!frame() || !view())
return nullptr;
Frame& frame = *this->frame();
float scaleFactor = frame.pageZoomFactor() * frame.frameScaleFactor();
LayoutPoint contentsPoint = clientPoint;
contentsPoint.scale(scaleFactor, scaleFactor);
contentsPoint.moveBy(view()->contentsScrollPosition());
LayoutRect visibleRect;
#if PLATFORM(IOS)
visibleRect = view()->unobscuredContentRect();
#else
visibleRect = view()->visibleContentRect();
#endif
if (!visibleRect.contains(contentsPoint))
return nullptr;
HitTestResult result(contentsPoint);
renderView()->hitTest(HitTestRequest(), result);
if (localPoint)
*localPoint = result.localPoint();
return result.innerNode();
}
Element* Document::elementFromPoint(const LayoutPoint& clientPoint)
{
if (!hasLivingRenderTree())
return nullptr;
Node* node = nodeFromPoint(clientPoint);
while (node && !is<Element>(*node))
node = node->parentNode();
if (node)
node = ancestorInThisScope(node);
return downcast<Element>(node);
}
RefPtr<Range> Document::caretRangeFromPoint(int x, int y)
{
return caretRangeFromPoint(LayoutPoint(x, y));
}
RefPtr<Range> Document::caretRangeFromPoint(const LayoutPoint& clientPoint)
{
if (!hasLivingRenderTree())
return nullptr;
LayoutPoint localPoint;
Node* node = nodeFromPoint(clientPoint, &localPoint);
if (!node)
return nullptr;
Node* shadowAncestorNode = ancestorInThisScope(node);
if (shadowAncestorNode != node) {
unsigned offset = shadowAncestorNode->computeNodeIndex();
ContainerNode* container = shadowAncestorNode->parentNode();
return Range::create(*this, container, offset, container, offset);
}
RenderObject* renderer = node->renderer();
if (!renderer)
return nullptr;
VisiblePosition visiblePosition = renderer->positionForPoint(localPoint, nullptr);
if (visiblePosition.isNull())
return nullptr;
Position rangeCompliantPosition = visiblePosition.deepEquivalent().parentAnchoredEquivalent();
return Range::create(*this, rangeCompliantPosition, rangeCompliantPosition);
}
Element* Document::scrollingElement()
{
return body();
}
template <typename CharacterType>
static inline StringWithDirection canonicalizedTitle(Document* document, const StringWithDirection& titleWithDirection)
{
const String& title = titleWithDirection.string();
const CharacterType* characters = title.characters<CharacterType>();
unsigned length = title.length();
unsigned i;
StringBuffer<CharacterType> buffer(length);
unsigned builderIndex = 0;
for (i = 0; i < length; ++i) {
CharacterType c = characters[i];
if (!(c <= 0x20 || c == 0x7F))
break;
}
if (i == length)
return StringWithDirection();
bool previousCharWasWS = false;
for (; i < length; ++i) {
CharacterType c = characters[i];
if (c <= 0x20 || c == 0x7F || (U_GET_GC_MASK(c) & (U_GC_ZL_MASK | U_GC_ZP_MASK))) {
if (previousCharWasWS)
continue;
buffer[builderIndex++] = ' ';
previousCharWasWS = true;
} else {
buffer[builderIndex++] = c;
previousCharWasWS = false;
}
}
while (builderIndex > 0) {
--builderIndex;
if (buffer[builderIndex] != ' ')
break;
}
if (!builderIndex && buffer[builderIndex] == ' ')
return StringWithDirection();
buffer.shrink(builderIndex + 1);
document->displayBufferModifiedByEncoding(buffer.characters(), buffer.length());
return StringWithDirection(String::adopt(buffer), titleWithDirection.direction());
}
void Document::updateTitle(const StringWithDirection& title)
{
if (m_rawTitle == title)
return;
m_rawTitle = title;
if (m_rawTitle.string().isEmpty())
m_title = StringWithDirection();
else {
if (m_rawTitle.string().is8Bit())
m_title = canonicalizedTitle<LChar>(this, m_rawTitle);
else
m_title = canonicalizedTitle<UChar>(this, m_rawTitle);
}
if (DocumentLoader* loader = this->loader())
loader->setTitle(m_title);
}
void Document::setTitle(const String& title)
{
m_titleSetExplicitly = true;
if (!isHTMLDocument() && !isXHTMLDocument())
m_titleElement = nullptr;
else if (!m_titleElement) {
if (HTMLElement* headElement = head()) {
m_titleElement = createElement(titleTag, false);
headElement->appendChild(m_titleElement, ASSERT_NO_EXCEPTION);
}
}
updateTitle(StringWithDirection(title, LTR));
if (is<HTMLTitleElement>(m_titleElement.get()))
downcast<HTMLTitleElement>(*m_titleElement).setText(title);
}
void Document::setTitleElement(const StringWithDirection& title, Element* titleElement)
{
if (titleElement != m_titleElement) {
if (m_titleElement || m_titleSetExplicitly) {
return;
}
m_titleElement = titleElement;
}
updateTitle(title);
}
void Document::removeTitle(Element* titleElement)
{
if (m_titleElement != titleElement)
return;
m_titleElement = nullptr;
m_titleSetExplicitly = false;
if (HTMLElement* headElement = head()) {
if (auto firstTitle = childrenOfType<HTMLTitleElement>(*headElement).first())
setTitleElement(firstTitle->textWithDirection(), firstTitle);
}
if (!m_titleElement)
updateTitle(StringWithDirection());
}
void Document::registerForVisibilityStateChangedCallbacks(Element* element)
{
m_visibilityStateCallbackElements.add(element);
}
void Document::unregisterForVisibilityStateChangedCallbacks(Element* element)
{
m_visibilityStateCallbackElements.remove(element);
}
void Document::visibilityStateChanged()
{
dispatchEvent(Event::create(eventNames().visibilitychangeEvent, false, false));
for (auto* element : m_visibilityStateCallbackElements)
element->visibilityStateChanged();
}
PageVisibilityState Document::pageVisibilityState() const
{
if (!m_frame || !m_frame->page())
return PageVisibilityStateHidden;
return m_frame->page()->visibilityState();
}
String Document::visibilityState() const
{
return pageVisibilityStateString(pageVisibilityState());
}
bool Document::hidden() const
{
return pageVisibilityState() != PageVisibilityStateVisible;
}
#if ENABLE(VIDEO)
void Document::registerForAllowsMediaDocumentInlinePlaybackChangedCallbacks(HTMLMediaElement& element)
{
m_allowsMediaDocumentInlinePlaybackElements.add(&element);
}
void Document::unregisterForAllowsMediaDocumentInlinePlaybackChangedCallbacks(HTMLMediaElement& element)
{
m_allowsMediaDocumentInlinePlaybackElements.remove(&element);
}
void Document::allowsMediaDocumentInlinePlaybackChanged()
{
for (auto* element : m_allowsMediaDocumentInlinePlaybackElements)
element->allowsMediaDocumentInlinePlaybackChanged();
}
#endif
#if ENABLE(CSP_NEXT)
DOMSecurityPolicy& Document::securityPolicy()
{
if (!m_domSecurityPolicy)
m_domSecurityPolicy = DOMSecurityPolicy::create(this);
return *m_domSecurityPolicy;
}
#endif
String Document::nodeName() const
{
return "#document";
}
Node::NodeType Document::nodeType() const
{
return DOCUMENT_NODE;
}
FormController& Document::formController()
{
if (!m_formController)
m_formController = std::make_unique<FormController>();
return *m_formController;
}
Vector<String> Document::formElementsState() const
{
if (!m_formController)
return Vector<String>();
return m_formController->formElementsState();
}
void Document::setStateForNewFormElements(const Vector<String>& stateVector)
{
if (!stateVector.size() && !m_formController)
return;
formController().setStateForNewFormElements(stateVector);
}
FrameView* Document::view() const
{
return m_frame ? m_frame->view() : nullptr;
}
Page* Document::page() const
{
return m_frame ? m_frame->page() : nullptr;
}
Settings* Document::settings() const
{
return m_frame ? &m_frame->settings() : nullptr;
}
Ref<Range> Document::createRange()
{
return Range::create(*this);
}
RefPtr<NodeIterator> Document::createNodeIterator(Node* root, unsigned whatToShow,
PassRefPtr<NodeFilter> filter, bool expandEntityReferences, ExceptionCode& ec)
{
if (!root) {
ec = NOT_SUPPORTED_ERR;
return nullptr;
}
return NodeIterator::create(root, whatToShow, filter, expandEntityReferences);
}
RefPtr<TreeWalker> Document::createTreeWalker(Node* root, unsigned whatToShow,
PassRefPtr<NodeFilter> filter, bool expandEntityReferences, ExceptionCode& ec)
{
if (!root) {
ec = NOT_SUPPORTED_ERR;
return nullptr;
}
return TreeWalker::create(root, whatToShow, filter, expandEntityReferences);
}
void Document::scheduleForcedStyleRecalc()
{
m_pendingStyleRecalcShouldForce = true;
scheduleStyleRecalc();
}
void Document::scheduleStyleRecalc()
{
if (m_styleRecalcTimer.isActive() || inPageCache())
return;
ASSERT(childNeedsStyleRecalc() || m_pendingStyleRecalcShouldForce);
invalidateAccessKeyMap();
m_styleRecalcTimer.startOneShot(0);
InspectorInstrumentation::didScheduleStyleRecalculation(*this);
}
void Document::unscheduleStyleRecalc()
{
ASSERT(!childNeedsStyleRecalc());
m_styleRecalcTimer.stop();
m_pendingStyleRecalcShouldForce = false;
}
bool Document::hasPendingStyleRecalc() const
{
return m_styleRecalcTimer.isActive() && !m_inStyleRecalc;
}
bool Document::hasPendingForcedStyleRecalc() const
{
return m_styleRecalcTimer.isActive() && m_pendingStyleRecalcShouldForce;
}
void Document::styleRecalcTimerFired()
{
updateStyleIfNeeded();
}
void Document::recalcStyle(Style::Change change)
{
ASSERT(!view() || !view()->isPainting());
if (!m_renderView)
return;
FrameView& frameView = m_renderView->frameView();
Ref<FrameView> protect(frameView);
if (frameView.isPainting())
return;
if (m_inStyleRecalc)
return;
RenderView::RepaintRegionAccumulator repaintRegionAccumulator(renderView());
AnimationUpdateBlock animationUpdateBlock(&m_frame->animation());
m_styleSheetCollection.flushPendingUpdates();
frameView.willRecalcStyle();
InspectorInstrumentationCookie cookie = InspectorInstrumentation::willRecalculateStyle(*this);
if (m_elementSheet && m_elementSheet->contents().usesRemUnits())
m_styleSheetCollection.setUsesRemUnit(true);
m_inStyleRecalc = true;
bool updatedCompositingLayers = false;
{
Style::PostResolutionCallbackDisabler disabler(*this);
WidgetHierarchyUpdatesSuspensionScope suspendWidgetHierarchyUpdates;
if (m_pendingStyleRecalcShouldForce)
change = Style::Force;
if (change == Style::Force) {
m_hasNodesWithPlaceholderStyle = false;
}
Style::resolveTree(*this, change);
updatedCompositingLayers = frameView.updateCompositingLayersAfterStyleChange();
clearNeedsStyleRecalc();
clearChildNeedsStyleRecalc();
unscheduleStyleRecalc();
m_inStyleRecalc = false;
if (m_styleResolver)
m_styleSheetCollection.resetCSSFeatureFlags();
}
if (m_closeAfterStyleRecalc) {
m_closeAfterStyleRecalc = false;
implicitClose();
}
++m_styleRecalcCount;
InspectorInstrumentation::didRecalculateStyle(cookie);
if (updatedCompositingLayers && !frameView.needsLayout())
frameView.viewportContentsChanged();
if (!frameView.needsLayout())
frameView.frame().selection().updateAppearanceAfterLayout();
if (m_hoveredElement && !m_hoveredElement->renderer())
frameView.frame().mainFrame().eventHandler().dispatchFakeMouseMoveEventSoon();
}
void Document::updateStyleIfNeeded()
{
ASSERT(isMainThread());
ASSERT(!view() || !view()->isPainting());
if (!view() || view()->isInLayout())
return;
if (m_optimizedStyleSheetUpdateTimer.isActive())
styleResolverChanged(RecalcStyleIfNeeded);
if (!needsStyleRecalc())
return;
recalcStyle(Style::NoChange);
}
void Document::updateLayout()
{
ASSERT(isMainThread());
FrameView* frameView = view();
if (frameView && frameView->isInLayout()) {
ASSERT_NOT_REACHED();
return;
}
RenderView::RepaintRegionAccumulator repaintRegionAccumulator(renderView());
if (HTMLFrameOwnerElement* owner = ownerElement())
owner->document().updateLayout();
updateStyleIfNeeded();
StackStats::LayoutCheckPoint layoutCheckPoint;
if (frameView && renderView() && (frameView->layoutPending() || renderView()->needsLayout()))
frameView->layout();
}
void Document::updateLayoutIgnorePendingStylesheets(Document::RunPostLayoutTasks runPostLayoutTasks)
{
bool oldIgnore = m_ignorePendingStylesheets;
if (!haveStylesheetsLoaded()) {
m_ignorePendingStylesheets = true;
HTMLElement* bodyElement = bodyOrFrameset();
if (bodyElement && !bodyElement->renderer() && m_pendingSheetLayout == NoLayoutWithPendingSheets) {
m_pendingSheetLayout = DidLayoutWithPendingSheets;
styleResolverChanged(RecalcStyleImmediately);
} else if (m_hasNodesWithPlaceholderStyle)
recalcStyle(Style::Force);
}
updateLayout();
if (runPostLayoutTasks == RunPostLayoutTasks::Synchronously && view())
view()->flushAnyPendingPostLayoutTasks();
m_ignorePendingStylesheets = oldIgnore;
}
Ref<RenderStyle> Document::styleForElementIgnoringPendingStylesheets(Element* element)
{
ASSERT_ARG(element, &element->document() == this);
ResourceLoadScheduler::Suspender suspender(*platformStrategies()->loaderStrategy()->resourceLoadScheduler());
TemporaryChange<bool> change(m_ignorePendingStylesheets, true);
return ensureStyleResolver().styleForElement(element, element->parentNode() ? element->parentNode()->computedStyle() : nullptr);
}
bool Document::updateLayoutIfDimensionsOutOfDate(Element& element, DimensionsCheck dimensionsCheck)
{
ASSERT(isMainThread());
if (!haveStylesheetsLoaded()) {
updateLayoutIgnorePendingStylesheets();
return true;
}
FrameView* frameView = view();
if (frameView && frameView->isInLayout()) {
ASSERT_NOT_REACHED();
return true;
}
RenderView::RepaintRegionAccumulator repaintRegionAccumulator(renderView());
bool requireFullLayout = false;
if (HTMLFrameOwnerElement* owner = ownerElement()) {
if (owner->document().updateLayoutIfDimensionsOutOfDate(*owner))
requireFullLayout = true;
}
updateStyleIfNeeded();
RenderObject* renderer = element.renderer();
if (!renderer || renderer->needsLayout() || element.renderNamedFlowFragment()) {
requireFullLayout = true;
}
bool isVertical = renderer && !renderer->isHorizontalWritingMode();
bool checkingLogicalWidth = ((dimensionsCheck & WidthDimensionsCheck) && !isVertical) || ((dimensionsCheck & HeightDimensionsCheck) && isVertical);
bool checkingLogicalHeight = ((dimensionsCheck & HeightDimensionsCheck) && !isVertical) || ((dimensionsCheck & WidthDimensionsCheck) && isVertical);
bool hasSpecifiedLogicalHeight = renderer && renderer->style().logicalMinHeight() == Length(0, Fixed) && renderer->style().logicalHeight().isFixed() && renderer->style().logicalMaxHeight().isAuto();
if (!requireFullLayout) {
RenderBox* previousBox = nullptr;
RenderBox* currentBox = nullptr;
for (RenderObject* currRenderer = element.renderer(); currRenderer && !currRenderer->isRenderView(); currRenderer = currRenderer->container()) {
if (!is<RenderBox>(currRenderer)) {
requireFullLayout = true;
break;
}
previousBox = currentBox;
currentBox = downcast<RenderBox>(currRenderer);
if (currentBox->selfNeedsLayout() ||
(checkingLogicalWidth && currRenderer->needsLayout() && currentBox->sizesLogicalWidthToFitContent(MainOrPreferredSize))) {
requireFullLayout = true;
break;
}
if (checkingLogicalHeight && !hasSpecifiedLogicalHeight && currentBox->isRenderBlockFlow()) {
RenderBlockFlow* currentBlockFlow = downcast<RenderBlockFlow>(currentBox);
if (currentBlockFlow->containsFloats() && previousBox && !previousBox->isFloatingOrOutOfFlowPositioned()) {
requireFullLayout = true;
break;
}
}
if (!currentBox->isRenderBlockFlow() || currentBox->flowThreadContainingBlock() || currentBox->isWritingModeRoot()) {
requireFullLayout = true;
break;
}
if (currRenderer == frameView->layoutRoot())
break;
}
}
StackStats::LayoutCheckPoint layoutCheckPoint;
if (requireFullLayout && frameView && renderView() && (frameView->layoutPending() || renderView()->needsLayout()))
frameView->layout();
return requireFullLayout;
}
bool Document::isPageBoxVisible(int pageIndex)
{
Ref<RenderStyle> pageStyle(ensureStyleResolver().styleForPage(pageIndex));
return pageStyle->visibility() != HIDDEN; }
void Document::pageSizeAndMarginsInPixels(int pageIndex, IntSize& pageSize, int& marginTop, int& marginRight, int& marginBottom, int& marginLeft)
{
RefPtr<RenderStyle> style = ensureStyleResolver().styleForPage(pageIndex);
int width = pageSize.width();
int height = pageSize.height();
switch (style->pageSizeType()) {
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: {
LengthSize size = style->pageSize();
ASSERT(size.width().isFixed());
ASSERT(size.height().isFixed());
width = valueForLength(size.width(), 0);
height = valueForLength(size.height(), 0);
break;
}
default:
ASSERT_NOT_REACHED();
}
pageSize = IntSize(width, height);
marginTop = style->marginTop().isAuto() ? marginTop : intValueForLength(style->marginTop(), width);
marginRight = style->marginRight().isAuto() ? marginRight : intValueForLength(style->marginRight(), width);
marginBottom = style->marginBottom().isAuto() ? marginBottom : intValueForLength(style->marginBottom(), width);
marginLeft = style->marginLeft().isAuto() ? marginLeft : intValueForLength(style->marginLeft(), width);
}
void Document::createStyleResolver()
{
bool matchAuthorAndUserStyles = true;
if (Settings* settings = this->settings())
matchAuthorAndUserStyles = settings->authorAndUserStylesEnabled();
m_styleResolver = std::make_unique<StyleResolver>(*this, matchAuthorAndUserStyles);
m_styleSheetCollection.combineCSSFeatureFlags();
}
void Document::fontsNeedUpdate(FontSelector&)
{
if (m_styleResolver)
m_styleResolver->invalidateMatchedPropertiesCache();
if (inPageCache() || !renderView())
return;
scheduleForcedStyleRecalc();
}
CSSFontSelector& Document::fontSelector()
{
if (!m_fontSelector) {
m_fontSelector = CSSFontSelector::create(*this);
m_fontSelector->registerForInvalidationCallbacks(*this);
}
return *m_fontSelector;
}
void Document::clearStyleResolver()
{
m_styleResolver = nullptr;
if (m_fontSelector) {
m_fontSelector->clearDocument();
m_fontSelector->unregisterForInvalidationCallbacks(*this);
m_fontSelector = nullptr;
}
}
void Document::createRenderTree()
{
ASSERT(!renderView());
ASSERT(!m_inPageCache);
ASSERT(!m_axObjectCache || this != &topDocument());
if (m_isNonRenderedPlaceholder)
return;
m_renderView = createRenderer<RenderView>(*this, RenderStyle::create());
Node::setRenderer(m_renderView.get());
renderView()->setIsInWindow(true);
recalcStyle(Style::Force);
}
void Document::didBecomeCurrentDocumentInFrame()
{
m_frame->script().updateDocument();
if (!hasLivingRenderTree())
createRenderTree();
updateViewportArguments();
if (page() && m_frame->isMainFrame())
wheelEventHandlersChanged();
#if ENABLE(TOUCH_EVENTS)
if (hasTouchEventHandlers() && page() && m_frame->isMainFrame())
page()->chrome().client().needTouchEvents(true);
#endif
#if PLATFORM(IOS)
if (m_frame->timersPaused())
suspendScheduledTasks(ActiveDOMObject::DocumentWillBePaused);
else
resumeScheduledTasks(ActiveDOMObject::DocumentWillBePaused);
#endif
if (m_frame->activeDOMObjectsAndAnimationsSuspended()) {
suspendScriptedAnimationControllerCallbacks();
m_frame->animation().suspendAnimationsForDocument(this);
suspendActiveDOMObjects(ActiveDOMObject::PageWillBeSuspended);
}
}
void Document::disconnectFromFrame()
{
m_frame = nullptr;
}
void Document::destroyRenderTree()
{
ASSERT(hasLivingRenderTree());
ASSERT(!m_inPageCache);
TemporaryChange<bool> change(m_renderTreeBeingDestroyed, true);
if (this == &topDocument())
clearAXObjectCache();
documentWillBecomeInactive();
if (FrameView* frameView = view())
frameView->detachCustomScrollbars();
#if ENABLE(FULLSCREEN_API)
if (m_fullScreenRenderer)
setFullScreenRenderer(nullptr);
#endif
m_hoveredElement = nullptr;
m_focusedElement = nullptr;
m_activeElement = nullptr;
if (m_documentElement)
Style::detachRenderTree(*m_documentElement);
clearChildNeedsStyleRecalc();
unscheduleStyleRecalc();
m_renderView = nullptr;
Node::setRenderer(nullptr);
#if ENABLE(IOS_TEXT_AUTOSIZING)
m_textAutoSizedNodes.clear();
#endif
}
void Document::prepareForDestruction()
{
if (m_hasPreparedForDestruction)
return;
#if ENABLE(IOS_TOUCH_EVENTS)
clearTouchEventListeners();
#endif
#if HAVE(ACCESSIBILITY)
if (this != &topDocument()) {
if (AXObjectCache* cache = existingAXObjectCache())
cache->clearTextMarkerNodesInUse(this);
}
#endif
disconnectDescendantFrames();
if (m_domWindow && m_frame)
m_domWindow->willDetachDocumentFromFrame();
if (hasLivingRenderTree())
destroyRenderTree();
if (is<PluginDocument>(*this))
downcast<PluginDocument>(*this).detachFromPluginElement();
#if ENABLE(POINTER_LOCK)
if (page())
page()->pointerLockController().documentDetached(this);
#endif
InspectorInstrumentation::documentDetached(*this);
stopActiveDOMObjects();
m_eventQueue.close();
#if ENABLE(FULLSCREEN_API)
m_fullScreenChangeEventTargetQueue.clear();
m_fullScreenErrorEventTargetQueue.clear();
#endif
commonTeardown();
#if ENABLE(TOUCH_EVENTS)
if (m_touchEventTargets && m_touchEventTargets->size() && parentDocument())
parentDocument()->didRemoveEventTargetNode(*this);
#endif
if (m_wheelEventTargets && m_wheelEventTargets->size() && parentDocument())
parentDocument()->didRemoveEventTargetNode(*this);
if (m_mediaQueryMatcher)
m_mediaQueryMatcher->documentDestroyed();
#if ENABLE(WIRELESS_PLAYBACK_TARGET)
if (!m_clientToIDMap.isEmpty() && page()) {
Vector<WebCore::MediaPlaybackTargetClient*> clients;
copyKeysToVector(m_clientToIDMap, clients);
for (auto client : clients)
removePlaybackTargetPickerClient(*client);
}
#endif
disconnectFromFrame();
m_hasPreparedForDestruction = true;
}
void Document::removeAllEventListeners()
{
EventTarget::removeAllEventListeners();
if (m_domWindow)
m_domWindow->removeAllEventListeners();
#if ENABLE(IOS_TOUCH_EVENTS)
clearTouchEventListeners();
#endif
for (Node* node = firstChild(); node; node = NodeTraversal::next(*node))
node->removeAllEventListeners();
}
void Document::suspendDeviceMotionAndOrientationUpdates()
{
if (m_areDeviceMotionAndOrientationUpdatesSuspended)
return;
m_areDeviceMotionAndOrientationUpdatesSuspended = true;
#if ENABLE(DEVICE_ORIENTATION) && PLATFORM(IOS)
if (m_deviceMotionController)
m_deviceMotionController->suspendUpdates();
if (m_deviceOrientationController)
m_deviceOrientationController->suspendUpdates();
#endif
}
void Document::resumeDeviceMotionAndOrientationUpdates()
{
if (!m_areDeviceMotionAndOrientationUpdatesSuspended)
return;
m_areDeviceMotionAndOrientationUpdatesSuspended = false;
#if ENABLE(DEVICE_ORIENTATION) && PLATFORM(IOS)
if (m_deviceMotionController)
m_deviceMotionController->resumeUpdates();
if (m_deviceOrientationController)
m_deviceOrientationController->resumeUpdates();
#endif
}
void Document::platformSuspendOrStopActiveDOMObjects()
{
#if PLATFORM(IOS)
if (WebThreadCountOfObservedContentModifiers() > 0) {
Frame* frame = this->frame();
if (Page* page = frame ? frame->page() : nullptr)
page->chrome().client().clearContentChangeObservers(frame);
}
#endif
}
void Document::suspendActiveDOMObjects(ActiveDOMObject::ReasonForSuspension why)
{
ScriptExecutionContext::suspendActiveDOMObjects(why);
suspendDeviceMotionAndOrientationUpdates();
platformSuspendOrStopActiveDOMObjects();
}
void Document::resumeActiveDOMObjects(ActiveDOMObject::ReasonForSuspension why)
{
ScriptExecutionContext::resumeActiveDOMObjects(why);
resumeDeviceMotionAndOrientationUpdates();
}
void Document::stopActiveDOMObjects()
{
ScriptExecutionContext::stopActiveDOMObjects();
platformSuspendOrStopActiveDOMObjects();
}
void Document::clearAXObjectCache()
{
ASSERT(&topDocument() == this);
m_axObjectCache = nullptr;
}
AXObjectCache* Document::existingAXObjectCache() const
{
Document& topDocument = this->topDocument();
if (!topDocument.hasLivingRenderTree())
return nullptr;
return topDocument.m_axObjectCache.get();
}
AXObjectCache* Document::axObjectCache() const
{
if (!AXObjectCache::accessibilityEnabled())
return nullptr;
Document& topDocument = this->topDocument();
if (!topDocument.hasLivingRenderTree())
return nullptr;
ASSERT(&topDocument == this || !m_axObjectCache);
if (!topDocument.m_axObjectCache)
topDocument.m_axObjectCache = std::make_unique<AXObjectCache>(topDocument);
return topDocument.m_axObjectCache.get();
}
void Document::setVisuallyOrdered()
{
m_visuallyOrdered = true;
if (renderView())
renderView()->style().setRTLOrdering(VisualOrder);
}
Ref<DocumentParser> Document::createParser()
{
return XMLDocumentParser::create(*this, view());
}
ScriptableDocumentParser* Document::scriptableDocumentParser() const
{
return parser() ? parser()->asScriptableDocumentParser() : nullptr;
}
void Document::open(Document* ownerDocument)
{
if (ownerDocument) {
setURL(ownerDocument->url());
setCookieURL(ownerDocument->cookieURL());
setSecurityOriginPolicy(ownerDocument->securityOriginPolicy());
}
if (m_frame) {
if (ScriptableDocumentParser* parser = scriptableDocumentParser()) {
if (parser->isParsing()) {
if (parser->isExecutingScript())
return;
if (!parser->wasCreatedByScript() && parser->hasInsertionPoint())
return;
}
}
if (m_frame->loader().state() == FrameStateProvisional)
m_frame->loader().stopAllLoaders();
}
removeAllEventListeners();
implicitOpen();
if (ScriptableDocumentParser* parser = scriptableDocumentParser())
parser->setWasCreatedByScript(true);
if (m_frame)
m_frame->loader().didExplicitOpen();
}
void Document::detachParser()
{
if (!m_parser)
return;
m_parser->detach();
m_parser = nullptr;
}
void Document::cancelParsing()
{
if (!m_parser)
return;
detachParser();
explicitClose();
}
void Document::implicitOpen()
{
cancelParsing();
removeChildren();
setCompatibilityMode(DocumentCompatibilityMode::NoQuirksMode);
m_parser = createParser();
setParsing(true);
setReadyState(Loading);
}
HTMLBodyElement* Document::body() const
{
auto* element = documentElement();
if (!element)
return nullptr;
return childrenOfType<HTMLBodyElement>(*element).first();
}
HTMLElement* Document::bodyOrFrameset() const
{
auto* element = documentElement();
if (!element)
return nullptr;
if (auto* frameset = childrenOfType<HTMLFrameSetElement>(*element).first())
return frameset;
return childrenOfType<HTMLBodyElement>(*element).first();
}
void Document::setBodyOrFrameset(PassRefPtr<HTMLElement> prpNewBody, ExceptionCode& ec)
{
RefPtr<HTMLElement> newBody = prpNewBody;
if (!newBody || !documentElement() || !newBody->hasTagName(bodyTag)) {
ec = HIERARCHY_REQUEST_ERR;
return;
}
if (&newBody->document() != this) {
ec = 0;
RefPtr<Node> node = importNode(newBody.get(), true, ec);
if (ec)
return;
newBody = downcast<HTMLElement>(node.get());
}
HTMLElement* b = bodyOrFrameset();
if (!b)
documentElement()->appendChild(newBody.release(), ec);
else
documentElement()->replaceChild(newBody.release(), b, ec);
}
HTMLHeadElement* Document::head()
{
if (auto element = documentElement())
return childrenOfType<HTMLHeadElement>(*element).first();
return nullptr;
}
void Document::close()
{
if (!scriptableDocumentParser() || !scriptableDocumentParser()->wasCreatedByScript() || !scriptableDocumentParser()->isParsing())
return;
explicitClose();
}
void Document::explicitClose()
{
if (RefPtr<DocumentParser> parser = m_parser)
parser->finish();
if (!m_frame) {
implicitClose();
return;
}
m_frame->loader().checkCompleted();
}
void Document::implicitClose()
{
if (m_inStyleRecalc) {
m_closeAfterStyleRecalc = true;
return;
}
bool wasLocationChangePending = frame() && frame()->navigationScheduler().locationChangePending();
bool doload = !parsing() && m_parser && !m_processingLoadEvent && !wasLocationChangePending;
if (!doload)
return;
Ref<Document> protect(*this);
m_processingLoadEvent = true;
ScriptableDocumentParser* parser = scriptableDocumentParser();
m_wellFormed = parser && parser->wellFormed();
detachParser();
Frame* f = frame();
if (f) {
f->loader().icon().startLoader();
f->animation().startAnimationsIfNotSuspended(this);
ImageLoader::dispatchPendingBeforeLoadEvents();
ImageLoader::dispatchPendingLoadEvents();
ImageLoader::dispatchPendingErrorEvents();
HTMLLinkElement::dispatchPendingLoadEvents();
HTMLStyleElement::dispatchPendingLoadEvents();
}
if (svgExtensions())
accessSVGExtensions().dispatchSVGLoadEventToOutermostSVGElements();
dispatchWindowLoadEvent();
enqueuePageshowEvent(PageshowEventNotPersisted);
if (m_pendingStateObject)
enqueuePopstateEvent(m_pendingStateObject.release());
if (f)
f->loader().handledOnloadEvents();
#ifdef INSTRUMENT_LAYOUT_SCHEDULING
if (!ownerElement())
printf("onload fired at %lld\n", elapsedTime().count());
#endif
if (!frame()) {
m_processingLoadEvent = false;
return;
}
if (frame()->navigationScheduler().locationChangePending() && elapsedTime() < settings()->layoutInterval()) {
m_processingLoadEvent = false;
view()->unscheduleRelayout();
return;
}
frame()->loader().checkCallImplicitClose();
m_overMinimumLayoutThreshold = true;
if (!ownerElement() || (ownerElement()->renderer() && !ownerElement()->renderer()->needsLayout())) {
updateStyleIfNeeded();
if (view() && renderView() && (!renderView()->firstChild() || renderView()->needsLayout()))
view()->layout();
}
m_processingLoadEvent = false;
#if PLATFORM(COCOA) || PLATFORM(WIN) || PLATFORM(GTK) || PLATFORM(EFL)
if (f && hasLivingRenderTree() && AXObjectCache::accessibilityEnabled()) {
axObjectCache()->getOrCreate(renderView());
if (this == &topDocument())
axObjectCache()->postNotification(renderView(), AXObjectCache::AXNewDocumentLoadComplete);
else {
axObjectCache()->postNotification(renderView(), AXObjectCache::AXLayoutComplete);
}
}
#endif
if (svgExtensions())
accessSVGExtensions().startAnimations();
}
void Document::setParsing(bool b)
{
m_bParsing = b;
if (m_bParsing && !m_sharedObjectPool)
m_sharedObjectPool = std::make_unique<DocumentSharedObjectPool>();
if (!m_bParsing && view() && !view()->needsLayout())
view()->fireLayoutRelatedMilestonesIfNeeded();
#ifdef INSTRUMENT_LAYOUT_SCHEDULING
if (!ownerElement() && !m_bParsing)
printf("Parsing finished at %lld\n", elapsedTime().count());
#endif
}
bool Document::shouldScheduleLayout()
{
return (haveStylesheetsLoaded() && bodyOrFrameset())
|| (documentElement() && !is<HTMLHtmlElement>(*documentElement()));
}
bool Document::isLayoutTimerActive()
{
return view() && view()->layoutPending() && !minimumLayoutDelay().count();
}
std::chrono::milliseconds Document::minimumLayoutDelay()
{
if (m_overMinimumLayoutThreshold)
return std::chrono::milliseconds(0);
std::chrono::milliseconds elapsed = elapsedTime();
m_overMinimumLayoutThreshold = elapsed > settings()->layoutInterval();
return std::max(std::chrono::milliseconds(0), settings()->layoutInterval() - elapsed);
}
std::chrono::milliseconds Document::elapsedTime() const
{
auto elapsedTime = std::chrono::steady_clock::now() - m_startTime;
return std::chrono::duration_cast<std::chrono::milliseconds>(elapsedTime);
}
void Document::write(const SegmentedString& text, Document* ownerDocument)
{
NestingLevelIncrementer nestingLevelIncrementer(m_writeRecursionDepth);
m_writeRecursionIsTooDeep = (m_writeRecursionDepth > 1) && m_writeRecursionIsTooDeep;
m_writeRecursionIsTooDeep = (m_writeRecursionDepth > cMaxWriteRecursionDepth) || m_writeRecursionIsTooDeep;
if (m_writeRecursionIsTooDeep)
return;
#ifdef INSTRUMENT_LAYOUT_SCHEDULING
if (!ownerElement())
printf("Beginning a document.write at %lld\n", elapsedTime().count());
#endif
bool hasInsertionPoint = m_parser && m_parser->hasInsertionPoint();
if (!hasInsertionPoint && m_ignoreDestructiveWriteCount)
return;
if (!hasInsertionPoint)
open(ownerDocument);
ASSERT(m_parser);
m_parser->insert(text);
#ifdef INSTRUMENT_LAYOUT_SCHEDULING
if (!ownerElement())
printf("Ending a document.write at %lld\n", elapsedTime().count());
#endif
}
void Document::write(const String& text, Document* ownerDocument)
{
write(SegmentedString(text), ownerDocument);
}
void Document::writeln(const String& text, Document* ownerDocument)
{
write(text, ownerDocument);
write("\n", ownerDocument);
}
double Document::minimumTimerInterval() const
{
Page* page = this->page();
if (!page)
return ScriptExecutionContext::minimumTimerInterval();
return page->settings().minimumDOMTimerInterval();
}
void Document::setTimerThrottlingEnabled(bool shouldThrottle)
{
if (m_isTimerThrottlingEnabled == shouldThrottle)
return;
m_isTimerThrottlingEnabled = shouldThrottle;
didChangeTimerAlignmentInterval();
}
double Document::timerAlignmentInterval(bool hasReachedMaxNestingLevel) const
{
if (m_isTimerThrottlingEnabled && hasReachedMaxNestingLevel)
return DOMTimer::hiddenPageAlignmentInterval();
Page* page = this->page();
if (!page)
return ScriptExecutionContext::timerAlignmentInterval(hasReachedMaxNestingLevel);
return page->settings().domTimerAlignmentInterval();
}
EventTarget* Document::errorEventTarget()
{
return m_domWindow.get();
}
void Document::logExceptionToConsole(const String& errorMessage, const String& sourceURL, int lineNumber, int columnNumber, RefPtr<Inspector::ScriptCallStack>&& callStack)
{
addMessage(MessageSource::JS, MessageLevel::Error, errorMessage, sourceURL, lineNumber, columnNumber, WTF::move(callStack));
}
void Document::setURL(const URL& url)
{
const URL& newURL = url.isEmpty() ? blankURL() : url;
if (newURL == m_url)
return;
m_url = newURL;
m_documentURI = m_url.string();
updateBaseURL();
}
void Document::updateBaseURL()
{
URL oldBaseURL = m_baseURL;
if (!m_baseElementURL.isEmpty())
m_baseURL = m_baseElementURL;
else if (!m_baseURLOverride.isEmpty())
m_baseURL = m_baseURLOverride;
else {
m_baseURL = URL(ParsedURLString, documentURI());
}
clearSelectorQueryCache();
if (!m_baseURL.isValid())
m_baseURL = URL();
if (m_elementSheet) {
ASSERT(!m_elementSheet->contents().ruleCount());
bool usesRemUnits = m_elementSheet->contents().usesRemUnits();
bool usesStyleBasedEditability = m_elementSheet->contents().usesStyleBasedEditability();
m_elementSheet = CSSStyleSheet::createInline(*this, m_baseURL);
if (usesRemUnits)
m_elementSheet->contents().parserSetUsesRemUnits();
if (usesStyleBasedEditability)
m_elementSheet->contents().parserSetUsesStyleBasedEditability();
}
if (!equalIgnoringFragmentIdentifier(oldBaseURL, m_baseURL)) {
for (auto& anchor : descendantsOfType<HTMLAnchorElement>(*this))
anchor.invalidateCachedVisitedLinkHash();
}
}
void Document::setBaseURLOverride(const URL& url)
{
m_baseURLOverride = url;
updateBaseURL();
}
void Document::processBaseElement()
{
const AtomicString* href = nullptr;
const AtomicString* target = nullptr;
auto baseDescendants = descendantsOfType<HTMLBaseElement>(*this);
for (auto& base : baseDescendants) {
if (!href) {
const AtomicString& value = base.fastGetAttribute(hrefAttr);
if (!value.isNull()) {
href = &value;
if (target)
break;
}
}
if (!target) {
const AtomicString& value = base.fastGetAttribute(targetAttr);
if (!value.isNull()) {
target = &value;
if (href)
break;
}
}
}
URL baseElementURL;
if (href) {
String strippedHref = stripLeadingAndTrailingHTMLSpaces(*href);
if (!strippedHref.isEmpty())
baseElementURL = URL(url(), strippedHref);
}
if (m_baseElementURL != baseElementURL && contentSecurityPolicy()->allowBaseURI(baseElementURL)) {
m_baseElementURL = baseElementURL;
updateBaseURL();
}
m_baseTarget = target ? *target : nullAtom;
}
String Document::userAgent(const URL& url) const
{
return frame() ? frame()->loader().userAgent(url) : String();
}
void Document::disableEval(const String& errorMessage)
{
if (!frame())
return;
frame()->script().disableEval(errorMessage);
}
bool Document::canNavigate(Frame* targetFrame)
{
if (!m_frame)
return false;
if (!targetFrame)
return true;
if (!isSandboxed(SandboxTopNavigation) && targetFrame == &m_frame->tree().top())
return true;
if (isSandboxed(SandboxNavigation)) {
if (targetFrame->tree().isDescendantOf(m_frame))
return true;
const char* reason = "The frame attempting navigation is sandboxed, and is therefore disallowed from navigating its ancestors.";
if (isSandboxed(SandboxTopNavigation) && targetFrame == &m_frame->tree().top())
reason = "The frame attempting navigation of the top-level window is sandboxed, but the 'allow-top-navigation' flag is not set.";
printNavigationErrorMessage(targetFrame, url(), reason);
return false;
}
if (canAccessAncestor(securityOrigin(), targetFrame))
return true;
if (!targetFrame->tree().parent()) {
if (targetFrame == m_frame->loader().opener())
return true;
if (canAccessAncestor(securityOrigin(), targetFrame->loader().opener()))
return true;
}
printNavigationErrorMessage(targetFrame, url(), "The frame attempting navigation is neither same-origin with the target, nor is it the target's parent or opener.");
return false;
}
Frame* Document::findUnsafeParentScrollPropagationBoundary()
{
Frame* currentFrame = m_frame;
if (!currentFrame)
return nullptr;
Frame* ancestorFrame = currentFrame->tree().parent();
while (ancestorFrame) {
if (!ancestorFrame->document()->securityOrigin()->canAccess(securityOrigin()))
return currentFrame;
currentFrame = ancestorFrame;
ancestorFrame = ancestorFrame->tree().parent();
}
return nullptr;
}
void Document::didRemoveAllPendingStylesheet()
{
m_needsNotifyRemoveAllPendingStylesheet = false;
styleResolverChanged(DeferRecalcStyleIfNeeded);
if (m_pendingSheetLayout == DidLayoutWithPendingSheets) {
m_pendingSheetLayout = IgnoreLayoutWithPendingSheets;
if (renderView())
renderView()->repaintViewAndCompositedLayers();
}
if (ScriptableDocumentParser* parser = scriptableDocumentParser())
parser->executeScriptsWaitingForStylesheets();
if (m_gotoAnchorNeededAfterStylesheetsLoad && view())
view()->scrollToFragment(m_url);
}
CSSStyleSheet& Document::elementSheet()
{
if (!m_elementSheet)
m_elementSheet = CSSStyleSheet::createInline(*this, m_baseURL);
return *m_elementSheet;
}
bool Document::usesStyleBasedEditability() const
{
if (m_elementSheet && m_elementSheet->contents().usesStyleBasedEditability())
return true;
ASSERT(!m_renderView || !m_renderView->frameView().isPainting());
ASSERT(!m_inStyleRecalc);
auto& collection = document().styleSheetCollection();
collection.flushPendingUpdates();
return collection.usesStyleBasedEditability();
}
void Document::processHttpEquiv(const String& equiv, const String& content)
{
ASSERT(!equiv.isNull() && !content.isNull());
HttpEquivPolicy policy = httpEquivPolicy();
if (policy != HttpEquivPolicy::Enabled) {
String reason;
switch (policy) {
case HttpEquivPolicy::Enabled:
ASSERT_NOT_REACHED();
break;
case HttpEquivPolicy::DisabledBySettings:
reason = "by the embedder.";
break;
case HttpEquivPolicy::DisabledByContentDispositionAttachmentSandbox:
reason = "for documents with Content-Disposition: attachment.";
break;
}
String message = "http-equiv '" + equiv + "' is disabled " + reason;
addConsoleMessage(MessageSource::Security, MessageLevel::Error, message);
return;
}
Frame* frame = this->frame();
HTTPHeaderName headerName;
if (!findHTTPHeaderName(equiv, headerName))
return;
switch (headerName) {
case HTTPHeaderName::DefaultStyle:
m_styleSheetCollection.setSelectedStylesheetSetName(content);
m_styleSheetCollection.setPreferredStylesheetSetName(content);
styleResolverChanged(DeferRecalcStyle);
break;
case HTTPHeaderName::Refresh: {
double delay;
String urlString;
if (frame && parseHTTPRefresh(content, true, delay, urlString)) {
URL completedURL;
if (urlString.isEmpty())
completedURL = m_url;
else
completedURL = completeURL(urlString);
if (!protocolIsJavaScript(completedURL))
frame->navigationScheduler().scheduleRedirect(this, delay, completedURL);
else {
String message = "Refused to refresh " + m_url.stringCenterEllipsizedToLength() + " to a javascript: URL";
addConsoleMessage(MessageSource::Security, MessageLevel::Error, message);
}
}
break;
}
case HTTPHeaderName::SetCookie:
if (is<HTMLDocument>(*this)) {
downcast<HTMLDocument>(*this).setCookie(content, IGNORE_EXCEPTION);
}
break;
case HTTPHeaderName::ContentLanguage:
setContentLanguage(content);
break;
case HTTPHeaderName::XDNSPrefetchControl:
parseDNSPrefetchControlHeader(content);
break;
case HTTPHeaderName::XFrameOptions:
if (frame) {
FrameLoader& frameLoader = frame->loader();
unsigned long requestIdentifier = 0;
if (frameLoader.activeDocumentLoader() && frameLoader.activeDocumentLoader()->mainResourceLoader())
requestIdentifier = frameLoader.activeDocumentLoader()->mainResourceLoader()->identifier();
if (frameLoader.shouldInterruptLoadForXFrameOptions(content, url(), requestIdentifier)) {
String message = "Refused to display '" + url().stringCenterEllipsizedToLength() + "' in a frame because it set 'X-Frame-Options' to '" + content + "'.";
frameLoader.stopAllLoaders();
frame->navigationScheduler().scheduleLocationChange(this, securityOrigin(), SecurityOrigin::urlWithUniqueSecurityOrigin(), String());
addConsoleMessage(MessageSource::Security, MessageLevel::Error, message, requestIdentifier);
}
}
break;
case HTTPHeaderName::ContentSecurityPolicy:
contentSecurityPolicy()->didReceiveHeader(content, ContentSecurityPolicy::Enforce);
break;
case HTTPHeaderName::ContentSecurityPolicyReportOnly:
contentSecurityPolicy()->didReceiveHeader(content, ContentSecurityPolicy::Report);
break;
case HTTPHeaderName::XWebKitCSP:
contentSecurityPolicy()->didReceiveHeader(content, ContentSecurityPolicy::PrefixedEnforce);
break;
case HTTPHeaderName::XWebKitCSPReportOnly:
contentSecurityPolicy()->didReceiveHeader(content, ContentSecurityPolicy::PrefixedReport);
break;
default:
break;
}
}
static bool isSeparator(UChar c)
{
return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '=' || c == ',' || c == '\0';
}
void Document::processArguments(const String& features, void* data, ArgumentsCallback callback)
{
unsigned keyBegin, keyEnd;
unsigned valueBegin, valueEnd;
String buffer = features.lower();
unsigned length = buffer.length();
for (unsigned i = 0; i < length; ) {
while (isSeparator(buffer[i])) {
if (i >= length)
break;
i++;
}
keyBegin = i;
while (!isSeparator(buffer[i]))
i++;
keyEnd = i;
while (buffer[i] != '=') {
if (buffer[i] == ',' || i >= length)
break;
i++;
}
while (isSeparator(buffer[i])) {
if (buffer[i] == ',' || i >= length)
break;
i++;
}
valueBegin = i;
while (!isSeparator(buffer[i]))
i++;
valueEnd = i;
ASSERT_WITH_SECURITY_IMPLICATION(i <= length);
String keyString = buffer.substring(keyBegin, keyEnd - keyBegin);
String valueString = buffer.substring(valueBegin, valueEnd - valueBegin);
callback(keyString, valueString, this, data);
}
}
void Document::processViewport(const String& features, ViewportArguments::Type origin)
{
ASSERT(!features.isNull());
if (origin < m_viewportArguments.type)
return;
m_viewportArguments = ViewportArguments(origin);
processArguments(features, (void*)&m_viewportArguments, &setViewportFeature);
updateViewportArguments();
}
void Document::updateViewportArguments()
{
if (page() && frame()->isMainFrame()) {
#ifndef NDEBUG
m_didDispatchViewportPropertiesChanged = true;
#endif
page()->chrome().dispatchViewportPropertiesDidChange(m_viewportArguments);
#if PLATFORM(IOS)
page()->chrome().didReceiveDocType(frame());
#endif
}
}
#if PLATFORM(IOS)
void setParserFeature(const String& key, const String& value, Document* document, void*)
{
if (key == "telephone" && equalIgnoringCase(value, "no"))
document->setIsTelephoneNumberParsingAllowed(false);
}
void Document::processFormatDetection(const String& features)
{
ASSERT(!features.isNull());
processArguments(features, nullptr, &setParserFeature);
}
void Document::processWebAppOrientations()
{
if (Page* page = this->page())
page->chrome().client().webAppOrientationsUpdated();
}
#endif
void Document::processReferrerPolicy(const String& policy)
{
ASSERT(!policy.isNull());
if (shouldEnforceContentDispositionAttachmentSandbox())
return;
if (equalIgnoringCase(policy, "no-referrer") || equalIgnoringCase(policy, "never"))
setReferrerPolicy(ReferrerPolicyNever);
else if (equalIgnoringCase(policy, "unsafe-url") || equalIgnoringCase(policy, "always"))
setReferrerPolicy(ReferrerPolicyAlways);
else if (equalIgnoringCase(policy, "origin"))
setReferrerPolicy(ReferrerPolicyOrigin);
else if (equalIgnoringCase(policy, "no-referrer-when-downgrade") || equalIgnoringCase(policy, "default"))
setReferrerPolicy(ReferrerPolicyDefault);
else {
addConsoleMessage(MessageSource::Rendering, MessageLevel::Error, "Failed to set referrer policy: The value '" + policy + "' is not one of 'no-referrer', 'origin', 'no-referrer-when-downgrade', or 'unsafe-url'. Defaulting to 'no-referrer'.");
setReferrerPolicy(ReferrerPolicyNever);
}
}
MouseEventWithHitTestResults Document::prepareMouseEvent(const HitTestRequest& request, const LayoutPoint& documentPoint, const PlatformMouseEvent& event)
{
if (!hasLivingRenderTree())
return MouseEventWithHitTestResults(event, HitTestResult(LayoutPoint()));
HitTestResult result(documentPoint);
renderView()->hitTest(request, result);
if (!request.readOnly())
updateHoverActiveState(request, result.innerElement());
return MouseEventWithHitTestResults(event, result);
}
bool Document::childTypeAllowed(NodeType type) const
{
switch (type) {
case ATTRIBUTE_NODE:
case CDATA_SECTION_NODE:
case DOCUMENT_FRAGMENT_NODE:
case DOCUMENT_NODE:
case ENTITY_NODE:
case ENTITY_REFERENCE_NODE:
case TEXT_NODE:
case XPATH_NAMESPACE_NODE:
return false;
case COMMENT_NODE:
case PROCESSING_INSTRUCTION_NODE:
return true;
case DOCUMENT_TYPE_NODE:
case ELEMENT_NODE:
for (Node* c = firstChild(); c; c = c->nextSibling())
if (c->nodeType() == type)
return false;
return true;
}
return false;
}
bool Document::canReplaceChild(Node* newChild, Node* oldChild)
{
if (!oldChild)
return true;
if (oldChild->nodeType() == newChild->nodeType())
return true;
int numDoctypes = 0;
int numElements = 0;
for (Node* c = firstChild(); c; c = c->nextSibling()) {
if (c == oldChild)
continue;
switch (c->nodeType()) {
case DOCUMENT_TYPE_NODE:
numDoctypes++;
break;
case ELEMENT_NODE:
numElements++;
break;
default:
break;
}
}
if (newChild->isDocumentFragment()) {
for (Node* c = newChild->firstChild(); c; c = c->nextSibling()) {
switch (c->nodeType()) {
case ATTRIBUTE_NODE:
case CDATA_SECTION_NODE:
case DOCUMENT_FRAGMENT_NODE:
case DOCUMENT_NODE:
case ENTITY_NODE:
case ENTITY_REFERENCE_NODE:
case TEXT_NODE:
case XPATH_NAMESPACE_NODE:
return false;
case COMMENT_NODE:
case PROCESSING_INSTRUCTION_NODE:
break;
case DOCUMENT_TYPE_NODE:
numDoctypes++;
break;
case ELEMENT_NODE:
numElements++;
break;
}
}
} else {
switch (newChild->nodeType()) {
case ATTRIBUTE_NODE:
case CDATA_SECTION_NODE:
case DOCUMENT_FRAGMENT_NODE:
case DOCUMENT_NODE:
case ENTITY_NODE:
case ENTITY_REFERENCE_NODE:
case TEXT_NODE:
case XPATH_NAMESPACE_NODE:
return false;
case COMMENT_NODE:
case PROCESSING_INSTRUCTION_NODE:
return true;
case DOCUMENT_TYPE_NODE:
numDoctypes++;
break;
case ELEMENT_NODE:
numElements++;
break;
}
}
if (numElements > 1 || numDoctypes > 1)
return false;
return true;
}
RefPtr<Node> Document::cloneNodeInternal(Document&, CloningOperation type)
{
Ref<Document> clone = cloneDocumentWithoutChildren();
clone->cloneDataFromDocument(*this);
switch (type) {
case CloningOperation::OnlySelf:
case CloningOperation::SelfWithTemplateContent:
break;
case CloningOperation::Everything:
cloneChildNodes(clone.ptr());
break;
}
return WTF::move(clone);
}
Ref<Document> Document::cloneDocumentWithoutChildren() const
{
return isXHTMLDocument() ? createXHTML(nullptr, url()) : create(nullptr, url());
}
void Document::cloneDataFromDocument(const Document& other)
{
ASSERT(m_url == other.url());
m_baseURL = other.baseURL();
m_baseURLOverride = other.baseURLOverride();
m_documentURI = other.documentURI();
setCompatibilityMode(other.m_compatibilityMode);
setSecurityOriginPolicy(other.securityOriginPolicy());
overrideMIMEType(other.contentType());
setDecoder(other.decoder());
}
StyleSheetList& Document::styleSheets()
{
if (!m_styleSheetList)
m_styleSheetList = StyleSheetList::create(this);
return *m_styleSheetList;
}
String Document::preferredStylesheetSet() const
{
return m_styleSheetCollection.preferredStylesheetSetName();
}
String Document::selectedStylesheetSet() const
{
return m_styleSheetCollection.selectedStylesheetSetName();
}
void Document::setSelectedStylesheetSet(const String& aString)
{
m_styleSheetCollection.setSelectedStylesheetSetName(aString);
styleResolverChanged(DeferRecalcStyle);
}
void Document::evaluateMediaQueryList()
{
if (m_mediaQueryMatcher)
m_mediaQueryMatcher->styleResolverChanged();
checkViewportDependentPictures();
}
void Document::checkViewportDependentPictures()
{
Vector<HTMLPictureElement*, 16> changedPictures;
HashSet<HTMLPictureElement*>::iterator end = m_viewportDependentPictures.end();
for (HashSet<HTMLPictureElement*>::iterator it = m_viewportDependentPictures.begin(); it != end; ++it) {
if ((*it)->viewportChangeAffectedPicture())
changedPictures.append(*it);
}
for (auto* picture : changedPictures)
picture->sourcesChanged();
}
void Document::optimizedStyleSheetUpdateTimerFired()
{
styleResolverChanged(RecalcStyleIfNeeded);
}
void Document::scheduleOptimizedStyleSheetUpdate()
{
if (m_optimizedStyleSheetUpdateTimer.isActive())
return;
m_styleSheetCollection.setPendingUpdateType(DocumentStyleSheetCollection::OptimizedUpdate);
m_optimizedStyleSheetUpdateTimer.startOneShot(0);
}
void Document::updateViewportUnitsOnResize()
{
if (!hasStyleWithViewportUnits())
return;
ensureStyleResolver().clearCachedPropertiesAffectedByViewportUnits();
for (Element* element = ElementTraversal::firstWithin(rootNode()); element; element = ElementTraversal::nextIncludingPseudo(*element)) {
auto* renderer = element->renderer();
if (renderer && renderer->style().hasViewportUnits())
element->setNeedsStyleRecalc(InlineStyleChange);
}
}
void Document::addAudioProducer(MediaProducer* audioProducer)
{
m_audioProducers.add(audioProducer);
updateIsPlayingMedia();
}
void Document::removeAudioProducer(MediaProducer* audioProducer)
{
m_audioProducers.remove(audioProducer);
updateIsPlayingMedia();
}
void Document::updateIsPlayingMedia()
{
MediaProducer::MediaStateFlags state = MediaProducer::IsNotPlaying;
for (auto audioProducer : m_audioProducers)
state |= audioProducer->mediaState();
if (state == m_mediaState)
return;
m_mediaState = state;
if (page())
page()->updateIsPlayingMedia();
}
void Document::pageMutedStateDidChange()
{
for (auto audioProducer : m_audioProducers)
audioProducer->pageMutedStateDidChange();
}
void Document::styleResolverChanged(StyleResolverUpdateFlag updateFlag)
{
if (m_optimizedStyleSheetUpdateTimer.isActive())
m_optimizedStyleSheetUpdateTimer.stop();
if (!hasLivingRenderTree() || (!m_didCalculateStyleResolver && !haveStylesheetsLoaded())) {
m_styleResolver = nullptr;
return;
}
m_didCalculateStyleResolver = true;
#ifdef INSTRUMENT_LAYOUT_SCHEDULING
if (!ownerElement())
printf("Beginning update of style selector at time %lld.\n", elapsedTime().count());
#endif
DocumentStyleSheetCollection::UpdateFlag styleSheetUpdate = (updateFlag == RecalcStyleIfNeeded || updateFlag == DeferRecalcStyleIfNeeded)
? DocumentStyleSheetCollection::OptimizedUpdate
: DocumentStyleSheetCollection::FullUpdate;
bool stylesheetChangeRequiresStyleRecalc = m_styleSheetCollection.updateActiveStyleSheets(styleSheetUpdate);
if (updateFlag == DeferRecalcStyle) {
scheduleForcedStyleRecalc();
return;
}
if (updateFlag == DeferRecalcStyleIfNeeded) {
if (stylesheetChangeRequiresStyleRecalc)
scheduleForcedStyleRecalc();
return;
}
if (!stylesheetChangeRequiresStyleRecalc)
return;
{
AnimationUpdateBlock animationUpdateBlock(m_frame ? &m_frame->animation() : nullptr);
recalcStyle(Style::Force);
}
#ifdef INSTRUMENT_LAYOUT_SCHEDULING
if (!ownerElement())
printf("Finished update of style selector at time %lld\n", elapsedTime().count());
#endif
if (renderView()) {
renderView()->setNeedsLayoutAndPrefWidthsRecalc();
if (view())
view()->scheduleRelayout();
}
evaluateMediaQueryList();
}
void Document::removeFocusedNodeOfSubtree(Node* node, bool amongChildrenOnly)
{
if (!m_focusedElement || this->inPageCache()) return;
Element* focusedElement = node->treeScope().focusedElement();
if (!focusedElement)
return;
bool nodeInSubtree = false;
if (amongChildrenOnly)
nodeInSubtree = focusedElement->isDescendantOf(node);
else
nodeInSubtree = (focusedElement == node) || focusedElement->isDescendantOf(node);
if (nodeInSubtree)
setFocusedElement(nullptr);
}
void Document::hoveredElementDidDetach(Element* element)
{
if (!m_hoveredElement || element != m_hoveredElement)
return;
m_hoveredElement = element->parentElement();
while (m_hoveredElement && !m_hoveredElement->renderer())
m_hoveredElement = m_hoveredElement->parentElement();
if (frame())
frame()->eventHandler().scheduleHoverStateUpdate();
}
void Document::elementInActiveChainDidDetach(Element* element)
{
if (!m_activeElement || element != m_activeElement)
return;
m_activeElement = element->parentElement();
while (m_activeElement && !m_activeElement->renderer())
m_activeElement = m_activeElement->parentElement();
}
#if ENABLE(DASHBOARD_SUPPORT)
const Vector<AnnotatedRegionValue>& Document::annotatedRegions() const
{
return m_annotatedRegions;
}
void Document::setAnnotatedRegions(const Vector<AnnotatedRegionValue>& regions)
{
m_annotatedRegions = regions;
setAnnotatedRegionsDirty(false);
}
#endif
bool Document::setFocusedElement(PassRefPtr<Element> prpNewFocusedElement, FocusDirection direction)
{
RefPtr<Element> newFocusedElement = prpNewFocusedElement;
if (newFocusedElement && (&newFocusedElement->document() != this))
return true;
if (m_focusedElement == newFocusedElement)
return true;
if (m_inPageCache)
return false;
bool focusChangeBlocked = false;
RefPtr<Element> oldFocusedElement = m_focusedElement.release();
if (oldFocusedElement) {
if (oldFocusedElement->active())
oldFocusedElement->setActive(false);
oldFocusedElement->setFocus(false);
if (is<HTMLFormControlElement>(*oldFocusedElement)) {
HTMLFormControlElement& formControlElement = downcast<HTMLFormControlElement>(*oldFocusedElement);
if (formControlElement.wasChangedSinceLastFormControlChangeEvent())
formControlElement.dispatchFormControlChangeEvent();
}
oldFocusedElement->dispatchBlurEvent(newFocusedElement.copyRef());
if (m_focusedElement) {
focusChangeBlocked = true;
newFocusedElement = nullptr;
}
oldFocusedElement->dispatchFocusOutEvent(eventNames().focusoutEvent, newFocusedElement.copyRef()); oldFocusedElement->dispatchFocusOutEvent(eventNames().DOMFocusOutEvent, newFocusedElement.copyRef());
if (m_focusedElement) {
focusChangeBlocked = true;
newFocusedElement = nullptr;
}
if (oldFocusedElement->isRootEditableElement())
frame()->editor().didEndEditing();
if (view()) {
if (Widget* oldWidget = widgetForElement(oldFocusedElement.get()))
oldWidget->setFocus(false);
else
view()->setFocus(false);
}
}
if (newFocusedElement && newFocusedElement->isFocusable()) {
if (newFocusedElement->isRootEditableElement() && !acceptsEditingFocus(newFocusedElement.get())) {
focusChangeBlocked = true;
goto SetFocusedNodeDone;
}
m_focusedElement = newFocusedElement;
m_focusedElement->dispatchFocusEvent(oldFocusedElement.copyRef(), direction);
if (m_focusedElement != newFocusedElement) {
focusChangeBlocked = true;
goto SetFocusedNodeDone;
}
m_focusedElement->dispatchFocusInEvent(eventNames().focusinEvent, oldFocusedElement.copyRef());
if (m_focusedElement != newFocusedElement) {
focusChangeBlocked = true;
goto SetFocusedNodeDone;
}
m_focusedElement->dispatchFocusInEvent(eventNames().DOMFocusInEvent, oldFocusedElement.copyRef());
if (m_focusedElement != newFocusedElement) {
focusChangeBlocked = true;
goto SetFocusedNodeDone;
}
m_focusedElement->setFocus(true);
if (m_focusedElement->isRootEditableElement())
frame()->editor().didBeginEditing();
if (view()) {
Widget* focusWidget = widgetForElement(m_focusedElement.get());
if (focusWidget) {
updateLayout();
focusWidget = widgetForElement(m_focusedElement.get());
}
if (focusWidget)
focusWidget->setFocus(true);
else
view()->setFocus(true);
}
}
if (!focusChangeBlocked && m_focusedElement) {
if (AXObjectCache* cache = axObjectCache())
cache->handleFocusedUIElementChanged(oldFocusedElement.get(), newFocusedElement.get());
}
if (!focusChangeBlocked && page())
page()->chrome().focusedElementChanged(m_focusedElement.get());
SetFocusedNodeDone:
updateStyleIfNeeded();
return !focusChangeBlocked;
}
void Document::setCSSTarget(Element* n)
{
if (m_cssTarget)
m_cssTarget->setNeedsStyleRecalc();
m_cssTarget = n;
if (n)
n->setNeedsStyleRecalc();
}
void Document::registerNodeListForInvalidation(LiveNodeList& list)
{
m_nodeListAndCollectionCounts[list.invalidationType()]++;
if (!list.isRootedAtDocument())
return;
ASSERT(!list.isRegisteredForInvalidationAtDocument());
list.setRegisteredForInvalidationAtDocument(true);
m_listsInvalidatedAtDocument.add(&list);
}
void Document::unregisterNodeListForInvalidation(LiveNodeList& list)
{
m_nodeListAndCollectionCounts[list.invalidationType()]--;
if (!list.isRegisteredForInvalidationAtDocument())
return;
list.setRegisteredForInvalidationAtDocument(false);
ASSERT(m_inInvalidateNodeListAndCollectionCaches
? m_listsInvalidatedAtDocument.isEmpty()
: m_listsInvalidatedAtDocument.contains(&list));
m_listsInvalidatedAtDocument.remove(&list);
}
void Document::registerCollection(HTMLCollection& collection)
{
m_nodeListAndCollectionCounts[collection.invalidationType()]++;
if (collection.isRootedAtDocument())
m_collectionsInvalidatedAtDocument.add(&collection);
}
void Document::unregisterCollection(HTMLCollection& collection)
{
ASSERT(m_nodeListAndCollectionCounts[collection.invalidationType()]);
m_nodeListAndCollectionCounts[collection.invalidationType()]--;
if (!collection.isRootedAtDocument())
return;
m_collectionsInvalidatedAtDocument.remove(&collection);
}
void Document::collectionCachedIdNameMap(const HTMLCollection& collection)
{
ASSERT_UNUSED(collection, collection.hasNamedElementCache());
m_nodeListAndCollectionCounts[InvalidateOnIdNameAttrChange]++;
}
void Document::collectionWillClearIdNameMap(const HTMLCollection& collection)
{
ASSERT_UNUSED(collection, collection.hasNamedElementCache());
ASSERT(m_nodeListAndCollectionCounts[InvalidateOnIdNameAttrChange]);
m_nodeListAndCollectionCounts[InvalidateOnIdNameAttrChange]--;
}
void Document::attachNodeIterator(NodeIterator* ni)
{
m_nodeIterators.add(ni);
}
void Document::detachNodeIterator(NodeIterator* ni)
{
m_nodeIterators.remove(ni);
}
void Document::moveNodeIteratorsToNewDocument(Node* node, Document* newDocument)
{
Vector<NodeIterator*> nodeIterators;
copyToVector(m_nodeIterators, nodeIterators);
for (auto* it : nodeIterators) {
if (it->root() == node) {
detachNodeIterator(it);
newDocument->attachNodeIterator(it);
}
}
}
void Document::updateRangesAfterChildrenChanged(ContainerNode& container)
{
for (auto* range : m_ranges)
range->nodeChildrenChanged(container);
}
void Document::nodeChildrenWillBeRemoved(ContainerNode& container)
{
for (auto* range : m_ranges)
range->nodeChildrenWillBeRemoved(container);
for (auto* it : m_nodeIterators) {
for (Node* n = container.firstChild(); n; n = n->nextSibling())
it->nodeWillBeRemoved(*n);
}
if (Frame* frame = this->frame()) {
for (Node* n = container.firstChild(); n; n = n->nextSibling()) {
frame->eventHandler().nodeWillBeRemoved(*n);
frame->selection().nodeWillBeRemoved(*n);
frame->page()->dragCaretController().nodeWillBeRemoved(*n);
}
}
if (m_markers->hasMarkers()) {
for (Text* textNode = TextNodeTraversal::firstChild(container); textNode; textNode = TextNodeTraversal::nextSibling(*textNode))
m_markers->removeMarkers(textNode);
}
}
void Document::nodeWillBeRemoved(Node& n)
{
for (auto* it : m_nodeIterators)
it->nodeWillBeRemoved(n);
for (auto* range : m_ranges)
range->nodeWillBeRemoved(n);
if (Frame* frame = this->frame()) {
frame->eventHandler().nodeWillBeRemoved(n);
frame->selection().nodeWillBeRemoved(n);
frame->page()->dragCaretController().nodeWillBeRemoved(n);
}
if (is<Text>(n))
m_markers->removeMarkers(&n);
}
void Document::textInserted(Node* text, unsigned offset, unsigned length)
{
if (!m_ranges.isEmpty()) {
for (auto* range : m_ranges)
range->textInserted(text, offset, length);
}
m_markers->shiftMarkers(text, offset, length);
}
void Document::textRemoved(Node* text, unsigned offset, unsigned length)
{
if (!m_ranges.isEmpty()) {
for (auto* range : m_ranges)
range->textRemoved(text, offset, length);
}
m_markers->removeMarkers(text, offset, length);
m_markers->shiftMarkers(text, offset + length, 0 - length);
}
void Document::textNodesMerged(Text* oldNode, unsigned offset)
{
if (!m_ranges.isEmpty()) {
NodeWithIndex oldNodeWithIndex(oldNode);
for (auto* range : m_ranges)
range->textNodesMerged(oldNodeWithIndex, offset);
}
}
void Document::textNodeSplit(Text* oldNode)
{
for (auto* range : m_ranges)
range->textNodeSplit(oldNode);
}
void Document::createDOMWindow()
{
ASSERT(m_frame);
ASSERT(!m_domWindow);
m_domWindow = DOMWindow::create(this);
ASSERT(m_domWindow->document() == this);
ASSERT(m_domWindow->frame() == m_frame);
}
void Document::takeDOMWindowFrom(Document* document)
{
ASSERT(m_frame);
ASSERT(!m_domWindow);
ASSERT(document->m_domWindow);
ASSERT(!document->inPageCache());
m_domWindow = document->m_domWindow.release();
m_domWindow->didSecureTransitionTo(this);
ASSERT(m_domWindow->document() == this);
ASSERT(m_domWindow->frame() == m_frame);
}
void Document::setWindowAttributeEventListener(const AtomicString& eventType, PassRefPtr<EventListener> listener)
{
if (!m_domWindow)
return;
m_domWindow->setAttributeEventListener(eventType, listener);
}
void Document::setWindowAttributeEventListener(const AtomicString& eventType, const QualifiedName& attributeName, const AtomicString& attributeValue)
{
if (!m_frame)
return;
setWindowAttributeEventListener(eventType, JSLazyEventListener::createForDOMWindow(*m_frame, attributeName, attributeValue));
}
EventListener* Document::getWindowAttributeEventListener(const AtomicString& eventType)
{
if (!m_domWindow)
return nullptr;
return m_domWindow->getAttributeEventListener(eventType);
}
void Document::dispatchWindowEvent(PassRefPtr<Event> event, PassRefPtr<EventTarget> target)
{
ASSERT_WITH_SECURITY_IMPLICATION(!NoEventDispatchAssertion::isEventDispatchForbidden());
if (!m_domWindow)
return;
m_domWindow->dispatchEvent(event, target);
}
void Document::dispatchWindowLoadEvent()
{
ASSERT_WITH_SECURITY_IMPLICATION(!NoEventDispatchAssertion::isEventDispatchForbidden());
if (!m_domWindow)
return;
m_domWindow->dispatchLoadEvent();
m_loadEventFinished = true;
m_cachedResourceLoader->documentDidFinishLoadEvent();
}
void Document::enqueueWindowEvent(PassRefPtr<Event> event)
{
event->setTarget(m_domWindow.get());
m_eventQueue.enqueueEvent(event);
}
void Document::enqueueDocumentEvent(PassRefPtr<Event> event)
{
event->setTarget(this);
m_eventQueue.enqueueEvent(event);
}
void Document::enqueueOverflowEvent(PassRefPtr<Event> event)
{
m_eventQueue.enqueueEvent(event);
}
RefPtr<Event> Document::createEvent(const String& eventType, ExceptionCode& ec)
{
RefPtr<Event> event = EventFactory::create(eventType);
if (event)
return event.release();
ec = NOT_SUPPORTED_ERR;
return nullptr;
}
bool Document::hasListenerTypeForEventType(PlatformEvent::Type eventType) const
{
switch (eventType) {
case PlatformEvent::MouseForceChanged:
return m_listenerTypes & Document::FORCECHANGED_LISTENER;
case PlatformEvent::MouseForceDown:
return m_listenerTypes & Document::FORCEDOWN_LISTENER;
case PlatformEvent::MouseForceUp:
return m_listenerTypes & Document::FORCEUP_LISTENER;
case PlatformEvent::MouseScroll:
return m_listenerTypes & Document::SCROLL_LISTENER;
default:
return false;
}
}
void Document::addListenerTypeIfNeeded(const AtomicString& eventType)
{
if (eventType == eventNames().DOMSubtreeModifiedEvent)
addListenerType(DOMSUBTREEMODIFIED_LISTENER);
else if (eventType == eventNames().DOMNodeInsertedEvent)
addListenerType(DOMNODEINSERTED_LISTENER);
else if (eventType == eventNames().DOMNodeRemovedEvent)
addListenerType(DOMNODEREMOVED_LISTENER);
else if (eventType == eventNames().DOMNodeRemovedFromDocumentEvent)
addListenerType(DOMNODEREMOVEDFROMDOCUMENT_LISTENER);
else if (eventType == eventNames().DOMNodeInsertedIntoDocumentEvent)
addListenerType(DOMNODEINSERTEDINTODOCUMENT_LISTENER);
else if (eventType == eventNames().DOMCharacterDataModifiedEvent)
addListenerType(DOMCHARACTERDATAMODIFIED_LISTENER);
else if (eventType == eventNames().overflowchangedEvent)
addListenerType(OVERFLOWCHANGED_LISTENER);
else if (eventType == eventNames().webkitAnimationStartEvent || eventType == eventNames().animationstartEvent)
addListenerType(ANIMATIONSTART_LISTENER);
else if (eventType == eventNames().webkitAnimationEndEvent || eventType == eventNames().animationendEvent)
addListenerType(ANIMATIONEND_LISTENER);
else if (eventType == eventNames().webkitAnimationIterationEvent || eventType == eventNames().animationiterationEvent)
addListenerType(ANIMATIONITERATION_LISTENER);
else if (eventType == eventNames().webkitTransitionEndEvent || eventType == eventNames().transitionendEvent)
addListenerType(TRANSITIONEND_LISTENER);
else if (eventType == eventNames().beforeloadEvent)
addListenerType(BEFORELOAD_LISTENER);
else if (eventType == eventNames().scrollEvent)
addListenerType(SCROLL_LISTENER);
else if (eventType == eventNames().webkitmouseforcewillbeginEvent)
addListenerType(FORCEWILLBEGIN_LISTENER);
else if (eventType == eventNames().webkitmouseforcechangedEvent)
addListenerType(FORCECHANGED_LISTENER);
else if (eventType == eventNames().webkitmouseforcedownEvent)
addListenerType(FORCEDOWN_LISTENER);
else if (eventType == eventNames().webkitmouseforceupEvent)
addListenerType(FORCEUP_LISTENER);
}
CSSStyleDeclaration* Document::getOverrideStyle(Element*, const String&)
{
return nullptr;
}
HTMLFrameOwnerElement* Document::ownerElement() const
{
if (!frame())
return nullptr;
return frame()->ownerElement();
}
String Document::cookie(ExceptionCode& ec)
{
if (page() && !page()->settings().cookieEnabled())
return String();
if (!securityOrigin()->canAccessCookies()) {
ec = SECURITY_ERR;
return String();
}
URL cookieURL = this->cookieURL();
if (cookieURL.isEmpty())
return String();
if (!isDOMCookieCacheValid())
setCachedDOMCookies(cookies(this, cookieURL));
return cachedDOMCookies();
}
void Document::setCookie(const String& value, ExceptionCode& ec)
{
if (page() && !page()->settings().cookieEnabled())
return;
if (!securityOrigin()->canAccessCookies()) {
ec = SECURITY_ERR;
return;
}
URL cookieURL = this->cookieURL();
if (cookieURL.isEmpty())
return;
invalidateDOMCookieCache();
setCookies(this, cookieURL, value);
}
String Document::referrer() const
{
if (frame())
return frame()->loader().referrer();
return String();
}
String Document::origin() const
{
return securityOrigin()->databaseIdentifier();
}
String Document::domain() const
{
return securityOrigin()->domain();
}
void Document::setDomain(const String& newDomain, ExceptionCode& ec)
{
if (SchemeRegistry::isDomainRelaxationForbiddenForURLScheme(securityOrigin()->protocol())) {
ec = SECURITY_ERR;
return;
}
if (equalIgnoringCase(domain(), newDomain)) {
securityOrigin()->setDomainFromDOM(newDomain);
return;
}
int oldLength = domain().length();
int newLength = newDomain.length();
if (newLength >= oldLength) {
ec = SECURITY_ERR;
return;
}
String test = domain();
if (test[oldLength - newLength - 1] != '.') {
ec = SECURITY_ERR;
return;
}
test.remove(0, oldLength - newLength);
if (test != newDomain) {
ec = SECURITY_ERR;
return;
}
securityOrigin()->setDomainFromDOM(newDomain);
}
String Document::lastModified() const
{
DateComponents date;
bool foundDate = false;
if (m_frame) {
auto lastModifiedDate = loader() ? loader()->response().lastModified() : Nullopt;
if (lastModifiedDate) {
using namespace std::chrono;
date.setMillisecondsSinceEpochForDateTime(duration_cast<milliseconds>(lastModifiedDate.value().time_since_epoch()).count());
foundDate = true;
}
}
if (!foundDate) {
double fallbackDate = currentTimeMS();
#if ENABLE(WEB_REPLAY)
InputCursor& cursor = inputCursor();
if (cursor.isCapturing())
cursor.appendInput<DocumentLastModifiedDate>(fallbackDate);
else if (cursor.isReplaying()) {
if (DocumentLastModifiedDate* input = cursor.fetchInput<DocumentLastModifiedDate>())
fallbackDate = input->fallbackValue();
}
#endif
date.setMillisecondsSinceEpochForDateTime(fallbackDate);
}
return String::format("%02d/%02d/%04d %02d:%02d:%02d", date.month() + 1, date.monthDay(), date.fullYear(), date.hour(), date.minute(), date.second());
}
void Document::setCookieURL(const URL& url)
{
if (m_cookieURL == url)
return;
m_cookieURL = url;
invalidateDOMCookieCache();
}
static bool isValidNameNonASCII(const LChar* characters, unsigned length)
{
if (!isValidNameStart(characters[0]))
return false;
for (unsigned i = 1; i < length; ++i) {
if (!isValidNamePart(characters[i]))
return false;
}
return true;
}
static bool isValidNameNonASCII(const UChar* characters, unsigned length)
{
unsigned i = 0;
UChar32 c;
U16_NEXT(characters, i, length, c)
if (!isValidNameStart(c))
return false;
while (i < length) {
U16_NEXT(characters, i, length, c)
if (!isValidNamePart(c))
return false;
}
return true;
}
template<typename CharType>
static inline bool isValidNameASCII(const CharType* characters, unsigned length)
{
CharType c = characters[0];
if (!(isASCIIAlpha(c) || c == ':' || c == '_'))
return false;
for (unsigned i = 1; i < length; ++i) {
c = characters[i];
if (!(isASCIIAlphanumeric(c) || c == ':' || c == '_' || c == '-' || c == '.'))
return false;
}
return true;
}
bool Document::isValidName(const String& name)
{
unsigned length = name.length();
if (!length)
return false;
if (name.is8Bit()) {
const LChar* characters = name.characters8();
if (isValidNameASCII(characters, length))
return true;
return isValidNameNonASCII(characters, length);
}
const UChar* characters = name.characters16();
if (isValidNameASCII(characters, length))
return true;
return isValidNameNonASCII(characters, length);
}
bool Document::parseQualifiedName(const String& qualifiedName, String& prefix, String& localName, ExceptionCode& ec)
{
unsigned length = qualifiedName.length();
if (!length) {
ec = INVALID_CHARACTER_ERR;
return false;
}
bool nameStart = true;
bool sawColon = false;
int colonPos = 0;
for (unsigned i = 0; i < length;) {
UChar32 c;
U16_NEXT(qualifiedName, i, length, c)
if (c == ':') {
if (sawColon) {
ec = NAMESPACE_ERR;
return false; }
nameStart = true;
sawColon = true;
colonPos = i - 1;
} else if (nameStart) {
if (!isValidNameStart(c)) {
ec = INVALID_CHARACTER_ERR;
return false;
}
nameStart = false;
} else {
if (!isValidNamePart(c)) {
ec = INVALID_CHARACTER_ERR;
return false;
}
}
}
if (!sawColon) {
prefix = String();
localName = qualifiedName;
} else {
prefix = qualifiedName.substring(0, colonPos);
if (prefix.isEmpty()) {
ec = NAMESPACE_ERR;
return false;
}
localName = qualifiedName.substring(colonPos + 1);
}
if (localName.isEmpty()) {
ec = NAMESPACE_ERR;
return false;
}
return true;
}
void Document::setDecoder(PassRefPtr<TextResourceDecoder> decoder)
{
m_decoder = decoder;
}
URL Document::completeURL(const String& url, const URL& baseURLOverride) const
{
if (url.isNull())
return URL();
const URL& baseURL = ((baseURLOverride.isEmpty() || baseURLOverride == blankURL()) && parentDocument()) ? parentDocument()->baseURL() : baseURLOverride;
if (!m_decoder)
return URL(baseURL, url);
return URL(baseURL, url, m_decoder->encoding());
}
URL Document::completeURL(const String& url) const
{
return completeURL(url, m_baseURL);
}
void Document::setInPageCache(bool flag)
{
if (m_inPageCache == flag)
return;
m_inPageCache = flag;
FrameView* v = view();
Page* page = this->page();
if (page)
page->lockAllOverlayScrollbarsToHidden(flag);
if (flag) {
if (v) {
v->cacheCurrentScrollPosition();
if (page && m_frame->isMainFrame()) {
v->resetScrollbarsAndClearContentsSize();
if (ScrollingCoordinator* scrollingCoordinator = page->scrollingCoordinator())
scrollingCoordinator->clearStateTree();
} else
v->resetScrollbars();
}
m_styleRecalcTimer.stop();
} else {
if (childNeedsStyleRecalc())
scheduleStyleRecalc();
}
}
void Document::documentWillBecomeInactive()
{
if (renderView())
renderView()->setIsInWindow(false);
}
void Document::documentWillSuspendForPageCache()
{
documentWillBecomeInactive();
for (auto* element : m_documentSuspensionCallbackElements)
element->documentWillSuspendForPageCache();
#ifndef NDEBUG
m_didDispatchViewportPropertiesChanged = false;
#endif
if (RenderView* view = renderView()) {
if (view->usesCompositing())
view->compositor().cancelCompositingLayerUpdate();
}
}
void Document::documentDidResumeFromPageCache()
{
Vector<Element*> elements;
copyToVector(m_documentSuspensionCallbackElements, elements);
for (auto* element : elements)
element->documentDidResumeFromPageCache();
if (renderView())
renderView()->setIsInWindow(true);
ASSERT(page());
page()->lockAllOverlayScrollbarsToHidden(false);
ASSERT(m_frame);
m_frame->loader().client().dispatchDidBecomeFrameset(isFrameSet());
}
void Document::registerForPageCacheSuspensionCallbacks(Element* e)
{
m_documentSuspensionCallbackElements.add(e);
}
void Document::unregisterForPageCacheSuspensionCallbacks(Element* e)
{
m_documentSuspensionCallbackElements.remove(e);
}
void Document::mediaVolumeDidChange()
{
for (auto* element : m_mediaVolumeCallbackElements)
element->mediaVolumeDidChange();
}
void Document::registerForMediaVolumeCallbacks(Element* e)
{
m_mediaVolumeCallbackElements.add(e);
}
void Document::unregisterForMediaVolumeCallbacks(Element* e)
{
m_mediaVolumeCallbackElements.remove(e);
}
void Document::storageBlockingStateDidChange()
{
if (Settings* settings = this->settings())
securityOrigin()->setStorageBlockingPolicy(settings->storageBlockingPolicy());
}
void Document::privateBrowsingStateDidChange()
{
for (auto* element : m_privateBrowsingStateChangedElements)
element->privateBrowsingStateDidChange();
}
void Document::registerForPrivateBrowsingStateChangedCallbacks(Element* e)
{
m_privateBrowsingStateChangedElements.add(e);
}
void Document::unregisterForPrivateBrowsingStateChangedCallbacks(Element* e)
{
m_privateBrowsingStateChangedElements.remove(e);
}
#if ENABLE(VIDEO_TRACK)
void Document::registerForCaptionPreferencesChangedCallbacks(Element* e)
{
if (page())
page()->group().captionPreferences()->setInterestedInCaptionPreferenceChanges();
m_captionPreferencesChangedElements.add(e);
}
void Document::unregisterForCaptionPreferencesChangedCallbacks(Element* e)
{
m_captionPreferencesChangedElements.remove(e);
}
void Document::captionPreferencesChanged()
{
for (auto* element : m_captionPreferencesChangedElements)
element->captionPreferencesChanged();
}
#endif
#if ENABLE(MEDIA_CONTROLS_SCRIPT)
void Document::registerForPageScaleFactorChangedCallbacks(HTMLMediaElement* element)
{
m_pageScaleFactorChangedElements.add(element);
}
void Document::unregisterForPageScaleFactorChangedCallbacks(HTMLMediaElement* element)
{
m_pageScaleFactorChangedElements.remove(element);
}
void Document::pageScaleFactorChangedAndStable()
{
for (HTMLMediaElement* mediaElement : m_pageScaleFactorChangedElements)
mediaElement->pageScaleFactorChanged();
}
#endif
void Document::setShouldCreateRenderers(bool f)
{
m_createRenderers = f;
}
bool Document::shouldCreateRenderers()
{
return m_createRenderers;
}
static Editor::Command command(Document* document, const String& commandName, bool userInterface = false)
{
Frame* frame = document->frame();
if (!frame || frame->document() != document)
return Editor::Command();
document->updateStyleIfNeeded();
return frame->editor().command(commandName,
userInterface ? CommandFromDOMWithUserInterface : CommandFromDOM);
}
bool Document::execCommand(const String& commandName, bool userInterface, const String& value)
{
return command(this, commandName, userInterface).execute(value);
}
bool Document::queryCommandEnabled(const String& commandName)
{
return command(this, commandName).isEnabled();
}
bool Document::queryCommandIndeterm(const String& commandName)
{
return command(this, commandName).state() == MixedTriState;
}
bool Document::queryCommandState(const String& commandName)
{
return command(this, commandName).state() == TrueTriState;
}
bool Document::queryCommandSupported(const String& commandName)
{
return command(this, commandName).isSupported();
}
String Document::queryCommandValue(const String& commandName)
{
return command(this, commandName).value();
}
void Document::pushCurrentScript(PassRefPtr<HTMLScriptElement> newCurrentScript)
{
ASSERT(newCurrentScript);
m_currentScriptStack.append(newCurrentScript);
}
void Document::popCurrentScript()
{
ASSERT(!m_currentScriptStack.isEmpty());
m_currentScriptStack.removeLast();
}
#if ENABLE(XSLT)
void Document::applyXSLTransform(ProcessingInstruction* pi)
{
RefPtr<XSLTProcessor> processor = XSLTProcessor::create();
processor->setXSLStyleSheet(downcast<XSLStyleSheet>(pi->sheet()));
String resultMIMEType;
String newSource;
String resultEncoding;
if (!processor->transformToString(*this, resultMIMEType, newSource, resultEncoding))
return;
Frame* ownerFrame = frame();
processor->createDocumentFromSource(newSource, resultEncoding, resultMIMEType, this, ownerFrame);
}
void Document::setTransformSource(std::unique_ptr<TransformSource> source)
{
m_transformSource = WTF::move(source);
}
#endif
void Document::setDesignMode(InheritedBool value)
{
m_designMode = value;
for (Frame* frame = m_frame; frame && frame->document(); frame = frame->tree().traverseNext(m_frame))
frame->document()->scheduleForcedStyleRecalc();
}
Document::InheritedBool Document::getDesignMode() const
{
return m_designMode;
}
bool Document::inDesignMode() const
{
for (const Document* d = this; d; d = d->parentDocument()) {
if (d->m_designMode != inherit)
return d->m_designMode;
}
return false;
}
Document* Document::parentDocument() const
{
if (!m_frame)
return nullptr;
Frame* parent = m_frame->tree().parent();
if (!parent)
return nullptr;
return parent->document();
}
Document& Document::topDocument() const
{
if (!m_inPageCache && !m_renderTreeBeingDestroyed) {
if (!m_frame)
return const_cast<Document&>(*this);
Document* mainFrameDocument = m_frame->mainFrame().document();
return mainFrameDocument ? *mainFrameDocument : const_cast<Document&>(*this);
}
Document* document = const_cast<Document*>(this);
while (HTMLFrameOwnerElement* element = document->ownerElement())
document = &element->document();
return *document;
}
RefPtr<Attr> Document::createAttribute(const String& name, ExceptionCode& ec)
{
return createAttributeNS(String(), name, ec, true);
}
RefPtr<Attr> Document::createAttributeNS(const String& namespaceURI, const String& qualifiedName, ExceptionCode& ec, bool shouldIgnoreNamespaceChecks)
{
String prefix, localName;
if (!parseQualifiedName(qualifiedName, prefix, localName, ec))
return nullptr;
QualifiedName qName(prefix, localName, namespaceURI);
if (!shouldIgnoreNamespaceChecks && !hasValidNamespaceForAttributes(qName)) {
ec = NAMESPACE_ERR;
return nullptr;
}
return Attr::create(*this, qName, emptyString());
}
const SVGDocumentExtensions* Document::svgExtensions()
{
return m_svgExtensions.get();
}
SVGDocumentExtensions& Document::accessSVGExtensions()
{
if (!m_svgExtensions)
m_svgExtensions = std::make_unique<SVGDocumentExtensions>(this);
return *m_svgExtensions;
}
bool Document::hasSVGRootNode() const
{
return documentElement() && documentElement()->hasTagName(SVGNames::svgTag);
}
Ref<HTMLCollection> Document::ensureCachedCollection(CollectionType type)
{
return ensureRareData().ensureNodeLists().addCachedCollection<HTMLCollection>(*this, type);
}
Ref<HTMLCollection> Document::images()
{
return ensureCachedCollection(DocImages);
}
Ref<HTMLCollection> Document::applets()
{
return ensureCachedCollection(DocApplets);
}
Ref<HTMLCollection> Document::embeds()
{
return ensureCachedCollection(DocEmbeds);
}
Ref<HTMLCollection> Document::plugins()
{
return ensureCachedCollection(DocEmbeds);
}
Ref<HTMLCollection> Document::scripts()
{
return ensureCachedCollection(DocScripts);
}
Ref<HTMLCollection> Document::links()
{
return ensureCachedCollection(DocLinks);
}
Ref<HTMLCollection> Document::forms()
{
return ensureCachedCollection(DocForms);
}
Ref<HTMLCollection> Document::anchors()
{
return ensureCachedCollection(DocAnchors);
}
Ref<HTMLCollection> Document::all()
{
return ensureRareData().ensureNodeLists().addCachedCollection<HTMLAllCollection>(*this, DocAll);
}
Ref<HTMLCollection> Document::windowNamedItems(const AtomicString& name)
{
return ensureRareData().ensureNodeLists().addCachedCollection<WindowNameCollection>(*this, WindowNamedItems, name);
}
Ref<HTMLCollection> Document::documentNamedItems(const AtomicString& name)
{
return ensureRareData().ensureNodeLists().addCachedCollection<DocumentNameCollection>(*this, DocumentNamedItems, name);
}
void Document::finishedParsing()
{
ASSERT(!scriptableDocumentParser() || !m_parser->isParsing());
ASSERT(!scriptableDocumentParser() || m_readyState != Loading);
setParsing(false);
#if ENABLE(WEB_TIMING)
if (!m_documentTiming.domContentLoadedEventStart)
m_documentTiming.domContentLoadedEventStart = monotonicallyIncreasingTime();
#endif
dispatchEvent(Event::create(eventNames().DOMContentLoadedEvent, true, false));
#if ENABLE(WEB_TIMING)
if (!m_documentTiming.domContentLoadedEventEnd)
m_documentTiming.domContentLoadedEventEnd = monotonicallyIncreasingTime();
#endif
if (RefPtr<Frame> f = frame()) {
updateStyleIfNeeded();
f->loader().finishedParsing();
InspectorInstrumentation::domContentLoadedEventFired(*f);
}
static const int timeToKeepSharedObjectPoolAliveAfterParsingFinishedInSeconds = 10;
m_sharedObjectPoolClearTimer.startOneShot(timeToKeepSharedObjectPoolAliveAfterParsingFinishedInSeconds);
m_cachedResourceLoader->clearPreloads();
}
void Document::sharedObjectPoolClearTimerFired()
{
m_sharedObjectPool = nullptr;
}
#if ENABLE(TELEPHONE_NUMBER_DETECTION)
bool Document::isTelephoneNumberParsingEnabled() const
{
Settings* settings = this->settings();
return settings && settings->telephoneNumberParsingEnabled() && m_isTelephoneNumberParsingAllowed;
}
void Document::setIsTelephoneNumberParsingAllowed(bool isTelephoneNumberParsingAllowed)
{
m_isTelephoneNumberParsingAllowed = isTelephoneNumberParsingAllowed;
}
bool Document::isTelephoneNumberParsingAllowed() const
{
return m_isTelephoneNumberParsingAllowed;
}
#endif
RefPtr<XPathExpression> Document::createExpression(const String& expression,
XPathNSResolver* resolver,
ExceptionCode& ec)
{
if (!m_xpathEvaluator)
m_xpathEvaluator = XPathEvaluator::create();
return m_xpathEvaluator->createExpression(expression, resolver, ec);
}
RefPtr<XPathNSResolver> Document::createNSResolver(Node* nodeResolver)
{
if (!m_xpathEvaluator)
m_xpathEvaluator = XPathEvaluator::create();
return m_xpathEvaluator->createNSResolver(nodeResolver);
}
RefPtr<XPathResult> Document::evaluate(const String& expression, Node* contextNode, XPathNSResolver* resolver, unsigned short type, XPathResult* result, ExceptionCode& ec)
{
if (!m_xpathEvaluator)
m_xpathEvaluator = XPathEvaluator::create();
return m_xpathEvaluator->evaluate(expression, contextNode, resolver, type, result, ec);
}
void Document::initSecurityContext()
{
if (haveInitializedSecurityOrigin()) {
ASSERT(securityOrigin());
return;
}
if (!m_frame) {
setCookieURL(URL(ParsedURLString, emptyString()));
setSecurityOriginPolicy(SecurityOriginPolicy::create(SecurityOrigin::createUnique()));
setContentSecurityPolicy(std::make_unique<ContentSecurityPolicy>(this));
return;
}
setCookieURL(m_url);
enforceSandboxFlags(m_frame->loader().effectiveSandboxFlags());
if (shouldEnforceContentDispositionAttachmentSandbox())
applyContentDispositionAttachmentSandbox();
setSecurityOriginPolicy(SecurityOriginPolicy::create(isSandboxed(SandboxOrigin) ? SecurityOrigin::createUnique() : SecurityOrigin::create(m_url)));
setContentSecurityPolicy(std::make_unique<ContentSecurityPolicy>(this));
if (Settings* settings = this->settings()) {
if (!settings->webSecurityEnabled()) {
securityOrigin()->grantUniversalAccess();
} else if (securityOrigin()->isLocal()) {
if (settings->allowUniversalAccessFromFileURLs() || m_frame->loader().client().shouldForceUniversalAccessFromLocalURL(m_url)) {
securityOrigin()->grantUniversalAccess();
} else if (!settings->allowFileAccessFromFileURLs()) {
securityOrigin()->enforceFilePathSeparation();
}
}
securityOrigin()->setStorageBlockingPolicy(settings->storageBlockingPolicy());
}
Document* parentDocument = ownerElement() ? &ownerElement()->document() : nullptr;
if (parentDocument && m_frame->loader().shouldTreatURLAsSrcdocDocument(url())) {
m_isSrcdocDocument = true;
setBaseURLOverride(parentDocument->baseURL());
}
if (!m_url.shouldInheritSecurityOriginFromOwner())
return;
Frame* ownerFrame = m_frame->tree().parent();
if (!ownerFrame)
ownerFrame = m_frame->loader().opener();
if (!ownerFrame) {
didFailToInitializeSecurityOrigin();
return;
}
if (isSandboxed(SandboxOrigin)) {
if (ownerFrame->document()->securityOrigin()->canLoadLocalResources())
securityOrigin()->grantLoadLocalResources();
return;
}
setCookieURL(ownerFrame->document()->cookieURL());
setSecurityOriginPolicy(ownerFrame->document()->securityOriginPolicy());
}
void Document::initContentSecurityPolicy()
{
if (!m_frame->tree().parent() || (!m_url.shouldInheritSecurityOriginFromOwner() && !isPluginDocument()))
return;
contentSecurityPolicy()->copyStateFrom(m_frame->tree().parent()->document()->contentSecurityPolicy());
}
bool Document::isContextThread() const
{
return isMainThread();
}
void Document::updateURLForPushOrReplaceState(const URL& url)
{
Frame* f = frame();
if (!f)
return;
setURL(url);
f->loader().setOutgoingReferrer(url);
if (DocumentLoader* documentLoader = loader())
documentLoader->replaceRequestURLForSameDocumentNavigation(url);
}
void Document::statePopped(PassRefPtr<SerializedScriptValue> stateObject)
{
if (!frame())
return;
if (m_readyState == Complete)
enqueuePopstateEvent(stateObject);
else
m_pendingStateObject = stateObject;
}
void Document::updateFocusAppearanceSoon(bool restorePreviousSelection)
{
m_updateFocusAppearanceRestoresSelection = restorePreviousSelection;
if (!m_updateFocusAppearanceTimer.isActive())
m_updateFocusAppearanceTimer.startOneShot(0);
}
void Document::cancelFocusAppearanceUpdate()
{
m_updateFocusAppearanceTimer.stop();
}
void Document::updateFocusAppearanceTimerFired()
{
Element* element = focusedElement();
if (!element)
return;
updateLayout();
if (element->isFocusable())
element->updateFocusAppearance(m_updateFocusAppearanceRestoresSelection);
}
void Document::attachRange(Range* range)
{
ASSERT(!m_ranges.contains(range));
m_ranges.add(range);
}
void Document::detachRange(Range* range)
{
m_ranges.remove(range);
}
CanvasRenderingContext* Document::getCSSCanvasContext(const String& type, const String& name, int width, int height)
{
HTMLCanvasElement* element = getCSSCanvasElement(name);
if (!element)
return nullptr;
element->setSize(IntSize(width, height));
return element->getContext(type);
}
HTMLCanvasElement* Document::getCSSCanvasElement(const String& name)
{
RefPtr<HTMLCanvasElement>& element = m_cssCanvasElements.add(name, nullptr).iterator->value;
if (!element)
element = HTMLCanvasElement::create(*this);
return element.get();
}
#if ENABLE(IOS_TEXT_AUTOSIZING)
void Document::addAutoSizingNode(Node* node, float candidateSize)
{
TextAutoSizingKey key(&node->renderer()->style(), &document());
TextAutoSizingMap::AddResult result = m_textAutoSizedNodes.add(key, nullptr);
if (result.isNewEntry)
result.iterator->value = TextAutoSizingValue::create();
result.iterator->value->addNode(node, candidateSize);
}
void Document::validateAutoSizingNodes()
{
Vector<TextAutoSizingKey> nodesForRemoval;
for (auto& keyValuePair : m_textAutoSizedNodes) {
TextAutoSizingValue* value = keyValuePair.value.get();
if (!value)
continue;
value->adjustNodeSizes();
if (!value->numNodes())
nodesForRemoval.append(keyValuePair.key);
}
for (auto& key : nodesForRemoval)
m_textAutoSizedNodes.remove(key);
}
void Document::resetAutoSizingNodes()
{
for (auto& value : m_textAutoSizedNodes.values()) {
if (value)
value->reset();
}
m_textAutoSizedNodes.clear();
}
#endif // ENABLE(IOS_TEXT_AUTOSIZING)
void Document::initDNSPrefetch()
{
Settings* settings = this->settings();
m_haveExplicitlyDisabledDNSPrefetch = false;
m_isDNSPrefetchEnabled = settings && settings->dnsPrefetchingEnabled() && securityOrigin()->protocol() == "http";
if (Document* parent = parentDocument()) {
if (!parent->isDNSPrefetchEnabled())
m_isDNSPrefetchEnabled = false;
}
}
void Document::parseDNSPrefetchControlHeader(const String& dnsPrefetchControl)
{
if (equalIgnoringCase(dnsPrefetchControl, "on") && !m_haveExplicitlyDisabledDNSPrefetch) {
m_isDNSPrefetchEnabled = true;
return;
}
m_isDNSPrefetchEnabled = false;
m_haveExplicitlyDisabledDNSPrefetch = true;
}
void Document::addConsoleMessage(MessageSource source, MessageLevel level, const String& message, unsigned long requestIdentifier)
{
if (!isContextThread()) {
postTask(AddConsoleMessageTask(source, level, StringCapture(message)));
return;
}
if (Page* page = this->page())
page->console().addMessage(source, level, message, requestIdentifier, this);
}
void Document::addMessage(MessageSource source, MessageLevel level, const String& message, const String& sourceURL, unsigned lineNumber, unsigned columnNumber, RefPtr<Inspector::ScriptCallStack>&& callStack, JSC::ExecState* state, unsigned long requestIdentifier)
{
if (!isContextThread()) {
postTask(AddConsoleMessageTask(source, level, StringCapture(message)));
return;
}
if (Page* page = this->page())
page->console().addMessage(source, level, message, sourceURL, lineNumber, columnNumber, WTF::move(callStack), state, requestIdentifier);
}
SecurityOrigin* Document::topOrigin() const
{
return topDocument().securityOrigin();
}
void Document::postTask(Task task)
{
Task* taskPtr = std::make_unique<Task>(WTF::move(task)).release();
WeakPtr<Document> documentReference(m_weakFactory.createWeakPtr());
callOnMainThread([=] {
ASSERT(isMainThread());
std::unique_ptr<Task> task(taskPtr);
Document* document = documentReference.get();
if (!document)
return;
Page* page = document->page();
if ((page && page->defersLoading() && document->activeDOMObjectsAreSuspended()) || !document->m_pendingTasks.isEmpty())
document->m_pendingTasks.append(WTF::move(*task.release()));
else
task->performTask(*document);
});
}
void Document::pendingTasksTimerFired()
{
Vector<Task> pendingTasks = WTF::move(m_pendingTasks);
for (auto& task : pendingTasks)
task.performTask(*this);
}
void Document::suspendScheduledTasks(ActiveDOMObject::ReasonForSuspension reason)
{
#if PLATFORM(IOS)
if (m_scheduledTasksAreSuspended) {
ASSERT(reasonForSuspendingActiveDOMObjects() == ActiveDOMObject::DocumentWillBePaused);
return;
}
#endif
ASSERT(!m_scheduledTasksAreSuspended);
suspendScriptedAnimationControllerCallbacks();
suspendActiveDOMObjects(reason);
scriptRunner()->suspend();
m_pendingTasksTimer.stop();
if (reason == ActiveDOMObject::WillDeferLoading && m_parser)
m_parser->suspendScheduledTasks();
m_scheduledTasksAreSuspended = true;
}
void Document::resumeScheduledTasks(ActiveDOMObject::ReasonForSuspension reason)
{
if (reasonForSuspendingActiveDOMObjects() != reason)
return;
ASSERT(m_scheduledTasksAreSuspended);
if (reason == ActiveDOMObject::WillDeferLoading && m_parser)
m_parser->resumeScheduledTasks();
if (!m_pendingTasks.isEmpty())
m_pendingTasksTimer.startOneShot(0);
scriptRunner()->resume();
resumeActiveDOMObjects(reason);
resumeScriptedAnimationControllerCallbacks();
m_scheduledTasksAreSuspended = false;
}
void Document::suspendScriptedAnimationControllerCallbacks()
{
#if ENABLE(REQUEST_ANIMATION_FRAME)
if (m_scriptedAnimationController)
m_scriptedAnimationController->suspend();
#endif
}
void Document::resumeScriptedAnimationControllerCallbacks()
{
#if ENABLE(REQUEST_ANIMATION_FRAME)
if (m_scriptedAnimationController)
m_scriptedAnimationController->resume();
#endif
}
void Document::scriptedAnimationControllerSetThrottled(bool isThrottled)
{
#if ENABLE(REQUEST_ANIMATION_FRAME)
if (m_scriptedAnimationController)
m_scriptedAnimationController->setThrottled(isThrottled);
#else
UNUSED_PARAM(isThrottled);
#endif
}
void Document::windowScreenDidChange(PlatformDisplayID displayID)
{
UNUSED_PARAM(displayID);
#if ENABLE(REQUEST_ANIMATION_FRAME)
if (m_scriptedAnimationController)
m_scriptedAnimationController->windowScreenDidChange(displayID);
#endif
if (RenderView* view = renderView()) {
if (view->usesCompositing())
view->compositor().windowScreenDidChange(displayID);
}
}
String Document::displayStringModifiedByEncoding(const String& str) const
{
if (m_decoder)
return m_decoder->encoding().displayString(str.impl());
return str;
}
RefPtr<StringImpl> Document::displayStringModifiedByEncoding(PassRefPtr<StringImpl> str) const
{
if (m_decoder)
return m_decoder->encoding().displayString(str);
return str;
}
template <typename CharacterType>
void Document::displayBufferModifiedByEncodingInternal(CharacterType* buffer, unsigned len) const
{
if (m_decoder)
m_decoder->encoding().displayBuffer(buffer, len);
}
template void Document::displayBufferModifiedByEncodingInternal<LChar>(LChar*, unsigned) const;
template void Document::displayBufferModifiedByEncodingInternal<UChar>(UChar*, unsigned) const;
void Document::enqueuePageshowEvent(PageshowEventPersistence persisted)
{
dispatchWindowEvent(PageTransitionEvent::create(eventNames().pageshowEvent, persisted), this);
}
void Document::enqueueHashchangeEvent(const String& oldURL, const String& newURL)
{
enqueueWindowEvent(HashChangeEvent::create(oldURL, newURL));
}
void Document::enqueuePopstateEvent(PassRefPtr<SerializedScriptValue> stateObject)
{
dispatchWindowEvent(PopStateEvent::create(stateObject, m_domWindow ? m_domWindow->history() : nullptr));
}
void Document::addMediaCanStartListener(MediaCanStartListener* listener)
{
ASSERT(!m_mediaCanStartListeners.contains(listener));
m_mediaCanStartListeners.add(listener);
}
void Document::removeMediaCanStartListener(MediaCanStartListener* listener)
{
ASSERT(m_mediaCanStartListeners.contains(listener));
m_mediaCanStartListeners.remove(listener);
}
MediaCanStartListener* Document::takeAnyMediaCanStartListener()
{
return m_mediaCanStartListeners.takeAny();
}
#if ENABLE(DEVICE_ORIENTATION) && PLATFORM(IOS)
DeviceMotionController* Document::deviceMotionController() const
{
return m_deviceMotionController.get();
}
DeviceOrientationController* Document::deviceOrientationController() const
{
return m_deviceOrientationController.get();
}
#endif
#if ENABLE(FULLSCREEN_API)
bool Document::fullScreenIsAllowedForElement(Element* element) const
{
ASSERT(element);
return isAttributeOnAllOwners(allowfullscreenAttr, webkitallowfullscreenAttr, element->document().ownerElement());
}
void Document::requestFullScreenForElement(Element* element, unsigned short flags, FullScreenCheckType checkType)
{
bool inLegacyMozillaMode = (flags & Element::LEGACY_MOZILLA_REQUEST);
do {
if (!element)
element = documentElement();
if (!element->inDocument())
break;
if (checkType == EnforceIFrameAllowFullScreenRequirement && !fullScreenIsAllowedForElement(element))
break;
if (!m_fullScreenElementStack.isEmpty() && !m_fullScreenElementStack.last()->contains(element) && !inLegacyMozillaMode)
break;
bool descendentHasNonEmptyStack = false;
for (Frame* descendant = frame() ? frame()->tree().traverseNext() : nullptr; descendant; descendant = descendant->tree().traverseNext()) {
if (descendant->document()->webkitFullscreenElement()) {
descendentHasNonEmptyStack = true;
break;
}
}
if (descendentHasNonEmptyStack && !inLegacyMozillaMode)
break;
if (!ScriptController::processingUserGesture())
break;
if (!page() || !page()->settings().fullScreenEnabled())
break;
if (!page()->chrome().client().supportsFullScreenForElement(element, flags & Element::ALLOW_KEYBOARD_INPUT)) {
if (!inLegacyMozillaMode && flags & Element::ALLOW_KEYBOARD_INPUT) {
flags &= ~Element::ALLOW_KEYBOARD_INPUT;
if (!page()->chrome().client().supportsFullScreenForElement(element, false))
break;
} else
break;
}
Document* currentDoc = this;
Deque<Document*> docs;
do {
docs.prepend(currentDoc);
currentDoc = currentDoc->ownerElement() ? ¤tDoc->ownerElement()->document() : nullptr;
} while (currentDoc);
Deque<Document*>::iterator current = docs.begin(), following = docs.begin();
do {
++following;
Document* currentDoc = *current;
Document* followingDoc = following != docs.end() ? *following : nullptr;
if (!followingDoc) {
currentDoc->pushFullscreenElementStack(element);
addDocumentToFullScreenChangeEventQueue(currentDoc);
continue;
}
Element* topElement = currentDoc->webkitFullscreenElement();
if (!topElement || topElement != followingDoc->ownerElement()) {
currentDoc->pushFullscreenElementStack(followingDoc->ownerElement());
addDocumentToFullScreenChangeEventQueue(currentDoc);
continue;
}
} while (++current != docs.end());
m_areKeysEnabledInFullScreen = flags & Element::ALLOW_KEYBOARD_INPUT;
page()->chrome().client().enterFullScreenForElement(element);
return;
} while (0);
m_fullScreenErrorEventTargetQueue.append(element ? element : documentElement());
m_fullScreenChangeDelayTimer.startOneShot(0);
}
void Document::webkitCancelFullScreen()
{
Document& topDocument = this->topDocument();
if (!topDocument.webkitFullscreenElement())
return;
Vector<RefPtr<Element>> replacementFullscreenElementStack;
replacementFullscreenElementStack.append(topDocument.webkitFullscreenElement());
topDocument.m_fullScreenElementStack.swap(replacementFullscreenElementStack);
topDocument.webkitExitFullscreen();
}
void Document::webkitExitFullscreen()
{
Document* currentDoc = this;
if (m_fullScreenElementStack.isEmpty())
return;
Deque<RefPtr<Document>> descendants;
for (Frame* descendant = frame() ? frame()->tree().traverseNext() : nullptr; descendant; descendant = descendant->tree().traverseNext()) {
if (descendant->document()->webkitFullscreenElement())
descendants.prepend(descendant->document());
}
for (auto& document : descendants) {
document->clearFullscreenElementStack();
addDocumentToFullScreenChangeEventQueue(document.get());
}
Element* newTop = nullptr;
while (currentDoc) {
currentDoc->popFullscreenElementStack();
newTop = currentDoc->webkitFullscreenElement();
if (newTop && (!newTop->inDocument() || &newTop->document() != currentDoc))
continue;
addDocumentToFullScreenChangeEventQueue(currentDoc);
if (!newTop && currentDoc->ownerElement()) {
currentDoc = ¤tDoc->ownerElement()->document();
continue;
}
currentDoc = nullptr;
}
if (!page())
return;
if (!newTop) {
page()->chrome().client().exitFullScreenForElement(m_fullScreenElement.get());
return;
}
page()->chrome().client().enterFullScreenForElement(newTop);
}
bool Document::webkitFullscreenEnabled() const
{
return isAttributeOnAllOwners(allowfullscreenAttr, webkitallowfullscreenAttr, ownerElement());
}
static void unwrapFullScreenRenderer(RenderFullScreen* fullScreenRenderer, Element* fullScreenElement)
{
if (!fullScreenRenderer)
return;
bool requiresRenderTreeRebuild;
fullScreenRenderer->unwrapRenderer(requiresRenderTreeRebuild);
if (requiresRenderTreeRebuild && fullScreenElement && fullScreenElement->parentNode())
fullScreenElement->parentNode()->setNeedsStyleRecalc(ReconstructRenderTree);
}
static bool hostIsYouTube(const String& host)
{
static NeverDestroyed<JSC::Yarr::RegularExpression> youtubePattern("(^|\\.)youtube.(com|co.uk|[a-z]{2})$", TextCaseInsensitive);
ASSERT(youtubePattern.get().isValid());
return youtubePattern.get().match(host);
}
void Document::webkitWillEnterFullScreenForElement(Element* element)
{
if (!hasLivingRenderTree() || inPageCache())
return;
ASSERT(element);
if (!page())
return;
ASSERT(page()->settings().fullScreenEnabled());
unwrapFullScreenRenderer(m_fullScreenRenderer, m_fullScreenElement.get());
m_fullScreenElement = element;
#if USE(NATIVE_FULLSCREEN_VIDEO)
if (element && element->isMediaElement())
return;
#endif
auto renderer = m_fullScreenElement->renderer();
bool shouldCreatePlaceholder = is<RenderBox>(renderer);
if (shouldCreatePlaceholder) {
m_savedPlaceholderFrameRect = downcast<RenderBox>(*renderer).frameRect();
m_savedPlaceholderRenderStyle = RenderStyle::clone(&renderer->style());
}
if (m_fullScreenElement != documentElement())
RenderFullScreen::wrapRenderer(renderer, renderer ? renderer->parent() : nullptr, *this);
m_fullScreenElement->setContainsFullScreenElementOnAncestorsCrossingFrameBoundaries(true);
recalcStyle(Style::Force);
if (settings() && settings()->needsSiteSpecificQuirks() && hostIsYouTube(url().host()))
fullScreenChangeDelayTimerFired();
}
void Document::webkitDidEnterFullScreenForElement(Element*)
{
if (!m_fullScreenElement)
return;
if (!hasLivingRenderTree() || inPageCache())
return;
m_fullScreenElement->didBecomeFullscreenElement();
if (!settings() || !settings()->needsSiteSpecificQuirks() || !hostIsYouTube(url().host()))
m_fullScreenChangeDelayTimer.startOneShot(0);
}
void Document::webkitWillExitFullScreenForElement(Element*)
{
if (!m_fullScreenElement)
return;
if (!hasLivingRenderTree() || inPageCache())
return;
m_fullScreenElement->willStopBeingFullscreenElement();
}
void Document::webkitDidExitFullScreenForElement(Element*)
{
if (!m_fullScreenElement)
return;
if (!hasLivingRenderTree() || inPageCache())
return;
m_fullScreenElement->setContainsFullScreenElementOnAncestorsCrossingFrameBoundaries(false);
m_areKeysEnabledInFullScreen = false;
unwrapFullScreenRenderer(m_fullScreenRenderer, m_fullScreenElement.get());
m_fullScreenElement = nullptr;
scheduleForcedStyleRecalc();
bool eventTargetQueuesEmpty = m_fullScreenChangeEventTargetQueue.isEmpty() && m_fullScreenErrorEventTargetQueue.isEmpty();
Document& exitingDocument = eventTargetQueuesEmpty ? topDocument() : *this;
if (settings() && settings()->needsSiteSpecificQuirks() && hostIsYouTube(url().host()))
exitingDocument.fullScreenChangeDelayTimerFired();
else
exitingDocument.m_fullScreenChangeDelayTimer.startOneShot(0);
}
void Document::setFullScreenRenderer(RenderFullScreen* renderer)
{
if (renderer == m_fullScreenRenderer)
return;
if (renderer && m_savedPlaceholderRenderStyle)
renderer->createPlaceholder(m_savedPlaceholderRenderStyle.releaseNonNull(), m_savedPlaceholderFrameRect);
else if (renderer && m_fullScreenRenderer && m_fullScreenRenderer->placeholder()) {
RenderBlock* placeholder = m_fullScreenRenderer->placeholder();
renderer->createPlaceholder(RenderStyle::clone(&placeholder->style()), placeholder->frameRect());
}
if (m_fullScreenRenderer)
m_fullScreenRenderer->destroy();
ASSERT(!m_fullScreenRenderer);
m_fullScreenRenderer = renderer;
}
void Document::fullScreenRendererDestroyed()
{
m_fullScreenRenderer = nullptr;
}
void Document::fullScreenChangeDelayTimerFired()
{
Ref<Document> protect(*this);
Deque<RefPtr<Node>> changeQueue;
m_fullScreenChangeEventTargetQueue.swap(changeQueue);
Deque<RefPtr<Node>> errorQueue;
m_fullScreenErrorEventTargetQueue.swap(errorQueue);
dispatchFullScreenChangeOrErrorEvent(changeQueue, eventNames().webkitfullscreenchangeEvent, true);
dispatchFullScreenChangeOrErrorEvent(errorQueue, eventNames().webkitfullscreenerrorEvent, false);
}
void Document::dispatchFullScreenChangeOrErrorEvent(Deque<RefPtr<Node>>& queue, const AtomicString& eventName, bool shouldNotifyMediaElement)
{
while (!queue.isEmpty()) {
RefPtr<Node> node = queue.takeFirst();
if (!node)
node = documentElement();
if (!node)
continue;
if (!node->inDocument())
queue.append(documentElement());
#if ENABLE(VIDEO)
if (shouldNotifyMediaElement && is<HTMLMediaElement>(*node))
downcast<HTMLMediaElement>(*node).enteredOrExitedFullscreen();
#endif
node->dispatchEvent(Event::create(eventName, true, false));
}
}
void Document::fullScreenElementRemoved()
{
m_fullScreenElement->setContainsFullScreenElementOnAncestorsCrossingFrameBoundaries(false);
webkitCancelFullScreen();
}
void Document::removeFullScreenElementOfSubtree(Node* node, bool amongChildrenOnly)
{
if (!m_fullScreenElement)
return;
bool elementInSubtree = false;
if (amongChildrenOnly)
elementInSubtree = m_fullScreenElement->isDescendantOf(node);
else
elementInSubtree = (m_fullScreenElement == node) || m_fullScreenElement->isDescendantOf(node);
if (elementInSubtree)
fullScreenElementRemoved();
}
bool Document::isAnimatingFullScreen() const
{
return m_isAnimatingFullScreen;
}
void Document::setAnimatingFullScreen(bool flag)
{
if (m_isAnimatingFullScreen == flag)
return;
m_isAnimatingFullScreen = flag;
if (m_fullScreenElement && m_fullScreenElement->isDescendantOf(this)) {
m_fullScreenElement->setNeedsStyleRecalc();
scheduleForcedStyleRecalc();
}
}
void Document::clearFullscreenElementStack()
{
m_fullScreenElementStack.clear();
}
void Document::popFullscreenElementStack()
{
if (m_fullScreenElementStack.isEmpty())
return;
m_fullScreenElementStack.removeLast();
}
void Document::pushFullscreenElementStack(Element* element)
{
m_fullScreenElementStack.append(element);
}
void Document::addDocumentToFullScreenChangeEventQueue(Document* doc)
{
ASSERT(doc);
Node* target = doc->webkitFullscreenElement();
if (!target)
target = doc->webkitCurrentFullScreenElement();
if (!target)
target = doc;
m_fullScreenChangeEventTargetQueue.append(target);
}
#endif
#if ENABLE(POINTER_LOCK)
void Document::exitPointerLock()
{
if (!page())
return;
if (Element* target = page()->pointerLockController().element()) {
if (&target->document() != this)
return;
}
page()->pointerLockController().requestPointerUnlock();
}
Element* Document::pointerLockElement() const
{
if (!page() || page()->pointerLockController().lockPending())
return nullptr;
if (Element* element = page()->pointerLockController().element()) {
if (&element->document() == this)
return element;
}
return nullptr;
}
#endif
void Document::decrementLoadEventDelayCount()
{
ASSERT(m_loadEventDelayCount);
--m_loadEventDelayCount;
if (frame() && !m_loadEventDelayCount && !m_loadEventDelayTimer.isActive())
m_loadEventDelayTimer.startOneShot(0);
}
void Document::loadEventDelayTimerFired()
{
if (frame())
frame()->loader().checkCompleted();
}
#if ENABLE(REQUEST_ANIMATION_FRAME)
int Document::requestAnimationFrame(PassRefPtr<RequestAnimationFrameCallback> callback)
{
if (!m_scriptedAnimationController) {
#if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
m_scriptedAnimationController = ScriptedAnimationController::create(this, page() ? page()->chrome().displayID() : 0);
#else
m_scriptedAnimationController = ScriptedAnimationController::create(this, 0);
#endif
if (!page() || page()->scriptedAnimationsSuspended())
m_scriptedAnimationController->suspend();
}
return m_scriptedAnimationController->registerCallback(callback);
}
void Document::cancelAnimationFrame(int id)
{
if (!m_scriptedAnimationController)
return;
m_scriptedAnimationController->cancelCallback(id);
}
void Document::serviceScriptedAnimations(double monotonicAnimationStartTime)
{
if (!m_scriptedAnimationController)
return;
m_scriptedAnimationController->serviceScriptedAnimations(monotonicAnimationStartTime);
}
void Document::clearScriptedAnimationController()
{
if (m_scriptedAnimationController)
m_scriptedAnimationController->clearDocumentPointer();
m_scriptedAnimationController = nullptr;
}
#endif
void Document::sendWillRevealEdgeEventsIfNeeded(const IntPoint& oldPosition, const IntPoint& newPosition, const IntRect& visibleRect, const IntSize& contentsSize, Element* target)
{
#if ENABLE(WILL_REVEAL_EDGE_EVENTS)
int willRevealBottomNotificationPoint = std::max(0, contentsSize.height() - 2 * visibleRect.height());
int willRevealTopNotificationPoint = visibleRect.height();
if (newPosition.y() >= willRevealBottomNotificationPoint && newPosition.y() > oldPosition.y()
&& willRevealBottomNotificationPoint >= oldPosition.y()) {
RefPtr<Event> willRevealEvent = Event::create(eventNames().webkitwillrevealbottomEvent, false, false);
if (!target)
enqueueWindowEvent(willRevealEvent.release());
else {
willRevealEvent->setTarget(target);
m_eventQueue.enqueueEvent(willRevealEvent.release());
}
}
if (newPosition.y() <= willRevealTopNotificationPoint && newPosition.y() < oldPosition.y()
&& willRevealTopNotificationPoint <= oldPosition.y()) {
RefPtr<Event> willRevealEvent = Event::create(eventNames().webkitwillrevealtopEvent, false, false);
if (!target)
enqueueWindowEvent(willRevealEvent.release());
else {
willRevealEvent->setTarget(target);
m_eventQueue.enqueueEvent(willRevealEvent.release());
}
}
int willRevealRightNotificationPoint = std::max(0, contentsSize.width() - 2 * visibleRect.width());
int willRevealLeftNotificationPoint = visibleRect.width();
if (newPosition.x() >= willRevealRightNotificationPoint && newPosition.x() > oldPosition.x()
&& willRevealRightNotificationPoint >= oldPosition.x()) {
RefPtr<Event> willRevealEvent = Event::create(eventNames().webkitwillrevealrightEvent, false, false);
if (!target)
enqueueWindowEvent(willRevealEvent.release());
else {
willRevealEvent->setTarget(target);
m_eventQueue.enqueueEvent(willRevealEvent.release());
}
}
if (newPosition.x() <= willRevealLeftNotificationPoint && newPosition.x() < oldPosition.x()
&& willRevealLeftNotificationPoint <= oldPosition.x()) {
RefPtr<Event> willRevealEvent = Event::create(eventNames().webkitwillrevealleftEvent, false, false);
if (!target)
enqueueWindowEvent(willRevealEvent.release());
else {
willRevealEvent->setTarget(target);
m_eventQueue.enqueueEvent(willRevealEvent.release());
}
}
#else
UNUSED_PARAM(oldPosition);
UNUSED_PARAM(newPosition);
UNUSED_PARAM(visibleRect);
UNUSED_PARAM(contentsSize);
UNUSED_PARAM(target);
#endif
}
#if !PLATFORM(IOS)
#if ENABLE(TOUCH_EVENTS)
RefPtr<Touch> Document::createTouch(DOMWindow* window, EventTarget* target, int identifier, int pageX, int pageY, int screenX, int screenY, int radiusX, int radiusY, float rotationAngle, float force, ExceptionCode&) const
{
Frame* frame = window ? window->frame() : this->frame();
return Touch::create(frame, target, identifier, screenX, screenY, pageX, pageY, radiusX, radiusY, rotationAngle, force);
}
#endif
#endif // !PLATFORM(IOS)
void Document::wheelEventHandlersChanged()
{
Page* page = this->page();
if (!page)
return;
if (FrameView* frameView = view()) {
if (ScrollingCoordinator* scrollingCoordinator = page->scrollingCoordinator())
scrollingCoordinator->frameViewNonFastScrollableRegionChanged(*frameView);
}
bool haveHandlers = m_wheelEventTargets && !m_wheelEventTargets->isEmpty();
page->chrome().client().wheelEventHandlersChanged(haveHandlers);
}
void Document::didAddWheelEventHandler(Node& node)
{
if (!m_wheelEventTargets)
m_wheelEventTargets = std::make_unique<EventTargetSet>();
m_wheelEventTargets->add(&node);
wheelEventHandlersChanged();
if (Frame* frame = this->frame())
DebugPageOverlays::didChangeEventHandlers(*frame);
}
HttpEquivPolicy Document::httpEquivPolicy() const
{
if (shouldEnforceContentDispositionAttachmentSandbox())
return HttpEquivPolicy::DisabledByContentDispositionAttachmentSandbox;
if (page() && !page()->settings().httpEquivEnabled())
return HttpEquivPolicy::DisabledBySettings;
return HttpEquivPolicy::Enabled;
}
static bool removeHandlerFromSet(EventTargetSet& handlerSet, Node& node, EventHandlerRemoval removal)
{
switch (removal) {
case EventHandlerRemoval::One:
return handlerSet.remove(&node);
case EventHandlerRemoval::All:
return handlerSet.removeAll(&node);
}
return false;
}
void Document::didRemoveWheelEventHandler(Node& node, EventHandlerRemoval removal)
{
if (!m_wheelEventTargets)
return;
if (!removeHandlerFromSet(*m_wheelEventTargets, node, removal))
return;
wheelEventHandlersChanged();
if (Frame* frame = this->frame())
DebugPageOverlays::didChangeEventHandlers(*frame);
}
unsigned Document::wheelEventHandlerCount() const
{
if (!m_wheelEventTargets)
return 0;
unsigned count = 0;
for (auto& handler : *m_wheelEventTargets)
count += handler.value;
return count;
}
void Document::didAddTouchEventHandler(Node& handler)
{
#if ENABLE(TOUCH_EVENTS)
if (!m_touchEventTargets)
m_touchEventTargets = std::make_unique<EventTargetSet>();
m_touchEventTargets->add(&handler);
if (Document* parent = parentDocument()) {
parent->didAddTouchEventHandler(*this);
return;
}
if (Page* page = this->page()) {
if (m_touchEventTargets->size() == 1)
page->chrome().client().needTouchEvents(true);
}
#else
UNUSED_PARAM(handler);
#endif
}
void Document::didRemoveTouchEventHandler(Node& handler, EventHandlerRemoval removal)
{
#if ENABLE(TOUCH_EVENTS)
if (!m_touchEventTargets)
return;
removeHandlerFromSet(*m_touchEventTargets, handler, removal);
if (Document* parent = parentDocument()) {
parent->didRemoveTouchEventHandler(*this);
return;
}
Page* page = this->page();
if (!page)
return;
if (m_touchEventTargets->size())
return;
for (const Frame* frame = &page->mainFrame(); frame; frame = frame->tree().traverseNext()) {
if (frame->document() && frame->document()->hasTouchEventHandlers())
return;
}
page->chrome().client().needTouchEvents(false);
#else
UNUSED_PARAM(handler);
UNUSED_PARAM(removal);
#endif
}
void Document::didRemoveEventTargetNode(Node& handler)
{
#if ENABLE(TOUCH_EVENTS)
if (m_touchEventTargets) {
m_touchEventTargets->removeAll(&handler);
if ((&handler == this || m_touchEventTargets->isEmpty()) && parentDocument())
parentDocument()->didRemoveEventTargetNode(*this);
}
#endif
if (m_wheelEventTargets) {
m_wheelEventTargets->removeAll(&handler);
if ((&handler == this || m_wheelEventTargets->isEmpty()) && parentDocument())
parentDocument()->didRemoveEventTargetNode(*this);
}
}
unsigned Document::touchEventHandlerCount() const
{
#if ENABLE(TOUCH_EVENTS)
if (!m_touchEventTargets)
return 0;
unsigned count = 0;
for (auto& handler : *m_touchEventTargets)
count += handler.value;
return count;
#else
return 0;
#endif
}
LayoutRect Document::absoluteEventHandlerBounds(bool& includesFixedPositionElements)
{
includesFixedPositionElements = false;
if (RenderView* renderView = this->renderView())
return renderView->documentRect();
return LayoutRect();
}
Document::RegionFixedPair Document::absoluteRegionForEventTargets(const EventTargetSet* targets)
{
if (!targets)
return RegionFixedPair(Region(), false);
Region targetRegion;
bool insideFixedPosition = false;
for (auto& keyValuePair : *targets) {
LayoutRect rootRelativeBounds;
if (is<Document>(keyValuePair.key)) {
Document* document = downcast<Document>(keyValuePair.key);
if (document == this)
rootRelativeBounds = absoluteEventHandlerBounds(insideFixedPosition);
else if (Element* element = document->ownerElement())
rootRelativeBounds = element->absoluteEventHandlerBounds(insideFixedPosition);
} else if (is<Element>(keyValuePair.key)) {
Element* element = downcast<Element>(keyValuePair.key);
rootRelativeBounds = element->absoluteEventHandlerBounds(insideFixedPosition);
}
if (!rootRelativeBounds.isEmpty())
targetRegion.unite(Region(enclosingIntRect(rootRelativeBounds)));
}
return RegionFixedPair(targetRegion, insideFixedPosition);
}
void Document::updateLastHandledUserGestureTimestamp()
{
m_lastHandledUserGestureTimestamp = monotonicallyIncreasingTime();
}
void Document::startTrackingStyleRecalcs()
{
m_styleRecalcCount = 0;
}
unsigned Document::styleRecalcCount() const
{
return m_styleRecalcCount;
}
DocumentLoader* Document::loader() const
{
if (!m_frame)
return nullptr;
DocumentLoader* loader = m_frame->loader().documentLoader();
if (!loader)
return nullptr;
if (m_frame->document() != this)
return nullptr;
return loader;
}
#if ENABLE(CSS_DEVICE_ADAPTATION)
IntSize Document::initialViewportSize() const
{
if (!view())
return IntSize();
return view()->initialViewportSize();
}
#endif
Element* eventTargetElementForDocument(Document* document)
{
if (!document)
return nullptr;
Element* element = document->focusedElement();
if (!element && is<PluginDocument>(*document))
element = downcast<PluginDocument>(*document).pluginElement();
if (!element && is<HTMLDocument>(*document))
element = document->bodyOrFrameset();
if (!element)
element = document->documentElement();
return element;
}
void Document::adjustFloatQuadsForScrollAndAbsoluteZoomAndFrameScale(Vector<FloatQuad>& quads, const RenderStyle& style)
{
if (!view())
return;
float zoom = style.effectiveZoom();
float inverseFrameScale = 1;
if (frame())
inverseFrameScale = 1 / frame()->frameScaleFactor();
LayoutRect visibleContentRect = view()->visibleContentRect();
for (size_t i = 0; i < quads.size(); ++i) {
quads[i].move(-visibleContentRect.x(), -visibleContentRect.y());
if (zoom != 1)
quads[i].scale(1 / zoom, 1 / zoom);
if (inverseFrameScale != 1)
quads[i].scale(inverseFrameScale, inverseFrameScale);
}
}
void Document::adjustFloatRectForScrollAndAbsoluteZoomAndFrameScale(FloatRect& rect, const RenderStyle& style)
{
if (!view())
return;
float zoom = style.effectiveZoom();
float inverseFrameScale = 1;
if (frame())
inverseFrameScale = 1 / frame()->frameScaleFactor();
LayoutRect visibleContentRect = view()->visibleContentRect();
rect.move(-visibleContentRect.x(), -visibleContentRect.y());
if (zoom != 1)
rect.scale(1 / zoom);
if (inverseFrameScale != 1)
rect.scale(inverseFrameScale);
}
bool Document::hasActiveParser()
{
return m_activeParserCount || (m_parser && m_parser->processingData());
}
void Document::decrementActiveParserCount()
{
--m_activeParserCount;
if (!frame())
return;
frame()->loader().checkLoadComplete();
}
static RenderElement* nearestCommonHoverAncestor(RenderElement* obj1, RenderElement* obj2)
{
if (!obj1 || !obj2)
return nullptr;
for (RenderElement* currObj1 = obj1; currObj1; currObj1 = currObj1->hoverAncestor()) {
for (RenderElement* currObj2 = obj2; currObj2; currObj2 = currObj2->hoverAncestor()) {
if (currObj1 == currObj2)
return currObj1;
}
}
return nullptr;
}
void Document::updateHoverActiveState(const HitTestRequest& request, Element* innerElement, StyleResolverUpdateFlag updateFlag)
{
ASSERT(!request.readOnly());
Element* innerElementInDocument = innerElement;
while (innerElementInDocument && &innerElementInDocument->document() != this) {
innerElementInDocument->document().updateHoverActiveState(request, innerElementInDocument);
innerElementInDocument = innerElementInDocument->document().ownerElement();
}
Element* oldActiveElement = m_activeElement.get();
if (oldActiveElement && !request.active()) {
for (Element* curr = oldActiveElement; curr; curr = curr->parentOrShadowHostElement()) {
curr->setActive(false);
m_userActionElements.setInActiveChain(curr, false);
}
m_activeElement = nullptr;
} else {
Element* newActiveElement = innerElementInDocument;
if (!oldActiveElement && newActiveElement && request.active() && !request.touchMove()) {
for (RenderElement* curr = newActiveElement->renderer(); curr; curr = curr->parent()) {
Element* element = curr->element();
if (!element || curr->isTextOrLineBreak())
continue;
m_userActionElements.setInActiveChain(element, true);
}
m_activeElement = newActiveElement;
}
}
bool allowActiveChanges = !oldActiveElement && m_activeElement;
bool mustBeInActiveChain = request.active() && request.move();
RefPtr<Element> oldHoveredElement = m_hoveredElement.release();
if (request.touchRelease())
innerElementInDocument = nullptr;
Element* newHoveredElement = innerElementInDocument;
while (newHoveredElement && !newHoveredElement->renderer())
newHoveredElement = newHoveredElement->parentOrShadowHostElement();
m_hoveredElement = newHoveredElement;
RenderElement* oldHoverObj = oldHoveredElement ? oldHoveredElement->renderer() : nullptr;
RenderElement* newHoverObj = newHoveredElement ? newHoveredElement->renderer() : nullptr;
RenderElement* ancestor = nearestCommonHoverAncestor(oldHoverObj, newHoverObj);
Vector<RefPtr<Element>, 32> elementsToRemoveFromChain;
Vector<RefPtr<Element>, 32> elementsToAddToChain;
if (oldHoverObj != newHoverObj) {
if (oldHoveredElement && !oldHoverObj) {
for (Element* element = oldHoveredElement.get(); element; element = element->parentElement()) {
if (!mustBeInActiveChain || element->inActiveChain())
elementsToRemoveFromChain.append(element);
}
}
for (RenderElement* curr = oldHoverObj; curr && curr != ancestor; curr = curr->hoverAncestor()) {
Element* element = curr->element();
if (!element)
continue;
if (!mustBeInActiveChain || element->inActiveChain())
elementsToRemoveFromChain.append(element);
}
if (is<HTMLFrameOwnerElement>(oldHoveredElement.get())) {
if (Document* contentDocument = downcast<HTMLFrameOwnerElement>(*oldHoveredElement).contentDocument())
contentDocument->updateHoverActiveState(request, nullptr);
}
}
for (RenderElement* curr = newHoverObj; curr; curr = curr->hoverAncestor()) {
Element* element = curr->element();
if (!element)
continue;
if (!mustBeInActiveChain || element->inActiveChain())
elementsToAddToChain.append(element);
}
size_t removeCount = elementsToRemoveFromChain.size();
for (size_t i = 0; i < removeCount; ++i)
elementsToRemoveFromChain[i]->setHovered(false);
bool sawCommonAncestor = false;
for (size_t i = 0, size = elementsToAddToChain.size(); i < size; ++i) {
if (allowActiveChanges)
elementsToAddToChain[i]->setActive(true);
if (ancestor && elementsToAddToChain[i] == ancestor->element())
sawCommonAncestor = true;
if (!sawCommonAncestor) {
elementsToAddToChain[i]->setHovered(true);
}
}
ASSERT(updateFlag == RecalcStyleIfNeeded || updateFlag == DeferRecalcStyleIfNeeded);
if (updateFlag == RecalcStyleIfNeeded)
updateStyleIfNeeded();
}
bool Document::haveStylesheetsLoaded() const
{
return !m_styleSheetCollection.hasPendingSheets() || m_ignorePendingStylesheets;
}
Locale& Document::getCachedLocale(const AtomicString& locale)
{
AtomicString localeKey = locale;
if (locale.isEmpty() || !RuntimeEnabledFeatures::sharedFeatures().langAttributeAwareFormControlUIEnabled())
localeKey = defaultLanguage();
LocaleIdentifierToLocaleMap::AddResult result = m_localeCache.add(localeKey, nullptr);
if (result.isNewEntry)
result.iterator->value = Locale::create(localeKey);
return *(result.iterator->value);
}
#if ENABLE(TEMPLATE_ELEMENT)
Document& Document::ensureTemplateDocument()
{
if (const Document* document = templateDocument())
return const_cast<Document&>(*document);
if (isHTMLDocument())
m_templateDocument = HTMLDocument::create(nullptr, blankURL());
else
m_templateDocument = Document::create(nullptr, blankURL());
m_templateDocument->setTemplateDocumentHost(this);
return *m_templateDocument;
}
#endif
#if ENABLE(FONT_LOAD_EVENTS)
RefPtr<FontLoader> Document::fonts()
{
if (!m_fontloader)
m_fontloader = FontLoader::create(this);
return m_fontloader;
}
#endif
float Document::deviceScaleFactor() const
{
float deviceScaleFactor = 1.0;
if (Page* documentPage = page())
deviceScaleFactor = documentPage->deviceScaleFactor();
return deviceScaleFactor;
}
void Document::didAssociateFormControl(Element* element)
{
if (!frame() || !frame()->page() || !frame()->page()->chrome().client().shouldNotifyOnFormChanges())
return;
m_associatedFormControls.add(element);
if (!m_didAssociateFormControlsTimer.isActive())
m_didAssociateFormControlsTimer.startOneShot(0);
}
void Document::didAssociateFormControlsTimerFired()
{
if (!frame() || !frame()->page())
return;
Vector<RefPtr<Element>> associatedFormControls;
copyToVector(m_associatedFormControls, associatedFormControls);
frame()->page()->chrome().client().didAssociateFormControls(associatedFormControls);
m_associatedFormControls.clear();
}
void Document::setCachedDOMCookies(const String& cookies)
{
ASSERT(!isDOMCookieCacheValid());
m_cachedDOMCookies = cookies;
m_cookieCacheExpiryTimer.startOneShot(0);
}
void Document::invalidateDOMCookieCache()
{
m_cookieCacheExpiryTimer.stop();
m_cachedDOMCookies = String();
}
void Document::domCookieCacheExpiryTimerFired()
{
invalidateDOMCookieCache();
}
void Document::didLoadResourceSynchronously(const ResourceRequest&)
{
invalidateDOMCookieCache();
}
void Document::ensurePlugInsInjectedScript(DOMWrapperWorld& world)
{
if (m_hasInjectedPlugInsScript)
return;
String jsString = page()->chrome().client().plugInExtraScript();
if (!jsString)
jsString = String(plugInsJavaScript, sizeof(plugInsJavaScript));
frame()->script().evaluateInWorld(ScriptSourceCode(jsString), world);
m_hasInjectedPlugInsScript = true;
}
#if ENABLE(SUBTLE_CRYPTO)
bool Document::wrapCryptoKey(const Vector<uint8_t>& key, Vector<uint8_t>& wrappedKey)
{
Page* page = this->page();
if (!page)
return false;
return page->chrome().client().wrapCryptoKey(key, wrappedKey);
}
bool Document::unwrapCryptoKey(const Vector<uint8_t>& wrappedKey, Vector<uint8_t>& key)
{
Page* page = this->page();
if (!page)
return false;
return page->chrome().client().unwrapCryptoKey(wrappedKey, key);
}
#endif // ENABLE(SUBTLE_CRYPTO)
Element* Document::activeElement()
{
if (Element* element = treeScope().focusedElement())
return element;
return bodyOrFrameset();
}
bool Document::hasFocus() const
{
Page* page = this->page();
if (!page || !page->focusController().isActive())
return false;
if (Frame* focusedFrame = page->focusController().focusedFrame()) {
if (focusedFrame->tree().isDescendantOf(frame()))
return true;
}
return false;
}
#if ENABLE(WEB_REPLAY)
void Document::setInputCursor(PassRefPtr<InputCursor> cursor)
{
m_inputCursor = cursor;
}
#endif
#if ENABLE(WIRELESS_PLAYBACK_TARGET)
static uint64_t nextPlaybackTargetClientContextId()
{
static uint64_t contextId = 0;
return ++contextId;
}
void Document::addPlaybackTargetPickerClient(MediaPlaybackTargetClient& client)
{
Page* page = this->page();
if (!page)
return;
if (m_clientToIDMap.contains(&client))
return;
uint64_t contextId = nextPlaybackTargetClientContextId();
m_clientToIDMap.add(&client, contextId);
m_idToClientMap.add(contextId, &client);
page->addPlaybackTargetPickerClient(contextId);
}
void Document::removePlaybackTargetPickerClient(MediaPlaybackTargetClient& client)
{
auto it = m_clientToIDMap.find(&client);
if (it == m_clientToIDMap.end())
return;
uint64_t clientId = it->value;
m_idToClientMap.remove(clientId);
m_clientToIDMap.remove(it);
Page* page = this->page();
if (!page)
return;
page->removePlaybackTargetPickerClient(clientId);
}
void Document::showPlaybackTargetPicker(MediaPlaybackTargetClient& client, bool isVideo)
{
Page* page = this->page();
if (!page)
return;
auto it = m_clientToIDMap.find(&client);
if (it == m_clientToIDMap.end())
return;
page->showPlaybackTargetPicker(it->value, view()->lastKnownMousePosition(), isVideo);
}
void Document::playbackTargetPickerClientStateDidChange(MediaPlaybackTargetClient& client, MediaProducer::MediaStateFlags state)
{
Page* page = this->page();
if (!page)
return;
auto it = m_clientToIDMap.find(&client);
if (it == m_clientToIDMap.end())
return;
page->playbackTargetPickerClientStateDidChange(it->value, state);
}
void Document::playbackTargetAvailabilityDidChange(uint64_t clientId, bool available)
{
auto it = m_idToClientMap.find(clientId);
if (it == m_idToClientMap.end())
return;
it->value->externalOutputDeviceAvailableDidChange(available);
}
void Document::setPlaybackTarget(uint64_t clientId, Ref<MediaPlaybackTarget>&& target)
{
auto it = m_idToClientMap.find(clientId);
if (it == m_idToClientMap.end())
return;
it->value->setPlaybackTarget(target.copyRef());
}
void Document::setShouldPlayToPlaybackTarget(uint64_t clientId, bool shouldPlay)
{
auto it = m_idToClientMap.find(clientId);
if (it == m_idToClientMap.end())
return;
it->value->setShouldPlayToPlaybackTarget(shouldPlay);
}
#endif // ENABLE(WIRELESS_PLAYBACK_TARGET)
#if ENABLE(MEDIA_SESSION)
MediaSession& Document::defaultMediaSession()
{
if (!m_defaultMediaSession)
m_defaultMediaSession = adoptRef(*new MediaSession(*this));
return *m_defaultMediaSession;
}
#endif
ShouldOpenExternalURLsPolicy Document::shouldOpenExternalURLsPolicyToPropagate() const
{
if (DocumentLoader* documentLoader = loader())
return documentLoader->shouldOpenExternalURLsPolicyToPropagate();
return ShouldOpenExternalURLsPolicy::ShouldNotAllow;
}
bool Document::shouldEnforceContentDispositionAttachmentSandbox() const
{
if (m_isSynthesized)
return false;
bool contentDispositionAttachmentSandboxEnabled = settings() && settings()->contentDispositionAttachmentSandboxEnabled();
bool responseIsAttachment = false;
if (DocumentLoader* documentLoader = m_frame ? m_frame->loader().activeDocumentLoader() : nullptr)
responseIsAttachment = documentLoader->response().isAttachment();
return contentDispositionAttachmentSandboxEnabled && responseIsAttachment;
}
void Document::applyContentDispositionAttachmentSandbox()
{
ASSERT(shouldEnforceContentDispositionAttachmentSandbox());
setReferrerPolicy(ReferrerPolicyNever);
if (!isMediaDocument())
enforceSandboxFlags(SandboxAll);
else
enforceSandboxFlags(SandboxOrigin);
}
void Document::addViewportDependentPicture(HTMLPictureElement& picture)
{
m_viewportDependentPictures.add(&picture);
}
void Document::removeViewportDependentPicture(HTMLPictureElement& picture)
{
m_viewportDependentPictures.remove(&picture);
}
}