summaryrefslogtreecommitdiff
path: root/Source/WebCore/dom/Document.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/dom/Document.cpp')
-rw-r--r--Source/WebCore/dom/Document.cpp4908
1 files changed, 2899 insertions, 2009 deletions
diff --git a/Source/WebCore/dom/Document.cpp b/Source/WebCore/dom/Document.cpp
index 1e677dc90..f9e0bf272 100644
--- a/Source/WebCore/dom/Document.cpp
+++ b/Source/WebCore/dom/Document.cpp
@@ -3,7 +3,7 @@
* (C) 1999 Antti Koivisto (koivisto@kde.org)
* (C) 2001 Dirk Mueller (mueller@kde.org)
* (C) 2006 Alexey Proskuryakov (ap@webkit.org)
- * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2011, 2012, 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2004-2017 Apple Inc. All rights reserved.
* Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
* Copyright (C) 2008, 2009, 2011, 2012 Google Inc. All rights reserved.
* Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies)
@@ -29,117 +29,163 @@
#include "Document.h"
#include "AXObjectCache.h"
-#include "AnimationController.h"
#include "Attr.h"
#include "CDATASection.h"
+#include "CSSAnimationController.h"
+#include "CSSFontSelector.h"
#include "CSSStyleDeclaration.h"
#include "CSSStyleSheet.h"
#include "CachedCSSStyleSheet.h"
+#include "CachedFrame.h"
#include "CachedResourceLoader.h"
#include "Chrome.h"
#include "ChromeClient.h"
#include "Comment.h"
+#include "CommonVM.h"
+#include "CompositionEvent.h"
#include "ContentSecurityPolicy.h"
#include "CookieJar.h"
+#include "CustomElementReactionQueue.h"
+#include "CustomElementRegistry.h"
+#include "CustomEvent.h"
#include "DOMImplementation.h"
#include "DOMNamedFlowCollection.h"
#include "DOMWindow.h"
#include "DateComponents.h"
-#include "Dictionary.h"
+#include "DebugPageOverlays.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 "FontLoader.h"
+#include "ExtensionStyleSheets.h"
+#include "FocusController.h"
+#include "FontFaceSet.h"
#include "FormController.h"
#include "FrameLoader.h"
#include "FrameLoaderClient.h"
#include "FrameView.h"
-#include "HashChangeEvent.h"
-#include "HistogramSupport.h"
-#include "History.h"
+#include "GenericCachedHTMLCollection.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 "HTMLHtmlElement.h"
#include "HTMLImageElement.h"
+#include "HTMLInputElement.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 "HTMLUnknownElement.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 "JSCustomElementInterface.h"
#include "JSLazyEventListener.h"
+#include "KeyboardEvent.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 "MessageEvent.h"
#include "MouseEventWithHitTestResults.h"
+#include "MutationEvent.h"
#include "NameNodeList.h"
+#include "NamedFlowCollection.h"
#include "NestingLevelIncrementer.h"
+#include "NoEventDispatchAssertion.h"
#include "NodeIterator.h"
#include "NodeRareData.h"
#include "NodeWithIndex.h"
-#include "PageConsole.h"
+#include "OriginAccessEntry.h"
+#include "OverflowEvent.h"
+#include "PageConsoleClient.h"
#include "PageGroup.h"
#include "PageTransitionEvent.h"
#include "PlatformLocale.h"
+#include "PlatformMediaSessionManager.h"
+#include "PlatformScreen.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 "RenderTreeUpdater.h"
#include "RenderView.h"
#include "RenderWidget.h"
-#include "ResourceLoadScheduler.h"
-#include "ResourceLoader.h"
+#include "RequestAnimationFrameCallback.h"
+#include "ResourceLoadObserver.h"
#include "RuntimeEnabledFeatures.h"
+#include "SVGDocumentExtensions.h"
+#include "SVGElement.h"
+#include "SVGElementFactory.h"
+#include "SVGNames.h"
+#include "SVGSVGElement.h"
+#include "SVGTitleElement.h"
+#include "SVGZoomEvent.h"
#include "SchemeRegistry.h"
#include "ScopedEventQueue.h"
-#include "ScriptCallStack.h"
#include "ScriptController.h"
+#include "ScriptModuleLoader.h"
#include "ScriptRunner.h"
#include "ScriptSourceCode.h"
+#include "ScriptedAnimationController.h"
#include "ScrollingCoordinator.h"
#include "SecurityOrigin.h"
+#include "SecurityOriginData.h"
+#include "SecurityOriginPolicy.h"
#include "SecurityPolicy.h"
#include "SegmentedString.h"
#include "SelectorQuery.h"
#include "Settings.h"
#include "ShadowRoot.h"
+#include "SocketProvider.h"
+#include "StorageEvent.h"
#include "StyleProperties.h"
+#include "StyleResolveForDocument.h"
#include "StyleResolver.h"
+#include "StyleScope.h"
#include "StyleSheetContents.h"
#include "StyleSheetList.h"
-#include "TextResourceDecoder.h"
+#include "StyleTreeResolver.h"
+#include "SubresourceLoader.h"
+#include "TextAutoSizing.h"
+#include "TextEvent.h"
+#include "TextNodeTraversal.h"
#include "TransformSource.h"
#include "TreeWalker.h"
+#include "ValidationMessageClient.h"
#include "VisitedLinkState.h"
+#include "WheelEvent.h"
+#include "WindowFeatures.h"
+#include "XMLDocument.h"
#include "XMLDocumentParser.h"
#include "XMLNSNames.h"
#include "XMLNames.h"
@@ -148,31 +194,27 @@
#include "XPathNSResolver.h"
#include "XPathResult.h"
#include "htmlediting.h"
+#include <ctime>
+#include <inspector/ScriptCallStack.h>
#include <wtf/CurrentTime.h>
-#include <wtf/TemporaryChange.h>
+#include <wtf/NeverDestroyed.h>
+#include <wtf/SetForScope.h>
+#include <wtf/SystemTracing.h>
#include <wtf/text/StringBuffer.h>
+#include <yarr/RegularExpression.h>
-#if USE(ACCELERATED_COMPOSITING)
-#include "RenderLayerCompositor.h"
-#endif
-
-#if ENABLE(SHARED_WORKERS)
-#include "SharedWorkerRepository.h"
-#endif
-
-#if ENABLE(XSLT)
-#include "XSLTProcessor.h"
+#if ENABLE(DEVICE_ORIENTATION)
+#include "DeviceMotionEvent.h"
+#include "DeviceOrientationEvent.h"
#endif
-#if ENABLE(SVG)
-#include "SVGDocumentExtensions.h"
-#include "SVGElementFactory.h"
-#include "SVGNames.h"
-#include "SVGSVGElement.h"
+#if ENABLE(FULLSCREEN_API)
+#include "RenderFullScreen.h"
#endif
-#if ENABLE(TOUCH_EVENTS)
-#include "TouchList.h"
+#if ENABLE(INDEXED_DATABASE)
+#include "IDBConnectionProxy.h"
+#include "IDBOpenDBRequest.h"
#endif
#if PLATFORM(IOS)
@@ -198,29 +240,35 @@
#include "MathMLNames.h"
#endif
-#if ENABLE(FULLSCREEN_API)
-#include "RenderFullScreen.h"
+#if ENABLE(MEDIA_SESSION)
+#include "MediaSession.h"
#endif
-#if ENABLE(REQUEST_ANIMATION_FRAME)
-#include "RequestAnimationFrameCallback.h"
-#include "ScriptedAnimationController.h"
+#if USE(QUICK_LOOK)
+#include "QuickLook.h"
#endif
-#if ENABLE(IOS_TEXT_AUTOSIZING)
-#include "TextAutoSizing.h"
+#if ENABLE(TOUCH_EVENTS)
+#include "TouchEvent.h"
+#include "TouchList.h"
#endif
-#if ENABLE(TEXT_AUTOSIZING)
-#include "TextAutosizer.h"
+#if ENABLE(VIDEO_TRACK)
+#include "CaptionUserPreferences.h"
#endif
-#if ENABLE(CSP_NEXT)
-#include "DOMSecurityPolicy.h"
+#if ENABLE(WEB_REPLAY)
+#include "WebReplayInputs.h"
+#include <replay/EmptyInputCursor.h>
+#include <replay/InputCursor.h>
#endif
-#if ENABLE(VIDEO_TRACK)
-#include "CaptionUserPreferences.h"
+#if ENABLE(WIRELESS_PLAYBACK_TARGET)
+#include "MediaPlaybackTargetClient.h"
+#endif
+
+#if ENABLE(XSLT)
+#include "XSLTProcessor.h"
#endif
using namespace WTF;
@@ -230,9 +278,8 @@ namespace WebCore {
using namespace HTMLNames;
-// #define INSTRUMENT_LAYOUT_SCHEDULING 1
-
static const unsigned cMaxWriteRecursionDepth = 21;
+bool Document::hasEverCreatedAnAXObjectCache = false;
// DOM Level 2 says (letters added):
//
@@ -305,27 +352,14 @@ static inline bool isValidNamePart(UChar32 c)
return true;
}
-static bool shouldInheritSecurityOriginFromOwner(const URL& url)
-{
- // http://www.whatwg.org/specs/web-apps/current-work/#origin-0
- //
- // If a Document has the address "about:blank"
- // The origin of the Document is the origin it was assigned when its browsing context was created.
- //
- // Note: We generalize this to all "blank" URLs and invalid URLs because we
- // treat all of these URLs as about:blank.
- //
- return url.isEmpty() || url.isBlankURL();
-}
-
static Widget* widgetForElement(Element* focusedElement)
{
if (!focusedElement)
return nullptr;
- auto renderer = focusedElement->renderer();
- if (!renderer || !renderer->isWidget())
+ auto* renderer = focusedElement->renderer();
+ if (!is<RenderWidget>(renderer))
return nullptr;
- return toRenderWidget(renderer)->widget();
+ return downcast<RenderWidget>(*renderer).widget();
}
static bool acceptsEditingFocus(Node* node)
@@ -338,31 +372,31 @@ static bool acceptsEditingFocus(Node* node)
if (!frame || !root)
return false;
- return frame->editor().shouldBeginEditing(rangeOfContents(*root).get());
+ return frame->editor().shouldBeginEditing(rangeOfContents(*root).ptr());
}
-static bool canAccessAncestor(const SecurityOrigin* activeSecurityOrigin, Frame* targetFrame)
+static bool canAccessAncestor(const SecurityOrigin& activeSecurityOrigin, Frame* targetFrame)
{
// targetFrame can be 0 when we're trying to navigate a top-level frame
// that has a 0 opener.
if (!targetFrame)
return false;
- const bool isLocalActiveOrigin = activeSecurityOrigin->isLocal();
+ const bool isLocalActiveOrigin = activeSecurityOrigin.isLocal();
for (Frame* ancestorFrame = targetFrame; ancestorFrame; ancestorFrame = ancestorFrame->tree().parent()) {
Document* ancestorDocument = ancestorFrame->document();
// FIXME: Should be an ASSERT? Frames should alway have documents.
if (!ancestorDocument)
return true;
- const SecurityOrigin* ancestorSecurityOrigin = ancestorDocument->securityOrigin();
- if (activeSecurityOrigin->canAccess(ancestorSecurityOrigin))
+ const SecurityOrigin& ancestorSecurityOrigin = ancestorDocument->securityOrigin();
+ if (activeSecurityOrigin.canAccess(ancestorSecurityOrigin))
return true;
// Allow file URL descendant navigation even when allowFileAccessFromFileURLs is false.
// FIXME: It's a bit strange to special-case local origins here. Should we be doing
// something more general instead?
- if (isLocalActiveOrigin && ancestorSecurityOrigin->isLocal())
+ if (isLocalActiveOrigin && ancestorSecurityOrigin.isLocal())
return true;
}
@@ -377,73 +411,73 @@ static void printNavigationErrorMessage(Frame* frame, const URL& activeURL, cons
frame->document()->domWindow()->printErrorMessage(message);
}
-uint64_t Document::s_globalTreeVersion = 0;
-
-static const double timeBeforeThrowingAwayStyleResolverAfterLastUseInSeconds = 30;
+#if ENABLE(TEXT_AUTOSIZING)
-#if ENABLE(IOS_TEXT_AUTOSIZING)
void TextAutoSizingTraits::constructDeletedValue(TextAutoSizingKey& slot)
{
- new (&slot) TextAutoSizingKey(TextAutoSizingKey::deletedKeyStyle(), TextAutoSizingKey::deletedKeyDoc());
+ new (NotNull, &slot) TextAutoSizingKey(TextAutoSizingKey::Deleted);
}
bool TextAutoSizingTraits::isDeletedValue(const TextAutoSizingKey& value)
{
- return value.style() == TextAutoSizingKey::deletedKeyStyle() && value.doc() == TextAutoSizingKey::deletedKeyDoc();
+ return value.isDeleted();
}
+
#endif
+uint64_t Document::s_globalTreeVersion = 0;
+
+HashSet<Document*>& Document::allDocuments()
+{
+ static NeverDestroyed<HashSet<Document*>> documents;
+ return documents;
+}
+
Document::Document(Frame* frame, const URL& url, unsigned documentClasses, unsigned constructionFlags)
- : ContainerNode(nullptr, CreateDocument)
- , TreeScope(this)
-#if ENABLE(TOUCH_EVENTS) && PLATFORM(IOS)
- , m_handlingTouchEvent(false)
- , m_touchEventRegionsDirty(false)
- , m_touchEventsChangedTimer(this, &Document::touchEventsChangedTimerFired)
+ : ContainerNode(*this, CreateDocument)
+ , TreeScope(*this)
+ , FrameDestructionObserver(frame)
+#if ENABLE(IOS_TOUCH_EVENTS)
+ , m_touchEventsChangedTimer(*this, &Document::touchEventsChangedTimerFired)
#endif
- , m_styleResolverThrowawayTimer(this, &Document::styleResolverThrowawayTimerFired, timeBeforeThrowingAwayStyleResolverAfterLastUseInSeconds)
- , m_didCalculateStyleResolver(false)
+ , m_referencingNodeCount(0)
+ , m_settings(frame ? Ref<Settings>(frame->settings()) : Settings::create(nullptr))
, 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_ignoreAutofocus(false)
- , m_compatibilityMode(NoQuirksMode)
+ , 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(VisitedLinkState::create(*this))
+ , m_styleScope(std::make_unique<Style::Scope>(*this))
+ , m_extensionStyleSheets(std::make_unique<ExtensionStyleSheets>(*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_styleRecalcTimer(*this, &Document::updateStyleIfNeeded)
, m_pendingStyleRecalcShouldForce(false)
, m_inStyleRecalc(false)
, m_closeAfterStyleRecalc(false)
, m_gotoAnchorNeededAfterStylesheetsLoad(false)
, m_frameElementsShouldIgnoreScrolling(false)
- , m_containsValidityStyleRules(false)
- , m_updateFocusAppearanceRestoresSelection(false)
- , m_ignoreDestructiveWriteCount(0)
- , m_titleSetExplicitly(false)
- , m_markers(adoptPtr(new DocumentMarkerController))
- , m_updateFocusAppearanceTimer(this, &Document::updateFocusAppearanceTimerFired)
- , m_resetHiddenFocusElementTimer(this, &Document::resetHiddenFocusElementTimer)
+ , m_updateFocusAppearanceRestoresSelection(SelectionRestorationMode::SetDefault)
+ , m_markers(std::make_unique<DocumentMarkerController>(*this))
+ , m_updateFocusAppearanceTimer(*this, &Document::updateFocusAppearanceTimerFired)
, m_cssTarget(nullptr)
, m_processingLoadEvent(false)
, m_loadEventFinished(false)
- , m_startTime(std::chrono::steady_clock::now())
+ , m_documentCreationTime(MonotonicTime::now())
, m_overMinimumLayoutThreshold(false)
, m_scriptRunner(std::make_unique<ScriptRunner>(*this))
+ , m_moduleLoader(std::make_unique<ScriptModuleLoader>(*this))
, m_xmlVersion(ASCIILiteral("1.0"))
, m_xmlStandalone(StandaloneUnspecified)
, m_hasXMLDeclaration(false)
@@ -453,56 +487,61 @@ Document::Document(Frame* frame, const URL& url, unsigned documentClasses, unsig
, 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_isViewSource(false)
, m_sawElementsInKnownNamespaces(false)
, m_isSrcdocDocument(false)
, m_eventQueue(*this)
, m_weakFactory(this)
- , m_idAttributeName(idAttr)
#if ENABLE(FULLSCREEN_API)
, m_areKeysEnabledInFullScreen(0)
, m_fullScreenRenderer(nullptr)
- , m_fullScreenChangeDelayTimer(this, &Document::fullScreenChangeDelayTimerFired)
+ , 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_loadEventDelayTimer(*this, &Document::loadEventDelayTimerFired)
+ , m_referrerPolicy(ReferrerPolicy::Default)
, m_writeRecursionIsTooDeep(false)
, m_writeRecursionDepth(0)
- , m_wheelEventHandlerCount(0)
, m_lastHandledUserGestureTimestamp(0)
#if PLATFORM(IOS)
#if ENABLE(DEVICE_ORIENTATION)
- , m_deviceMotionClient(DeviceMotionClientIOS::create())
- , m_deviceMotionController(DeviceMotionController::create(m_deviceMotionClient.get()))
- , m_deviceOrientationClient(DeviceOrientationClientIOS::create())
- , m_deviceOrientationController(DeviceOrientationController::create(m_deviceOrientationClient.get()))
+ , 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_pendingTasksTimer(*this, &Document::pendingTasksTimerFired)
, m_scheduledTasksAreSuspended(false)
, m_visualUpdatesAllowed(true)
- , m_visualUpdatesSuppressionTimer(this, &Document::visualUpdatesSuppressionTimerFired)
- , m_sharedObjectPoolClearTimer(this, &Document::sharedObjectPoolClearTimerFired)
+ , m_visualUpdatesSuppressionTimer(*this, &Document::visualUpdatesSuppressionTimerFired)
+ , m_sharedObjectPoolClearTimer(*this, &Document::clearSharedObjectPool)
#ifndef NDEBUG
, m_didDispatchViewportPropertiesChanged(false)
#endif
-#if ENABLE(TEMPLATE_ELEMENT)
, m_templateDocumentHost(nullptr)
+ , m_fontSelector(CSSFontSelector::create(*this))
+#if ENABLE(WEB_REPLAY)
+ , m_inputCursor(EmptyInputCursor::create())
#endif
- , m_didAssociateFormControlsTimer(this, &Document::didAssociateFormControlsTimerFired)
+ , m_didAssociateFormControlsTimer(*this, &Document::didAssociateFormControlsTimerFired)
+ , m_cookieCacheExpiryTimer(*this, &Document::invalidateDOMCookieCache)
+ , m_disabledFieldsetElementsCount(0)
, m_hasInjectedPlugInsScript(false)
- , m_renderTreeBeingDestroyed(false)
+ , m_hasStyleWithViewportUnits(false)
+#if ENABLE(WEB_SOCKETS)
+ , m_socketProvider(page() ? &page()->socketProvider() : nullptr)
+#endif
{
+ allDocuments().add(this);
+
// We depend on the url getting immediately set in subframes, but we
// also depend on the url NOT getting immediately set in opened windows.
// See fast/dom/early-frame-url.html
@@ -511,16 +550,8 @@ Document::Document(Frame* frame, const URL& url, unsigned documentClasses, unsig
if ((frame && frame->ownerElement()) || !url.isEmpty())
setURL(url);
- if (m_frame)
- m_cachedResourceLoader = &m_frame->loader().activeDocumentLoader()->cachedResourceLoader();
- if (!m_cachedResourceLoader)
- m_cachedResourceLoader = CachedResourceLoader::create(nullptr);
m_cachedResourceLoader->setDocument(this);
-#if ENABLE(TEXT_AUTOSIZING)
- m_textAutosizer = TextAutosizer::create(this);
-#endif
-
resetLinkColor();
resetVisitedLinkColor();
resetActiveLinkColor();
@@ -528,23 +559,14 @@ Document::Document(Frame* frame, const URL& url, unsigned documentClasses, unsig
initSecurityContext();
initDNSPrefetch();
- for (unsigned i = 0; i < WTF_ARRAY_LENGTH(m_nodeListAndCollectionCounts); ++i)
- m_nodeListAndCollectionCounts[i] = 0;
+ m_fontSelector->registerForInvalidationCallbacks(*this);
- InspectorCounters::incrementCounter(InspectorCounters::DocumentCounter);
-}
-
-static void histogramMutationEventUsage(const unsigned short& listenerTypes)
-{
- HistogramSupport::histogramEnumeration("DOMAPI.PerDocumentMutationEventUsage.DOMSubtreeModified", static_cast<bool>(listenerTypes & Document::DOMSUBTREEMODIFIED_LISTENER), 2);
- HistogramSupport::histogramEnumeration("DOMAPI.PerDocumentMutationEventUsage.DOMNodeInserted", static_cast<bool>(listenerTypes & Document::DOMNODEINSERTED_LISTENER), 2);
- HistogramSupport::histogramEnumeration("DOMAPI.PerDocumentMutationEventUsage.DOMNodeRemoved", static_cast<bool>(listenerTypes & Document::DOMNODEREMOVED_LISTENER), 2);
- HistogramSupport::histogramEnumeration("DOMAPI.PerDocumentMutationEventUsage.DOMNodeRemovedFromDocument", static_cast<bool>(listenerTypes & Document::DOMNODEREMOVEDFROMDOCUMENT_LISTENER), 2);
- HistogramSupport::histogramEnumeration("DOMAPI.PerDocumentMutationEventUsage.DOMNodeInsertedIntoDocument", static_cast<bool>(listenerTypes & Document::DOMNODEINSERTEDINTODOCUMENT_LISTENER), 2);
- HistogramSupport::histogramEnumeration("DOMAPI.PerDocumentMutationEventUsage.DOMCharacterDataModified", static_cast<bool>(listenerTypes & Document::DOMCHARACTERDATAMODIFIED_LISTENER), 2);
+ for (auto& nodeListAndCollectionCount : m_nodeListAndCollectionCounts)
+ nodeListAndCollectionCount = 0;
}
#if ENABLE(FULLSCREEN_API)
+
static bool isAttributeOnAllOwners(const WebCore::QualifiedName& attribute, const WebCore::QualifiedName& prefixedAttribute, const HTMLFrameOwnerElement* owner)
{
if (!owner)
@@ -555,32 +577,42 @@ static bool isAttributeOnAllOwners(const WebCore::QualifiedName& attribute, cons
} while ((owner = owner->document().ownerElement()));
return true;
}
+
#endif
+Ref<Document> Document::create(Document& contextDocument)
+{
+ auto document = adoptRef(*new Document(nullptr, URL()));
+ document->setContextDocument(contextDocument);
+ document->setSecurityOriginPolicy(contextDocument.securityOriginPolicy());
+ return document;
+}
+
Document::~Document()
{
+ allDocuments().remove(this);
+
ASSERT(!renderView());
- ASSERT(!m_inPageCache);
+ ASSERT(m_pageCacheState != InPageCache);
ASSERT(m_ranges.isEmpty());
ASSERT(!m_parentTreeScope);
+ ASSERT(!m_disabledFieldsetElementsCount);
+ ASSERT(m_inDocumentShadowRoots.isEmpty());
#if ENABLE(DEVICE_ORIENTATION) && PLATFORM(IOS)
m_deviceMotionClient->deviceMotionControllerDestroyed();
m_deviceOrientationClient->deviceOrientationControllerDestroyed();
#endif
-
-#if ENABLE(TEMPLATE_ELEMENT)
+
if (m_templateDocument)
m_templateDocument->setTemplateDocumentHost(nullptr); // balanced in templateDocument().
-#endif
// FIXME: Should we reset m_domWindow when we detach from the Frame?
if (m_domWindow)
- m_domWindow->resetUnlessSuspendedForPageCache();
+ m_domWindow->resetUnlessSuspendedForDocumentSuspension();
m_scriptRunner = nullptr;
-
- histogramMutationEventUsage(m_listenerTypes);
+ m_moduleLoader = nullptr;
removeAllEventListeners();
@@ -601,18 +633,22 @@ Document::~Document()
if (m_styleSheetList)
m_styleSheetList->detachFromDocument();
- if (m_elementSheet)
- m_elementSheet->detachFromDocument();
- m_styleSheetCollection.detachFromDocument();
+ extensionStyleSheets().detachFromDocument();
- clearStyleResolver(); // We need to destroy CSSFontSelector before destroying m_cachedResourceLoader.
+ styleScope().clearResolver(); // We need to destroy CSSFontSelector before destroying m_cachedResourceLoader.
+ m_fontSelector->clearDocument();
+ m_fontSelector->unregisterForInvalidationCallbacks(*this);
// It's possible for multiple Documents to end up referencing the same CachedResourceLoader (e.g., SVGImages
// load the initial empty document and the SVGDocument with the same DocumentLoader).
if (m_cachedResourceLoader->document() == this)
m_cachedResourceLoader->setDocument(nullptr);
- m_cachedResourceLoader.clear();
+#if ENABLE(VIDEO)
+ if (auto* platformMediaSessionManager = PlatformMediaSessionManager::sharedManagerIfExists())
+ platformMediaSessionManager->stopAllMediaPlaybackForDocument(this);
+#endif
+
// We must call clearRareData() here since a Document class inherits TreeScope
// as well as Node. See a comment on TreeScope.h for the reason.
if (hasRareData())
@@ -623,56 +659,68 @@ Document::~Document()
for (unsigned i = 0; i < WTF_ARRAY_LENGTH(m_nodeListAndCollectionCounts); ++i)
ASSERT(!m_nodeListAndCollectionCounts[i]);
-
- clearDocumentScope();
-
- InspectorCounters::decrementCounter(InspectorCounters::DocumentCounter);
}
-void Document::dropChildren()
+void Document::removedLastRef()
{
ASSERT(!m_deletionHasBegun);
-
- // We must make sure not to be retaining any of our children through
- // these extra pointers or we will create a reference cycle.
- m_focusedElement = nullptr;
- m_hoveredElement = nullptr;
- m_activeElement = nullptr;
- m_titleElement = nullptr;
- m_documentElement = nullptr;
- m_userActionElements.documentDidRemoveLastRef();
+ if (m_referencingNodeCount) {
+ // If removing a child removes the last node reference, we don't want the scope to be destroyed
+ // until after removeDetachedChildren returns, so we protect ourselves.
+ incrementReferencingNodeCount();
+
+ RELEASE_ASSERT(!hasLivingRenderTree());
+ // We must make sure not to be retaining any of our children through
+ // these extra pointers or we will create a reference cycle.
+ m_focusedElement = nullptr;
+ m_hoveredElement = nullptr;
+ m_activeElement = nullptr;
+ m_titleElement = nullptr;
+ m_documentElement = nullptr;
+ m_focusNavigationStartingNode = nullptr;
+ m_userActionElements.documentDidRemoveLastRef();
#if ENABLE(FULLSCREEN_API)
- m_fullScreenElement = nullptr;
- m_fullScreenElementStack.clear();
+ m_fullScreenElement = nullptr;
+ m_fullScreenElementStack.clear();
#endif
+ m_associatedFormControls.clear();
- detachParser();
-
- // removeDetachedChildren() doesn't always unregister IDs,
- // so tear down scope information up front to avoid having
- // stale references in the map.
-
- destroyTreeScopeData();
- removeDetachedChildren();
- m_formController.clear();
+ detachParser();
- m_markers->detach();
+ // removeDetachedChildren() doesn't always unregister IDs,
+ // so tear down scope information up front to avoid having
+ // stale references in the map.
- m_cssCanvasElements.clear();
+ destroyTreeScopeData();
+ removeDetachedChildren();
+ m_formController = nullptr;
+
+ m_markers->detach();
+
+ m_cssCanvasElements.clear();
+
+ commonTeardown();
- commonTeardown();
+#ifndef NDEBUG
+ // We need to do this right now since selfOnlyDeref() can delete this.
+ m_inRemovedLastRefFunction = false;
+#endif
+ decrementReferencingNodeCount();
+ } else {
+#ifndef NDEBUG
+ m_inRemovedLastRefFunction = false;
+ m_deletionHasBegun = true;
+#endif
+ delete this;
+ }
}
void Document::commonTeardown()
{
-#if ENABLE(SVG)
if (svgExtensions())
- accessSVGExtensions()->pauseAnimations();
-#endif
+ accessSVGExtensions().pauseAnimations();
-#if ENABLE(REQUEST_ANIMATION_FRAME)
clearScriptedAnimationController();
-#endif
}
Element* Document::getElementByAccessKey(const String& key)
@@ -689,9 +737,8 @@ Element* Document::getElementByAccessKey(const String& key)
void Document::buildAccessKeyMap(TreeScope* scope)
{
ASSERT(scope);
- ContainerNode* rootNode = scope->rootNode();
- for (auto& element : descendantsOfType<Element>(*rootNode)) {
- const AtomicString& accessKey = element.fastGetAttribute(accesskeyAttr);
+ for (auto& element : descendantsOfType<Element>(scope->rootNode())) {
+ const AtomicString& accessKey = element.attributeWithoutSynchronization(accesskeyAttr);
if (!accessKey.isEmpty())
m_elementsByAccessKey.set(accessKey.impl(), &element);
@@ -706,49 +753,55 @@ void Document::invalidateAccessKeyMap()
m_elementsByAccessKey.clear();
}
-void Document::addImageElementByLowercasedUsemap(const AtomicStringImpl& name, HTMLImageElement& element)
+void Document::addImageElementByUsemap(const AtomicStringImpl& name, HTMLImageElement& element)
{
return m_imagesByUsemap.add(name, element, *this);
}
-void Document::removeImageElementByLowercasedUsemap(const AtomicStringImpl& name, HTMLImageElement& element)
+void Document::removeImageElementByUsemap(const AtomicStringImpl& name, HTMLImageElement& element)
{
return m_imagesByUsemap.remove(name, element);
}
-HTMLImageElement* Document::imageElementByLowercasedUsemap(const AtomicStringImpl& name) const
+HTMLImageElement* Document::imageElementByUsemap(const AtomicStringImpl& name) const
{
- return m_imagesByUsemap.getElementByLowercasedUsemap(name, *this);
+ return m_imagesByUsemap.getElementByUsemap(name, *this);
}
-SelectorQueryCache& Document::selectorQueryCache()
+ExceptionOr<SelectorQuery&> Document::selectorQueryForString(const String& selectorString)
{
+ if (selectorString.isEmpty())
+ return Exception { SYNTAX_ERR };
if (!m_selectorQueryCache)
- m_selectorQueryCache = adoptPtr(new SelectorQueryCache());
- return *m_selectorQueryCache;
+ m_selectorQueryCache = std::make_unique<SelectorQueryCache>();
+ return m_selectorQueryCache->add(selectorString, *this);
+}
+
+void Document::clearSelectorQueryCache()
+{
+ m_selectorQueryCache = nullptr;
}
MediaQueryMatcher& Document::mediaQueryMatcher()
{
if (!m_mediaQueryMatcher)
- m_mediaQueryMatcher = MediaQueryMatcher::create(this);
+ m_mediaQueryMatcher = MediaQueryMatcher::create(*this);
return *m_mediaQueryMatcher;
}
-void Document::setCompatibilityMode(CompatibilityMode mode)
+void Document::setCompatibilityMode(DocumentCompatibilityMode mode)
{
if (m_compatibilityModeLocked || mode == m_compatibilityMode)
return;
bool wasInQuirksMode = inQuirksMode();
m_compatibilityMode = mode;
- if (m_selectorQueryCache)
- m_selectorQueryCache->invalidate();
+ clearSelectorQueryCache();
if (inQuirksMode() != wasInQuirksMode) {
// All user stylesheets have to reparse using the different mode.
- m_styleSheetCollection.clearPageUserSheet();
- m_styleSheetCollection.invalidateInjectedStyleSheetCache();
+ extensionStyleSheets().clearPageUserSheet();
+ extensionStyleSheets().invalidateInjectedStyleSheetCache();
}
}
@@ -769,19 +822,19 @@ void Document::resetVisitedLinkColor()
void Document::resetActiveLinkColor()
{
- m_activeLinkColor.setNamedColor("red");
+ m_activeLinkColor = Color(255, 0, 0);
}
-DOMImplementation* Document::implementation()
+DOMImplementation& Document::implementation()
{
if (!m_implementation)
- m_implementation = DOMImplementation::create(*this);
- return m_implementation.get();
+ m_implementation = std::make_unique<DOMImplementation>(*this);
+ return *m_implementation;
}
bool Document::hasManifest() const
{
- return documentElement() && documentElement()->hasTagName(htmlTag) && documentElement()->hasAttribute(manifestAttr);
+ return documentElement() && documentElement()->hasTagName(htmlTag) && documentElement()->hasAttributeWithoutSynchronization(manifestAttr);
}
DocumentType* Document::doctype() const
@@ -797,235 +850,188 @@ void Document::childrenChanged(const ChildChange& change)
{
ContainerNode::childrenChanged(change);
- // NOTE: Per DOM, dynamically inserting/removing doctype nodes doesn't affect compatibility mode.
-
-#if ENABLE(LEGACY_VIEWPORT_ADAPTION)
- // FIXME: It's a little strange to add these rules when a DocumentType with this prefix is added,
- // but not remove these rules when a DocumentType with this prefix is removed. It seems this should
- // be handled more the way the compatibility mode is, by fetching the doctype at the appropriate time,
- // rather than by responding when a document type node is inserted.
- if (DocumentType* documentType = doctype()) {
- if (documentType->publicId().startsWith("-//wapforum//dtd xhtml mobile 1.", /* caseSensitive */ false))
- processViewport("width=device-width, height=device-height", ViewportArguments::XHTMLMobileProfile);
- }
-#endif
+ // FIXME: Chrome::didReceiveDocType() used to be called only when the doctype changed. We need to check the
+ // impact of calling this systematically. If the overhead is negligible, we need to rename didReceiveDocType,
+ // otherwise, we need to detect the doc type changes before updating the viewport.
+ if (Page* page = this->page())
+ page->chrome().didReceiveDocType(*frame());
Element* newDocumentElement = childrenOfType<Element>(*this).first();
-
if (newDocumentElement == m_documentElement)
return;
m_documentElement = newDocumentElement;
// The root style used for media query matching depends on the document element.
- clearStyleResolver();
+ styleScope().clearResolver();
}
-PassRefPtr<Element> Document::createElement(const AtomicString& name, ExceptionCode& ec)
+static ALWAYS_INLINE Ref<HTMLElement> createUpgradeCandidateElement(Document& document, const QualifiedName& name)
{
- if (!isValidName(name)) {
- ec = INVALID_CHARACTER_ERR;
- return nullptr;
+ if (!RuntimeEnabledFeatures::sharedFeatures().customElementsEnabled()
+ || Document::validateCustomElementName(name.localName()) != CustomElementNameValidationStatus::Valid)
+ return HTMLUnknownElement::create(name, document);
+
+ auto element = HTMLElement::create(name, document);
+ element->setIsCustomElementUpgradeCandidate();
+ return element;
+}
+
+static ALWAYS_INLINE Ref<HTMLElement> createUpgradeCandidateElement(Document& document, const AtomicString& localName)
+{
+ return createUpgradeCandidateElement(document, QualifiedName { nullAtom, localName, xhtmlNamespaceURI });
+}
+
+static inline bool isValidHTMLElementName(const AtomicString& localName)
+{
+ return Document::isValidName(localName);
+}
+
+static inline bool isValidHTMLElementName(const QualifiedName& name)
+{
+ return Document::isValidName(name.localName());
+}
+
+template<typename NameType>
+static ExceptionOr<Ref<Element>> createHTMLElementWithNameValidation(Document& document, const NameType& name)
+{
+ auto element = HTMLElementFactory::createKnownElement(name, document);
+ if (LIKELY(element))
+ return Ref<Element> { element.releaseNonNull() };
+
+ if (auto* window = document.domWindow()) {
+ auto* registry = window->customElementRegistry();
+ if (UNLIKELY(registry)) {
+ if (auto* elementInterface = registry->findInterface(name))
+ return elementInterface->constructElementWithFallback(document, name);
+ }
}
+ if (UNLIKELY(!isValidHTMLElementName(name)))
+ return Exception { INVALID_CHARACTER_ERR };
+
+ return Ref<Element> { createUpgradeCandidateElement(document, name) };
+}
+
+ExceptionOr<Ref<Element>> Document::createElementForBindings(const AtomicString& name)
+{
+ if (isHTMLDocument())
+ return createHTMLElementWithNameValidation(*this, name.convertToASCIILowercase());
+
if (isXHTMLDocument())
- return HTMLElementFactory::createElement(QualifiedName(nullAtom, name, xhtmlNamespaceURI), *this);
+ return createHTMLElementWithNameValidation(*this, name);
+
+ if (!isValidName(name))
+ return Exception { INVALID_CHARACTER_ERR };
return createElement(QualifiedName(nullAtom, name, nullAtom), false);
}
-PassRefPtr<DocumentFragment> Document::createDocumentFragment()
+Ref<DocumentFragment> Document::createDocumentFragment()
{
return DocumentFragment::create(document());
}
-PassRefPtr<Text> Document::createTextNode(const String& data)
+Ref<Text> Document::createTextNode(const String& data)
{
return Text::create(*this, data);
}
-PassRefPtr<Comment> Document::createComment(const String& data)
+Ref<Comment> Document::createComment(const String& data)
{
return Comment::create(*this, data);
}
-PassRefPtr<CDATASection> Document::createCDATASection(const String& data, ExceptionCode& ec)
+ExceptionOr<Ref<CDATASection>> Document::createCDATASection(const String& data)
{
- if (isHTMLDocument()) {
- ec = NOT_SUPPORTED_ERR;
- return nullptr;
- }
+ if (isHTMLDocument())
+ return Exception { NOT_SUPPORTED_ERR };
return CDATASection::create(*this, data);
}
-PassRefPtr<ProcessingInstruction> Document::createProcessingInstruction(const String& target, const String& data, ExceptionCode& ec)
+ExceptionOr<Ref<ProcessingInstruction>> Document::createProcessingInstruction(const String& target, const String& data)
{
- if (!isValidName(target)) {
- ec = INVALID_CHARACTER_ERR;
- return nullptr;
- }
- if (isHTMLDocument()) {
- ec = NOT_SUPPORTED_ERR;
- return nullptr;
- }
- return ProcessingInstruction::create(*this, target, data);
-}
+ if (!isValidName(target))
+ return Exception { INVALID_CHARACTER_ERR };
-PassRefPtr<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);
+ if (data.contains("?>"))
+ return Exception { INVALID_CHARACTER_ERR };
+
+ return ProcessingInstruction::create(*this, target, data);
}
-PassRefPtr<Text> Document::createEditingTextNode(const String& text)
+Ref<Text> Document::createEditingTextNode(const String& text)
{
return Text::createEditingText(*this, text);
}
-PassRefPtr<CSSStyleDeclaration> Document::createCSSStyleDeclaration()
+Ref<CSSStyleDeclaration> Document::createCSSStyleDeclaration()
{
Ref<MutableStyleProperties> propertySet(MutableStyleProperties::create());
- return propertySet->ensureCSSStyleDeclaration();
+ return *propertySet->ensureCSSStyleDeclaration();
}
-PassRefPtr<Node> Document::importNode(Node* importedNode, bool deep, ExceptionCode& ec)
+ExceptionOr<Ref<Node>> Document::importNode(Node& nodeToImport, bool deep)
{
- ec = 0;
-
- if (!importedNode) {
- ec = NOT_SUPPORTED_ERR;
- return nullptr;
- }
-
- switch (importedNode->nodeType()) {
+ switch (nodeToImport.nodeType()) {
+ case DOCUMENT_FRAGMENT_NODE:
+ if (nodeToImport.isShadowRoot())
+ break;
+ FALLTHROUGH;
+ case ELEMENT_NODE:
case TEXT_NODE:
- return createTextNode(importedNode->nodeValue());
case CDATA_SECTION_NODE:
- return createCDATASection(importedNode->nodeValue(), ec);
- case ENTITY_REFERENCE_NODE:
- return createEntityReference(importedNode->nodeName(), ec);
case PROCESSING_INSTRUCTION_NODE:
- return createProcessingInstruction(importedNode->nodeName(), importedNode->nodeValue(), ec);
case COMMENT_NODE:
- return createComment(importedNode->nodeValue());
- case ELEMENT_NODE: {
- Element& oldElement = toElement(*importedNode);
- // FIXME: The following check might be unnecessary. Is it possible that
- // oldElement has mismatched prefix/namespace?
- if (!hasValidNamespaceForElements(oldElement.tagQName())) {
- ec = NAMESPACE_ERR;
- return nullptr;
- }
-
- RefPtr<Element> newElement = createElement(oldElement.tagQName(), false);
- newElement->cloneDataFromElement(oldElement);
-
- if (deep) {
- for (Node* oldChild = oldElement.firstChild(); oldChild; oldChild = oldChild->nextSibling()) {
- RefPtr<Node> newChild = importNode(oldChild, true, ec);
- if (ec)
- return nullptr;
- newElement->appendChild(newChild.release(), ec);
- if (ec)
- return nullptr;
- }
- }
+ return nodeToImport.cloneNodeInternal(document(), deep ? CloningOperation::Everything : CloningOperation::OnlySelf);
- return newElement.release();
- }
case ATTRIBUTE_NODE:
- return Attr::create(*this, QualifiedName(nullAtom, toAttr(*importedNode).name(), nullAtom), toAttr(*importedNode).value());
- case DOCUMENT_FRAGMENT_NODE: {
- if (importedNode->isShadowRoot()) {
- // ShadowRoot nodes should not be explicitly importable.
- // Either they are imported along with their host node, or created implicitly.
- break;
- }
- DocumentFragment& oldFragment = toDocumentFragment(*importedNode);
- RefPtr<DocumentFragment> newFragment = createDocumentFragment();
- if (deep) {
- for (Node* oldChild = oldFragment.firstChild(); oldChild; oldChild = oldChild->nextSibling()) {
- RefPtr<Node> newChild = importNode(oldChild, true, ec);
- if (ec)
- return nullptr;
- newFragment->appendChild(newChild.release(), ec);
- if (ec)
- return nullptr;
- }
- }
-
- return newFragment.release();
- }
- case ENTITY_NODE:
- case NOTATION_NODE:
- // FIXME: It should be possible to import these node types, however in DOM3 the DocumentType is readonly, so there isn't much sense in doing that.
- // Ability to add these imported nodes to a DocumentType will be considered for addition to a future release of the DOM.
- case DOCUMENT_NODE:
- case DOCUMENT_TYPE_NODE:
- case XPATH_NAMESPACE_NODE:
+ // FIXME: This will "Attr::normalize" child nodes of Attr.
+ return Ref<Node> { Attr::create(*this, QualifiedName(nullAtom, downcast<Attr>(nodeToImport).name(), nullAtom), downcast<Attr>(nodeToImport).value()) };
+
+ case DOCUMENT_NODE: // Can't import a document into another document.
+ case DOCUMENT_TYPE_NODE: // FIXME: Support cloning a DocumentType node per DOM4.
break;
}
- ec = NOT_SUPPORTED_ERR;
- return nullptr;
+
+ return Exception { NOT_SUPPORTED_ERR };
}
-PassRefPtr<Node> Document::adoptNode(PassRefPtr<Node> source, ExceptionCode& ec)
+ExceptionOr<Ref<Node>> Document::adoptNode(Node& source)
{
- 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 NOTATION_NODE:
+ switch (source.nodeType()) {
case DOCUMENT_NODE:
- case DOCUMENT_TYPE_NODE:
- case XPATH_NAMESPACE_NODE:
- ec = NOT_SUPPORTED_ERR;
- return nullptr;
- case ATTRIBUTE_NODE: {
- Attr& attr = toAttr(*source);
- if (attr.ownerElement())
- attr.ownerElement()->removeAttributeNode(&attr, ec);
+ return Exception { NOT_SUPPORTED_ERR };
+ case ATTRIBUTE_NODE: {
+ auto& attr = downcast<Attr>(source);
+ if (auto* element = attr.ownerElement()) {
+ auto result = element->removeAttributeNode(attr);
+ if (result.hasException())
+ return result.releaseException();
+ }
break;
}
default:
- if (source->isShadowRoot()) {
+ if (source.isShadowRoot()) {
// ShadowRoot cannot disconnect itself from the host node.
- ec = HIERARCHY_REQUEST_ERR;
- return nullptr;
- }
- if (source->isFrameOwnerElement()) {
- HTMLFrameOwnerElement& frameOwnerElement = toHTMLFrameOwnerElement(*source);
- if (frame() && frame()->tree().isDescendantOf(frameOwnerElement.contentFrame())) {
- ec = HIERARCHY_REQUEST_ERR;
- return nullptr;
- }
+ return Exception { HIERARCHY_REQUEST_ERR };
}
- if (source->parentNode()) {
- source->parentNode()->removeChild(source.get(), ec);
- if (ec)
- return nullptr;
+ if (is<HTMLFrameOwnerElement>(source)) {
+ auto& frameOwnerElement = downcast<HTMLFrameOwnerElement>(source);
+ if (frame() && frame()->tree().isDescendantOf(frameOwnerElement.contentFrame()))
+ return Exception { HIERARCHY_REQUEST_ERR };
}
+ auto result = source.remove();
+ if (result.hasException())
+ return result.releaseException();
+ ASSERT_WITH_SECURITY_IMPLICATION(!source.isConnected());
+ ASSERT_WITH_SECURITY_IMPLICATION(!source.parentNode());
}
- adoptIfNeeded(source.get());
+ adoptIfNeeded(source);
- return source;
+ return Ref<Node> { source };
}
bool Document::hasValidNamespaceForElements(const QualifiedName& qName)
@@ -1050,18 +1056,34 @@ bool Document::hasValidNamespaceForAttributes(const QualifiedName& qName)
return hasValidNamespaceForElements(qName);
}
+static Ref<HTMLElement> createFallbackHTMLElement(Document& document, const QualifiedName& name)
+{
+ if (auto* window = document.domWindow()) {
+ auto* registry = window->customElementRegistry();
+ if (UNLIKELY(registry)) {
+ if (auto* elementInterface = registry->findInterface(name)) {
+ auto element = HTMLElement::create(name, document);
+ element->enqueueToUpgrade(*elementInterface);
+ return element;
+ }
+ }
+ }
+ // FIXME: Should we also check the equality of prefix between the custom element and name?
+ return createUpgradeCandidateElement(document, name);
+}
+
// FIXME: This should really be in a possible ElementFactory class.
-PassRefPtr<Element> Document::createElement(const QualifiedName& name, bool createdByParser)
+Ref<Element> Document::createElement(const QualifiedName& name, bool createdByParser)
{
RefPtr<Element> element;
// FIXME: Use registered namespaces and look up in a hash to find the right factory.
- if (name.namespaceURI() == xhtmlNamespaceURI)
- element = HTMLElementFactory::createElement(name, *this, nullptr, createdByParser);
-#if ENABLE(SVG)
- else if (name.namespaceURI() == SVGNames::svgNamespaceURI)
+ if (name.namespaceURI() == xhtmlNamespaceURI) {
+ element = HTMLElementFactory::createKnownElement(name, *this, nullptr, createdByParser);
+ if (UNLIKELY(!element))
+ element = createFallbackHTMLElement(*this, name);
+ } else if (name.namespaceURI() == SVGNames::svgNamespaceURI)
element = SVGElementFactory::createElement(name, *this, createdByParser);
-#endif
#if ENABLE(MATHML)
else if (name.namespaceURI() == MathMLNames::mathmlNamespaceURI)
element = MathMLElementFactory::createElement(name, *this, createdByParser);
@@ -1075,76 +1097,88 @@ PassRefPtr<Element> Document::createElement(const QualifiedName& name, bool crea
// <image> uses imgTag so we need a special rule.
ASSERT((name.matches(imageTag) && element->tagQName().matches(imgTag) && element->tagQName().prefix() == name.prefix()) || name == element->tagQName());
- return element.release();
+ return element.releaseNonNull();
}
-bool Document::regionBasedColumnsEnabled() const
+CustomElementNameValidationStatus Document::validateCustomElementName(const AtomicString& localName)
{
- return settings() && settings()->regionBasedColumnsEnabled();
-}
+ bool containsHyphen = false;
+ for (auto character : StringView(localName).codeUnits()) {
+ if (isASCIIUpper(character))
+ return CustomElementNameValidationStatus::ContainsUpperCase;
+ if (character == '-')
+ containsHyphen = true;
+ }
-bool Document::cssStickyPositionEnabled() const
-{
- return settings() && settings()->cssStickyPositionEnabled();
-}
+ if (!containsHyphen)
+ return CustomElementNameValidationStatus::NoHyphen;
-bool Document::cssRegionsEnabled() const
-{
- return RuntimeEnabledFeatures::sharedFeatures().cssRegionsEnabled();
-}
+#if ENABLE(MATHML)
+ const auto& annotationXmlLocalName = MathMLNames::annotation_xmlTag.localName();
+#else
+ static NeverDestroyed<const AtomicString> annotationXmlLocalName("annotation-xml", AtomicString::ConstructFromLiteral);
+#endif
-bool Document::cssCompositingEnabled() const
-{
- return RuntimeEnabledFeatures::sharedFeatures().cssCompositingEnabled();
+ if (localName == SVGNames::color_profileTag.localName()
+ || localName == SVGNames::font_faceTag.localName()
+ || localName == SVGNames::font_face_formatTag.localName()
+ || localName == SVGNames::font_face_nameTag.localName()
+ || localName == SVGNames::font_face_srcTag.localName()
+ || localName == SVGNames::font_face_uriTag.localName()
+ || localName == SVGNames::missing_glyphTag.localName()
+ || localName == annotationXmlLocalName)
+ return CustomElementNameValidationStatus::ConflictsWithBuiltinNames;
+
+ return CustomElementNameValidationStatus::Valid;
}
-bool Document::cssGridLayoutEnabled() const
+bool Document::isCSSGridLayoutEnabled() const
{
- return settings() && settings()->cssGridLayoutEnabled();
+ return RuntimeEnabledFeatures::sharedFeatures().isCSSGridLayoutEnabled();
}
#if ENABLE(CSS_REGIONS)
-PassRefPtr<DOMNamedFlowCollection> Document::webkitGetNamedFlows()
+RefPtr<DOMNamedFlowCollection> Document::webkitGetNamedFlows()
{
- if (!cssRegionsEnabled() || !renderView())
+ if (!renderView())
return nullptr;
updateStyleIfNeeded();
- return namedFlows()->createCSSOMSnapshot();
+ return namedFlows().createCSSOMSnapshot();
}
#endif
-NamedFlowCollection* Document::namedFlows()
+NamedFlowCollection& Document::namedFlows()
{
if (!m_namedFlows)
m_namedFlows = NamedFlowCollection::create(this);
- return m_namedFlows.get();
+ return *m_namedFlows;
}
-PassRefPtr<Element> Document::createElementNS(const String& namespaceURI, const String& qualifiedName, ExceptionCode& ec)
+ExceptionOr<Ref<Element>> Document::createElementNS(const AtomicString& namespaceURI, const String& qualifiedName)
{
- String prefix, localName;
- if (!parseQualifiedName(qualifiedName, prefix, localName, ec))
- return nullptr;
+ auto parseResult = parseQualifiedName(namespaceURI, qualifiedName);
+ if (parseResult.hasException())
+ return parseResult.releaseException();
+ QualifiedName parsedName { parseResult.releaseReturnValue() };
+ if (!hasValidNamespaceForElements(parsedName))
+ return Exception { NAMESPACE_ERR };
- QualifiedName qName(prefix, localName, namespaceURI);
- if (!hasValidNamespaceForElements(qName)) {
- ec = NAMESPACE_ERR;
- return nullptr;
- }
+ if (parsedName.namespaceURI() == xhtmlNamespaceURI)
+ return createHTMLElementWithNameValidation(*this, parsedName);
- return createElement(qName, false);
+ return createElement(parsedName, false);
}
String Document::readyState() const
{
- DEFINE_STATIC_LOCAL(const String, loading, (ASCIILiteral("loading")));
- DEFINE_STATIC_LOCAL(const String, interactive, (ASCIILiteral("interactive")));
- DEFINE_STATIC_LOCAL(const String, complete, (ASCIILiteral("complete")));
+ static NeverDestroyed<const String> loading(ASCIILiteral("loading"));
+ static NeverDestroyed<const String> interactive(ASCIILiteral("interactive"));
+ static NeverDestroyed<const String> complete(ASCIILiteral("complete"));
switch (m_readyState) {
case Loading:
@@ -1168,15 +1202,15 @@ void Document::setReadyState(ReadyState readyState)
switch (readyState) {
case Loading:
if (!m_documentTiming.domLoading)
- m_documentTiming.domLoading = monotonicallyIncreasingTime();
+ m_documentTiming.domLoading = MonotonicTime::now();
break;
case Interactive:
if (!m_documentTiming.domInteractive)
- m_documentTiming.domInteractive = monotonicallyIncreasingTime();
+ m_documentTiming.domInteractive = MonotonicTime::now();
break;
case Complete:
if (!m_documentTiming.domComplete)
- m_documentTiming.domComplete = monotonicallyIncreasingTime();
+ m_documentTiming.domComplete = MonotonicTime::now();
break;
}
#endif
@@ -1184,13 +1218,13 @@ void Document::setReadyState(ReadyState readyState)
m_readyState = readyState;
dispatchEvent(Event::create(eventNames().readystatechangeEvent, false, false));
- if (settings() && settings()->suppressesIncrementalRendering())
+ if (settings().suppressesIncrementalRendering())
setVisualUpdatesAllowed(readyState);
}
void Document::setVisualUpdatesAllowed(ReadyState readyState)
{
- ASSERT(settings() && settings()->suppressesIncrementalRendering());
+ ASSERT(settings().suppressesIncrementalRendering());
switch (readyState) {
case Loading:
ASSERT(!m_visualUpdatesSuppressionTimer.isActive());
@@ -1224,7 +1258,7 @@ void Document::setVisualUpdatesAllowed(bool visualUpdatesAllowed)
if (visualUpdatesAllowed)
m_visualUpdatesSuppressionTimer.stop();
else
- m_visualUpdatesSuppressionTimer.startOneShot(settings()->incrementalRenderingSuppressionTimeoutInSeconds());
+ m_visualUpdatesSuppressionTimer.startOneShot(settings().incrementalRenderingSuppressionTimeoutInSeconds());
if (!visualUpdatesAllowed)
return;
@@ -1238,14 +1272,12 @@ void Document::setVisualUpdatesAllowed(bool visualUpdatesAllowed)
if (frame()->isMainFrame()) {
frameView->addPaintPendingMilestones(DidFirstPaintAfterSuppressedIncrementalRendering);
if (page->requestedLayoutMilestones() & DidFirstLayoutAfterSuppressedIncrementalRendering)
- frame()->loader().didLayout(DidFirstLayoutAfterSuppressedIncrementalRendering);
+ frame()->loader().didReachLayoutMilestone(DidFirstLayoutAfterSuppressedIncrementalRendering);
}
}
-#if USE(ACCELERATED_COMPOSITING)
if (view())
view()->updateCompositingLayersAfterLayout();
-#endif
if (RenderView* renderView = this->renderView())
renderView->repaintViewAndCompositedLayers();
@@ -1254,7 +1286,7 @@ void Document::setVisualUpdatesAllowed(bool visualUpdatesAllowed)
frame->loader().forcePageTransitionIfNeeded();
}
-void Document::visualUpdatesSuppressionTimerFired(Timer<Document>&)
+void Document::visualUpdatesSuppressionTimerFired()
{
ASSERT(!m_visualUpdatesAllowed);
@@ -1275,18 +1307,19 @@ void Document::setVisualUpdatesAllowedByClient(bool visualUpdatesAllowedByClient
setVisualUpdatesAllowed(true);
}
-String Document::encoding() const
+String Document::characterSetWithUTF8Fallback() const
{
- if (TextResourceDecoder* d = decoder())
- return d->encoding().domName();
- return String();
+ AtomicString name = encoding();
+ if (!name.isNull())
+ return name;
+ return UTF8Encoding().domName();
}
-String Document::defaultCharset() const
+String Document::defaultCharsetForLegacyBindings() const
{
- if (Settings* settings = this->settings())
- return settings->defaultTextEncodingName();
- return String();
+ if (!frame())
+ UTF8Encoding().domName();
+ return settings().defaultTextEncodingName();
}
void Document::setCharset(const String& charset)
@@ -1303,31 +1336,20 @@ void Document::setContentLanguage(const String& language)
m_contentLanguage = language;
// Recalculate style so language is used when selecting the initial font.
- styleResolverChanged(DeferRecalcStyle);
+ m_styleScope->didChangeStyleSheetEnvironment();
}
-void Document::setXMLVersion(const String& version, ExceptionCode& ec)
+ExceptionOr<void> Document::setXMLVersion(const String& version)
{
- if (!implementation()->hasFeature("XML", String())) {
- ec = NOT_SUPPORTED_ERR;
- return;
- }
-
- if (!XMLDocumentParser::supportsXMLVersion(version)) {
- ec = NOT_SUPPORTED_ERR;
- return;
- }
+ if (!XMLDocumentParser::supportsXMLVersion(version))
+ return Exception { NOT_SUPPORTED_ERR };
m_xmlVersion = version;
+ return { };
}
-void Document::setXMLStandalone(bool standalone, ExceptionCode& ec)
+void Document::setXMLStandalone(bool standalone)
{
- if (!implementation()->hasFeature("XML", String())) {
- ec = NOT_SUPPORTED_ERR;
- return;
- }
-
m_xmlStandalone = standalone ? Standalone : NotStandalone;
}
@@ -1338,11 +1360,6 @@ void Document::setDocumentURI(const String& uri)
updateBaseURL();
}
-URL Document::baseURI() const
-{
- return m_baseURL;
-}
-
void Document::setContent(const String& content)
{
open();
@@ -1369,99 +1386,102 @@ String Document::suggestedMIMEType() const
return String();
}
-Element* Document::elementFromPoint(int x, int y) const
+void Document::overrideMIMEType(const String& mimeType)
{
- if (!hasLivingRenderTree())
- return nullptr;
+ 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 TreeScope::elementFromPoint(x, y);
+ return ASCIILiteral("application/xml");
}
-PassRefPtr<Range> Document::caretRangeFromPoint(int x, int y)
+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(this, x, y, &localPoint);
+ Node* node = nodeFromPoint(clientPoint, &localPoint);
if (!node)
return nullptr;
- Node* shadowAncestorNode = ancestorInThisScope(node);
- if (shadowAncestorNode != node) {
- unsigned offset = shadowAncestorNode->nodeIndex();
- 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);
- if (visiblePosition.isNull())
+ Position rangeCompliantPosition = renderer->positionForPoint(localPoint).parentAnchoredEquivalent();
+ if (rangeCompliantPosition.isNull())
return nullptr;
- Position rangeCompliantPosition = visiblePosition.deepEquivalent().parentAnchoredEquivalent();
- return Range::create(*this, rangeCompliantPosition, rangeCompliantPosition);
+ unsigned offset = rangeCompliantPosition.offsetInContainerNode();
+ node = &retargetToScope(*rangeCompliantPosition.containerNode());
+ if (node != rangeCompliantPosition.containerNode())
+ offset = 0;
+
+ return Range::create(*this, node, offset, node, offset);
}
-/*
- * Performs three operations:
- * 1. Convert control characters to spaces
- * 2. Trim leading and trailing spaces
- * 3. Collapse internal whitespace.
- */
-template <typename CharacterType>
-static inline StringWithDirection canonicalizedTitle(Document* document, const StringWithDirection& titleWithDirection)
+Element* Document::scrollingElement()
{
- const String& title = titleWithDirection.string();
- const CharacterType* characters = title.getCharacters<CharacterType>();
- unsigned length = title.length();
- unsigned i;
-
- StringBuffer<CharacterType> buffer(length);
- unsigned builderIndex = 0;
+ // FIXME: When we fix https://bugs.webkit.org/show_bug.cgi?id=106133, this should be replaced with the full implementation
+ // of Document.scrollingElement() as specified at http://dev.w3.org/csswg/cssom-view/#dom-document-scrollingelement.
- // Skip leading spaces and leading characters that would convert to spaces
- for (i = 0; i < length; ++i) {
- CharacterType c = characters[i];
- if (!(c <= 0x20 || c == 0x7F))
- break;
- }
+ return body();
+}
- if (i == length)
- return StringWithDirection();
+template<typename CharacterType> static inline String canonicalizedTitle(Document& document, const String& title)
+{
+ // FIXME: Compiling a separate copy of this for LChar and UChar is likely unnecessary.
+ // FIXME: Missing an optimized case for when title is fine as-is. This unnecessarily allocates
+ // and keeps around a new copy, and it's even the less optimal type of StringImpl with a separate buffer.
+ // Could probably just use StringBuilder instead.
- // Replace control characters with spaces, and backslashes with currency symbols, and collapse whitespace.
- 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;
- }
- }
+ auto* characters = title.characters<CharacterType>();
+ unsigned length = title.length();
- // Strip trailing spaces
- while (builderIndex > 0) {
- --builderIndex;
- if (buffer[builderIndex] != ' ')
- break;
- }
+ StringBuffer<CharacterType> buffer { length };
+ unsigned bufferLength = 0;
- if (!builderIndex && buffer[builderIndex] == ' ')
- return StringWithDirection();
+ auto* decoder = document.decoder();
+ auto backslashAsCurrencySymbol = decoder ? decoder->encoding().backslashAsCurrencySymbol() : '\\';
- buffer.shrink(builderIndex + 1);
+ // Collapse runs of HTML spaces into single space characters.
+ // Strip leading and trailing spaces.
+ // Replace backslashes with currency symbols.
+ bool previousCharacterWasHTMLSpace = false;
+ for (unsigned i = 0; i < length; ++i) {
+ auto character = characters[i];
+ if (isHTMLSpace(character))
+ previousCharacterWasHTMLSpace = true;
+ else {
+ if (character == '\\')
+ character = backslashAsCurrencySymbol;
+ if (previousCharacterWasHTMLSpace && bufferLength)
+ buffer[bufferLength++] = ' ';
+ buffer[bufferLength++] = character;
+ previousCharacterWasHTMLSpace = false;
+ }
+ }
+ if (!bufferLength)
+ return { };
- // Replace the backslashes with currency symbols if the encoding requires it.
- document->displayBufferModifiedByEncoding(buffer.characters(), buffer.length());
-
- return StringWithDirection(String::adopt(buffer), titleWithDirection.direction());
+ buffer.shrink(bufferLength);
+ return String::adopt(WTFMove(buffer));
}
void Document::updateTitle(const StringWithDirection& title)
@@ -1470,71 +1490,104 @@ void Document::updateTitle(const StringWithDirection& title)
return;
m_rawTitle = title;
+ m_title = title;
- if (m_rawTitle.string().isEmpty())
- m_title = StringWithDirection();
- else {
- if (m_rawTitle.string().is8Bit())
- m_title = canonicalizedTitle<LChar>(this, m_rawTitle);
+ if (!m_title.string.isEmpty()) {
+ if (m_title.string.is8Bit())
+ m_title.string = canonicalizedTitle<LChar>(*this, m_title.string);
else
- m_title = canonicalizedTitle<UChar>(this, m_rawTitle);
+ m_title.string = canonicalizedTitle<UChar>(*this, m_title.string);
}
- if (DocumentLoader* loader = this->loader())
+
+ if (auto* loader = this->loader())
loader->setTitle(m_title);
}
+void Document::updateTitleFromTitleElement()
+{
+ if (!m_titleElement) {
+ updateTitle({ });
+ return;
+ }
+
+ if (is<HTMLTitleElement>(*m_titleElement))
+ updateTitle(downcast<HTMLTitleElement>(*m_titleElement).textWithDirection());
+ else if (is<SVGTitleElement>(*m_titleElement)) {
+ // FIXME: Does the SVG title element have a text direction?
+ updateTitle({ downcast<SVGTitleElement>(*m_titleElement).textContent(), LTR });
+ }
+}
+
void Document::setTitle(const String& title)
{
- // Title set by JavaScript -- overrides any title elements.
- 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);
+ if (!m_titleElement) {
+ if (isHTMLDocument() || isXHTMLDocument()) {
+ auto* headElement = head();
+ if (!headElement)
+ return;
+ m_titleElement = HTMLTitleElement::create(HTMLNames::titleTag, *this);
+ headElement->appendChild(*m_titleElement);
+ } else if (isSVGDocument()) {
+ auto* element = documentElement();
+ if (!is<SVGSVGElement>(element))
+ return;
+ m_titleElement = SVGTitleElement::create(SVGNames::titleTag, *this);
+ element->insertBefore(*m_titleElement, element->firstChild());
}
+ } else if (!isHTMLDocument() && !isXHTMLDocument() && !isSVGDocument()) {
+ // FIXME: What exactly is the point of this? This seems like a strange moment
+ // in time to demote something from being m_titleElement, when setting the
+ // value of the title attribute. Do we have test coverage for this?
+ m_titleElement = nullptr;
}
- // The DOM API has no method of specifying direction, so assume LTR.
- updateTitle(StringWithDirection(title, LTR));
-
- if (m_titleElement && isHTMLTitleElement(m_titleElement.get()))
- toHTMLTitleElement(m_titleElement.get())->setText(title);
+ if (is<HTMLTitleElement>(m_titleElement.get()))
+ downcast<HTMLTitleElement>(*m_titleElement).setTextContent(title);
+ else if (is<SVGTitleElement>(m_titleElement.get()))
+ downcast<SVGTitleElement>(*m_titleElement).setTextContent(title);
+ else
+ updateTitle({ title, LTR });
}
-void Document::setTitleElement(const StringWithDirection& title, Element* titleElement)
+void Document::updateTitleElement(Element* newTitleElement)
{
- if (titleElement != m_titleElement) {
- if (m_titleElement || m_titleSetExplicitly) {
- // Only allow the first title element to change the title -- others have no effect.
- return;
- }
- m_titleElement = titleElement;
+ if (is<SVGSVGElement>(documentElement()))
+ m_titleElement = childrenOfType<SVGTitleElement>(*documentElement()).first();
+ else {
+ if (m_titleElement) {
+ if (isHTMLDocument() || isXHTMLDocument())
+ m_titleElement = descendantsOfType<HTMLTitleElement>(*this).first();
+ } else
+ m_titleElement = newTitleElement;
}
- updateTitle(title);
+ updateTitleFromTitleElement();
}
-void Document::removeTitle(Element* titleElement)
+void Document::titleElementAdded(Element& titleElement)
{
- if (m_titleElement != titleElement)
+ if (m_titleElement == &titleElement)
return;
- m_titleElement = nullptr;
- m_titleSetExplicitly = false;
+ updateTitleElement(&titleElement);
+}
- // Update title based on first title element in the head, if one exists.
- if (HTMLElement* headElement = head()) {
- if (auto firstTitle = childrenOfType<HTMLTitleElement>(*headElement).first())
- setTitleElement(firstTitle->textWithDirection(), firstTitle);
- }
+void Document::titleElementRemoved(Element& titleElement)
+{
+ if (m_titleElement != &titleElement)
+ return;
+
+ updateTitleElement(nullptr);
+}
+
+void Document::titleElementTextChanged(Element& titleElement)
+{
+ if (m_titleElement != &titleElement)
+ return;
- if (!m_titleElement)
- updateTitle(StringWithDirection());
+ updateTitleFromTitleElement();
}
-#if ENABLE(PAGE_VISIBILITY_API)
void Document::registerForVisibilityStateChangedCallbacks(Element* element)
{
m_visibilityStateCallbackElements.add(element);
@@ -1548,44 +1601,49 @@ void Document::unregisterForVisibilityStateChangedCallbacks(Element* element)
void Document::visibilityStateChanged()
{
dispatchEvent(Event::create(eventNames().visibilitychangeEvent, false, false));
- for (auto it = m_visibilityStateCallbackElements.begin(); it != m_visibilityStateCallbackElements.end(); ++it)
- (*it)->visibilityStateChanged();
+ for (auto* element : m_visibilityStateCallbackElements)
+ element->visibilityStateChanged();
}
-PageVisibilityState Document::pageVisibilityState() const
+auto Document::visibilityState() const -> VisibilityState
{
// The visibility of the document is inherited from the visibility of the
// page. If there is no page associated with the document, we will assume
// that the page is hidden, as specified by the spec:
// http://dvcs.w3.org/hg/webperf/raw-file/tip/specs/PageVisibility/Overview.html#dom-document-hidden
if (!m_frame || !m_frame->page())
- return PageVisibilityStateHidden;
+ return VisibilityState::Hidden;
return m_frame->page()->visibilityState();
}
-String Document::visibilityState() const
+bool Document::hidden() const
{
- return pageVisibilityStateString(pageVisibilityState());
+ return visibilityState() != VisibilityState::Visible;
}
-bool Document::hidden() const
+#if ENABLE(VIDEO)
+
+void Document::registerForAllowsMediaDocumentInlinePlaybackChangedCallbacks(HTMLMediaElement& element)
{
- return pageVisibilityState() != PageVisibilityStateVisible;
+ m_allowsMediaDocumentInlinePlaybackElements.add(&element);
}
-#endif
-#if ENABLE(CSP_NEXT)
-DOMSecurityPolicy* Document::securityPolicy()
+void Document::unregisterForAllowsMediaDocumentInlinePlaybackChangedCallbacks(HTMLMediaElement& element)
{
- if (!m_domSecurityPolicy)
- m_domSecurityPolicy = DOMSecurityPolicy::create(this);
- return m_domSecurityPolicy.get();
+ m_allowsMediaDocumentInlinePlaybackElements.remove(&element);
}
+
+void Document::allowsMediaDocumentInlinePlaybackChanged()
+{
+ for (auto* element : m_allowsMediaDocumentInlinePlaybackElements)
+ element->allowsMediaDocumentInlinePlaybackChanged();
+}
+
#endif
String Document::nodeName() const
{
- return "#document";
+ return ASCIILiteral("#document");
}
Node::NodeType Document::nodeType() const
@@ -1596,7 +1654,7 @@ Node::NodeType Document::nodeType() const
FormController& Document::formController()
{
if (!m_formController)
- m_formController = FormController::create();
+ m_formController = std::make_unique<FormController>();
return *m_formController;
}
@@ -1624,34 +1682,19 @@ Page* Document::page() const
return m_frame ? m_frame->page() : nullptr;
}
-Settings* Document::settings() const
-{
- return m_frame ? &m_frame->settings() : nullptr;
-}
-
-PassRefPtr<Range> Document::createRange()
+Ref<Range> Document::createRange()
{
return Range::create(*this);
}
-PassRefPtr<NodeIterator> Document::createNodeIterator(Node* root, unsigned whatToShow,
- PassRefPtr<NodeFilter> filter, bool expandEntityReferences, ExceptionCode& ec)
+Ref<NodeIterator> Document::createNodeIterator(Node& root, unsigned long whatToShow, RefPtr<NodeFilter>&& filter, bool)
{
- if (!root) {
- ec = NOT_SUPPORTED_ERR;
- return nullptr;
- }
- return NodeIterator::create(root, whatToShow, filter, expandEntityReferences);
+ return NodeIterator::create(root, whatToShow, WTFMove(filter));
}
-PassRefPtr<TreeWalker> Document::createTreeWalker(Node* root, unsigned whatToShow,
- PassRefPtr<NodeFilter> filter, bool expandEntityReferences, ExceptionCode& ec)
+Ref<TreeWalker> Document::createTreeWalker(Node& root, unsigned long whatToShow, RefPtr<NodeFilter>&& filter, bool)
{
- if (!root) {
- ec = NOT_SUPPORTED_ERR;
- return nullptr;
- }
- return TreeWalker::create(root, whatToShow, filter, expandEntityReferences);
+ return TreeWalker::create(root, whatToShow, WTFMove(filter));
}
void Document::scheduleForcedStyleRecalc()
@@ -1662,14 +1705,9 @@ void Document::scheduleForcedStyleRecalc()
void Document::scheduleStyleRecalc()
{
- if (shouldDisplaySeamlesslyWithParent()) {
- // When we're seamless, our parent document manages our style recalcs.
- ownerElement()->setNeedsStyleRecalc();
- ownerElement()->document().scheduleStyleRecalc();
- return;
- }
+ ASSERT(!m_renderView || !m_renderView->inHitTesting());
- if (m_styleRecalcTimer.isActive() || inPageCache())
+ if (m_styleRecalcTimer.isActive() || pageCacheState() != NotInPageCache)
return;
ASSERT(childNeedsStyleRecalc() || m_pendingStyleRecalcShouldForce);
@@ -1679,7 +1717,7 @@ void Document::scheduleStyleRecalc()
m_styleRecalcTimer.startOneShot(0);
- InspectorInstrumentation::didScheduleStyleRecalculation(this);
+ InspectorInstrumentation::didScheduleStyleRecalculation(*this);
}
void Document::unscheduleStyleRecalc()
@@ -1700,11 +1738,6 @@ bool Document::hasPendingForcedStyleRecalc() const
return m_styleRecalcTimer.isActive() && m_pendingStyleRecalcShouldForce;
}
-void Document::styleRecalcTimerFired(Timer<Document>&)
-{
- updateStyleIfNeeded();
-}
-
void Document::recalcStyle(Style::Change change)
{
ASSERT(!view() || !view()->isPainting());
@@ -1714,13 +1747,17 @@ void Document::recalcStyle(Style::Change change)
return;
FrameView& frameView = m_renderView->frameView();
+ Ref<FrameView> protect(frameView);
if (frameView.isPainting())
return;
if (m_inStyleRecalc)
return; // Guard against re-entrancy. -dwh
+ TraceScope tracingScope(StyleRecalcStart, StyleRecalcEnd);
+
RenderView::RepaintRegionAccumulator repaintRegionAccumulator(renderView());
+ AnimationUpdateBlock animationUpdateBlock(&m_frame->animation());
// FIXME: We should update style on our ancestor chain before proceeding (especially for seamless),
// however doing so currently causes several tests to crash, as Frame::setDocument calls Document::attach
@@ -1729,16 +1766,16 @@ void Document::recalcStyle(Style::Change change)
// re-attaching our containing iframe, which when asked HTMLFrameElementBase::isURLAllowed
// hits a null-dereference due to security code always assuming the document has a SecurityOrigin.
- m_styleSheetCollection.flushPendingUpdates();
+ styleScope().flushPendingUpdate();
- InspectorInstrumentationCookie cookie = InspectorInstrumentation::willRecalculateStyle(this);
+ frameView.willRecalcStyle();
- if (m_elementSheet && m_elementSheet->contents().usesRemUnits())
- m_styleSheetCollection.setUsesRemUnit(true);
+ InspectorInstrumentationCookie cookie = InspectorInstrumentation::willRecalculateStyle(*this);
m_inStyleRecalc = true;
+ bool updatedCompositingLayers = false;
{
- PostAttachCallbackDisabler disabler(*this);
+ Style::PostResolutionCallbackDisabler disabler(*this);
WidgetHierarchyUpdatesSuspensionScope suspendWidgetHierarchyUpdates;
if (m_pendingStyleRecalcShouldForce)
@@ -1747,23 +1784,38 @@ void Document::recalcStyle(Style::Change change)
if (change == Style::Force) {
// This may get set again during style resolve.
m_hasNodesWithPlaceholderStyle = false;
+
+ auto documentStyle = Style::resolveForDocument(*this);
+
+ // Inserting the pictograph font at the end of the font fallback list is done by the
+ // font selector, so set a font selector if needed.
+ if (settings().fontFallbackPrefersPictographs())
+ documentStyle.fontCascade().update(&fontSelector());
+
+ auto documentChange = Style::determineChange(documentStyle, m_renderView->style());
+ if (documentChange != Style::NoChange)
+ renderView()->setStyle(WTFMove(documentStyle));
}
- Style::resolveTree(*this, change);
+ Style::TreeResolver resolver(*this);
+ auto styleUpdate = resolver.resolve(change);
-#if USE(ACCELERATED_COMPOSITING)
- frameView.updateCompositingLayersAfterStyleChange();
-#endif
+ m_lastStyleUpdateSizeForTesting = styleUpdate ? styleUpdate->size() : 0;
- clearNeedsStyleRecalc();
+ setHasValidStyle();
clearChildNeedsStyleRecalc();
unscheduleStyleRecalc();
m_inStyleRecalc = false;
- // Pseudo element removal and similar may only work with these flags still set. Reset them after the style recalc.
- if (m_styleResolver)
- m_styleSheetCollection.resetCSSFeatureFlags();
+ if (styleUpdate) {
+ SetForScope<bool> inRenderTreeUpdate(m_inRenderTreeUpdate, true);
+
+ RenderTreeUpdater updater(*this);
+ updater.commit(WTFMove(styleUpdate));
+ }
+
+ updatedCompositingLayers = frameView.updateCompositingLayersAfterStyleChange();
}
// If we wanted to call implicitClose() during recalcStyle, do so now that we're finished.
@@ -1771,17 +1823,39 @@ void Document::recalcStyle(Style::Change change)
m_closeAfterStyleRecalc = false;
implicitClose();
}
+
+ ++m_styleRecalcCount;
InspectorInstrumentation::didRecalculateStyle(cookie);
+ // Some animated images may now be inside the viewport due to style recalc,
+ // resume them if necessary if there is no layout pending. Otherwise, we'll
+ // check if they need to be resumed after layout.
+ if (updatedCompositingLayers && !frameView.needsLayout())
+ frameView.viewportContentsChanged();
+
+ // Usually this is handled by post-layout.
+ if (!frameView.needsLayout())
+ frameView.frame().selection().scheduleAppearanceUpdateAfterStyleChange();
+
// As a result of the style recalculation, the currently hovered element might have been
// detached (for example, by setting display:none in the :hover style), schedule another mouseMove event
// to check if any other elements ended up under the mouse pointer due to re-layout.
if (m_hoveredElement && !m_hoveredElement->renderer())
- frameView.frame().eventHandler().dispatchFakeMouseMoveEventSoon();
+ frameView.frame().mainFrame().eventHandler().dispatchFakeMouseMoveEventSoon();
+
+ if (m_gotoAnchorNeededAfterStylesheetsLoad && !styleScope().hasPendingSheets())
+ frameView.scrollToFragment(m_url);
- // Style change may reset the focus, e.g. display: none, visibility: hidden.
- resetHiddenFocusElementSoon();
+ // FIXME: Ideally we would ASSERT(!needsStyleRecalc()) here but we have some cases where it is not true.
+}
+
+bool Document::needsStyleRecalc() const
+{
+ if (pageCacheState() != NotInPageCache)
+ return false;
+
+ return m_pendingStyleRecalcShouldForce || childNeedsStyleRecalc() || styleScope().hasPendingUpdate();
}
void Document::updateStyleIfNeeded()
@@ -1789,17 +1863,15 @@ void Document::updateStyleIfNeeded()
ASSERT(isMainThread());
ASSERT(!view() || !view()->isPainting());
- if (!view() || view()->isInLayout())
+ if (!view() || view()->isInRenderTreeLayout())
return;
- if (m_optimizedStyleSheetUpdateTimer.isActive())
- styleResolverChanged(RecalcStyleIfNeeded);
+ styleScope().flushPendingUpdate();
- if ((!m_pendingStyleRecalcShouldForce && !childNeedsStyleRecalc()) || inPageCache())
+ if (!needsStyleRecalc())
return;
- AnimationUpdateBlock animationUpdateBlock(m_frame ? &m_frame->animation() : nullptr);
- recalcStyle(Style::NoChange);
+ recalcStyle();
}
void Document::updateLayout()
@@ -1807,7 +1879,7 @@ void Document::updateLayout()
ASSERT(isMainThread());
FrameView* frameView = view();
- if (frameView && frameView->isInLayout()) {
+ if (frameView && frameView->isInRenderTreeLayout()) {
// View layout should not be re-entrant.
ASSERT_NOT_REACHED();
return;
@@ -1815,8 +1887,8 @@ void Document::updateLayout()
RenderView::RepaintRegionAccumulator repaintRegionAccumulator(renderView());
- if (Element* oe = ownerElement())
- oe->document().updateLayout();
+ if (HTMLFrameOwnerElement* owner = ownerElement())
+ owner->document().updateLayout();
updateStyleIfNeeded();
@@ -1825,9 +1897,6 @@ void Document::updateLayout()
// Only do a layout if changes have occurred that make it necessary.
if (frameView && renderView() && (frameView->layoutPending() || renderView()->needsLayout()))
frameView->layout();
-
- // Active focus element's isFocusable() state may change after Layout. e.g. width: 0px or height: 0px.
- resetHiddenFocusElementSoon();
}
// FIXME: This is a bad idea and needs to be removed eventually.
@@ -1848,10 +1917,11 @@ void Document::updateLayoutIgnorePendingStylesheets(Document::RunPostLayoutTasks
// moment. If it were more refined, we might be able to do something better.)
// It's worth noting though that this entire method is a hack, since what we really want to do is
// suspend JS instead of doing a layout with inaccurate information.
- HTMLElement* bodyElement = body();
+ HTMLElement* bodyElement = bodyOrFrameset();
if (bodyElement && !bodyElement->renderer() && m_pendingSheetLayout == NoLayoutWithPendingSheets) {
m_pendingSheetLayout = DidLayoutWithPendingSheets;
- styleResolverChanged(RecalcStyleImmediately);
+ styleScope().didChangeActiveStyleSheetCandidates();
+ recalcStyle(Style::Force);
} else if (m_hasNodesWithPlaceholderStyle)
// If new nodes have been added or style recalc has been done with style sheets still pending, some nodes
// may not have had their real style calculated yet. Normally this gets cleaned when style sheets arrive
@@ -1861,33 +1931,141 @@ void Document::updateLayoutIgnorePendingStylesheets(Document::RunPostLayoutTasks
updateLayout();
- if (runPostLayoutTasks == RunPostLayoutTasksSynchronously && view())
+ if (runPostLayoutTasks == RunPostLayoutTasks::Synchronously && view())
view()->flushAnyPendingPostLayoutTasks();
m_ignorePendingStylesheets = oldIgnore;
}
-PassRef<RenderStyle> Document::styleForElementIgnoringPendingStylesheets(Element* element)
+std::unique_ptr<RenderStyle> Document::styleForElementIgnoringPendingStylesheets(Element& element, const RenderStyle* parentStyle)
{
- ASSERT_ARG(element, &element->document() == this);
+ ASSERT(&element.document() == this);
// On iOS request delegates called during styleForElement may result in re-entering WebKit and killing the style resolver.
- ResourceLoadScheduler::Suspender suspender(*platformStrategies()->loaderStrategy()->resourceLoadScheduler());
+ Style::PostResolutionCallbackDisabler disabler(*this);
- TemporaryChange<bool> change(m_ignorePendingStylesheets, true);
- return ensureStyleResolver().styleForElement(element, element->parentNode() ? element->parentNode()->computedStyle() : nullptr);
+ SetForScope<bool> change(m_ignorePendingStylesheets, true);
+ auto elementStyle = element.resolveStyle(parentStyle);
+
+ if (elementStyle.relations) {
+ Style::Update emptyUpdate(*this);
+ Style::commitRelations(WTFMove(elementStyle.relations), emptyUpdate);
+ }
+
+ return WTFMove(elementStyle.renderStyle);
+}
+
+bool Document::updateLayoutIfDimensionsOutOfDate(Element& element, DimensionsCheck dimensionsCheck)
+{
+ ASSERT(isMainThread());
+
+ // If the stylesheets haven't loaded, just give up and do a full layout ignoring pending stylesheets.
+ if (!haveStylesheetsLoaded()) {
+ updateLayoutIgnorePendingStylesheets();
+ return true;
+ }
+
+ // Check for re-entrancy and assert (same code that is in updateLayout()).
+ FrameView* frameView = view();
+ if (frameView && frameView->isInRenderTreeLayout()) {
+ // View layout should not be re-entrant.
+ ASSERT_NOT_REACHED();
+ return true;
+ }
+
+ RenderView::RepaintRegionAccumulator repaintRegionAccumulator(renderView());
+
+ // Mimic the structure of updateLayout(), but at each step, see if we have been forced into doing a full
+ // layout.
+ 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()) {
+ // If we don't have a renderer or if the renderer needs layout for any reason, give up.
+ // Named flows can have auto height, so don't try to enforce the optimization in this case.
+ // The 2-pass nature of auto height named flow layout means the region may not be dirty yet.
+ 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;
+
+ // Check our containing block chain. If anything in the chain needs a layout, then require a full layout.
+ for (RenderObject* currRenderer = element.renderer(); currRenderer && !currRenderer->isRenderView(); currRenderer = currRenderer->container()) {
+
+ // Require the entire container chain to be boxes.
+ if (!is<RenderBox>(currRenderer)) {
+ requireFullLayout = true;
+ break;
+ }
+
+ previousBox = currentBox;
+ currentBox = downcast<RenderBox>(currRenderer);
+
+ // If a box needs layout for itself or if a box has changed children and sizes its width to
+ // its content, then require a full layout.
+ if (currentBox->selfNeedsLayout() ||
+ (checkingLogicalWidth && currRenderer->needsLayout() && currentBox->sizesLogicalWidthToFitContent(MainOrPreferredSize))) {
+ requireFullLayout = true;
+ break;
+ }
+
+ // If a block contains floats and the child's height isn't specified, then
+ // give up also, since our height could end up being influenced by the floats.
+ 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()) {
+ // FIXME: For now require only block flows all the way back to the root. This limits the optimization
+ // for now, and we'll expand it in future patches to apply to more and more scenarios.
+ // Disallow regions/columns from having the optimization.
+ // Give up if the writing mode changes at all in the containing block chain.
+ requireFullLayout = true;
+ break;
+ }
+
+ if (currRenderer == frameView->layoutRoot())
+ break;
+ }
+ }
+
+ StackStats::LayoutCheckPoint layoutCheckPoint;
+
+ // Only do a layout if changes have occurred that make it necessary.
+ if (requireFullLayout && frameView && renderView() && (frameView->layoutPending() || renderView()->needsLayout()))
+ frameView->layout();
+
+ return requireFullLayout;
}
bool Document::isPageBoxVisible(int pageIndex)
{
- Ref<RenderStyle> pageStyle(ensureStyleResolver().styleForPage(pageIndex));
+ updateStyleIfNeeded();
+ std::unique_ptr<RenderStyle> pageStyle(styleScope().resolver().styleForPage(pageIndex));
return pageStyle->visibility() != HIDDEN; // display property doesn't apply to @page.
}
void Document::pageSizeAndMarginsInPixels(int pageIndex, IntSize& pageSize, int& marginTop, int& marginRight, int& marginBottom, int& marginLeft)
{
- RefPtr<RenderStyle> style = ensureStyleResolver().styleForPage(pageIndex);
- RenderView* view = renderView();
+ updateStyleIfNeeded();
+ auto style = styleScope().resolver().styleForPage(pageIndex);
int width = pageSize.width();
int height = pageSize.height();
@@ -1903,11 +2081,11 @@ void Document::pageSizeAndMarginsInPixels(int pageIndex, IntSize& pageSize, int&
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, view);
- height = valueForLength(size.height(), 0, view);
+ auto& size = style->pageSize();
+ ASSERT(size.width.isFixed());
+ ASSERT(size.height.isFixed());
+ width = valueForLength(size.width, 0);
+ height = valueForLength(size.height, 0);
break;
}
default:
@@ -1917,39 +2095,39 @@ void Document::pageSizeAndMarginsInPixels(int pageIndex, IntSize& pageSize, int&
// The percentage is calculated with respect to the width even for margin top and bottom.
// http://www.w3.org/TR/CSS2/box.html#margin-properties
- marginTop = style->marginTop().isAuto() ? marginTop : intValueForLength(style->marginTop(), width, view);
- marginRight = style->marginRight().isAuto() ? marginRight : intValueForLength(style->marginRight(), width, view);
- marginBottom = style->marginBottom().isAuto() ? marginBottom : intValueForLength(style->marginBottom(), width, view);
- marginLeft = style->marginLeft().isAuto() ? marginLeft : intValueForLength(style->marginLeft(), width, view);
+ 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::setIsViewSource(bool isViewSource)
+StyleResolver& Document::userAgentShadowTreeStyleResolver()
{
- m_isViewSource = isViewSource;
- if (!m_isViewSource)
- return;
-
- setSecurityOrigin(SecurityOrigin::createUnique());
+ if (!m_userAgentShadowTreeStyleResolver)
+ m_userAgentShadowTreeStyleResolver = std::make_unique<StyleResolver>(*this);
+ return *m_userAgentShadowTreeStyleResolver;
}
-void Document::createStyleResolver()
+void Document::fontsNeedUpdate(FontSelector&)
{
- bool matchAuthorAndUserStyles = true;
- if (Settings* settings = this->settings())
- matchAuthorAndUserStyles = settings->authorAndUserStylesEnabled();
- m_styleResolver = adoptPtr(new StyleResolver(*this, matchAuthorAndUserStyles));
- m_styleSheetCollection.combineCSSFeatureFlags();
+ if (auto* resolver = styleScope().resolverIfExists())
+ resolver->invalidateMatchedPropertiesCache();
+ if (pageCacheState() != NotInPageCache || !renderView())
+ return;
+ scheduleForcedStyleRecalc();
}
-void Document::clearStyleResolver()
+void Document::didClearStyleResolver()
{
- m_styleResolver.clear();
+ m_userAgentShadowTreeStyleResolver = nullptr;
+
+ m_fontSelector->buildStarted();
}
void Document::createRenderTree()
{
ASSERT(!renderView());
- ASSERT(!m_inPageCache);
+ ASSERT(m_pageCacheState != InPageCache);
ASSERT(!m_axObjectCache || this != &topDocument());
if (m_isNonRenderedPlaceholder)
@@ -1959,23 +2137,11 @@ void Document::createRenderTree()
m_renderView = createRenderer<RenderView>(*this, RenderStyle::create());
Node::setRenderer(m_renderView.get());
-#if USE(ACCELERATED_COMPOSITING)
renderView()->setIsInWindow(true);
-#endif
recalcStyle(Style::Force);
}
-static void pageWheelEventHandlerCountChanged(Page& page)
-{
- unsigned count = 0;
- for (const Frame* frame = &page.mainFrame(); frame; frame = frame->tree().traverseNext()) {
- if (Document* document = frame->document())
- count += document->wheelEventHandlerCount();
- }
- page.chrome().client().numWheelEventHandlersChanged(count);
-}
-
void Document::didBecomeCurrentDocumentInFrame()
{
// FIXME: Are there cases where the document can be dislodged from the frame during the event handling below?
@@ -1996,52 +2162,71 @@ void Document::didBecomeCurrentDocumentInFrame()
// subframes' documents have no wheel event handlers, then the count did not change,
// unless the documents they are replacing had wheel event handlers.
if (page() && m_frame->isMainFrame())
- pageWheelEventHandlerCountChanged(*page());
-
-#if ENABLE(TOUCH_EVENTS)
- // FIXME: Doing this only for the main frame is insufficient.
- // A subframe could have touch event handlers.
- if (hasTouchEventHandlers() && page() && m_frame->isMainFrame())
- page()->chrome().client().needTouchEvents(true);
-#endif
-
-#if PLATFORM(IOS)
- // Ensure that document scheduled task state matches frame timer state. It can be out of sync
- // if timers state changed while the document was not in the frame (possibly in page cache,
- // or simply newly created).
- // FIXME: How does this interact with cross-platform code below?
- if (m_frame->timersPaused())
- suspendScheduledTasks(ActiveDOMObject::DocumentWillBePaused);
- else
- resumeScheduledTasks(ActiveDOMObject::DocumentWillBePaused);
-#endif
+ wheelEventHandlersChanged();
+ // Ensure that the scheduled task state of the document matches the DOM suspension state of the frame. It can
+ // be out of sync if the DOM suspension state changed while the document was not in the frame (possibly in the
+ // page cache, or simply newly created).
if (m_frame->activeDOMObjectsAndAnimationsSuspended()) {
- suspendScriptedAnimationControllerCallbacks();
m_frame->animation().suspendAnimationsForDocument(this);
- suspendActiveDOMObjects(ActiveDOMObject::PageWillBeSuspended);
+ suspendScheduledTasks(ActiveDOMObject::PageWillBeSuspended);
+ } else {
+ resumeScheduledTasks(ActiveDOMObject::PageWillBeSuspended);
+ m_frame->animation().resumeAnimationsForDocument(this);
}
}
-void Document::disconnectFromFrame()
+void Document::frameDestroyed()
{
- m_frame = nullptr;
+ // detachFromFrame() must be called before destroying the Frame.
+ ASSERT_WITH_SECURITY_IMPLICATION(!m_frame);
+ FrameDestructionObserver::frameDestroyed();
+}
+
+void Document::didBecomeCurrentDocumentInView()
+{
+ ASSERT(view());
+ if (!hasLivingRenderTree())
+ createRenderTree();
+}
+
+void Document::attachToCachedFrame(CachedFrameBase& cachedFrame)
+{
+ ASSERT_WITH_SECURITY_IMPLICATION(cachedFrame.document() == this);
+ ASSERT(cachedFrame.view());
+ ASSERT(m_pageCacheState == Document::InPageCache);
+ observeFrame(&cachedFrame.view()->frame());
+}
+
+void Document::detachFromCachedFrame(CachedFrameBase& cachedFrame)
+{
+ ASSERT_UNUSED(cachedFrame, cachedFrame.view());
+ ASSERT_WITH_SECURITY_IMPLICATION(cachedFrame.document() == this);
+ ASSERT(m_frame == &cachedFrame.view()->frame());
+ ASSERT(m_pageCacheState == Document::InPageCache);
+ detachFromFrame();
}
void Document::destroyRenderTree()
{
ASSERT(hasLivingRenderTree());
- ASSERT(!m_inPageCache);
+ ASSERT(frame());
+ ASSERT(page());
+
+ FrameView* frameView = frame()->document() == this ? frame()->view() : nullptr;
+
+ // Prevent Widget tree changes from committing until the RenderView is dead and gone.
+ WidgetHierarchyUpdatesSuspensionScope suspendWidgetHierarchyUpdates;
- TemporaryChange<bool> change(m_renderTreeBeingDestroyed, true);
+ SetForScope<bool> change(m_renderTreeBeingDestroyed, true);
if (this == &topDocument())
clearAXObjectCache();
documentWillBecomeInactive();
- if (FrameView* frameView = view())
- frameView->detachCustomScrollbars();
+ if (frameView)
+ frameView->willDestroyRenderTree();
#if ENABLE(FULLSCREEN_API)
if (m_fullScreenRenderer)
@@ -2051,9 +2236,10 @@ void Document::destroyRenderTree()
m_hoveredElement = nullptr;
m_focusedElement = nullptr;
m_activeElement = nullptr;
+ m_focusNavigationStartingNode = nullptr;
if (m_documentElement)
- Style::detachRenderTree(*m_documentElement);
+ RenderTreeUpdater::tearDownRenderers(*m_documentElement);
clearChildNeedsStyleRecalc();
@@ -2062,32 +2248,61 @@ void Document::destroyRenderTree()
m_renderView = nullptr;
Node::setRenderer(nullptr);
-#if ENABLE(IOS_TEXT_AUTOSIZING)
+#if ENABLE(TEXT_AUTOSIZING)
// Do this before the arena is cleared, which is needed to deref the RenderStyle on TextAutoSizingKey.
m_textAutoSizedNodes.clear();
#endif
+
+ if (frameView)
+ frameView->didDestroyRenderTree();
}
void Document::prepareForDestruction()
{
-#if ENABLE(TOUCH_EVENTS) && PLATFORM(IOS)
- clearTouchEventListeners();
+ if (m_hasPreparedForDestruction)
+ return;
+
+ if (m_frame)
+ m_frame->animation().detachFromDocument(this);
+
+#if ENABLE(IOS_TOUCH_EVENTS)
+ clearTouchEventHandlersAndListeners();
+#endif
+
+#if HAVE(ACCESSIBILITY)
+ // Sub-frames need to cleanup Nodes in the text marker cache when the Document disappears.
+ if (this != &topDocument()) {
+ if (AXObjectCache* cache = existingAXObjectCache())
+ cache->clearTextMarkerNodesInUse(this);
+ }
#endif
- disconnectDescendantFrames();
+ {
+ NavigationDisabler navigationDisabler;
+ disconnectDescendantFrames();
+ }
+
if (m_domWindow && m_frame)
m_domWindow->willDetachDocumentFromFrame();
- destroyRenderTree();
+ if (hasLivingRenderTree())
+ destroyRenderTree();
- if (isPluginDocument())
- toPluginDocument(this)->detachFromPluginElement();
+ if (is<PluginDocument>(*this))
+ downcast<PluginDocument>(*this).detachFromPluginElement();
#if ENABLE(POINTER_LOCK)
if (page())
- page()->pointerLockController()->documentDetached(this);
+ page()->pointerLockController().documentDetached(*this);
#endif
+ if (auto* page = this->page()) {
+ if (auto* validationMessageClient = page->validationMessageClient())
+ validationMessageClient->documentDetached(*this);
+ }
+
+ InspectorInstrumentation::documentDetached(*this);
+
stopActiveDOMObjects();
m_eventQueue.close();
#if ENABLE(FULLSCREEN_API)
@@ -2097,19 +2312,34 @@ void Document::prepareForDestruction()
commonTeardown();
-#if ENABLE(SHARED_WORKERS)
- SharedWorkerRepository::documentDetached(this);
-#endif
-
#if ENABLE(TOUCH_EVENTS)
if (m_touchEventTargets && m_touchEventTargets->size() && parentDocument())
- parentDocument()->didRemoveEventTargetNode(this);
+ parentDocument()->didRemoveEventTargetNode(*this);
#endif
+ if (m_wheelEventTargets && m_wheelEventTargets->size() && parentDocument())
+ parentDocument()->didRemoveEventTargetNode(*this);
+
if (m_mediaQueryMatcher)
m_mediaQueryMatcher->documentDestroyed();
- disconnectFromFrame();
+#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
+
+ detachFromFrame();
+
+ m_hasPreparedForDestruction = true;
+
+ // Note that m_pageCacheState can be Document::AboutToEnterPageCache if our frame
+ // was removed in an onpagehide event handler fired when the top-level frame is
+ // about to enter the page cache.
+ ASSERT_WITH_SECURITY_IMPLICATION(m_pageCacheState != Document::InPageCache);
}
void Document::removeAllEventListeners()
@@ -2118,27 +2348,63 @@ void Document::removeAllEventListeners()
if (m_domWindow)
m_domWindow->removeAllEventListeners();
-#if ENABLE(TOUCH_EVENTS) && PLATFORM(IOS)
- clearTouchEventListeners();
+#if ENABLE(IOS_TOUCH_EVENTS)
+ clearTouchEventHandlersAndListeners();
#endif
- for (Node* node = firstChild(); node; node = NodeTraversal::next(node))
+ for (Node* node = firstChild(); node; node = NodeTraversal::next(*node))
node->removeAllEventListeners();
+
+#if ENABLE(TOUCH_EVENTS)
+ m_touchEventTargets = nullptr;
+#endif
+ m_wheelEventTargets = nullptr;
}
-void Document::platformSuspendOrStopActiveDOMObjects()
+void Document::suspendDeviceMotionAndOrientationUpdates()
{
-#if PLATFORM(IOS)
-#if ENABLE(DEVICE_ORIENTATION)
+ 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
+}
+
+bool Document::shouldBypassMainWorldContentSecurityPolicy() const
+{
+ JSC::CallFrame* callFrame = commonVM().topCallFrame;
+ if (callFrame == JSC::CallFrame::noCaller())
+ return false;
+ DOMWrapperWorld& domWrapperWorld = currentWorld(callFrame);
+ if (domWrapperWorld.isNormal())
+ return false;
+ return true;
+}
+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);
+ if (auto* frame = this->frame()) {
+ if (auto* page = frame->page())
+ page->chrome().client().clearContentChangeObservers(*frame);
+ }
}
#endif
}
@@ -2146,19 +2412,14 @@ void Document::platformSuspendOrStopActiveDOMObjects()
void Document::suspendActiveDOMObjects(ActiveDOMObject::ReasonForSuspension why)
{
ScriptExecutionContext::suspendActiveDOMObjects(why);
+ suspendDeviceMotionAndOrientationUpdates();
platformSuspendOrStopActiveDOMObjects();
}
void Document::resumeActiveDOMObjects(ActiveDOMObject::ReasonForSuspension why)
{
ScriptExecutionContext::resumeActiveDOMObjects(why);
-
-#if ENABLE(DEVICE_ORIENTATION) && PLATFORM(IOS)
- if (m_deviceMotionController)
- m_deviceMotionController->resumeUpdates();
- if (m_deviceOrientationController)
- m_deviceOrientationController->resumeUpdates();
-#endif
+ resumeDeviceMotionAndOrientationUpdates();
// FIXME: For iOS, do we need to add content change observers that were removed in Document::suspendActiveDOMObjects()?
}
@@ -2173,11 +2434,12 @@ void Document::clearAXObjectCache()
ASSERT(&topDocument() == this);
// Clear the cache member variable before calling delete because attempts
// are made to access it during destruction.
- m_axObjectCache.clear();
+ m_axObjectCache = nullptr;
}
-AXObjectCache* Document::existingAXObjectCache() const
+AXObjectCache* Document::existingAXObjectCacheSlow() const
{
+ ASSERT(hasEverCreatedAnAXObjectCache);
Document& topDocument = this->topDocument();
if (!topDocument.hasLivingRenderTree())
return nullptr;
@@ -2200,8 +2462,10 @@ AXObjectCache* Document::axObjectCache() const
return nullptr;
ASSERT(&topDocument == this || !m_axObjectCache);
- if (!topDocument.m_axObjectCache)
- topDocument.m_axObjectCache = adoptPtr(new AXObjectCache(topDocument));
+ if (!topDocument.m_axObjectCache) {
+ topDocument.m_axObjectCache = std::make_unique<AXObjectCache>(topDocument);
+ hasEverCreatedAnAXObjectCache = true;
+ }
return topDocument.m_axObjectCache.get();
}
@@ -2209,10 +2473,10 @@ void Document::setVisuallyOrdered()
{
m_visuallyOrdered = true;
if (renderView())
- renderView()->style().setRTLOrdering(VisualOrder);
+ renderView()->mutableStyle().setRTLOrdering(VisualOrder);
}
-PassRefPtr<DocumentParser> Document::createParser()
+Ref<DocumentParser> Document::createParser()
{
// FIXME: this should probably pass the frame instead
return XMLDocumentParser::create(*this, view());
@@ -2225,10 +2489,13 @@ ScriptableDocumentParser* Document::scriptableDocumentParser() const
void Document::open(Document* ownerDocument)
{
+ if (m_ignoreOpensDuringUnloadCount)
+ return;
+
if (ownerDocument) {
setURL(ownerDocument->url());
- m_cookieURL = ownerDocument->cookieURL();
- setSecurityOrigin(ownerDocument->securityOrigin());
+ setCookieURL(ownerDocument->cookieURL());
+ setSecurityOriginPolicy(ownerDocument->securityOriginPolicy());
}
if (m_frame) {
@@ -2261,7 +2528,7 @@ void Document::detachParser()
if (!m_parser)
return;
m_parser->detach();
- m_parser.clear();
+ m_parser = nullptr;
}
void Document::cancelParsing()
@@ -2279,57 +2546,61 @@ void Document::cancelParsing()
void Document::implicitOpen()
{
- cancelParsing();
-
removeChildren();
- setCompatibilityMode(NoQuirksMode);
-
- // Documents rendered seamlessly should start out requiring a stylesheet
- // collection update in order to ensure they inherit all the relevant data
- // from their parent.
- if (shouldDisplaySeamlesslyWithParent())
- styleResolverChanged(DeferRecalcStyle);
+ setCompatibilityMode(DocumentCompatibilityMode::NoQuirksMode);
+ cancelParsing();
m_parser = createParser();
setParsing(true);
setReadyState(Loading);
}
-HTMLElement* Document::body() const
+HTMLBodyElement* Document::body() const
{
- // If the document element contains both a frameset and a body, the frameset wins.
- auto element = documentElement();
+ auto* element = documentElement();
if (!element)
return nullptr;
- if (auto frameset = childrenOfType<HTMLFrameSetElement>(*element).first())
- return frameset;
return childrenOfType<HTMLBodyElement>(*element).first();
}
-void Document::setBody(PassRefPtr<HTMLElement> prpNewBody, ExceptionCode& ec)
+HTMLElement* Document::bodyOrFrameset() const
{
- RefPtr<HTMLElement> newBody = prpNewBody;
-
- if (!newBody || !documentElement() || !newBody->hasTagName(bodyTag)) {
- ec = HIERARCHY_REQUEST_ERR;
- return;
+ // Return the first body or frameset child of the html element.
+ auto* element = documentElement();
+ if (!is<HTMLHtmlElement>(element))
+ return nullptr;
+ for (auto& child : childrenOfType<HTMLElement>(*element)) {
+ if (is<HTMLBodyElement>(child) || is<HTMLFrameSetElement>(child))
+ return &child;
}
+ return nullptr;
+}
- if (&newBody->document() != this) {
- ec = 0;
- RefPtr<Node> node = importNode(newBody.get(), true, ec);
- if (ec)
- return;
-
- newBody = toHTMLElement(node.get());
- }
+ExceptionOr<void> Document::setBodyOrFrameset(RefPtr<HTMLElement>&& newBody)
+{
+ if (!is<HTMLBodyElement>(newBody.get()) && !is<HTMLFrameSetElement>(newBody.get()))
+ return Exception { HIERARCHY_REQUEST_ERR };
- HTMLElement* b = body();
- if (!b)
- documentElement()->appendChild(newBody.release(), ec);
- else
- documentElement()->replaceChild(newBody.release(), b, ec);
+ auto* currentBody = bodyOrFrameset();
+ if (newBody == currentBody)
+ return { };
+
+ if (!m_documentElement)
+ return Exception { HIERARCHY_REQUEST_ERR };
+
+ if (currentBody)
+ return m_documentElement->replaceChild(*newBody, *currentBody);
+ return m_documentElement->appendChild(*newBody);
+}
+
+Location* Document::location() const
+{
+ auto* window = domWindow();
+ if (!window)
+ return nullptr;
+
+ return window->location();
}
HTMLHeadElement* Document::head()
@@ -2363,7 +2634,7 @@ void Document::explicitClose()
return;
}
- m_frame->loader().checkCompleted();
+ checkCompleted();
}
void Document::implicitClose()
@@ -2381,7 +2652,7 @@ void Document::implicitClose()
return;
// Call to dispatchWindowLoadEvent can blow us from underneath.
- Ref<Document> protect(*this);
+ Ref<Document> protectedThis(*this);
m_processingLoadEvent = true;
@@ -2400,35 +2671,37 @@ void Document::implicitClose()
// ramifications, and we need to decide what is the Right Thing To Do(tm)
Frame* f = frame();
if (f) {
- f->loader().icon().startLoader();
- f->animation().startAnimationsIfNotSuspended(this);
- }
+ if (f->loader().client().useIconLoadingClient()) {
+ if (auto* documentLoader = loader())
+ documentLoader->startIconLoading();
+ } else
+ f->loader().icon().startLoader();
- ImageLoader::dispatchPendingBeforeLoadEvents();
- ImageLoader::dispatchPendingLoadEvents();
- ImageLoader::dispatchPendingErrorEvents();
+ f->animation().startAnimationsIfNotSuspended(this);
- HTMLLinkElement::dispatchPendingLoadEvents();
- HTMLStyleElement::dispatchPendingLoadEvents();
+ // FIXME: We shouldn't be dispatching pending events globally on all Documents here.
+ // For now, only do this when there is a Frame, otherwise this could cause JS reentrancy
+ // below SVG font parsing, for example. <https://webkit.org/b/136269>
+ ImageLoader::dispatchPendingBeforeLoadEvents();
+ ImageLoader::dispatchPendingLoadEvents();
+ ImageLoader::dispatchPendingErrorEvents();
+ HTMLLinkElement::dispatchPendingLoadEvents();
+ HTMLStyleElement::dispatchPendingLoadEvents();
-#if ENABLE(SVG)
- // To align the HTML load event and the SVGLoad event for the outermost <svg> element, fire it from
- // here, instead of doing it from SVGElement::finishedParsingChildren (if externalResourcesRequired="false",
- // which is the default, for ='true' its fired at a later time, once all external resources finished loading).
- if (svgExtensions())
- accessSVGExtensions()->dispatchSVGLoadEventToOutermostSVGElements();
-#endif
+ // To align the HTML load event and the SVGLoad event for the outermost <svg> element, fire it from
+ // here, instead of doing it from SVGElement::finishedParsingChildren (if externalResourcesRequired="false",
+ // which is the default, for ='true' its fired at a later time, once all external resources finished loading).
+ if (svgExtensions())
+ accessSVGExtensions().dispatchSVGLoadEventToOutermostSVGElements();
+ }
dispatchWindowLoadEvent();
- enqueuePageshowEvent(PageshowEventNotPersisted);
- enqueuePopstateEvent(m_pendingStateObject ? m_pendingStateObject.release() : SerializedScriptValue::nullValue());
-
+ dispatchPageshowEvent(PageshowEventNotPersisted);
+ if (m_pendingStateObject)
+ dispatchPopstateEvent(WTFMove(m_pendingStateObject));
+
if (f)
- f->loader().handledOnloadEvents();
-#ifdef INSTRUMENT_LAYOUT_SCHEDULING
- if (!ownerElement())
- printf("onload fired at %lld\n", elapsedTime().count());
-#endif
+ f->loader().dispatchOnloadEvents();
// An event handler may have removed the frame
if (!frame()) {
@@ -2440,7 +2713,7 @@ void Document::implicitClose()
// fires. This will improve onload scores, and other browsers do it.
// If they wanna cheat, we can too. -dwh
- if (frame()->navigationScheduler().locationChangePending() && elapsedTime() < settings()->layoutInterval()) {
+ if (frame()->navigationScheduler().locationChangePending() && timeSinceDocumentCreation() < settings().layoutInterval()) {
// Just bail out. Before or during the onload we were shifted to another page.
// The old i-Bench suite does this. When this happens don't bother painting or laying out.
m_processingLoadEvent = false;
@@ -2464,15 +2737,19 @@ void Document::implicitClose()
m_processingLoadEvent = false;
-#if PLATFORM(MAC) || PLATFORM(WIN) || PLATFORM(GTK) || PLATFORM(EFL)
+#if PLATFORM(COCOA) || PLATFORM(WIN) || PLATFORM(GTK)
if (f && hasLivingRenderTree() && AXObjectCache::accessibilityEnabled()) {
// The AX cache may have been cleared at this point, but we need to make sure it contains an
// AX object to send the notification to. getOrCreate will make sure that an valid AX object
// exists in the cache (we ignore the return value because we don't need it here). This is
- // only safe to call when a layout is not in progress, so it can not be used in postNotification.
+ // only safe to call when a layout is not in progress, so it can not be used in postNotification.
+ //
+ // This notification is now called AXNewDocumentLoadComplete because there are other handlers that will
+ // catch new AND page history loads, and that uses AXLoadComplete
+
axObjectCache()->getOrCreate(renderView());
if (this == &topDocument())
- axObjectCache()->postNotification(renderView(), AXObjectCache::AXLoadComplete);
+ axObjectCache()->postNotification(renderView(), AXObjectCache::AXNewDocumentLoadComplete);
else {
// AXLoadComplete can only be posted on the top document, so if it's a document
// in an iframe that just finished loading, post AXLayoutComplete instead.
@@ -2481,10 +2758,8 @@ void Document::implicitClose()
}
#endif
-#if ENABLE(SVG)
if (svgExtensions())
- accessSVGExtensions()->startAnimations();
-#endif
+ accessSVGExtensions().startAnimations();
}
void Document::setParsing(bool b)
@@ -2492,15 +2767,10 @@ void Document::setParsing(bool b)
m_bParsing = b;
if (m_bParsing && !m_sharedObjectPool)
- m_sharedObjectPool = DocumentSharedObjectPool::create();
-
- if (!m_bParsing && view())
- view()->scheduleRelayout();
+ m_sharedObjectPool = std::make_unique<DocumentSharedObjectPool>();
-#ifdef INSTRUMENT_LAYOUT_SCHEDULING
- if (!ownerElement() && !m_bParsing)
- printf("Parsing finished at %lld\n", elapsedTime().count());
-#endif
+ if (!m_bParsing && view() && !view()->needsLayout())
+ view()->fireLayoutRelatedMilestonesIfNeeded();
}
bool Document::shouldScheduleLayout()
@@ -2511,35 +2781,33 @@ bool Document::shouldScheduleLayout()
// (a) Only schedule a layout once the stylesheets are loaded.
// (b) Only schedule layout once we have a body element.
- return (haveStylesheetsLoaded() && body())
- || (documentElement() && !documentElement()->hasTagName(htmlTag));
+ return (haveStylesheetsLoaded() && bodyOrFrameset())
+ || (documentElement() && !is<HTMLHtmlElement>(*documentElement()));
}
bool Document::isLayoutTimerActive()
{
- return view() && view()->layoutPending() && !minimumLayoutDelay().count();
+ return view() && view()->layoutPending() && !minimumLayoutDelay();
}
-std::chrono::milliseconds Document::minimumLayoutDelay()
+Seconds Document::minimumLayoutDelay()
{
if (m_overMinimumLayoutThreshold)
- return std::chrono::milliseconds(0);
+ return 0_s;
- std::chrono::milliseconds elapsed = elapsedTime();
- m_overMinimumLayoutThreshold = elapsed > settings()->layoutInterval();
+ auto elapsed = timeSinceDocumentCreation();
+ m_overMinimumLayoutThreshold = elapsed > settings().layoutInterval();
// We'll want to schedule the timer to fire at the minimum layout threshold.
- return std::max(std::chrono::milliseconds(0), settings()->layoutInterval() - elapsed);
+ return std::max(0_s, settings().layoutInterval() - elapsed);
}
-std::chrono::milliseconds Document::elapsedTime() const
+Seconds Document::timeSinceDocumentCreation() const
{
- auto elapsedTime = std::chrono::steady_clock::now() - m_startTime;
-
- return std::chrono::duration_cast<std::chrono::milliseconds>(elapsedTime);
+ return MonotonicTime::now() - m_documentCreationTime;
}
-void Document::write(const SegmentedString& text, Document* ownerDocument)
+void Document::write(SegmentedString&& text, Document* ownerDocument)
{
NestingLevelIncrementer nestingLevelIncrementer(m_writeRecursionDepth);
@@ -2547,54 +2815,60 @@ void Document::write(const SegmentedString& text, Document* ownerDocument)
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
+ return;
bool hasInsertionPoint = m_parser && m_parser->hasInsertionPoint();
- if (!hasInsertionPoint && m_ignoreDestructiveWriteCount)
+ if (!hasInsertionPoint && (m_ignoreOpensDuringUnloadCount || 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
+ m_parser->insert(WTFMove(text));
}
void Document::write(const String& text, Document* ownerDocument)
{
- write(SegmentedString(text), ownerDocument);
+ write(SegmentedString { text }, ownerDocument);
}
void Document::writeln(const String& text, Document* ownerDocument)
{
- write(text, ownerDocument);
- write("\n", ownerDocument);
+ SegmentedString textWithNewline { text };
+ textWithNewline.append(String { "\n" });
+ write(WTFMove(textWithNewline), ownerDocument);
}
-double Document::minimumTimerInterval() const
+std::chrono::milliseconds Document::minimumTimerInterval() const
{
- Page* p = page();
- if (!p)
+ auto* page = this->page();
+ if (!page)
return ScriptExecutionContext::minimumTimerInterval();
- return p->settings().minDOMTimerInterval();
+ return page->settings().minimumDOMTimerInterval();
}
-double Document::timerAlignmentInterval() const
+void Document::setTimerThrottlingEnabled(bool shouldThrottle)
{
- Page* p = page();
- if (!p)
- return ScriptExecutionContext::timerAlignmentInterval();
- return p->settings().domTimerAlignmentInterval();
+ if (m_isTimerThrottlingEnabled == shouldThrottle)
+ return;
+
+ m_isTimerThrottlingEnabled = shouldThrottle;
+ didChangeTimerAlignmentInterval();
+}
+
+std::chrono::milliseconds Document::timerAlignmentInterval(bool hasReachedMaxNestingLevel) const
+{
+ auto alignmentInterval = ScriptExecutionContext::timerAlignmentInterval(hasReachedMaxNestingLevel);
+
+ // Apply Document-level DOMTimer throttling only if timers have reached their maximum nesting level as the Page may still be visible.
+ if (m_isTimerThrottlingEnabled && hasReachedMaxNestingLevel)
+ alignmentInterval = std::max(alignmentInterval, DOMTimer::hiddenPageAlignmentInterval());
+
+ if (Page* page = this->page())
+ alignmentInterval = std::max(alignmentInterval, page->domTimerAlignmentInterval());
+
+ return alignmentInterval;
}
EventTarget* Document::errorEventTarget()
@@ -2602,9 +2876,9 @@ EventTarget* Document::errorEventTarget()
return m_domWindow.get();
}
-void Document::logExceptionToConsole(const String& errorMessage, const String& sourceURL, int lineNumber, int columnNumber, PassRefPtr<ScriptCallStack> callStack)
+void Document::logExceptionToConsole(const String& errorMessage, const String& sourceURL, int lineNumber, int columnNumber, RefPtr<Inspector::ScriptCallStack>&& callStack)
{
- addMessage(JSMessageSource, ErrorMessageLevel, errorMessage, sourceURL, lineNumber, columnNumber, callStack);
+ addMessage(MessageSource::JS, MessageLevel::Error, errorMessage, sourceURL, lineNumber, columnNumber, WTFMove(callStack));
}
void Document::setURL(const URL& url)
@@ -2635,21 +2909,11 @@ void Document::updateBaseURL()
m_baseURL = URL(ParsedURLString, documentURI());
}
- if (m_selectorQueryCache)
- m_selectorQueryCache->invalidate();
+ clearSelectorQueryCache();
if (!m_baseURL.isValid())
m_baseURL = URL();
- if (m_elementSheet) {
- // Element sheet is silly. It never contains anything.
- ASSERT(!m_elementSheet->contents().ruleCount());
- bool usesRemUnits = m_elementSheet->contents().usesRemUnits();
- m_elementSheet = CSSStyleSheet::createInline(*this, m_baseURL);
- // FIXME: So we are not really the parser. The right fix is to eliminate the element sheet completely.
- m_elementSheet->contents().parserSetUsesRemUnits(usesRemUnits);
- }
-
if (!equalIgnoringFragmentIdentifier(oldBaseURL, m_baseURL)) {
// Base URL change changes any relative visited links.
// FIXME: There are other URLs in the tree that would need to be re-evaluated on dynamic base URL change. Style should be invalidated too.
@@ -2670,16 +2934,22 @@ void Document::processBaseElement()
const AtomicString* href = nullptr;
const AtomicString* target = nullptr;
auto baseDescendants = descendantsOfType<HTMLBaseElement>(*this);
- for (auto base = baseDescendants.begin(), end = baseDescendants.end(); base != end && (!href || !target); ++base) {
+ for (auto& base : baseDescendants) {
if (!href) {
- const AtomicString& value = base->fastGetAttribute(hrefAttr);
- if (!value.isNull())
+ const AtomicString& value = base.attributeWithoutSynchronization(hrefAttr);
+ if (!value.isNull()) {
href = &value;
+ if (target)
+ break;
+ }
}
if (!target) {
- const AtomicString& value = base->fastGetAttribute(targetAttr);
- if (!value.isNull())
+ const AtomicString& value = base.attributeWithoutSynchronization(targetAttr);
+ if (!value.isNull()) {
target = &value;
+ if (href)
+ break;
+ }
}
}
@@ -2711,6 +2981,30 @@ void Document::disableEval(const String& errorMessage)
frame()->script().disableEval(errorMessage);
}
+#if ENABLE(INDEXED_DATABASE)
+
+IDBClient::IDBConnectionProxy* Document::idbConnectionProxy()
+{
+ if (!m_idbConnectionProxy) {
+ Page* currentPage = page();
+ if (!currentPage)
+ return nullptr;
+ m_idbConnectionProxy = &currentPage->idbConnection().proxy();
+ }
+ return m_idbConnectionProxy.get();
+}
+
+#endif
+
+#if ENABLE(WEB_SOCKETS)
+
+SocketProvider* Document::socketProvider()
+{
+ return m_socketProvider.get();
+}
+
+#endif
+
bool Document::canNavigate(Frame* targetFrame)
{
if (!m_frame)
@@ -2780,7 +3074,7 @@ Frame* Document::findUnsafeParentScrollPropagationBoundary()
Frame* ancestorFrame = currentFrame->tree().parent();
while (ancestorFrame) {
- if (!ancestorFrame->document()->securityOrigin()->canAccess(securityOrigin()))
+ if (!ancestorFrame->document()->securityOrigin().canAccess(securityOrigin()))
return currentFrame;
currentFrame = ancestorFrame;
ancestorFrame = ancestorFrame->tree().parent();
@@ -2788,159 +3082,141 @@ Frame* Document::findUnsafeParentScrollPropagationBoundary()
return nullptr;
}
-
-void Document::seamlessParentUpdatedStylesheets()
-{
- styleResolverChanged(RecalcStyleImmediately);
-}
-
void Document::didRemoveAllPendingStylesheet()
{
- m_needsNotifyRemoveAllPendingStylesheet = false;
-
- styleResolverChanged(DeferRecalcStyleIfNeeded);
-
if (m_pendingSheetLayout == DidLayoutWithPendingSheets) {
+ // Painting is disabled when doing layouts with pending sheets to avoid FOUC.
+ // We need to force paint when coming out from this state.
+ // FIXME: This is not very elegant.
m_pendingSheetLayout = IgnoreLayoutWithPendingSheets;
if (renderView())
renderView()->repaintViewAndCompositedLayers();
}
- if (ScriptableDocumentParser* parser = scriptableDocumentParser())
- parser->executeScriptsWaitingForStylesheets();
+ if (auto* parser = scriptableDocumentParser())
+ parser->executeScriptsWaitingForStylesheetsSoon();
+}
+
+bool Document::usesStyleBasedEditability() const
+{
+ if (m_hasElementUsingStyleBasedEditability)
+ return true;
- if (m_gotoAnchorNeededAfterStylesheetsLoad && view())
- view()->scrollToFragment(m_url);
+ ASSERT(!m_renderView || !m_renderView->frameView().isPainting());
+ ASSERT(!m_inStyleRecalc);
+
+ auto& styleScope = const_cast<Style::Scope&>(this->styleScope());
+ styleScope.flushPendingUpdate();
+ return styleScope.usesStyleBasedEditability();
}
-CSSStyleSheet& Document::elementSheet()
+void Document::setHasElementUsingStyleBasedEditability()
{
- if (!m_elementSheet)
- m_elementSheet = CSSStyleSheet::createInline(*this, m_baseURL);
- return *m_elementSheet;
+ m_hasElementUsingStyleBasedEditability = true;
}
-void Document::processHttpEquiv(const String& equiv, const String& content)
+void Document::processHttpEquiv(const String& equiv, const String& content, bool isInDocumentHead)
{
- ASSERT(!equiv.isNull() && !content.isNull());
+ ASSERT(!equiv.isNull());
+ ASSERT(!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();
- if (equalIgnoringCase(equiv, "default-style")) {
- // The preferred style set has been overridden as per section
+ HTTPHeaderName headerName;
+ if (!findHTTPHeaderName(equiv, headerName))
+ return;
+
+ switch (headerName) {
+ case HTTPHeaderName::DefaultStyle:
+ // The preferred style set has been overridden as per section
// 14.3.2 of the HTML4.0 specification. We need to update the
- // sheet used variable and then update our style selector.
+ // sheet used variable and then update our style selector.
// For more info, see the test at:
// http://www.hixie.ch/tests/evil/css/import/main/preferred.html
// -dwh
- m_styleSheetCollection.setSelectedStylesheetSetName(content);
- m_styleSheetCollection.setPreferredStylesheetSetName(content);
- styleResolverChanged(DeferRecalcStyle);
- } else if (equalIgnoringCase(equiv, "refresh")) {
+ styleScope().setSelectedStylesheetSetName(content);
+ styleScope().setPreferredStylesheetSetName(content);
+ break;
+
+ case HTTPHeaderName::Refresh: {
double delay;
- String url;
- if (frame && parseHTTPRefresh(content, true, delay, url)) {
- if (url.isEmpty())
- url = m_url.string();
+ String urlString;
+ if (frame && parseMetaHTTPEquivRefresh(content, delay, urlString)) {
+ URL completedURL;
+ if (urlString.isEmpty())
+ completedURL = m_url;
else
- url = completeURL(url).string();
- if (!protocolIsJavaScript(url))
- frame->navigationScheduler().scheduleRedirect(delay, url);
+ 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(SecurityMessageSource, ErrorMessageLevel, message);
+ addConsoleMessage(MessageSource::Security, MessageLevel::Error, message);
}
}
- } else if (equalIgnoringCase(equiv, "set-cookie")) {
+
+ break;
+ }
+
+ case HTTPHeaderName::SetCookie:
// FIXME: make setCookie work on XML documents too; e.g. in case of <html:meta .....>
- if (isHTMLDocument()) {
+ if (is<HTMLDocument>(*this)) {
// Exception (for sandboxed documents) ignored.
- toHTMLDocument(this)->setCookie(content, IGNORE_EXCEPTION);
+ downcast<HTMLDocument>(*this).setCookie(content);
}
- } else if (equalIgnoringCase(equiv, "content-language"))
+ break;
+
+ case HTTPHeaderName::ContentLanguage:
setContentLanguage(content);
- else if (equalIgnoringCase(equiv, "x-dns-prefetch-control"))
+ break;
+
+ case HTTPHeaderName::XDNSPrefetchControl:
parseDNSPrefetchControlHeader(content);
- else if (equalIgnoringCase(equiv, "x-frame-options")) {
+ 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();
- // Stopping the loader isn't enough, as we're already parsing the document; to honor the header's
- // intent, we must navigate away from the possibly partially-rendered document to a location that
- // doesn't inherit the parent's SecurityOrigin.
- frame->navigationScheduler().scheduleLocationChange(securityOrigin(), SecurityOrigin::urlWithUniqueSecurityOrigin(), String());
- addConsoleMessage(SecurityMessageSource, ErrorMessageLevel, message, requestIdentifier);
- }
- }
- } else if (equalIgnoringCase(equiv, "content-security-policy"))
- contentSecurityPolicy()->didReceiveHeader(content, ContentSecurityPolicy::Enforce);
- else if (equalIgnoringCase(equiv, "content-security-policy-report-only"))
- contentSecurityPolicy()->didReceiveHeader(content, ContentSecurityPolicy::Report);
- else if (equalIgnoringCase(equiv, "x-webkit-csp"))
- contentSecurityPolicy()->didReceiveHeader(content, ContentSecurityPolicy::PrefixedEnforce);
- else if (equalIgnoringCase(equiv, "x-webkit-csp-report-only"))
- contentSecurityPolicy()->didReceiveHeader(content, ContentSecurityPolicy::PrefixedReport);
-}
-// Though isspace() considers \t and \v to be whitespace, Win IE doesn't.
-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)
-{
- // Tread lightly in this code -- it was specifically designed to mimic Win IE's parsing behavior.
- int keyBegin, keyEnd;
- int valueBegin, valueEnd;
-
- int i = 0;
- int length = features.length();
- String buffer = features.lower();
- while (i < length) {
- // skip to first non-separator, but don't skip past the end of the string
- while (isSeparator(buffer[i])) {
- if (i >= length)
- break;
- i++;
+ String message = "The X-Frame-Option '" + content + "' supplied in a <meta> element was ignored. X-Frame-Options may only be provided by an HTTP header sent with the document.";
+ addConsoleMessage(MessageSource::Security, MessageLevel::Error, message, requestIdentifier);
}
- keyBegin = i;
-
- // skip to first separator
- while (!isSeparator(buffer[i]))
- i++;
- keyEnd = i;
-
- // skip to first '=', but don't skip past a ',' or the end of the string
- while (buffer[i] != '=') {
- if (buffer[i] == ',' || i >= length)
- break;
- i++;
- }
-
- // skip to first non-separator, but don't skip past a ',' or the end of the string
- while (isSeparator(buffer[i])) {
- if (buffer[i] == ',' || i >= length)
- break;
- i++;
- }
- valueBegin = i;
+ break;
- // skip to first separator
- while (!isSeparator(buffer[i]))
- i++;
- valueEnd = i;
+ case HTTPHeaderName::ContentSecurityPolicy:
+ if (isInDocumentHead)
+ contentSecurityPolicy()->didReceiveHeader(content, ContentSecurityPolicyHeaderType::Enforce, ContentSecurityPolicy::PolicyFrom::HTTPEquivMeta);
+ break;
- ASSERT_WITH_SECURITY_IMPLICATION(i <= length);
+ case HTTPHeaderName::XWebKitCSP:
+ if (isInDocumentHead)
+ contentSecurityPolicy()->didReceiveHeader(content, ContentSecurityPolicyHeaderType::PrefixedEnforce, ContentSecurityPolicy::PolicyFrom::HTTPEquivMeta);
+ break;
- String keyString = buffer.substring(keyBegin, keyEnd - keyBegin);
- String valueString = buffer.substring(valueBegin, valueEnd - valueBegin);
- callback(keyString, valueString, this, data);
+ default:
+ break;
}
}
@@ -2952,15 +3228,10 @@ void Document::processViewport(const String& features, ViewportArguments::Type o
return;
m_viewportArguments = ViewportArguments(origin);
- processArguments(features, (void*)&m_viewportArguments, &setViewportFeature);
-#if PLATFORM(IOS)
- // FIXME: <rdar://problem/8955959> Investigate moving to ToT WebKit's extended Viewport Implementation
- // Moving to ToT's implementation would mean calling findConfigurationForViewportData, which does
- // bounds checking and determining concrete values for ValueAuto which we already do in UIKit.
- // To maintain old behavior, we just need to update a few values, leaving Auto's for UIKit.
- finalizeViewportArguments(m_viewportArguments);
-#endif
+ processFeaturesString(features, [this](StringView key, StringView value) {
+ setViewportFeature(m_viewportArguments, *this, key, value);
+ });
updateViewportArguments();
}
@@ -2972,24 +3243,19 @@ void Document::updateViewportArguments()
m_didDispatchViewportPropertiesChanged = true;
#endif
page()->chrome().dispatchViewportPropertiesDidChange(m_viewportArguments);
-#if PLATFORM(IOS)
- page()->chrome().didReceiveDocType(frame());
-#endif
+ page()->chrome().didReceiveDocType(*frame());
}
}
#if PLATFORM(IOS)
-// FIXME: Find a better place for this functionality.
-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);
+ // FIXME: Find a better place for this function.
+ processFeaturesString(features, [this](StringView key, StringView value) {
+ if (equalLettersIgnoringASCIICase(key, "telephone") && equalLettersIgnoringASCIICase(value, "no"))
+ setIsTelephoneNumberParsingAllowed(false);
+ });
}
void Document::processWebAppOrientations()
@@ -2997,20 +3263,37 @@ void Document::processWebAppOrientations()
if (Page* page = this->page())
page->chrome().client().webAppOrientationsUpdated();
}
+
#endif
void Document::processReferrerPolicy(const String& policy)
{
ASSERT(!policy.isNull());
- m_referrerPolicy = ReferrerPolicyDefault;
+ // Documents in a Content-Disposition: attachment sandbox should never send a Referer header,
+ // even if the document has a meta tag saying otherwise.
+ if (shouldEnforceContentDispositionAttachmentSandbox())
+ return;
+
+#if USE(QUICK_LOOK)
+ if (shouldEnforceQuickLookSandbox())
+ return;
+#endif
- if (equalIgnoringCase(policy, "never"))
- m_referrerPolicy = ReferrerPolicyNever;
- else if (equalIgnoringCase(policy, "always"))
- m_referrerPolicy = ReferrerPolicyAlways;
- else if (equalIgnoringCase(policy, "origin"))
- m_referrerPolicy = ReferrerPolicyOrigin;
+ // Note that we're supporting both the standard and legacy keywords for referrer
+ // policies, as defined by http://www.w3.org/TR/referrer-policy/#referrer-policy-delivery-meta
+ if (equalLettersIgnoringASCIICase(policy, "no-referrer") || equalLettersIgnoringASCIICase(policy, "never"))
+ setReferrerPolicy(ReferrerPolicy::Never);
+ else if (equalLettersIgnoringASCIICase(policy, "unsafe-url") || equalLettersIgnoringASCIICase(policy, "always"))
+ setReferrerPolicy(ReferrerPolicy::Always);
+ else if (equalLettersIgnoringASCIICase(policy, "origin"))
+ setReferrerPolicy(ReferrerPolicy::Origin);
+ else if (equalLettersIgnoringASCIICase(policy, "no-referrer-when-downgrade") || equalLettersIgnoringASCIICase(policy, "default"))
+ setReferrerPolicy(ReferrerPolicy::Default);
+ 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(ReferrerPolicy::Never);
+ }
}
MouseEventWithHitTestResults Document::prepareMouseEvent(const HitTestRequest& request, const LayoutPoint& documentPoint, const PlatformMouseEvent& event)
@@ -3022,7 +3305,7 @@ MouseEventWithHitTestResults Document::prepareMouseEvent(const HitTestRequest& r
renderView()->hitTest(request, result);
if (!request.readOnly())
- updateHoverActiveState(request, result.innerElement(), &event);
+ updateHoverActiveState(request, result.targetElement());
return MouseEventWithHitTestResults(event, result);
}
@@ -3035,11 +3318,7 @@ bool Document::childTypeAllowed(NodeType type) const
case CDATA_SECTION_NODE:
case DOCUMENT_FRAGMENT_NODE:
case DOCUMENT_NODE:
- case ENTITY_NODE:
- case ENTITY_REFERENCE_NODE:
- case NOTATION_NODE:
case TEXT_NODE:
- case XPATH_NAMESPACE_NODE:
return false;
case COMMENT_NODE:
case PROCESSING_INSTRUCTION_NODE:
@@ -3056,103 +3335,97 @@ bool Document::childTypeAllowed(NodeType type) const
return false;
}
-bool Document::canReplaceChild(Node* newChild, Node* oldChild)
+bool Document::canAcceptChild(const Node& newChild, const Node* refChild, AcceptChildOperation operation) const
{
- if (!oldChild)
- // ContainerNode::replaceChild will raise a NOT_FOUND_ERR.
+ if (operation == AcceptChildOperation::Replace && refChild->nodeType() == newChild.nodeType())
return true;
- if (oldChild->nodeType() == newChild->nodeType())
+ switch (newChild.nodeType()) {
+ case ATTRIBUTE_NODE:
+ case CDATA_SECTION_NODE:
+ case DOCUMENT_NODE:
+ case TEXT_NODE:
+ return false;
+ case COMMENT_NODE:
+ case PROCESSING_INSTRUCTION_NODE:
return true;
-
- int numDoctypes = 0;
- int numElements = 0;
-
- // First, check how many doctypes and elements we have, not counting
- // the child we're about to remove.
- 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;
+ case DOCUMENT_FRAGMENT_NODE: {
+ bool hasSeenElementChild = false;
+ for (auto* node = downcast<DocumentFragment>(newChild).firstChild(); node; node = node->nextSibling()) {
+ if (is<Element>(*node)) {
+ if (hasSeenElementChild)
+ return false;
+ hasSeenElementChild = true;
+ }
+ if (!canAcceptChild(*node, refChild, operation))
+ return false;
}
+ break;
}
-
- // Then, see how many doctypes and elements might be added by the new child.
- 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 NOTATION_NODE:
- case TEXT_NODE:
- case XPATH_NAMESPACE_NODE:
+ case DOCUMENT_TYPE_NODE: {
+ auto* existingDocType = childrenOfType<DocumentType>(*this).first();
+ if (operation == AcceptChildOperation::Replace) {
+ // parent has a doctype child that is not child, or an element is preceding child.
+ if (existingDocType && existingDocType != refChild)
return false;
- case COMMENT_NODE:
- case PROCESSING_INSTRUCTION_NODE:
- break;
- case DOCUMENT_TYPE_NODE:
- numDoctypes++;
- break;
- case ELEMENT_NODE:
- numElements++;
- break;
+ if (refChild->previousElementSibling())
+ return false;
+ } else {
+ ASSERT(operation == AcceptChildOperation::InsertOrAdd);
+ if (existingDocType)
+ return false;
+ if ((refChild && refChild->previousElementSibling()) || (!refChild && firstElementChild()))
+ return false;
+ }
+ break;
+ }
+ case ELEMENT_NODE: {
+ auto* existingElementChild = firstElementChild();
+ if (operation == AcceptChildOperation::Replace) {
+ if (existingElementChild && existingElementChild != refChild)
+ return false;
+ for (auto* child = refChild->nextSibling(); child; child = child->nextSibling()) {
+ if (is<DocumentType>(*child))
+ return false;
+ }
+ } else {
+ ASSERT(operation == AcceptChildOperation::InsertOrAdd);
+ if (existingElementChild)
+ return false;
+ for (auto* child = refChild; child; child = child->nextSibling()) {
+ if (is<DocumentType>(*child))
+ return false;
}
}
- } 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 NOTATION_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;
- }
+ break;
+ }
}
-
- if (numElements > 1 || numDoctypes > 1)
- return false;
-
return true;
}
-PassRefPtr<Node> Document::cloneNode(bool deep)
+Ref<Node> Document::cloneNodeInternal(Document&, CloningOperation type)
{
- RefPtr<Document> clone = cloneDocumentWithoutChildren();
+ Ref<Document> clone = cloneDocumentWithoutChildren();
clone->cloneDataFromDocument(*this);
- if (deep)
- cloneChildNodes(clone.get());
- return clone.release();
+ switch (type) {
+ case CloningOperation::OnlySelf:
+ case CloningOperation::SelfWithTemplateContent:
+ break;
+ case CloningOperation::Everything:
+ cloneChildNodes(clone);
+ break;
+ }
+ return WTFMove(clone);
}
-PassRefPtr<Document> Document::cloneDocumentWithoutChildren() const
+Ref<Document> Document::cloneDocumentWithoutChildren() const
{
- return isXHTMLDocument() ? createXHTML(nullptr, url()) : create(nullptr, url());
+ if (isXMLDocument()) {
+ if (isXHTMLDocument())
+ return XMLDocument::createXHTML(nullptr, url());
+ return XMLDocument::create(nullptr, url());
+ }
+ return create(nullptr, url());
}
void Document::cloneDataFromDocument(const Document& other)
@@ -3162,144 +3435,147 @@ void Document::cloneDataFromDocument(const Document& other)
m_baseURLOverride = other.baseURLOverride();
m_documentURI = other.documentURI();
- setCompatibilityMode(other.compatibilityMode());
- setSecurityOrigin(other.securityOrigin());
+ setCompatibilityMode(other.m_compatibilityMode);
+ setContextDocument(other.contextDocument());
+ setSecurityOriginPolicy(other.securityOriginPolicy());
+ overrideMIMEType(other.contentType());
setDecoder(other.decoder());
}
-StyleSheetList* Document::styleSheets()
+StyleSheetList& Document::styleSheets()
{
if (!m_styleSheetList)
m_styleSheetList = StyleSheetList::create(this);
- return m_styleSheetList.get();
+ return *m_styleSheetList;
}
String Document::preferredStylesheetSet() const
{
- return m_styleSheetCollection.preferredStylesheetSetName();
+ return styleScope().preferredStylesheetSetName();
}
String Document::selectedStylesheetSet() const
{
- return m_styleSheetCollection.selectedStylesheetSetName();
+ return styleScope().selectedStylesheetSetName();
}
void Document::setSelectedStylesheetSet(const String& aString)
{
- m_styleSheetCollection.setSelectedStylesheetSetName(aString);
- styleResolverChanged(DeferRecalcStyle);
+ styleScope().setSelectedStylesheetSetName(aString);
}
void Document::evaluateMediaQueryList()
{
if (m_mediaQueryMatcher)
m_mediaQueryMatcher->styleResolverChanged();
+
+ checkViewportDependentPictures();
}
-void Document::optimizedStyleSheetUpdateTimerFired(Timer<Document>&)
+void Document::checkViewportDependentPictures()
{
- styleResolverChanged(RecalcStyleIfNeeded);
+ 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::scheduleOptimizedStyleSheetUpdate()
+void Document::updateViewportUnitsOnResize()
{
- if (m_optimizedStyleSheetUpdateTimer.isActive())
+ if (!hasStyleWithViewportUnits())
return;
- m_styleSheetCollection.setPendingUpdateType(DocumentStyleSheetCollection::OptimizedUpdate);
- m_optimizedStyleSheetUpdateTimer.startOneShot(0);
-}
-void Document::styleResolverChanged(StyleResolverUpdateFlag updateFlag)
-{
- if (m_optimizedStyleSheetUpdateTimer.isActive())
- m_optimizedStyleSheetUpdateTimer.stop();
+ styleScope().resolver().clearCachedPropertiesAffectedByViewportUnits();
- // Don't bother updating, since we haven't loaded all our style info yet
- // and haven't calculated the style selector for the first time.
- if (!hasLivingRenderTree() || (!m_didCalculateStyleResolver && !haveStylesheetsLoaded())) {
- m_styleResolver.clear();
- return;
+ // FIXME: Ideally, we should save the list of elements that have viewport units and only iterate over those.
+ for (Element* element = ElementTraversal::firstWithin(rootNode()); element; element = ElementTraversal::nextIncludingPseudo(*element)) {
+ auto* renderer = element->renderer();
+ if (renderer && renderer->style().hasViewportUnits())
+ element->invalidateStyle();
}
- 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);
+void Document::addAudioProducer(MediaProducer* audioProducer)
+{
+ m_audioProducers.add(audioProducer);
+ updateIsPlayingMedia();
+}
- if (updateFlag == DeferRecalcStyle) {
- scheduleForcedStyleRecalc();
- return;
- }
+void Document::removeAudioProducer(MediaProducer* audioProducer)
+{
+ m_audioProducers.remove(audioProducer);
+ updateIsPlayingMedia();
+}
- if (updateFlag == DeferRecalcStyleIfNeeded) {
- if (stylesheetChangeRequiresStyleRecalc)
- scheduleForcedStyleRecalc();
- return;
- }
+void Document::updateIsPlayingMedia(uint64_t sourceElementID)
+{
+ MediaProducer::MediaStateFlags state = MediaProducer::IsNotPlaying;
+ for (auto* audioProducer : m_audioProducers)
+ state |= audioProducer->mediaState();
- if (!stylesheetChangeRequiresStyleRecalc)
- return;
+#if ENABLE(MEDIA_SESSION)
+ if (HTMLMediaElement* sourceElement = HTMLMediaElement::elementWithID(sourceElementID)) {
+ if (sourceElement->isPlaying())
+ state |= MediaProducer::IsSourceElementPlaying;
- // This recalcStyle initiates a new recalc cycle. We need to bracket it to
- // make sure animations get the correct update time
- {
- AnimationUpdateBlock animationUpdateBlock(m_frame ? &m_frame->animation() : nullptr);
- recalcStyle(Style::Force);
+ if (auto* session = sourceElement->session()) {
+ if (auto* controls = session->controls()) {
+ if (controls->previousTrackEnabled())
+ state |= MediaProducer::IsPreviousTrackControlEnabled;
+ if (controls->nextTrackEnabled())
+ state |= MediaProducer::IsNextTrackControlEnabled;
+ }
+ }
}
-
-#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();
- }
+ if (state == m_mediaState)
+ return;
- evaluateMediaQueryList();
+ m_mediaState = state;
+
+ if (page())
+ page()->updateIsPlayingMedia(sourceElementID);
}
-void Document::notifySeamlessChildDocumentsOfStylesheetUpdate() const
+void Document::pageMutedStateDidChange()
{
- // If we're not in a frame yet any potential child documents won't have a StyleResolver to update.
- if (!frame())
- return;
+ for (auto* audioProducer : m_audioProducers)
+ audioProducer->pageMutedStateDidChange();
+}
- // Seamless child frames are expected to notify their seamless children recursively, so we only do direct children.
- for (Frame* child = frame()->tree().firstChild(); child; child = child->tree().nextSibling()) {
- Document* childDocument = child->document();
- if (childDocument->shouldDisplaySeamlesslyWithParent()) {
- ASSERT(&childDocument->seamlessParentIFrame()->document() == this);
- childDocument->seamlessParentUpdatedStylesheets();
- }
- }
+static bool isNodeInSubtree(Node& node, Node& container, bool amongChildrenOnly)
+{
+ if (amongChildrenOnly)
+ return node.isDescendantOf(container);
+ else
+ return &node == &container || node.isDescendantOf(container);
}
-void Document::removeFocusedNodeOfSubtree(Node* node, bool amongChildrenOnly)
+void Document::removeFocusedNodeOfSubtree(Node& node, bool amongChildrenOnly)
{
- if (!m_focusedElement || this->inPageCache()) // If the document is in the page cache, then we don't need to clear out the focused node.
+ if (!m_focusedElement || pageCacheState() != NotInPageCache) // If the document is in the page cache, then we don't need to clear out the focused node.
return;
- Element* focusedElement = node->treeScope().focusedElement();
+ Element* focusedElement = node.treeScope().focusedElementInScope();
if (!focusedElement)
return;
-
- bool nodeInSubtree = false;
- if (amongChildrenOnly)
- nodeInSubtree = focusedElement->isDescendantOf(node);
- else
- nodeInSubtree = (focusedElement == node) || focusedElement->isDescendantOf(node);
- if (nodeInSubtree)
- setFocusedElement(nullptr);
+ if (isNodeInSubtree(*focusedElement, node, amongChildrenOnly)) {
+ // FIXME: We should avoid synchronously updating the style inside setFocusedElement.
+ // FIXME: Object elements should avoid loading a frame synchronously in a post style recalc callback.
+ SubframeLoadingDisabler disabler(is<ContainerNode>(node) ? &downcast<ContainerNode>(node) : nullptr);
+ setFocusedElement(nullptr, FocusDirectionNone, FocusRemovalEventsMode::DoNotDispatch);
+ // Set the focus navigation starting node to the previous focused element so that
+ // we can fallback to the siblings or parent node for the next search.
+ // Also we need to call removeFocusNavigationNodeOfSubtree after this function because
+ // setFocusedElement(nullptr) will reset m_focusNavigationStartingNode.
+ setFocusNavigationStartingNode(focusedElement);
+ }
}
void Document::hoveredElementDidDetach(Element* element)
@@ -3325,6 +3601,7 @@ void Document::elementInActiveChainDidDetach(Element* element)
}
#if ENABLE(DASHBOARD_SUPPORT)
+
const Vector<AnnotatedRegionValue>& Document::annotatedRegions() const
{
return m_annotatedRegions;
@@ -3335,12 +3612,12 @@ void Document::setAnnotatedRegions(const Vector<AnnotatedRegionValue>& regions)
m_annotatedRegions = regions;
setAnnotatedRegionsDirty(false);
}
+
#endif
-bool Document::setFocusedElement(PassRefPtr<Element> prpNewFocusedElement, FocusDirection direction)
+bool Document::setFocusedElement(Element* element, FocusDirection direction, FocusRemovalEventsMode eventsMode)
{
- RefPtr<Element> newFocusedElement = prpNewFocusedElement;
-
+ RefPtr<Element> newFocusedElement = element;
// Make sure newFocusedElement is actually in this document
if (newFocusedElement && (&newFocusedElement->document() != this))
return true;
@@ -3348,46 +3625,53 @@ bool Document::setFocusedElement(PassRefPtr<Element> prpNewFocusedElement, Focus
if (m_focusedElement == newFocusedElement)
return true;
- if (m_inPageCache)
+ if (pageCacheState() != NotInPageCache)
return false;
bool focusChangeBlocked = false;
- RefPtr<Element> oldFocusedElement = m_focusedElement.release();
+ RefPtr<Element> oldFocusedElement = WTFMove(m_focusedElement);
// Remove focus from the existing focus node (if any)
if (oldFocusedElement) {
- if (oldFocusedElement->active())
- oldFocusedElement->setActive(false);
-
oldFocusedElement->setFocus(false);
+ setFocusNavigationStartingNode(nullptr);
+
+ if (eventsMode == FocusRemovalEventsMode::Dispatch) {
+ // Dispatch a change event for form control elements that have been edited.
+ if (is<HTMLFormControlElement>(*oldFocusedElement)) {
+ HTMLFormControlElement& formControlElement = downcast<HTMLFormControlElement>(*oldFocusedElement);
+ if (formControlElement.wasChangedSinceLastFormControlChangeEvent())
+ formControlElement.dispatchFormControlChangeEvent();
+ }
- // Dispatch a change event for form control elements that have been edited.
- if (oldFocusedElement->isFormControlElement()) {
- HTMLFormControlElement* formControlElement = toHTMLFormControlElement(oldFocusedElement.get());
- if (formControlElement->wasChangedSinceLastFormControlChangeEvent())
- formControlElement->dispatchFormControlChangeEvent();
- }
+ // Dispatch the blur event and let the node do any other blur related activities (important for text fields)
+ oldFocusedElement->dispatchBlurEvent(newFocusedElement.copyRef());
- // Dispatch the blur event and let the node do any other blur related activities (important for text fields)
- oldFocusedElement->dispatchBlurEvent(newFocusedElement);
+ if (m_focusedElement) {
+ // handler shifted focus
+ focusChangeBlocked = true;
+ newFocusedElement = nullptr;
+ }
- if (m_focusedElement) {
- // handler shifted focus
- focusChangeBlocked = true;
- newFocusedElement = nullptr;
- }
-
- oldFocusedElement->dispatchFocusOutEvent(eventNames().focusoutEvent, newFocusedElement); // DOM level 3 name for the bubbling blur event.
- // FIXME: We should remove firing DOMFocusOutEvent event when we are sure no content depends
- // on it, probably when <rdar://problem/8503958> is resolved.
- oldFocusedElement->dispatchFocusOutEvent(eventNames().DOMFocusOutEvent, newFocusedElement); // DOM level 2 name for compatibility.
+ oldFocusedElement->dispatchFocusOutEvent(eventNames().focusoutEvent, newFocusedElement.copyRef()); // DOM level 3 name for the bubbling blur event.
+ // FIXME: We should remove firing DOMFocusOutEvent event when we are sure no content depends
+ // on it, probably when <rdar://problem/8503958> is resolved.
+ oldFocusedElement->dispatchFocusOutEvent(eventNames().DOMFocusOutEvent, newFocusedElement.copyRef()); // DOM level 2 name for compatibility.
- if (m_focusedElement) {
- // handler shifted focus
- focusChangeBlocked = true;
- newFocusedElement = nullptr;
+ if (m_focusedElement) {
+ // handler shifted focus
+ focusChangeBlocked = true;
+ newFocusedElement = nullptr;
+ }
+ } else {
+ // Match the order in HTMLTextFormControlElement::dispatchBlurEvent.
+ if (is<HTMLInputElement>(*oldFocusedElement))
+ downcast<HTMLInputElement>(*oldFocusedElement).endEditing();
+ if (page())
+ page()->chrome().client().elementDidBlur(*oldFocusedElement);
+ ASSERT(!m_focusedElement);
}
-
+
if (oldFocusedElement->isRootEditableElement())
frame()->editor().didEndEditing();
@@ -3407,9 +3691,10 @@ bool Document::setFocusedElement(PassRefPtr<Element> prpNewFocusedElement, Focus
}
// Set focus on the new node
m_focusedElement = newFocusedElement;
+ setFocusNavigationStartingNode(m_focusedElement.get());
// Dispatch the focus event and let the node do any other focus related activities (important for text fields)
- m_focusedElement->dispatchFocusEvent(oldFocusedElement, direction);
+ m_focusedElement->dispatchFocusEvent(oldFocusedElement.copyRef(), direction);
if (m_focusedElement != newFocusedElement) {
// handler shifted focus
@@ -3417,7 +3702,7 @@ bool Document::setFocusedElement(PassRefPtr<Element> prpNewFocusedElement, Focus
goto SetFocusedNodeDone;
}
- m_focusedElement->dispatchFocusInEvent(eventNames().focusinEvent, oldFocusedElement); // DOM level 3 bubbling focus event.
+ m_focusedElement->dispatchFocusInEvent(eventNames().focusinEvent, oldFocusedElement.copyRef()); // DOM level 3 bubbling focus event.
if (m_focusedElement != newFocusedElement) {
// handler shifted focus
@@ -3427,7 +3712,7 @@ bool Document::setFocusedElement(PassRefPtr<Element> prpNewFocusedElement, Focus
// FIXME: We should remove firing DOMFocusInEvent event when we are sure no content depends
// on it, probably when <rdar://problem/8503958> is m.
- m_focusedElement->dispatchFocusInEvent(eventNames().DOMFocusInEvent, oldFocusedElement); // DOM level 2 for compatibility.
+ m_focusedElement->dispatchFocusInEvent(eventNames().DOMFocusInEvent, oldFocusedElement.copyRef()); // DOM level 2 for compatibility.
if (m_focusedElement != newFocusedElement) {
// handler shifted focus
@@ -3473,34 +3758,93 @@ SetFocusedNodeDone:
return !focusChangeBlocked;
}
-void Document::setCSSTarget(Element* n)
+static bool shouldResetFocusNavigationStartingNode(Node& node)
+{
+ // Setting focus navigation starting node to the following nodes means that we should start
+ // the search from the beginning of the document.
+ return is<HTMLHtmlElement>(node) || is<HTMLDocument>(node);
+}
+
+void Document::setFocusNavigationStartingNode(Node* node)
+{
+ if (!m_frame)
+ return;
+
+ m_focusNavigationStartingNodeIsRemoved = false;
+ if (!node || shouldResetFocusNavigationStartingNode(*node)) {
+ m_focusNavigationStartingNode = nullptr;
+ return;
+ }
+
+ m_focusNavigationStartingNode = node;
+}
+
+Element* Document::focusNavigationStartingNode(FocusDirection direction) const
+{
+ if (m_focusedElement) {
+ if (!m_focusNavigationStartingNode || !m_focusNavigationStartingNode->isDescendantOf(m_focusedElement.get()))
+ return m_focusedElement.get();
+ }
+
+ if (!m_focusNavigationStartingNode)
+ return nullptr;
+
+ Node* node = m_focusNavigationStartingNode.get();
+
+ // When the node was removed from the document tree. This case is not specified in the spec:
+ // https://html.spec.whatwg.org/multipage/interaction.html#sequential-focus-navigation-starting-point
+ // Current behaivor is to move the sequential navigation node to / after (based on the focus direction)
+ // the previous sibling of the removed node.
+ if (m_focusNavigationStartingNodeIsRemoved) {
+ Node* nextNode = NodeTraversal::next(*node);
+ if (!nextNode)
+ nextNode = node;
+ if (direction == FocusDirectionForward)
+ return ElementTraversal::previous(*nextNode);
+ if (is<Element>(*nextNode))
+ return downcast<Element>(nextNode);
+ return ElementTraversal::next(*nextNode);
+ }
+
+ if (is<Element>(*node))
+ return downcast<Element>(node);
+ if (Element* elementBeforeNextFocusableElement = direction == FocusDirectionForward ? ElementTraversal::previous(*node) : ElementTraversal::next(*node))
+ return elementBeforeNextFocusableElement;
+ return node->parentOrShadowHostElement();
+}
+
+void Document::setCSSTarget(Element* targetNode)
{
if (m_cssTarget)
- m_cssTarget->didAffectSelector(AffectedSelectorTarget);
- m_cssTarget = n;
- if (n)
- n->didAffectSelector(AffectedSelectorTarget);
+ m_cssTarget->invalidateStyleForSubtree();
+ m_cssTarget = targetNode;
+ if (targetNode)
+ targetNode->invalidateStyleForSubtree();
}
-void Document::registerNodeList(LiveNodeList& list)
+void Document::registerNodeListForInvalidation(LiveNodeList& list)
{
m_nodeListAndCollectionCounts[list.invalidationType()]++;
- if (list.isRootedAtDocument())
- m_listsInvalidatedAtDocument.add(&list);
+ if (!list.isRootedAtDocument())
+ return;
+ ASSERT(!list.isRegisteredForInvalidationAtDocument());
+ list.setRegisteredForInvalidationAtDocument(true);
+ m_listsInvalidatedAtDocument.add(&list);
}
-void Document::unregisterNodeList(LiveNodeList& list)
+void Document::unregisterNodeListForInvalidation(LiveNodeList& list)
{
m_nodeListAndCollectionCounts[list.invalidationType()]--;
- if (list.isRootedAtDocument()) {
- ASSERT(m_listsInvalidatedAtDocument.contains(&list));
- m_listsInvalidatedAtDocument.remove(&list);
- }
+ if (!list.isRegisteredForInvalidationAtDocument())
+ return;
+
+ list.setRegisteredForInvalidationAtDocument(false);
+ ASSERT(m_listsInvalidatedAtDocument.contains(&list));
+ m_listsInvalidatedAtDocument.remove(&list);
}
void Document::registerCollection(HTMLCollection& collection)
{
- m_nodeListAndCollectionCounts[InvalidateOnIdNameAttrChange]++;
m_nodeListAndCollectionCounts[collection.invalidationType()]++;
if (collection.isRootedAtDocument())
m_collectionsInvalidatedAtDocument.add(&collection);
@@ -3508,12 +3852,25 @@ void Document::registerCollection(HTMLCollection& collection)
void Document::unregisterCollection(HTMLCollection& collection)
{
- m_nodeListAndCollectionCounts[InvalidateOnIdNameAttrChange]--;
+ ASSERT(m_nodeListAndCollectionCounts[collection.invalidationType()]);
m_nodeListAndCollectionCounts[collection.invalidationType()]--;
- if (collection.isRootedAtDocument()) {
- ASSERT(m_collectionsInvalidatedAtDocument.contains(&collection));
- m_collectionsInvalidatedAtDocument.remove(&collection);
- }
+ 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)
@@ -3528,72 +3885,105 @@ void Document::detachNodeIterator(NodeIterator* ni)
m_nodeIterators.remove(ni);
}
-void Document::moveNodeIteratorsToNewDocument(Node* node, Document* newDocument)
+void Document::moveNodeIteratorsToNewDocument(Node& node, Document& newDocument)
{
- HashSet<NodeIterator*> nodeIteratorsList = m_nodeIterators;
- HashSet<NodeIterator*>::const_iterator nodeIteratorsEnd = nodeIteratorsList.end();
- for (HashSet<NodeIterator*>::const_iterator it = nodeIteratorsList.begin(); it != nodeIteratorsEnd; ++it) {
- if ((*it)->root() == node) {
- detachNodeIterator(*it);
- newDocument->attachNodeIterator(*it);
+ 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)
{
- if (!m_ranges.isEmpty()) {
- for (auto it = m_ranges.begin(), end = m_ranges.end(); it != end; ++it)
- (*it)->nodeChildrenChanged(container);
- }
+ for (auto* range : m_ranges)
+ range->nodeChildrenChanged(container);
}
void Document::nodeChildrenWillBeRemoved(ContainerNode& container)
{
- if (!m_ranges.isEmpty()) {
- for (auto it = m_ranges.begin(), end = m_ranges.end(); it != end; ++it)
- (*it)->nodeChildrenWillBeRemoved(container);
- }
+ NoEventDispatchAssertion assertNoEventDispatch;
+
+ removeFocusedNodeOfSubtree(container, true /* amongChildrenOnly */);
+ removeFocusNavigationNodeOfSubtree(container, true /* amongChildrenOnly */);
+
+#if ENABLE(FULLSCREEN_API)
+ removeFullScreenElementOfSubtree(container, true /* amongChildrenOnly */);
+#endif
- for (auto it = m_nodeIterators.begin(), end = m_nodeIterators.end(); it != end; ++it) {
+ 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);
+ 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);
+ 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)
+void Document::nodeWillBeRemoved(Node& node)
{
- HashSet<NodeIterator*>::const_iterator nodeIteratorsEnd = m_nodeIterators.end();
- for (HashSet<NodeIterator*>::const_iterator it = m_nodeIterators.begin(); it != nodeIteratorsEnd; ++it)
- (*it)->nodeWillBeRemoved(n);
+ NoEventDispatchAssertion assertNoEventDispatch;
- if (!m_ranges.isEmpty()) {
- HashSet<Range*>::const_iterator rangesEnd = m_ranges.end();
- for (HashSet<Range*>::const_iterator it = m_ranges.begin(); it != rangesEnd; ++it)
- (*it)->nodeWillBeRemoved(n);
- }
+ removeFocusedNodeOfSubtree(node);
+ removeFocusNavigationNodeOfSubtree(node);
+
+#if ENABLE(FULLSCREEN_API)
+ removeFullScreenElementOfSubtree(node);
+#endif
+
+ for (auto* it : m_nodeIterators)
+ it->nodeWillBeRemoved(node);
+
+ for (auto* range : m_ranges)
+ range->nodeWillBeRemoved(node);
if (Frame* frame = this->frame()) {
- frame->eventHandler().nodeWillBeRemoved(n);
- frame->selection().nodeWillBeRemoved(n);
- frame->page()->dragCaretController().nodeWillBeRemoved(n);
+ frame->eventHandler().nodeWillBeRemoved(node);
+ frame->selection().nodeWillBeRemoved(node);
+ frame->page()->dragCaretController().nodeWillBeRemoved(node);
+ }
+
+ if (is<Text>(node))
+ m_markers->removeMarkers(&node);
+}
+
+static Node* fallbackFocusNavigationStartingNodeAfterRemoval(Node& node)
+{
+ return node.previousSibling() ? node.previousSibling() : node.parentNode();
+}
+
+void Document::removeFocusNavigationNodeOfSubtree(Node& node, bool amongChildrenOnly)
+{
+ if (!m_focusNavigationStartingNode)
+ return;
+
+ if (isNodeInSubtree(*m_focusNavigationStartingNode, node, amongChildrenOnly)) {
+ m_focusNavigationStartingNode = amongChildrenOnly ? &node : fallbackFocusNavigationStartingNodeAfterRemoval(node);
+ m_focusNavigationStartingNodeIsRemoved = true;
}
}
void Document::textInserted(Node* text, unsigned offset, unsigned length)
{
if (!m_ranges.isEmpty()) {
- HashSet<Range*>::const_iterator end = m_ranges.end();
- for (HashSet<Range*>::const_iterator it = m_ranges.begin(); it != end; ++it)
- (*it)->textInserted(text, offset, length);
+ for (auto* range : m_ranges)
+ range->textInserted(text, offset, length);
}
// Update the markers for spelling and grammar checking.
@@ -3603,9 +3993,8 @@ void Document::textInserted(Node* text, unsigned offset, unsigned length)
void Document::textRemoved(Node* text, unsigned offset, unsigned length)
{
if (!m_ranges.isEmpty()) {
- HashSet<Range*>::const_iterator end = m_ranges.end();
- for (HashSet<Range*>::const_iterator it = m_ranges.begin(); it != end; ++it)
- (*it)->textRemoved(text, offset, length);
+ for (auto* range : m_ranges)
+ range->textRemoved(text, offset, length);
}
// Update the markers for spelling and grammar checking.
@@ -3617,9 +4006,8 @@ void Document::textNodesMerged(Text* oldNode, unsigned offset)
{
if (!m_ranges.isEmpty()) {
NodeWithIndex oldNodeWithIndex(oldNode);
- HashSet<Range*>::const_iterator end = m_ranges.end();
- for (HashSet<Range*>::const_iterator it = m_ranges.begin(); it != end; ++it)
- (*it)->textNodesMerged(oldNodeWithIndex, offset);
+ for (auto* range : m_ranges)
+ range->textNodesMerged(oldNodeWithIndex, offset);
}
// FIXME: This should update markers for spelling and grammar checking.
@@ -3627,11 +4015,8 @@ void Document::textNodesMerged(Text* oldNode, unsigned offset)
void Document::textNodeSplit(Text* oldNode)
{
- if (!m_ranges.isEmpty()) {
- HashSet<Range*>::const_iterator end = m_ranges.end();
- for (HashSet<Range*>::const_iterator it = m_ranges.begin(); it != end; ++it)
- (*it)->textNodeSplit(oldNode);
- }
+ for (auto* range : m_ranges)
+ range->textNodeSplit(oldNode);
// FIXME: This should update markers for spelling and grammar checking.
}
@@ -3641,7 +4026,7 @@ void Document::createDOMWindow()
ASSERT(m_frame);
ASSERT(!m_domWindow);
- m_domWindow = DOMWindow::create(this);
+ m_domWindow = DOMWindow::create(*this);
ASSERT(m_domWindow->document() == this);
ASSERT(m_domWindow->frame() == m_frame);
@@ -3653,39 +4038,51 @@ void Document::takeDOMWindowFrom(Document* document)
ASSERT(!m_domWindow);
ASSERT(document->m_domWindow);
// A valid DOMWindow is needed by CachedFrame for its documents.
- ASSERT(!document->inPageCache());
+ ASSERT(pageCacheState() == NotInPageCache);
- m_domWindow = document->m_domWindow.release();
- m_domWindow->didSecureTransitionTo(this);
+ m_domWindow = WTFMove(document->m_domWindow);
+ m_domWindow->didSecureTransitionTo(*this);
ASSERT(m_domWindow->document() == this);
ASSERT(m_domWindow->frame() == m_frame);
}
-void Document::setWindowAttributeEventListener(const AtomicString& eventType, PassRefPtr<EventListener> listener)
+Document& Document::contextDocument() const
+{
+ if (m_contextDocument)
+ return *m_contextDocument.get();
+ return const_cast<Document&>(*this);
+}
+
+void Document::setAttributeEventListener(const AtomicString& eventType, const QualifiedName& attributeName, const AtomicString& attributeValue, DOMWrapperWorld& isolatedWorld)
+{
+ setAttributeEventListener(eventType, JSLazyEventListener::create(*this, attributeName, attributeValue), isolatedWorld);
+}
+
+void Document::setWindowAttributeEventListener(const AtomicString& eventType, RefPtr<EventListener>&& listener, DOMWrapperWorld& isolatedWorld)
{
if (!m_domWindow)
return;
- m_domWindow->setAttributeEventListener(eventType, listener);
+ m_domWindow->setAttributeEventListener(eventType, WTFMove(listener), isolatedWorld);
}
-void Document::setWindowAttributeEventListener(const AtomicString& eventType, const QualifiedName& attributeName, const AtomicString& attributeValue)
+void Document::setWindowAttributeEventListener(const AtomicString& eventType, const QualifiedName& attributeName, const AtomicString& attributeValue, DOMWrapperWorld& isolatedWorld)
{
- if (!m_frame)
+ if (!m_domWindow)
return;
- setWindowAttributeEventListener(eventType, JSLazyEventListener::createForDOMWindow(*m_frame, attributeName, attributeValue));
+ setWindowAttributeEventListener(eventType, JSLazyEventListener::create(*m_domWindow, attributeName, attributeValue), isolatedWorld);
}
-EventListener* Document::getWindowAttributeEventListener(const AtomicString& eventType)
+EventListener* Document::getWindowAttributeEventListener(const AtomicString& eventType, DOMWrapperWorld& isolatedWorld)
{
if (!m_domWindow)
return nullptr;
- return m_domWindow->getAttributeEventListener(eventType);
+ return m_domWindow->attributeEventListener(eventType, isolatedWorld);
}
-void Document::dispatchWindowEvent(PassRefPtr<Event> event, PassRefPtr<EventTarget> target)
+void Document::dispatchWindowEvent(Event& event, EventTarget* target)
{
- ASSERT(!NoEventDispatchAssertion::isEventDispatchForbidden());
+ ASSERT_WITH_SECURITY_IMPLICATION(NoEventDispatchAssertion::isEventAllowedInMainThread());
if (!m_domWindow)
return;
m_domWindow->dispatchEvent(event, target);
@@ -3693,38 +4090,117 @@ void Document::dispatchWindowEvent(PassRefPtr<Event> event, PassRefPtr<EventTar
void Document::dispatchWindowLoadEvent()
{
- ASSERT(!NoEventDispatchAssertion::isEventDispatchForbidden());
+ ASSERT_WITH_SECURITY_IMPLICATION(NoEventDispatchAssertion::isEventAllowedInMainThread());
if (!m_domWindow)
return;
m_domWindow->dispatchLoadEvent();
m_loadEventFinished = true;
+ m_cachedResourceLoader->documentDidFinishLoadEvent();
}
-void Document::enqueueWindowEvent(PassRefPtr<Event> event)
+void Document::enqueueWindowEvent(Ref<Event>&& event)
{
event->setTarget(m_domWindow.get());
- m_eventQueue.enqueueEvent(event);
+ m_eventQueue.enqueueEvent(WTFMove(event));
}
-void Document::enqueueDocumentEvent(PassRefPtr<Event> event)
+void Document::enqueueDocumentEvent(Ref<Event>&& event)
{
event->setTarget(this);
- m_eventQueue.enqueueEvent(event);
+ m_eventQueue.enqueueEvent(WTFMove(event));
}
-void Document::enqueueOverflowEvent(PassRefPtr<Event> event)
+void Document::enqueueOverflowEvent(Ref<Event>&& event)
{
- m_eventQueue.enqueueEvent(event);
+ m_eventQueue.enqueueEvent(WTFMove(event));
}
-PassRefPtr<Event> Document::createEvent(const String& eventType, ExceptionCode& ec)
+ExceptionOr<Ref<Event>> Document::createEvent(const String& type)
{
- RefPtr<Event> event = EventFactory::create(eventType);
- if (event)
- return event.release();
+ // Please do *not* add new event classes to this function unless they are
+ // required for compatibility of some actual legacy web content.
- ec = NOT_SUPPORTED_ERR;
- return nullptr;
+ // This mechanism is superceded by use of event constructors.
+ // That is what we should use for any new event classes.
+
+ // The following strings are the ones from the DOM specification
+ // <https://dom.spec.whatwg.org/#dom-document-createevent>.
+
+ if (equalLettersIgnoringASCIICase(type, "customevent"))
+ return Ref<Event> { CustomEvent::create() };
+ if (equalLettersIgnoringASCIICase(type, "event") || equalLettersIgnoringASCIICase(type, "events") || equalLettersIgnoringASCIICase(type, "htmlevents"))
+ return Event::createForBindings();
+ if (equalLettersIgnoringASCIICase(type, "keyboardevent") || equalLettersIgnoringASCIICase(type, "keyboardevents"))
+ return Ref<Event> { KeyboardEvent::createForBindings() };
+ if (equalLettersIgnoringASCIICase(type, "messageevent"))
+ return Ref<Event> { MessageEvent::createForBindings() };
+ if (equalLettersIgnoringASCIICase(type, "mouseevent") || equalLettersIgnoringASCIICase(type, "mouseevents"))
+ return Ref<Event> { MouseEvent::createForBindings() };
+ if (equalLettersIgnoringASCIICase(type, "uievent") || equalLettersIgnoringASCIICase(type, "uievents"))
+ return Ref<Event> { UIEvent::createForBindings() };
+ if (equalLettersIgnoringASCIICase(type, "popstateevent"))
+ return Ref<Event> { PopStateEvent::createForBindings() };
+
+#if ENABLE(TOUCH_EVENTS)
+ if (equalLettersIgnoringASCIICase(type, "touchevent"))
+ return Ref<Event> { TouchEvent::createForBindings() };
+#endif
+
+ // The following string comes from the SVG specifications
+ // <http://www.w3.org/TR/SVG/script.html#InterfaceSVGZoomEvent>
+ // <http://www.w3.org/TR/SVG2/interact.html#InterfaceSVGZoomEvent>.
+ // However, since there is no provision for initializing the event once it is created,
+ // there is no practical value in this feature.
+
+ if (equalLettersIgnoringASCIICase(type, "svgzoomevents"))
+ return Ref<Event> { SVGZoomEvent::createForBindings() };
+
+ // The following strings are for event classes where WebKit supplies an init function.
+ // These strings are not part of the DOM specification and we would like to eliminate them.
+ // However, we currently include these because we have concerns about backward compatibility.
+
+ // FIXME: For each of the strings below, prove there is no content depending on it and remove
+ // both the string and the corresponding init function for that class.
+
+ if (equalLettersIgnoringASCIICase(type, "compositionevent"))
+ return Ref<Event> { CompositionEvent::createForBindings() };
+ if (equalLettersIgnoringASCIICase(type, "hashchangeevent"))
+ return Ref<Event> { HashChangeEvent::createForBindings() };
+ if (equalLettersIgnoringASCIICase(type, "mutationevent") || equalLettersIgnoringASCIICase(type, "mutationevents"))
+ return Ref<Event> { MutationEvent::createForBindings() };
+ if (equalLettersIgnoringASCIICase(type, "overflowevent"))
+ return Ref<Event> { OverflowEvent::createForBindings() };
+ if (equalLettersIgnoringASCIICase(type, "storageevent"))
+ return Ref<Event> { StorageEvent::createForBindings() };
+ if (equalLettersIgnoringASCIICase(type, "textevent"))
+ return Ref<Event> { TextEvent::createForBindings() };
+ if (equalLettersIgnoringASCIICase(type, "wheelevent"))
+ return Ref<Event> { WheelEvent::createForBindings() };
+
+#if ENABLE(DEVICE_ORIENTATION)
+ if (equalLettersIgnoringASCIICase(type, "devicemotionevent"))
+ return Ref<Event> { DeviceMotionEvent::createForBindings() };
+ if (equalLettersIgnoringASCIICase(type, "deviceorientationevent"))
+ return Ref<Event> { DeviceOrientationEvent::createForBindings() };
+#endif
+
+ return Exception { NOT_SUPPORTED_ERR };
+}
+
+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)
@@ -3743,11 +4219,11 @@ void Document::addListenerTypeIfNeeded(const AtomicString& eventType)
addListenerType(DOMCHARACTERDATAMODIFIED_LISTENER);
else if (eventType == eventNames().overflowchangedEvent)
addListenerType(OVERFLOWCHANGED_LISTENER);
- else if (eventType == eventNames().webkitAnimationStartEvent)
+ else if (eventType == eventNames().webkitAnimationStartEvent || eventType == eventNames().animationstartEvent)
addListenerType(ANIMATIONSTART_LISTENER);
- else if (eventType == eventNames().webkitAnimationEndEvent)
+ else if (eventType == eventNames().webkitAnimationEndEvent || eventType == eventNames().animationendEvent)
addListenerType(ANIMATIONEND_LISTENER);
- else if (eventType == eventNames().webkitAnimationIterationEvent)
+ else if (eventType == eventNames().webkitAnimationIterationEvent || eventType == eventNames().animationiterationEvent)
addListenerType(ANIMATIONITERATION_LISTENER);
else if (eventType == eventNames().webkitTransitionEndEvent || eventType == eventNames().transitionendEvent)
addListenerType(TRANSITIONEND_LISTENER);
@@ -3755,6 +4231,14 @@ void Document::addListenerTypeIfNeeded(const AtomicString& eventType)
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&)
@@ -3769,7 +4253,7 @@ HTMLFrameOwnerElement* Document::ownerElement() const
return frame()->ownerElement();
}
-String Document::cookie(ExceptionCode& ec) const
+ExceptionOr<String> Document::cookie()
{
if (page() && !page()->settings().cookieEnabled())
return String();
@@ -3778,37 +4262,38 @@ String Document::cookie(ExceptionCode& ec) const
// INVALID_STATE_ERR exception on getting if the Document has no
// browsing context.
- if (!securityOrigin()->canAccessCookies()) {
- ec = SECURITY_ERR;
- return String();
- }
+ if (!securityOrigin().canAccessCookies())
+ return Exception { SECURITY_ERR };
URL cookieURL = this->cookieURL();
if (cookieURL.isEmpty())
return String();
- return cookies(this, cookieURL);
+ if (!isDOMCookieCacheValid())
+ setCachedDOMCookies(cookies(*this, cookieURL));
+
+ return String { cachedDOMCookies() };
}
-void Document::setCookie(const String& value, ExceptionCode& ec)
+ExceptionOr<void> Document::setCookie(const String& value)
{
if (page() && !page()->settings().cookieEnabled())
- return;
+ return { };
// FIXME: The HTML5 DOM spec states that this attribute can raise an
// INVALID_STATE_ERR exception on setting if the Document has no
// browsing context.
- if (!securityOrigin()->canAccessCookies()) {
- ec = SECURITY_ERR;
- return;
- }
+ if (!securityOrigin().canAccessCookies())
+ return Exception { SECURITY_ERR };
URL cookieURL = this->cookieURL();
if (cookieURL.isEmpty())
- return;
+ return { };
- setCookies(this, cookieURL, value);
+ invalidateDOMCookieCache();
+ setCookies(*this, cookieURL, value);
+ return { };
}
String Document::referrer() const
@@ -3818,80 +4303,94 @@ String Document::referrer() const
return String();
}
+String Document::origin() const
+{
+ return securityOrigin().toString();
+}
+
String Document::domain() const
{
- return securityOrigin()->domain();
+ return securityOrigin().domain();
}
-void Document::setDomain(const String& newDomain, ExceptionCode& ec)
+ExceptionOr<void> Document::setDomain(const String& newDomain)
{
- if (SchemeRegistry::isDomainRelaxationForbiddenForURLScheme(securityOrigin()->protocol())) {
- ec = SECURITY_ERR;
- return;
- }
+ if (SchemeRegistry::isDomainRelaxationForbiddenForURLScheme(securityOrigin().protocol()))
+ return Exception { SECURITY_ERR };
// Both NS and IE specify that changing the domain is only allowed when
// the new domain is a suffix of the old domain.
// FIXME: We should add logging indicating why a domain was not allowed.
+ String oldDomain = domain();
+
// If the new domain is the same as the old domain, still call
- // securityOrigin()->setDomainForDOM. This will change the
+ // securityOrigin().setDomainForDOM. This will change the
// security check behavior. For example, if a page loaded on port 8000
// assigns its current domain using document.domain, the page will
// allow other pages loaded on different ports in the same domain that
// have also assigned to access this page.
- if (equalIgnoringCase(domain(), newDomain)) {
- securityOrigin()->setDomainFromDOM(newDomain);
- return;
+ if (equalIgnoringASCIICase(oldDomain, newDomain)) {
+ securityOrigin().setDomainFromDOM(newDomain);
+ return { };
}
- int oldLength = domain().length();
- int newLength = newDomain.length();
// e.g. newDomain = webkit.org (10) and domain() = www.webkit.org (14)
- if (newLength >= oldLength) {
- ec = SECURITY_ERR;
- return;
- }
+ unsigned oldLength = oldDomain.length();
+ unsigned newLength = newDomain.length();
+ if (newLength >= oldLength)
+ return Exception { SECURITY_ERR };
- String test = domain();
- // Check that it's a subdomain, not e.g. "ebkit.org"
- if (test[oldLength - newLength - 1] != '.') {
- ec = SECURITY_ERR;
- return;
- }
+ auto ipAddressSetting = settings().treatIPAddressAsDomain() ? OriginAccessEntry::TreatIPAddressAsDomain : OriginAccessEntry::TreatIPAddressAsIPAddress;
+ OriginAccessEntry accessEntry { securityOrigin().protocol(), newDomain, OriginAccessEntry::AllowSubdomains, ipAddressSetting };
+ if (!accessEntry.matchesOrigin(securityOrigin()))
+ return Exception { SECURITY_ERR };
- // Now test is "webkit.org" from domain()
- // and we check that it's the same thing as newDomain
- test.remove(0, oldLength - newLength);
- if (test != newDomain) {
- ec = SECURITY_ERR;
- return;
- }
+ if (oldDomain[oldLength - newLength - 1] != '.')
+ return Exception { SECURITY_ERR };
+ if (StringView { oldDomain }.substring(oldLength - newLength) != newDomain)
+ return Exception { SECURITY_ERR };
- securityOrigin()->setDomainFromDOM(newDomain);
+ securityOrigin().setDomainFromDOM(newDomain);
+ return { };
}
// http://www.whatwg.org/specs/web-apps/current-work/#dom-document-lastmodified
-String Document::lastModified() const
+String Document::lastModified()
{
- DateComponents date;
- bool foundDate = false;
- if (m_frame) {
- String httpLastModified;
- if (DocumentLoader* documentLoader = loader())
- httpLastModified = documentLoader->response().httpHeaderField("Last-Modified");
- if (!httpLastModified.isEmpty()) {
- date.setMillisecondsSinceEpochForDateTime(parseDate(httpLastModified));
- foundDate = true;
- }
- }
+ using namespace std::chrono;
+ std::optional<system_clock::time_point> dateTime;
+ if (m_frame && loader())
+ dateTime = loader()->response().lastModified();
+
// FIXME: If this document came from the file system, the HTML5
- // specificiation tells us to read the last modification date from the file
+ // specification tells us to read the last modification date from the file
// system.
- if (!foundDate)
- date.setMillisecondsSinceEpochForDateTime(currentTimeMS());
- return String::format("%02d/%02d/%04d %02d:%02d:%02d", date.month() + 1, date.monthDay(), date.fullYear(), date.hour(), date.minute(), date.second());
+ if (!dateTime) {
+ dateTime = system_clock::now();
+#if ENABLE(WEB_REPLAY)
+ auto& cursor = inputCursor();
+ if (cursor.isCapturing())
+ cursor.appendInput<DocumentLastModifiedDate>(duration_cast<milliseconds>(dateTime.value().time_since_epoch()).count());
+ else if (cursor.isReplaying()) {
+ if (auto* input = cursor.fetchInput<DocumentLastModifiedDate>())
+ dateTime = system_clock::time_point(milliseconds(static_cast<long long>(input->fallbackValue())));
+ }
+#endif
+ }
+
+ auto ctime = system_clock::to_time_t(dateTime.value());
+ auto localDateTime = std::localtime(&ctime);
+ return String::format("%02d/%02d/%04d %02d:%02d:%02d", localDateTime->tm_mon + 1, localDateTime->tm_mday, 1900 + localDateTime->tm_year, localDateTime->tm_hour, localDateTime->tm_min, localDateTime->tm_sec);
+}
+
+void Document::setCookieURL(const URL& url)
+{
+ if (m_cookieURL == url)
+ return;
+ m_cookieURL = url;
+ invalidateDOMCookieCache();
}
static bool isValidNameNonASCII(const LChar* characters, unsigned length)
@@ -3964,68 +4463,57 @@ bool Document::isValidName(const String& name)
return isValidNameNonASCII(characters, length);
}
-bool Document::parseQualifiedName(const String& qualifiedName, String& prefix, String& localName, ExceptionCode& ec)
+ExceptionOr<std::pair<AtomicString, AtomicString>> Document::parseQualifiedName(const String& qualifiedName)
{
unsigned length = qualifiedName.length();
- if (!length) {
- ec = INVALID_CHARACTER_ERR;
- return false;
- }
+ if (!length)
+ return Exception { INVALID_CHARACTER_ERR };
bool nameStart = true;
bool sawColon = false;
- int colonPos = 0;
+ unsigned colonPosition = 0;
- const UChar* s = qualifiedName.deprecatedCharacters();
- for (unsigned i = 0; i < length;) {
+ for (unsigned i = 0; i < length; ) {
UChar32 c;
- U16_NEXT(s, i, length, c)
+ U16_NEXT(qualifiedName, i, length, c)
if (c == ':') {
- if (sawColon) {
- ec = NAMESPACE_ERR;
- return false; // multiple colons: not allowed
- }
+ if (sawColon)
+ return Exception { NAMESPACE_ERR };
nameStart = true;
sawColon = true;
- colonPos = i - 1;
+ colonPosition = i - 1;
} else if (nameStart) {
- if (!isValidNameStart(c)) {
- ec = INVALID_CHARACTER_ERR;
- return false;
- }
+ if (!isValidNameStart(c))
+ return Exception { INVALID_CHARACTER_ERR };
nameStart = false;
} else {
- if (!isValidNamePart(c)) {
- ec = INVALID_CHARACTER_ERR;
- return false;
- }
+ if (!isValidNamePart(c))
+ return Exception { INVALID_CHARACTER_ERR };
}
}
- 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 (!sawColon)
+ return std::pair<AtomicString, AtomicString> { { }, { qualifiedName } };
- if (localName.isEmpty()) {
- ec = NAMESPACE_ERR;
- return false;
- }
+ if (!colonPosition || length - colonPosition <= 1)
+ return Exception { NAMESPACE_ERR };
- return true;
+ return std::pair<AtomicString, AtomicString> { StringView { qualifiedName }.substring(0, colonPosition).toAtomicString(), StringView { qualifiedName }.substring(colonPosition + 1).toAtomicString() };
+}
+
+ExceptionOr<QualifiedName> Document::parseQualifiedName(const AtomicString& namespaceURI, const String& qualifiedName)
+{
+ auto parseResult = parseQualifiedName(qualifiedName);
+ if (parseResult.hasException())
+ return parseResult.releaseException();
+ auto parsedPieces = parseResult.releaseReturnValue();
+ return QualifiedName { parsedPieces.first, parsedPieces.second, namespaceURI };
}
-void Document::setDecoder(PassRefPtr<TextResourceDecoder> decoder)
+void Document::setDecoder(RefPtr<TextResourceDecoder>&& decoder)
{
- m_decoder = decoder;
+ m_decoder = WTFMove(decoder);
}
URL Document::completeURL(const String& url, const URL& baseURLOverride) const
@@ -4046,20 +4534,18 @@ URL Document::completeURL(const String& url) const
return completeURL(url, m_baseURL);
}
-void Document::setInPageCache(bool flag)
+void Document::setPageCacheState(PageCacheState state)
{
- if (m_inPageCache == flag)
+ if (m_pageCacheState == state)
return;
- m_inPageCache = flag;
+ m_pageCacheState = state;
FrameView* v = view();
Page* page = this->page();
- if (page)
- page->lockAllOverlayScrollbarsToHidden(flag);
-
- if (flag) {
+ switch (state) {
+ case InPageCache:
if (v) {
// FIXME: There is some scrolling related work that needs to happen whenever a page goes into the
// page cache and similar work that needs to occur when it comes out. This is where we do the work
@@ -4073,74 +4559,110 @@ void Document::setInPageCache(bool flag)
v->resetScrollbarsAndClearContentsSize();
if (ScrollingCoordinator* scrollingCoordinator = page->scrollingCoordinator())
scrollingCoordinator->clearStateTree();
- } else
- v->resetScrollbars();
+ }
}
+
+#if ENABLE(POINTER_LOCK)
+ exitPointerLock();
+#endif
+
+ styleScope().clearResolver();
+ clearSelectorQueryCache();
m_styleRecalcTimer.stop();
- } else {
+
+ clearSharedObjectPool();
+ break;
+ case NotInPageCache:
if (childNeedsStyleRecalc())
scheduleStyleRecalc();
+ break;
+ case AboutToEnterPageCache:
+ break;
}
}
void Document::documentWillBecomeInactive()
{
-#if USE(ACCELERATED_COMPOSITING)
if (renderView())
renderView()->setIsInWindow(false);
-#endif
}
-void Document::documentWillSuspendForPageCache()
+void Document::suspend(ActiveDOMObject::ReasonForSuspension reason)
{
+ if (m_isSuspended)
+ return;
+
documentWillBecomeInactive();
- HashSet<Element*>::iterator end = m_documentSuspensionCallbackElements.end();
- for (HashSet<Element*>::iterator i = m_documentSuspensionCallbackElements.begin(); i != end; ++i)
- (*i)->documentWillSuspendForPageCache();
+ for (auto* element : m_documentSuspensionCallbackElements)
+ element->prepareForDocumentSuspension();
#ifndef NDEBUG
// Clear the update flag to be able to check if the viewport arguments update
// is dispatched, after the document is restored from the page cache.
m_didDispatchViewportPropertiesChanged = false;
#endif
+
+ ASSERT(page());
+ page()->lockAllOverlayScrollbarsToHidden(true);
+
+ if (RenderView* view = renderView()) {
+ if (view->usesCompositing())
+ view->compositor().cancelCompositingLayerUpdate();
+ }
+
+ suspendScheduledTasks(reason);
+
+ ASSERT(m_frame);
+ m_frame->clearTimers();
+
+ m_visualUpdatesAllowed = false;
+ m_visualUpdatesSuppressionTimer.stop();
+
+ m_isSuspended = true;
}
-void Document::documentDidResumeFromPageCache()
+void Document::resume(ActiveDOMObject::ReasonForSuspension reason)
{
+ if (!m_isSuspended)
+ return;
+
Vector<Element*> elements;
copyToVector(m_documentSuspensionCallbackElements, elements);
- Vector<Element*>::iterator end = elements.end();
- for (Vector<Element*>::iterator i = elements.begin(); i != end; ++i)
- (*i)->documentDidResumeFromPageCache();
+ for (auto* element : elements)
+ element->resumeFromDocumentSuspension();
-#if USE(ACCELERATED_COMPOSITING)
if (renderView())
renderView()->setIsInWindow(true);
-#endif
ASSERT(page());
page()->lockAllOverlayScrollbarsToHidden(false);
ASSERT(m_frame);
m_frame->loader().client().dispatchDidBecomeFrameset(isFrameSet());
+ m_frame->animation().resumeAnimationsForDocument(this);
+
+ resumeScheduledTasks(reason);
+
+ m_visualUpdatesAllowed = true;
+
+ m_isSuspended = false;
}
-void Document::registerForPageCacheSuspensionCallbacks(Element* e)
+void Document::registerForDocumentSuspensionCallbacks(Element* e)
{
m_documentSuspensionCallbackElements.add(e);
}
-void Document::unregisterForPageCacheSuspensionCallbacks(Element* e)
+void Document::unregisterForDocumentSuspensionCallbacks(Element* e)
{
m_documentSuspensionCallbackElements.remove(e);
}
void Document::mediaVolumeDidChange()
{
- HashSet<Element*>::iterator end = m_mediaVolumeCallbackElements.end();
- for (HashSet<Element*>::iterator i = m_mediaVolumeCallbackElements.begin(); i != end; ++i)
- (*i)->mediaVolumeDidChange();
+ for (auto* element : m_mediaVolumeCallbackElements)
+ element->mediaVolumeDidChange();
}
void Document::registerForMediaVolumeCallbacks(Element* e)
@@ -4153,17 +4675,39 @@ void Document::unregisterForMediaVolumeCallbacks(Element* e)
m_mediaVolumeCallbackElements.remove(e);
}
+bool Document::audioPlaybackRequiresUserGesture() const
+{
+ if (DocumentLoader* loader = this->loader()) {
+ // If an audio playback policy was set during navigation, use it. If not, use the global settings.
+ AutoplayPolicy policy = loader->autoplayPolicy();
+ if (policy != AutoplayPolicy::Default)
+ return policy == AutoplayPolicy::AllowWithoutSound || policy == AutoplayPolicy::Deny;
+ }
+
+ return settings().audioPlaybackRequiresUserGesture();
+}
+
+bool Document::videoPlaybackRequiresUserGesture() const
+{
+ if (DocumentLoader* loader = this->loader()) {
+ // If a video playback policy was set during navigation, use it. If not, use the global settings.
+ AutoplayPolicy policy = loader->autoplayPolicy();
+ if (policy != AutoplayPolicy::Default)
+ return policy == AutoplayPolicy::Deny;
+ }
+
+ return settings().videoPlaybackRequiresUserGesture();
+}
+
void Document::storageBlockingStateDidChange()
{
- if (Settings* settings = this->settings())
- securityOrigin()->setStorageBlockingPolicy(settings->storageBlockingPolicy());
+ securityOrigin().setStorageBlockingPolicy(settings().storageBlockingPolicy());
}
void Document::privateBrowsingStateDidChange()
{
- HashSet<Element*>::iterator end = m_privateBrowsingStateChangedElements.end();
- for (HashSet<Element*>::iterator it = m_privateBrowsingStateChangedElements.begin(); it != end; ++it)
- (*it)->privateBrowsingStateDidChange();
+ for (auto* element : m_privateBrowsingStateChangedElements)
+ element->privateBrowsingStateDidChange();
}
void Document::registerForPrivateBrowsingStateChangedCallbacks(Element* e)
@@ -4177,10 +4721,11 @@ void Document::unregisterForPrivateBrowsingStateChangedCallbacks(Element* e)
}
#if ENABLE(VIDEO_TRACK)
+
void Document::registerForCaptionPreferencesChangedCallbacks(Element* e)
{
if (page())
- page()->group().captionPreferences()->setInterestedInCaptionPreferenceChanges();
+ page()->group().captionPreferences().setInterestedInCaptionPreferenceChanges();
m_captionPreferencesChangedElements.add(e);
}
@@ -4192,10 +4737,46 @@ void Document::unregisterForCaptionPreferencesChangedCallbacks(Element* e)
void Document::captionPreferencesChanged()
{
- HashSet<Element*>::iterator end = m_captionPreferencesChangedElements.end();
- for (HashSet<Element*>::iterator it = m_captionPreferencesChangedElements.begin(); it != end; ++it)
- (*it)->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();
+}
+
+void Document::registerForUserInterfaceLayoutDirectionChangedCallbacks(HTMLMediaElement& element)
+{
+ m_userInterfaceLayoutDirectionChangedElements.add(&element);
+}
+
+void Document::unregisterForUserInterfaceLayoutDirectionChangedCallbacks(HTMLMediaElement& element)
+{
+ m_userInterfaceLayoutDirectionChangedElements.remove(&element);
+}
+
+void Document::userInterfaceLayoutDirectionChanged()
+{
+ for (auto* mediaElement : m_userInterfaceLayoutDirectionChangedElements)
+ mediaElement->userInterfaceLayoutDirectionChanged();
+}
+
#endif
void Document::setShouldCreateRenderers(bool f)
@@ -4224,6 +4805,7 @@ static Editor::Command command(Document* document, const String& commandName, bo
bool Document::execCommand(const String& commandName, bool userInterface, const String& value)
{
+ EventQueueScope eventQueueScope;
return command(this, commandName, userInterface).execute(value);
}
@@ -4252,9 +4834,8 @@ String Document::queryCommandValue(const String& commandName)
return command(this, commandName).value();
}
-void Document::pushCurrentScript(PassRefPtr<HTMLScriptElement> newCurrentScript)
+void Document::pushCurrentScript(HTMLScriptElement* newCurrentScript)
{
- ASSERT(newCurrentScript);
m_currentScriptStack.append(newCurrentScript);
}
@@ -4269,7 +4850,7 @@ void Document::popCurrentScript()
void Document::applyXSLTransform(ProcessingInstruction* pi)
{
RefPtr<XSLTProcessor> processor = XSLTProcessor::create();
- processor->setXSLStyleSheet(static_cast<XSLStyleSheet*>(pi->sheet()));
+ processor->setXSLStyleSheet(downcast<XSLStyleSheet>(pi->sheet()));
String resultMIMEType;
String newSource;
String resultEncoding;
@@ -4278,12 +4859,11 @@ void Document::applyXSLTransform(ProcessingInstruction* pi)
// FIXME: If the transform failed we should probably report an error (like Mozilla does).
Frame* ownerFrame = frame();
processor->createDocumentFromSource(newSource, resultEncoding, resultMIMEType, this, ownerFrame);
- InspectorInstrumentation::frameDocumentUpdated(ownerFrame);
}
-void Document::setTransformSource(PassOwnPtr<TransformSource> source)
+void Document::setTransformSource(std::unique_ptr<TransformSource> source)
{
- m_transformSource = source;
+ m_transformSource = WTFMove(source);
}
#endif
@@ -4295,7 +4875,24 @@ void Document::setDesignMode(InheritedBool value)
frame->document()->scheduleForcedStyleRecalc();
}
-Document::InheritedBool Document::getDesignMode() const
+String Document::designMode() const
+{
+ return inDesignMode() ? ASCIILiteral("on") : ASCIILiteral("off");
+}
+
+void Document::setDesignMode(const String& value)
+{
+ InheritedBool mode;
+ if (equalLettersIgnoringASCIICase(value, "on"))
+ mode = on;
+ else if (equalLettersIgnoringASCIICase(value, "off"))
+ mode = off;
+ else
+ mode = inherit;
+ setDesignMode(mode);
+}
+
+auto Document::getDesignMode() const -> InheritedBool
{
return m_designMode;
}
@@ -4321,109 +4918,113 @@ Document* Document::parentDocument() const
Document& Document::topDocument() const
{
+ // FIXME: This special-casing avoids incorrectly determined top documents during the process
+ // of AXObjectCache teardown or notification posting for cached or being-destroyed documents.
+ if (pageCacheState() == NotInPageCache && !m_renderTreeBeingDestroyed) {
+ if (!m_frame)
+ return const_cast<Document&>(*this);
+ // This should always be non-null.
+ Document* mainFrameDocument = m_frame->mainFrame().document();
+ return mainFrameDocument ? *mainFrameDocument : const_cast<Document&>(*this);
+ }
+
Document* document = const_cast<Document*>(this);
- while (Element* element = document->ownerElement())
+ while (HTMLFrameOwnerElement* element = document->ownerElement())
document = &element->document();
return *document;
}
-PassRefPtr<Attr> Document::createAttribute(const String& name, ExceptionCode& ec)
+ExceptionOr<Ref<Attr>> Document::createAttribute(const String& name)
{
- return createAttributeNS(String(), name, ec, true);
+ return createAttributeNS({ }, isHTMLDocument() ? name.convertToASCIILowercase() : name, true);
}
-PassRefPtr<Attr> Document::createAttributeNS(const String& namespaceURI, const String& qualifiedName, ExceptionCode& ec, bool shouldIgnoreNamespaceChecks)
+ExceptionOr<Ref<Attr>> Document::createAttributeNS(const AtomicString& namespaceURI, const String& qualifiedName, 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());
+ auto parseResult = parseQualifiedName(namespaceURI, qualifiedName);
+ if (parseResult.hasException())
+ return parseResult.releaseException();
+ QualifiedName parsedName { parseResult.releaseReturnValue() };
+ if (!shouldIgnoreNamespaceChecks && !hasValidNamespaceForAttributes(parsedName))
+ return Exception { NAMESPACE_ERR };
+ return Attr::create(*this, parsedName, emptyString());
}
-#if ENABLE(SVG)
const SVGDocumentExtensions* Document::svgExtensions()
{
return m_svgExtensions.get();
}
-SVGDocumentExtensions* Document::accessSVGExtensions()
+SVGDocumentExtensions& Document::accessSVGExtensions()
{
if (!m_svgExtensions)
- m_svgExtensions = adoptPtr(new SVGDocumentExtensions(this));
- return m_svgExtensions.get();
+ m_svgExtensions = std::make_unique<SVGDocumentExtensions>(this);
+ return *m_svgExtensions;
}
bool Document::hasSVGRootNode() const
{
return documentElement() && documentElement()->hasTagName(SVGNames::svgTag);
}
-#endif
-PassRefPtr<HTMLCollection> Document::ensureCachedCollection(CollectionType type)
+template <CollectionType collectionType>
+Ref<HTMLCollection> Document::ensureCachedCollection()
{
- return ensureRareData().ensureNodeLists().addCachedCollection<HTMLCollection>(*this, type);
+ return ensureRareData().ensureNodeLists().addCachedCollection<GenericCachedHTMLCollection<CollectionTypeTraits<collectionType>::traversalType>>(*this, collectionType);
}
-PassRefPtr<HTMLCollection> Document::images()
+Ref<HTMLCollection> Document::images()
{
- return ensureCachedCollection(DocImages);
+ return ensureCachedCollection<DocImages>();
}
-PassRefPtr<HTMLCollection> Document::applets()
+Ref<HTMLCollection> Document::applets()
{
- return ensureCachedCollection(DocApplets);
+ return ensureCachedCollection<DocApplets>();
}
-PassRefPtr<HTMLCollection> Document::embeds()
+Ref<HTMLCollection> Document::embeds()
{
- return ensureCachedCollection(DocEmbeds);
+ return ensureCachedCollection<DocEmbeds>();
}
-PassRefPtr<HTMLCollection> Document::plugins()
+Ref<HTMLCollection> Document::plugins()
{
// This is an alias for embeds() required for the JS DOM bindings.
- return ensureCachedCollection(DocEmbeds);
+ return ensureCachedCollection<DocEmbeds>();
}
-PassRefPtr<HTMLCollection> Document::scripts()
+Ref<HTMLCollection> Document::scripts()
{
- return ensureCachedCollection(DocScripts);
+ return ensureCachedCollection<DocScripts>();
}
-PassRefPtr<HTMLCollection> Document::links()
+Ref<HTMLCollection> Document::links()
{
- return ensureCachedCollection(DocLinks);
+ return ensureCachedCollection<DocLinks>();
}
-PassRefPtr<HTMLCollection> Document::forms()
+Ref<HTMLCollection> Document::forms()
{
- return ensureCachedCollection(DocForms);
+ return ensureCachedCollection<DocForms>();
}
-PassRefPtr<HTMLCollection> Document::anchors()
+Ref<HTMLCollection> Document::anchors()
{
- return ensureCachedCollection(DocAnchors);
+ return ensureCachedCollection<DocAnchors>();
}
-PassRefPtr<HTMLCollection> Document::all()
+Ref<HTMLCollection> Document::all()
{
return ensureRareData().ensureNodeLists().addCachedCollection<HTMLAllCollection>(*this, DocAll);
}
-PassRefPtr<HTMLCollection> Document::windowNamedItems(const AtomicString& name)
+Ref<HTMLCollection> Document::windowNamedItems(const AtomicString& name)
{
return ensureRareData().ensureNodeLists().addCachedCollection<WindowNameCollection>(*this, WindowNamedItems, name);
}
-PassRefPtr<HTMLCollection> Document::documentNamedItems(const AtomicString& name)
+Ref<HTMLCollection> Document::documentNamedItems(const AtomicString& name)
{
return ensureRareData().ensureNodeLists().addCachedCollection<DocumentNameCollection>(*this, DocumentNamedItems, name);
}
@@ -4436,14 +5037,14 @@ void Document::finishedParsing()
#if ENABLE(WEB_TIMING)
if (!m_documentTiming.domContentLoadedEventStart)
- m_documentTiming.domContentLoadedEventStart = monotonicallyIncreasingTime();
+ m_documentTiming.domContentLoadedEventStart = MonotonicTime::now();
#endif
dispatchEvent(Event::create(eventNames().DOMContentLoadedEvent, true, false));
#if ENABLE(WEB_TIMING)
if (!m_documentTiming.domContentLoadedEventEnd)
- m_documentTiming.domContentLoadedEventEnd = monotonicallyIncreasingTime();
+ m_documentTiming.domContentLoadedEventEnd = MonotonicTime::now();
#endif
if (RefPtr<Frame> f = frame()) {
@@ -4458,7 +5059,7 @@ void Document::finishedParsing()
f->loader().finishedParsing();
- InspectorInstrumentation::domContentLoadedEventFired(f.get());
+ InspectorInstrumentation::domContentLoadedEventFired(*f);
}
// Schedule dropping of the DocumentSharedObjectPool. We keep it alive for a while after parsing finishes
@@ -4468,32 +5069,23 @@ void Document::finishedParsing()
static const int timeToKeepSharedObjectPoolAliveAfterParsingFinishedInSeconds = 10;
m_sharedObjectPoolClearTimer.startOneShot(timeToKeepSharedObjectPoolAliveAfterParsingFinishedInSeconds);
- // Parser should have picked up all preloads by now
- m_cachedResourceLoader->clearPreloads();
+ // Parser should have picked up all speculative preloads by now
+ m_cachedResourceLoader->clearPreloads(CachedResourceLoader::ClearPreloadsMode::ClearSpeculativePreloads);
}
-void Document::sharedObjectPoolClearTimerFired(Timer<Document>&)
+void Document::clearSharedObjectPool()
{
- m_sharedObjectPool.clear();
+ m_sharedObjectPool = nullptr;
+ m_sharedObjectPoolClearTimer.stop();
}
-void Document::didAccessStyleResolver()
-{
- m_styleResolverThrowawayTimer.restart();
-}
+#if ENABLE(TELEPHONE_NUMBER_DETECTION)
-void Document::styleResolverThrowawayTimerFired(DeferrableOneShotTimer<Document>&)
-{
- ASSERT(!m_inStyleRecalc);
- clearStyleResolver();
-}
+// FIXME: Find a better place for this code.
-#if PLATFORM(IOS)
-// FIXME: Find a better place for this functionality.
bool Document::isTelephoneNumberParsingEnabled() const
{
- Settings* settings = this->settings();
- return settings && settings->telephoneNumberParsingEnabled() && m_isTelephoneNumberParsingAllowed;
+ return settings().telephoneNumberParsingEnabled() && m_isTelephoneNumberParsingAllowed;
}
void Document::setIsTelephoneNumberParsingAllowed(bool isTelephoneNumberParsingAllowed)
@@ -4505,196 +5097,185 @@ bool Document::isTelephoneNumberParsingAllowed() const
{
return m_isTelephoneNumberParsingAllowed;
}
+
#endif
-PassRefPtr<XPathExpression> Document::createExpression(const String& expression,
- XPathNSResolver* resolver,
- ExceptionCode& ec)
+ExceptionOr<Ref<XPathExpression>> Document::createExpression(const String& expression, RefPtr<XPathNSResolver>&& resolver)
{
if (!m_xpathEvaluator)
m_xpathEvaluator = XPathEvaluator::create();
- return m_xpathEvaluator->createExpression(expression, resolver, ec);
+ return m_xpathEvaluator->createExpression(expression, WTFMove(resolver));
}
-PassRefPtr<XPathNSResolver> Document::createNSResolver(Node* nodeResolver)
+Ref<XPathNSResolver> Document::createNSResolver(Node* nodeResolver)
{
if (!m_xpathEvaluator)
m_xpathEvaluator = XPathEvaluator::create();
return m_xpathEvaluator->createNSResolver(nodeResolver);
}
-PassRefPtr<XPathResult> Document::evaluate(const String& expression,
- Node* contextNode,
- XPathNSResolver* resolver,
- unsigned short type,
- XPathResult* result,
- ExceptionCode& ec)
+ExceptionOr<Ref<XPathResult>> Document::evaluate(const String& expression, Node* contextNode, RefPtr<XPathNSResolver>&& resolver, unsigned short type, XPathResult* result)
{
if (!m_xpathEvaluator)
m_xpathEvaluator = XPathEvaluator::create();
- return m_xpathEvaluator->evaluate(expression, contextNode, resolver, type, result, ec);
-}
-
-const Vector<IconURL>& Document::shortcutIconURLs()
-{
- // Include any icons where type = link, rel = "shortcut icon".
- return iconURLs(Favicon);
-}
-
-const Vector<IconURL>& Document::iconURLs(int iconTypesMask)
-{
- m_iconURLs.clear();
-
- if (!head() || !(head()->children()))
- return m_iconURLs;
-
- RefPtr<HTMLCollection> children = head()->children();
- unsigned int length = children->length();
- for (unsigned int i = 0; i < length; ++i) {
- Node* child = children->item(i);
- if (!child->hasTagName(linkTag))
- continue;
- HTMLLinkElement* linkElement = toHTMLLinkElement(child);
- if (!(linkElement->iconType() & iconTypesMask))
- continue;
- if (linkElement->href().isEmpty())
- continue;
-
- // Put it at the front to ensure that icons seen later take precedence as required by the spec.
- IconURL newURL(linkElement->href(), linkElement->iconSizes(), linkElement->type(), linkElement->iconType());
- m_iconURLs.append(newURL);
- }
-
- m_iconURLs.reverse();
- return m_iconURLs;
-}
-
-void Document::addIconURL(const String& url, const String&, const String&, IconType iconType)
-{
- if (url.isEmpty())
- return;
-
- Frame* f = frame();
- if (!f)
- return;
-
- f->loader().didChangeIcons(iconType);
+ return m_xpathEvaluator->evaluate(expression, contextNode, WTFMove(resolver), type, result);
}
-static bool isEligibleForSeamless(Document* parent, Document* child)
+static bool shouldInheritSecurityOriginFromOwner(const URL& url)
{
- // It should not matter what we return for the top-most document.
- if (!parent)
- return false;
- if (parent->isSandboxed(SandboxSeamlessIframes))
- return false;
- if (child->isSrcdocDocument())
- return true;
- if (parent->securityOrigin()->canAccess(child->securityOrigin()))
- return true;
- return parent->securityOrigin()->canRequest(child->url());
+ // Paraphrased from <https://html.spec.whatwg.org/multipage/browsers.html#origin> (8 July 2016)
+ //
+ // If a Document has the address "about:blank"
+ // The origin of the document is the origin it was assigned when its browsing context was created.
+ // If a Document has the address "about:srcdoc"
+ // The origin of the document is the origin of its parent document.
+ //
+ // Note: We generalize this to invalid URLs because we treat such URLs as about:blank.
+ //
+ return url.isEmpty() || equalIgnoringASCIICase(url.string(), blankURL()) || equalLettersIgnoringASCIICase(url.string(), "about:srcdoc");
}
void Document::initSecurityContext()
{
if (haveInitializedSecurityOrigin()) {
- ASSERT(securityOrigin());
+ ASSERT(SecurityContext::securityOrigin());
return;
}
if (!m_frame) {
// No source for a security context.
// This can occur via document.implementation.createDocument().
- m_cookieURL = URL(ParsedURLString, emptyString());
- setSecurityOrigin(SecurityOrigin::createUnique());
- setContentSecurityPolicy(ContentSecurityPolicy::create(this));
+ setCookieURL(URL(ParsedURLString, emptyString()));
+ setSecurityOriginPolicy(SecurityOriginPolicy::create(SecurityOrigin::createUnique()));
+ setContentSecurityPolicy(std::make_unique<ContentSecurityPolicy>(*this));
return;
}
// In the common case, create the security context from the currently
// loading URL with a fresh content security policy.
- m_cookieURL = m_url;
+ setCookieURL(m_url);
enforceSandboxFlags(m_frame->loader().effectiveSandboxFlags());
-#if PLATFORM(IOS)
- // On iOS we display attachments inline regardless of whether the response includes
- // the HTTP header "Content-Disposition: attachment". So, we enforce a unique
- // security origin for such documents. As an optimization, we don't need to parse
- // the responde header (i.e. call ResourceResponse::isAttachment()) for a synthesized
- // document because such documents cannot be an attachment.
- if (!m_isSynthesized && m_frame->loader().activeDocumentLoader()->response().isAttachment())
- enforceSandboxFlags(SandboxOrigin);
+ if (shouldEnforceContentDispositionAttachmentSandbox())
+ applyContentDispositionAttachmentSandbox();
+
+ setSecurityOriginPolicy(SecurityOriginPolicy::create(isSandboxed(SandboxOrigin) ? SecurityOrigin::createUnique() : SecurityOrigin::create(m_url)));
+ setContentSecurityPolicy(std::make_unique<ContentSecurityPolicy>(*this));
+
+ String overrideContentSecurityPolicy = m_frame->loader().client().overrideContentSecurityPolicy();
+ if (!overrideContentSecurityPolicy.isNull())
+ contentSecurityPolicy()->didReceiveHeader(overrideContentSecurityPolicy, ContentSecurityPolicyHeaderType::Enforce, ContentSecurityPolicy::PolicyFrom::API);
+
+#if USE(QUICK_LOOK)
+ if (shouldEnforceQuickLookSandbox())
+ applyQuickLookSandbox();
#endif
- setSecurityOrigin(isSandboxed(SandboxOrigin) ? SecurityOrigin::createUnique() : SecurityOrigin::create(m_url));
- setContentSecurityPolicy(ContentSecurityPolicy::create(this));
-
- if (Settings* settings = this->settings()) {
- if (!settings->webSecurityEnabled()) {
- // Web security is turned off. We should let this document access every other document. This is used primary by testing
- // harnesses for web sites.
- securityOrigin()->grantUniversalAccess();
- } else if (securityOrigin()->isLocal()) {
- if (settings->allowUniversalAccessFromFileURLs() || m_frame->loader().client().shouldForceUniversalAccessFromLocalURL(m_url)) {
- // Some clients want local URLs to have universal access, but that setting is dangerous for other clients.
- securityOrigin()->grantUniversalAccess();
- } else if (!settings->allowFileAccessFromFileURLs()) {
- // Some clients want local URLs to have even tighter restrictions by default, and not be able to access other local files.
- // FIXME 81578: The naming of this is confusing. Files with restricted access to other local files
- // still can have other privileges that can be remembered, thereby not making them unique origins.
- securityOrigin()->enforceFilePathSeparation();
- }
+ if (shouldEnforceHTTP09Sandbox()) {
+ String message = makeString("Sandboxing '", m_url.stringCenterEllipsizedToLength(), "' because it is using HTTP/0.9.");
+ addConsoleMessage(MessageSource::Security, MessageLevel::Error, message);
+ enforceSandboxFlags(SandboxScripts | SandboxPlugins);
+ }
+
+ if (settings().needsStorageAccessFromFileURLsQuirk())
+ securityOrigin().grantStorageAccessFromFileURLsQuirk();
+ if (!settings().webSecurityEnabled()) {
+ // Web security is turned off. We should let this document access every other document. This is used primary by testing
+ // harnesses for web sites.
+ securityOrigin().grantUniversalAccess();
+ } else if (securityOrigin().isLocal()) {
+ if (settings().allowUniversalAccessFromFileURLs() || m_frame->loader().client().shouldForceUniversalAccessFromLocalURL(m_url)) {
+ // Some clients want local URLs to have universal access, but that setting is dangerous for other clients.
+ securityOrigin().grantUniversalAccess();
+ } else if (!settings().allowFileAccessFromFileURLs()) {
+ // Some clients want local URLs to have even tighter restrictions by default, and not be able to access other local files.
+ // FIXME 81578: The naming of this is confusing. Files with restricted access to other local files
+ // still can have other privileges that can be remembered, thereby not making them unique origins.
+ securityOrigin().enforceFilePathSeparation();
}
- securityOrigin()->setStorageBlockingPolicy(settings->storageBlockingPolicy());
}
+ securityOrigin().setStorageBlockingPolicy(settings().storageBlockingPolicy());
Document* parentDocument = ownerElement() ? &ownerElement()->document() : nullptr;
if (parentDocument && m_frame->loader().shouldTreatURLAsSrcdocDocument(url())) {
m_isSrcdocDocument = true;
setBaseURLOverride(parentDocument->baseURL());
}
-
- // FIXME: What happens if we inherit the security origin? This check may need to be later.
- // <iframe seamless src="about:blank"> likely won't work as-is.
- m_mayDisplaySeamlesslyWithParent = isEligibleForSeamless(parentDocument, this);
+ if (parentDocument)
+ setStrictMixedContentMode(parentDocument->isStrictMixedContentMode());
if (!shouldInheritSecurityOriginFromOwner(m_url))
return;
// If we do not obtain a meaningful origin from the URL, then we try to
// find one via the frame hierarchy.
+ Frame* parentFrame = m_frame->tree().parent();
+ Frame* openerFrame = m_frame->loader().opener();
- Frame* ownerFrame = m_frame->tree().parent();
+ Frame* ownerFrame = parentFrame;
if (!ownerFrame)
- ownerFrame = m_frame->loader().opener();
+ ownerFrame = openerFrame;
if (!ownerFrame) {
didFailToInitializeSecurityOrigin();
return;
}
+ Document* openerDocument = openerFrame ? openerFrame->document() : nullptr;
+
+ // Per <http://www.w3.org/TR/upgrade-insecure-requests/>, new browsing contexts must inherit from an
+ // ongoing set of upgraded requests. When opening a new browsing context, we need to capture its
+ // existing upgrade request. Nested browsing contexts are handled during DocumentWriter::begin.
+ if (openerDocument)
+ contentSecurityPolicy()->inheritInsecureNavigationRequestsToUpgradeFromOpener(*openerDocument->contentSecurityPolicy());
+
if (isSandboxed(SandboxOrigin)) {
// If we're supposed to inherit our security origin from our owner,
// but we're also sandboxed, the only thing we inherit is the ability
// to load local resources. This lets about:blank iframes in file://
// URL documents load images and other resources from the file system.
- if (ownerFrame->document()->securityOrigin()->canLoadLocalResources())
- securityOrigin()->grantLoadLocalResources();
+ if (ownerFrame->document()->securityOrigin().canLoadLocalResources())
+ securityOrigin().grantLoadLocalResources();
return;
}
- m_cookieURL = ownerFrame->document()->cookieURL();
+ setCookieURL(ownerFrame->document()->cookieURL());
// We alias the SecurityOrigins to match Firefox, see Bug 15313
// https://bugs.webkit.org/show_bug.cgi?id=15313
- setSecurityOrigin(ownerFrame->document()->securityOrigin());
+ setSecurityOriginPolicy(ownerFrame->document()->securityOriginPolicy());
+}
+
+bool Document::shouldInheritContentSecurityPolicyFromOwner() const
+{
+ ASSERT(m_frame);
+ if (shouldInheritSecurityOriginFromOwner(m_url))
+ return true;
+ if (!isPluginDocument())
+ return false;
+ if (m_frame->tree().parent())
+ return true;
+ Frame* openerFrame = m_frame->loader().opener();
+ if (!openerFrame)
+ return false;
+ return openerFrame->document()->securityOrigin().canAccess(securityOrigin());
}
void Document::initContentSecurityPolicy()
{
- if (!m_frame->tree().parent() || (!shouldInheritSecurityOriginFromOwner(m_url) && !isPluginDocument()))
- return;
+ // 1. Inherit Upgrade Insecure Requests
+ Frame* parentFrame = m_frame->tree().parent();
+ if (parentFrame)
+ contentSecurityPolicy()->copyUpgradeInsecureRequestStateFrom(*parentFrame->document()->contentSecurityPolicy());
- contentSecurityPolicy()->copyStateFrom(m_frame->tree().parent()->document()->contentSecurityPolicy());
+ // 2. Inherit Content Security Policy
+ if (!shouldInheritContentSecurityPolicyFromOwner())
+ return;
+ Frame* ownerFrame = parentFrame;
+ if (!ownerFrame)
+ ownerFrame = m_frame->loader().opener();
+ if (!ownerFrame)
+ return;
+ contentSecurityPolicy()->copyStateFrom(ownerFrame->document()->contentSecurityPolicy()); // Does not copy Upgrade Insecure Requests state.
}
bool Document::isContextThread() const
@@ -4715,7 +5296,7 @@ void Document::updateURLForPushOrReplaceState(const URL& url)
documentLoader->replaceRequestURLForSameDocumentNavigation(url);
}
-void Document::statePopped(PassRefPtr<SerializedScriptValue> stateObject)
+void Document::statePopped(Ref<SerializedScriptValue>&& stateObject)
{
if (!frame())
return;
@@ -4723,14 +5304,14 @@ void Document::statePopped(PassRefPtr<SerializedScriptValue> stateObject)
// Per step 11 of section 6.5.9 (history traversal) of the HTML5 spec, we
// defer firing of popstate until we're in the complete state.
if (m_readyState == Complete)
- enqueuePopstateEvent(stateObject);
+ dispatchPopstateEvent(WTFMove(stateObject));
else
- m_pendingStateObject = stateObject;
+ m_pendingStateObject = WTFMove(stateObject);
}
-void Document::updateFocusAppearanceSoon(bool restorePreviousSelection)
+void Document::updateFocusAppearanceSoon(SelectionRestorationMode mode)
{
- m_updateFocusAppearanceRestoresSelection = restorePreviousSelection;
+ m_updateFocusAppearanceRestoresSelection = mode;
if (!m_updateFocusAppearanceTimer.isActive())
m_updateFocusAppearanceTimer.startOneShot(0);
}
@@ -4740,13 +5321,7 @@ void Document::cancelFocusAppearanceUpdate()
m_updateFocusAppearanceTimer.stop();
}
-void Document::resetHiddenFocusElementSoon()
-{
- if (!m_resetHiddenFocusElementTimer.isActive() && m_focusedElement)
- m_resetHiddenFocusElementTimer.startOneShot(0);
-}
-
-void Document::updateFocusAppearanceTimerFired(Timer<Document>&)
+void Document::updateFocusAppearanceTimerFired()
{
Element* element = focusedElement();
if (!element)
@@ -4757,15 +5332,6 @@ void Document::updateFocusAppearanceTimerFired(Timer<Document>&)
element->updateFocusAppearance(m_updateFocusAppearanceRestoresSelection);
}
-void Document::resetHiddenFocusElementTimer(Timer<Document>&)
-{
- if (view() && view()->needsLayout())
- return;
-
- if (m_focusedElement && !m_focusedElement->isFocusable())
- setFocusedElement(nullptr);
-}
-
void Document::attachRange(Range* range)
{
ASSERT(!m_ranges.contains(range));
@@ -4796,53 +5362,35 @@ HTMLCanvasElement* Document::getCSSCanvasElement(const String& name)
return element.get();
}
-#if ENABLE(IOS_TEXT_AUTOSIZING)
-void Document::addAutoSizingNode(Node* node, float candidateSize)
+#if ENABLE(TEXT_AUTOSIZING)
+
+void Document::addAutoSizedNode(Text& 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);
+ LOG(TextAutosizing, " addAutoSizedNode %p candidateSize=%f", &node, candidateSize);
+ auto addResult = m_textAutoSizedNodes.add<TextAutoSizingHashTranslator>(node.renderer()->style(), nullptr);
+ if (addResult.isNewEntry)
+ addResult.iterator->value = std::make_unique<TextAutoSizingValue>();
+ addResult.iterator->value->addTextNode(node, candidateSize);
}
-void Document::validateAutoSizingNodes()
+void Document::updateAutoSizedNodes()
{
- Vector<TextAutoSizingKey> nodesForRemoval;
- for (auto it = m_textAutoSizedNodes.begin(), end = m_textAutoSizedNodes.end(); it != end; ++it) {
- RefPtr<TextAutoSizingValue> value = it->value;
- // Update all the nodes in the collection to reflect the new
- // candidate size.
- if (!value)
- continue;
-
- value->adjustNodeSizes();
- if (!value->numNodes())
- nodesForRemoval.append(it->key);
- }
- unsigned count = nodesForRemoval.size();
- for (unsigned i = 0; i < count; i++)
- m_textAutoSizedNodes.remove(nodesForRemoval[i]);
+ m_textAutoSizedNodes.removeIf([](auto& keyAndValue) {
+ return keyAndValue.value->adjustTextNodeSizes() == TextAutoSizingValue::StillHasNodes::No;
+ });
}
-void Document::resetAutoSizingNodes()
+void Document::clearAutoSizedNodes()
{
- for (auto it = m_textAutoSizedNodes.begin(), end = m_textAutoSizedNodes.end(); it != end; ++it) {
- RefPtr<TextAutoSizingValue> value = it->value;
- if (value)
- value->reset();
- }
m_textAutoSizedNodes.clear();
}
-#endif // ENABLE(IOS_TEXT_AUTOSIZING)
+#endif // ENABLE(TEXT_AUTOSIZING)
void Document::initDNSPrefetch()
{
- Settings* settings = this->settings();
-
m_haveExplicitlyDisabledDNSPrefetch = false;
- m_isDNSPrefetchEnabled = settings && settings->dnsPrefetchingEnabled() && securityOrigin()->protocol() == "http";
+ m_isDNSPrefetchEnabled = settings().dnsPrefetchingEnabled() && securityOrigin().protocol() == "http";
// Inherit DNS prefetch opt-out from parent frame
if (Document* parent = parentDocument()) {
@@ -4853,7 +5401,7 @@ void Document::initDNSPrefetch()
void Document::parseDNSPrefetchControlHeader(const String& dnsPrefetchControl)
{
- if (equalIgnoringCase(dnsPrefetchControl, "on") && !m_haveExplicitlyDisabledDNSPrefetch) {
+ if (equalLettersIgnoringASCIICase(dnsPrefetchControl, "on") && !m_haveExplicitlyDisabledDNSPrefetch) {
m_isDNSPrefetchEnabled = true;
return;
}
@@ -4865,7 +5413,7 @@ void Document::parseDNSPrefetchControlHeader(const String& dnsPrefetchControl)
void Document::addConsoleMessage(MessageSource source, MessageLevel level, const String& message, unsigned long requestIdentifier)
{
if (!isContextThread()) {
- postTask(AddConsoleMessageTask::create(source, level, message));
+ postTask(AddConsoleMessageTask(source, level, message));
return;
}
@@ -4873,79 +5421,50 @@ void Document::addConsoleMessage(MessageSource source, MessageLevel level, const
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, PassRefPtr<ScriptCallStack> callStack, JSC::ExecState* state, unsigned long requestIdentifier)
+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::create(source, level, message));
+ postTask(AddConsoleMessageTask(source, level, message));
return;
}
if (Page* page = this->page())
- page->console().addMessage(source, level, message, sourceURL, lineNumber, columnNumber, callStack, state, requestIdentifier);
+ page->console().addMessage(source, level, message, sourceURL, lineNumber, columnNumber, WTFMove(callStack), state, requestIdentifier);
}
-SecurityOrigin* Document::topOrigin() const
+void Document::postTask(Task&& task)
{
- return topDocument().securityOrigin();
-}
-
-struct PerformTaskContext {
- WTF_MAKE_NONCOPYABLE(PerformTaskContext); WTF_MAKE_FAST_ALLOCATED;
-public:
- PerformTaskContext(WeakPtr<Document> document, PassOwnPtr<ScriptExecutionContext::Task> task)
- : documentReference(document)
- , task(task)
- {
- }
-
- WeakPtr<Document> documentReference;
- OwnPtr<ScriptExecutionContext::Task> task;
-};
-
-void Document::didReceiveTask(void* untypedContext)
-{
- ASSERT(isMainThread());
-
- OwnPtr<PerformTaskContext> context = adoptPtr(static_cast<PerformTaskContext*>(untypedContext));
- ASSERT(context);
-
- Document* document = context->documentReference.get();
- if (!document)
- return;
+ callOnMainThread([documentReference = m_weakFactory.createWeakPtr(), task = WTFMove(task)]() mutable {
+ ASSERT(isMainThread());
- Page* page = document->page();
- if ((page && page->defersLoading()) || !document->m_pendingTasks.isEmpty()) {
- document->m_pendingTasks.append(context->task.release());
- return;
- }
-
- context->task->performTask(document);
-}
+ Document* document = documentReference.get();
+ if (!document)
+ return;
-void Document::postTask(PassOwnPtr<Task> task)
-{
- callOnMainThread(didReceiveTask, new PerformTaskContext(m_weakFactory.createWeakPtr(), task));
+ Page* page = document->page();
+ if ((page && page->defersLoading() && document->activeDOMObjectsAreSuspended()) || !document->m_pendingTasks.isEmpty())
+ document->m_pendingTasks.append(WTFMove(task));
+ else
+ task.performTask(*document);
+ });
}
-void Document::pendingTasksTimerFired(Timer<Document>&)
+void Document::pendingTasksTimerFired()
{
- while (!m_pendingTasks.isEmpty()) {
- OwnPtr<Task> task = m_pendingTasks[0].release();
- m_pendingTasks.remove(0);
- task->performTask(this);
- }
+ Vector<Task> pendingTasks = WTFMove(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);
+ // A page may subsequently suspend DOM objects, say as part of handling a scroll or zoom gesture, after the
+ // embedding client requested the page be suspended. We ignore such requests so long as the embedding client
+ // requested the suspension first. See <rdar://problem/13754896> for more details.
+ ASSERT(reasonForSuspendingActiveDOMObjects() == ActiveDOMObject::PageWillBeSuspended);
return;
}
-#endif
-
- ASSERT(!m_scheduledTasksAreSuspended);
suspendScriptedAnimationControllerCallbacks();
suspendActiveDOMObjects(reason);
@@ -4982,73 +5501,41 @@ void Document::resumeScheduledTasks(ActiveDOMObject::ReasonForSuspension reason)
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 USE(ACCELERATED_COMPOSITING)
if (RenderView* view = renderView()) {
if (view->usesCompositing())
view->compositor().windowScreenDidChange(displayID);
}
-#endif
-}
-
-String Document::displayStringModifiedByEncoding(const String& str) const
-{
- if (m_decoder)
- return m_decoder->encoding().displayString(str.impl());
- return str;
-}
-
-PassRefPtr<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
+String Document::displayStringModifiedByEncoding(const String& string) const
{
- if (m_decoder)
- m_decoder->encoding().displayBuffer(buffer, len);
+ if (!m_decoder)
+ return string;
+ return String { string }.replace('\\', m_decoder->encoding().backslashAsCurrencySymbol());
}
-// Generate definitions for both character types
-template void Document::displayBufferModifiedByEncodingInternal<LChar>(LChar*, unsigned) const;
-template void Document::displayBufferModifiedByEncodingInternal<UChar>(UChar*, unsigned) const;
-
-void Document::enqueuePageshowEvent(PageshowEventPersistence persisted)
+void Document::dispatchPageshowEvent(PageshowEventPersistence persisted)
{
// FIXME: https://bugs.webkit.org/show_bug.cgi?id=36334 Pageshow event needs to fire asynchronously.
dispatchWindowEvent(PageTransitionEvent::create(eventNames().pageshowEvent, persisted), this);
@@ -5059,10 +5546,9 @@ void Document::enqueueHashchangeEvent(const String& oldURL, const String& newURL
enqueueWindowEvent(HashChangeEvent::create(oldURL, newURL));
}
-void Document::enqueuePopstateEvent(PassRefPtr<SerializedScriptValue> stateObject)
+void Document::dispatchPopstateEvent(RefPtr<SerializedScriptValue>&& stateObject)
{
- // FIXME: https://bugs.webkit.org/show_bug.cgi?id=36202 Popstate event needs to fire asynchronously
- dispatchWindowEvent(PopStateEvent::create(stateObject, m_domWindow ? m_domWindow->history() : nullptr));
+ dispatchWindowEvent(PopStateEvent::create(WTFMove(stateObject), m_domWindow ? m_domWindow->history() : nullptr));
}
void Document::addMediaCanStartListener(MediaCanStartListener* listener)
@@ -5079,15 +5565,11 @@ void Document::removeMediaCanStartListener(MediaCanStartListener* listener)
MediaCanStartListener* Document::takeAnyMediaCanStartListener()
{
- HashSet<MediaCanStartListener*>::iterator slot = m_mediaCanStartListeners.begin();
- if (slot == m_mediaCanStartListeners.end())
- return nullptr;
- MediaCanStartListener* listener = *slot;
- m_mediaCanStartListeners.remove(slot);
- return listener;
+ return m_mediaCanStartListeners.takeAny();
}
#if ENABLE(DEVICE_ORIENTATION) && PLATFORM(IOS)
+
DeviceMotionController* Document::deviceMotionController() const
{
return m_deviceMotionController.get();
@@ -5097,21 +5579,19 @@ 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)
+void Document::requestFullScreenForElement(Element* element, FullScreenCheckType checkType)
{
- // The Mozilla Full Screen API <https://wiki.mozilla.org/Gecko:FullScreenAPI> has different requirements
- // for full screen mode, and do not have the concept of a full screen element stack.
- bool inLegacyMozillaMode = (flags & Element::LEGACY_MOZILLA_REQUEST);
-
do {
if (!element)
element = documentElement();
@@ -5121,7 +5601,7 @@ void Document::requestFullScreenForElement(Element* element, unsigned short flag
// node document:
// The context object is not in a document.
- if (!element->inDocument())
+ if (!element->isConnected())
break;
// The context object's node document, or an ancestor browsing context's document does not have
@@ -5130,9 +5610,8 @@ void Document::requestFullScreenForElement(Element* element, unsigned short flag
break;
// The context object's node document fullscreen element stack is not empty and its top element
- // is not an ancestor of the context object. (NOTE: Ignore this requirement if the request was
- // made via the legacy Mozilla-style API.)
- if (!m_fullScreenElementStack.isEmpty() && !m_fullScreenElementStack.last()->contains(element) && !inLegacyMozillaMode)
+ // is not an ancestor of the context object.
+ if (!m_fullScreenElementStack.isEmpty() && !m_fullScreenElementStack.last()->contains(element))
break;
// A descendant browsing context's document has a non-empty fullscreen element stack.
@@ -5143,7 +5622,7 @@ void Document::requestFullScreenForElement(Element* element, unsigned short flag
break;
}
}
- if (descendentHasNonEmptyStack && !inLegacyMozillaMode)
+ if (descendentHasNonEmptyStack)
break;
// This algorithm is not allowed to show a pop-up:
@@ -5157,14 +5636,13 @@ void Document::requestFullScreenForElement(Element* element, unsigned short flag
if (!page() || !page()->settings().fullScreenEnabled())
break;
- if (!page()->chrome().client().supportsFullScreenForElement(element, flags & Element::ALLOW_KEYBOARD_INPUT)) {
+ bool hasKeyboardAccess = true;
+ if (!page()->chrome().client().supportsFullScreenForElement(*element, hasKeyboardAccess)) {
// The new full screen API does not accept a "flags" parameter, so fall back to disallowing
// keyboard input if the chrome client refuses to allow keyboard input.
- if (!inLegacyMozillaMode && flags & Element::ALLOW_KEYBOARD_INPUT) {
- flags &= ~Element::ALLOW_KEYBOARD_INPUT;
- if (!page()->chrome().client().supportsFullScreenForElement(element, false))
- break;
- } else
+ hasKeyboardAccess = false;
+
+ if (!page()->chrome().client().supportsFullScreenForElement(*element, hasKeyboardAccess))
break;
}
@@ -5216,8 +5694,8 @@ void Document::requestFullScreenForElement(Element* element, unsigned short flag
// 5. Return, and run the remaining steps asynchronously.
// 6. Optionally, perform some animation.
- m_areKeysEnabledInFullScreen = flags & Element::ALLOW_KEYBOARD_INPUT;
- page()->chrome().client().enterFullScreenForElement(element);
+ m_areKeysEnabledInFullScreen = hasKeyboardAccess;
+ page()->chrome().client().enterFullScreenForElement(*element);
// 7. Optionally, display a message indicating how the user can exit displaying the context object fullscreen.
return;
@@ -5268,9 +5746,9 @@ void Document::webkitExitFullscreen()
// 4. For each descendant in descendants, empty descendant's fullscreen element stack, and queue a
// task to fire an event named fullscreenchange with its bubbles attribute set to true on descendant.
- for (Deque<RefPtr<Document>>::iterator i = descendants.begin(); i != descendants.end(); ++i) {
- (*i)->clearFullscreenElementStack();
- addDocumentToFullScreenChangeEventQueue(i->get());
+ for (auto& document : descendants) {
+ document->clearFullscreenElementStack();
+ addDocumentToFullScreenChangeEventQueue(document.get());
}
// 5. While doc is not null, run these substeps:
@@ -5282,7 +5760,7 @@ void Document::webkitExitFullscreen()
// If doc's fullscreen element stack is non-empty and the element now at the top is either
// not in a document or its node document is not doc, repeat this substep.
newTop = currentDoc->webkitFullscreenElement();
- if (newTop && (!newTop->inDocument() || &newTop->document() != currentDoc))
+ if (newTop && (!newTop->isConnected() || &newTop->document() != currentDoc))
continue;
// 2. Queue a task to fire an event named fullscreenchange with its bubbles attribute set to true
@@ -5314,7 +5792,7 @@ void Document::webkitExitFullscreen()
}
// Otherwise, notify the chrome of the new full screen element.
- page()->chrome().client().enterFullScreenForElement(newTop);
+ page()->chrome().client().enterFullScreenForElement(*newTop);
}
bool Document::webkitFullscreenEnabled() const
@@ -5326,9 +5804,20 @@ 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->parentElement())
+ fullScreenElement->parentElement()->invalidateStyleAndRenderersForSubtree();
+}
+
void Document::webkitWillEnterFullScreenForElement(Element* element)
{
- if (!hasLivingRenderTree() || inPageCache())
+ if (!hasLivingRenderTree() || pageCacheState() != NotInPageCache)
return;
ASSERT(element);
@@ -5339,9 +5828,11 @@ void Document::webkitWillEnterFullScreenForElement(Element* element)
ASSERT(page()->settings().fullScreenEnabled());
- if (m_fullScreenRenderer)
- m_fullScreenRenderer->unwrapRenderer();
+ unwrapFullScreenRenderer(m_fullScreenRenderer, m_fullScreenElement.get());
+ if (element)
+ element->willBecomeFullscreenElement();
+
m_fullScreenElement = element;
#if USE(NATIVE_FULLSCREEN_VIDEO)
@@ -5354,17 +5845,17 @@ void Document::webkitWillEnterFullScreenForElement(Element* element)
// a box will have a frameRect. The placeholder will be created in setFullScreenRenderer()
// during layout.
auto renderer = m_fullScreenElement->renderer();
- bool shouldCreatePlaceholder = renderer && renderer->isBox();
+ bool shouldCreatePlaceholder = is<RenderBox>(renderer);
if (shouldCreatePlaceholder) {
- m_savedPlaceholderFrameRect = toRenderBox(renderer)->frameRect();
- m_savedPlaceholderRenderStyle = RenderStyle::clone(&renderer->style());
+ m_savedPlaceholderFrameRect = downcast<RenderBox>(*renderer).frameRect();
+ m_savedPlaceholderRenderStyle = RenderStyle::clonePtr(renderer->style());
}
if (m_fullScreenElement != documentElement())
RenderFullScreen::wrapRenderer(renderer, renderer ? renderer->parent() : nullptr, *this);
m_fullScreenElement->setContainsFullScreenElementOnAncestorsCrossingFrameBoundaries(true);
-
+
recalcStyle(Style::Force);
}
@@ -5373,7 +5864,7 @@ void Document::webkitDidEnterFullScreenForElement(Element*)
if (!m_fullScreenElement)
return;
- if (!hasLivingRenderTree() || inPageCache())
+ if (!hasLivingRenderTree() || pageCacheState() != NotInPageCache)
return;
m_fullScreenElement->didBecomeFullscreenElement();
@@ -5386,7 +5877,7 @@ void Document::webkitWillExitFullScreenForElement(Element*)
if (!m_fullScreenElement)
return;
- if (!hasLivingRenderTree() || inPageCache())
+ if (!hasLivingRenderTree() || pageCacheState() != NotInPageCache)
return;
m_fullScreenElement->willStopBeingFullscreenElement();
@@ -5397,40 +5888,37 @@ void Document::webkitDidExitFullScreenForElement(Element*)
if (!m_fullScreenElement)
return;
- if (!hasLivingRenderTree() || inPageCache())
+ if (!hasLivingRenderTree() || pageCacheState() != NotInPageCache)
return;
m_fullScreenElement->setContainsFullScreenElementOnAncestorsCrossingFrameBoundaries(false);
m_areKeysEnabledInFullScreen = false;
-
- if (m_fullScreenRenderer)
- m_fullScreenRenderer->unwrapRenderer();
- if (m_fullScreenElement->parentNode())
- m_fullScreenElement->parentNode()->setNeedsStyleRecalc(ReconstructRenderTree);
+ unwrapFullScreenRenderer(m_fullScreenRenderer, m_fullScreenElement.get());
m_fullScreenElement = nullptr;
scheduleForcedStyleRecalc();
-
+
// When webkitCancelFullScreen is called, we call webkitExitFullScreen on the topDocument(). That
// means that the events will be queued there. So if we have no events here, start the timer on
// the exiting document.
bool eventTargetQueuesEmpty = m_fullScreenChangeEventTargetQueue.isEmpty() && m_fullScreenErrorEventTargetQueue.isEmpty();
Document& exitingDocument = eventTargetQueuesEmpty ? topDocument() : *this;
+
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);
+ renderer->createPlaceholder(WTFMove(m_savedPlaceholderRenderStyle), m_savedPlaceholderFrameRect);
else if (renderer && m_fullScreenRenderer && m_fullScreenRenderer->placeholder()) {
RenderBlock* placeholder = m_fullScreenRenderer->placeholder();
- renderer->createPlaceholder(RenderStyle::clone(&placeholder->style()), placeholder->frameRect());
+ renderer->createPlaceholder(RenderStyle::clonePtr(placeholder->style()), placeholder->frameRect());
}
if (m_fullScreenRenderer)
@@ -5445,12 +5933,12 @@ void Document::fullScreenRendererDestroyed()
m_fullScreenRenderer = nullptr;
}
-void Document::fullScreenChangeDelayTimerFired(Timer<Document>&)
+void Document::fullScreenChangeDelayTimerFired()
{
// Since we dispatch events in this function, it's possible that the
// document will be detached and GC'd. We protect it here to make sure we
// can finish the function successfully.
- Ref<Document> protect(*this);
+ Ref<Document> protectedThis(*this);
Deque<RefPtr<Node>> changeQueue;
m_fullScreenChangeEventTargetQueue.swap(changeQueue);
Deque<RefPtr<Node>> errorQueue;
@@ -5471,12 +5959,12 @@ void Document::dispatchFullScreenChangeOrErrorEvent(Deque<RefPtr<Node>>& queue,
// If the element was removed from our tree, also message the documentElement. Since we may
// have a document hierarchy, check that node isn't in another document.
- if (!node->inDocument())
+ if (!node->isConnected())
queue.append(documentElement());
#if ENABLE(VIDEO)
- if (shouldNotifyMediaElement && isHTMLMediaElement(*node))
- toHTMLMediaElement(*node).enteredOrExitedFullscreen();
+ if (shouldNotifyMediaElement && is<HTMLMediaElement>(*node))
+ downcast<HTMLMediaElement>(*node).enteredOrExitedFullscreen();
#endif
node->dispatchEvent(Event::create(eventName, true, false));
}
@@ -5488,7 +5976,7 @@ void Document::fullScreenElementRemoved()
webkitCancelFullScreen();
}
-void Document::removeFullScreenElementOfSubtree(Node* node, bool amongChildrenOnly)
+void Document::removeFullScreenElementOfSubtree(Node& node, bool amongChildrenOnly)
{
if (!m_fullScreenElement)
return;
@@ -5497,7 +5985,7 @@ void Document::removeFullScreenElementOfSubtree(Node* node, bool amongChildrenOn
if (amongChildrenOnly)
elementInSubtree = m_fullScreenElement->isDescendantOf(node);
else
- elementInSubtree = (m_fullScreenElement == node) || m_fullScreenElement->isDescendantOf(node);
+ elementInSubtree = (m_fullScreenElement == &node) || m_fullScreenElement->isDescendantOf(node);
if (elementInSubtree)
fullScreenElementRemoved();
@@ -5514,8 +6002,8 @@ void Document::setAnimatingFullScreen(bool flag)
return;
m_isAnimatingFullScreen = flag;
- if (m_fullScreenElement && m_fullScreenElement->isDescendantOf(this)) {
- m_fullScreenElement->setNeedsStyleRecalc();
+ if (m_fullScreenElement && m_fullScreenElement->isDescendantOf(*this)) {
+ m_fullScreenElement->invalidateStyleForSubtree();
scheduleForcedStyleRecalc();
}
}
@@ -5548,30 +6036,23 @@ void Document::addDocumentToFullScreenChangeEventQueue(Document* doc)
target = doc;
m_fullScreenChangeEventTargetQueue.append(target);
}
+
#endif
#if ENABLE(POINTER_LOCK)
-void Document::webkitExitPointerLock()
+
+void Document::exitPointerLock()
{
- if (!page())
+ Page* page = this->page();
+ if (!page)
return;
- if (Element* target = page()->pointerLockController()->element()) {
- if (target->document() != this)
+ if (auto* target = page->pointerLockController().element()) {
+ if (&target->document() != this)
return;
}
- page()->pointerLockController()->requestPointerUnlock();
+ page->pointerLockController().requestPointerUnlock();
}
-Element* Document::webkitPointerLockElement() 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()
@@ -5583,14 +6064,27 @@ void Document::decrementLoadEventDelayCount()
m_loadEventDelayTimer.startOneShot(0);
}
-void Document::loadEventDelayTimerFired(Timer<Document>&)
+void Document::loadEventDelayTimerFired()
{
- if (frame())
- frame()->loader().checkCompleted();
+ checkCompleted();
}
-#if ENABLE(REQUEST_ANIMATION_FRAME)
-int Document::requestAnimationFrame(PassRefPtr<RequestAnimationFrameCallback> callback)
+void Document::checkCompleted()
+{
+ if (auto* frame = this->frame())
+ frame->loader().checkCompleted();
+}
+
+double Document::monotonicTimestamp() const
+{
+ auto* loader = this->loader();
+ if (!loader)
+ return 0;
+
+ return loader->timing().secondsSinceStartTime(MonotonicTime::now()).seconds();
+}
+
+int Document::requestAnimationFrame(Ref<RequestAnimationFrameCallback>&& callback)
{
if (!m_scriptedAnimationController) {
#if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
@@ -5605,7 +6099,7 @@ int Document::requestAnimationFrame(PassRefPtr<RequestAnimationFrameCallback> ca
m_scriptedAnimationController->suspend();
}
- return m_scriptedAnimationController->registerCallback(callback);
+ return m_scriptedAnimationController->registerCallback(WTFMove(callback));
}
void Document::cancelAnimationFrame(int id)
@@ -5615,11 +6109,11 @@ void Document::cancelAnimationFrame(int id)
m_scriptedAnimationController->cancelCallback(id);
}
-void Document::serviceScriptedAnimations(double monotonicAnimationStartTime)
+void Document::serviceScriptedAnimations(double timestamp)
{
if (!m_scriptedAnimationController)
return;
- m_scriptedAnimationController->serviceScriptedAnimations(monotonicAnimationStartTime);
+ m_scriptedAnimationController->serviceScriptedAnimations(timestamp);
}
void Document::clearScriptedAnimationController()
@@ -5627,13 +6121,83 @@ void Document::clearScriptedAnimationController()
// FIXME: consider using ActiveDOMObject.
if (m_scriptedAnimationController)
m_scriptedAnimationController->clearDocumentPointer();
- m_scriptedAnimationController.clear();
+ m_scriptedAnimationController = nullptr;
}
+
+void Document::sendWillRevealEdgeEventsIfNeeded(const IntPoint& oldPosition, const IntPoint& newPosition, const IntRect& visibleRect, const IntSize& contentsSize, Element* target)
+{
+ // For each edge (top, bottom, left and right), send the will reveal edge event for that direction
+ // if newPosition is at or beyond the notification point, if the scroll direction is heading in the
+ // direction of that edge point, and if oldPosition is before the notification point (which indicates
+ // that this is the first moment that we know we crossed the magic line).
+
+#if ENABLE(WILL_REVEAL_EDGE_EVENTS)
+ // FIXME: broken in RTL documents.
+ int willRevealBottomNotificationPoint = std::max(0, contentsSize.height() - 2 * visibleRect.height());
+ int willRevealTopNotificationPoint = visibleRect.height();
+
+ // Bottom edge.
+ if (newPosition.y() >= willRevealBottomNotificationPoint && newPosition.y() > oldPosition.y()
+ && willRevealBottomNotificationPoint >= oldPosition.y()) {
+ Ref<Event> willRevealEvent = Event::create(eventNames().webkitwillrevealbottomEvent, false, false);
+ if (!target)
+ enqueueWindowEvent(WTFMove(willRevealEvent));
+ else {
+ willRevealEvent->setTarget(target);
+ m_eventQueue.enqueueEvent(WTFMove(willRevealEvent));
+ }
+ }
+
+ // Top edge.
+ if (newPosition.y() <= willRevealTopNotificationPoint && newPosition.y() < oldPosition.y()
+ && willRevealTopNotificationPoint <= oldPosition.y()) {
+ Ref<Event> willRevealEvent = Event::create(eventNames().webkitwillrevealtopEvent, false, false);
+ if (!target)
+ enqueueWindowEvent(WTFMove(willRevealEvent));
+ else {
+ willRevealEvent->setTarget(target);
+ m_eventQueue.enqueueEvent(WTFMove(willRevealEvent));
+ }
+ }
+
+ int willRevealRightNotificationPoint = std::max(0, contentsSize.width() - 2 * visibleRect.width());
+ int willRevealLeftNotificationPoint = visibleRect.width();
+
+ // Right edge.
+ if (newPosition.x() >= willRevealRightNotificationPoint && newPosition.x() > oldPosition.x()
+ && willRevealRightNotificationPoint >= oldPosition.x()) {
+ Ref<Event> willRevealEvent = Event::create(eventNames().webkitwillrevealrightEvent, false, false);
+ if (!target)
+ enqueueWindowEvent(WTFMove(willRevealEvent));
+ else {
+ willRevealEvent->setTarget(target);
+ m_eventQueue.enqueueEvent(WTFMove(willRevealEvent));
+ }
+ }
+
+ // Left edge.
+ if (newPosition.x() <= willRevealLeftNotificationPoint && newPosition.x() < oldPosition.x()
+ && willRevealLeftNotificationPoint <= oldPosition.x()) {
+ Ref<Event> willRevealEvent = Event::create(eventNames().webkitwillrevealleftEvent, false, false);
+ if (!target)
+ enqueueWindowEvent(WTFMove(willRevealEvent));
+ else {
+ willRevealEvent->setTarget(target);
+ m_eventQueue.enqueueEvent(WTFMove(willRevealEvent));
+ }
+ }
+#else
+ UNUSED_PARAM(oldPosition);
+ UNUSED_PARAM(newPosition);
+ UNUSED_PARAM(visibleRect);
+ UNUSED_PARAM(contentsSize);
+ UNUSED_PARAM(target);
#endif
+}
-#if !PLATFORM(IOS)
-#if ENABLE(TOUCH_EVENTS)
-PassRefPtr<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
+#if ENABLE(TOUCH_EVENTS) && !PLATFORM(IOS)
+
+Ref<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) const
{
// FIXME: It's not clear from the documentation at
// http://developer.apple.com/library/safari/#documentation/UserExperience/Reference/DocumentAdditionsReference/DocumentAdditions/DocumentAdditions.html
@@ -5642,124 +6206,206 @@ PassRefPtr<Touch> Document::createTouch(DOMWindow* window, EventTarget* target,
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)
-static void wheelEventHandlerCountChanged(Document* document)
+void Document::wheelEventHandlersChanged()
{
- Page* page = document->page();
+ Page* page = this->page();
if (!page)
return;
- pageWheelEventHandlerCountChanged(*page);
+ if (FrameView* frameView = view()) {
+ if (ScrollingCoordinator* scrollingCoordinator = page->scrollingCoordinator())
+ scrollingCoordinator->frameViewEventTrackingRegionsChanged(*frameView);
+ }
- ScrollingCoordinator* scrollingCoordinator = page->scrollingCoordinator();
- if (!scrollingCoordinator)
- return;
+ bool haveHandlers = m_wheelEventTargets && !m_wheelEventTargets->isEmpty();
+ page->chrome().client().wheelEventHandlersChanged(haveHandlers);
+}
- FrameView* frameView = document->view();
- if (!frameView)
- return;
+void Document::didAddWheelEventHandler(Node& node)
+{
+ if (!m_wheelEventTargets)
+ m_wheelEventTargets = std::make_unique<EventTargetSet>();
+
+ m_wheelEventTargets->add(&node);
- // FIXME: Why doesn't this need to be called in didBecomeCurrentDocumentInFrame?
- scrollingCoordinator->frameViewWheelEventHandlerCountChanged(frameView);
+ 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::didAddWheelEventHandler()
+void Document::didRemoveWheelEventHandler(Node& node, EventHandlerRemoval removal)
{
- ++m_wheelEventHandlerCount;
- wheelEventHandlerCountChanged(this);
+ if (!m_wheelEventTargets)
+ return;
+
+ if (!removeHandlerFromSet(*m_wheelEventTargets, node, removal))
+ return;
+
+ wheelEventHandlersChanged();
+
+ if (Frame* frame = this->frame())
+ DebugPageOverlays::didChangeEventHandlers(*frame);
}
-void Document::didRemoveWheelEventHandler()
+unsigned Document::wheelEventHandlerCount() const
{
- ASSERT(m_wheelEventHandlerCount > 0);
- --m_wheelEventHandlerCount;
- wheelEventHandlerCountChanged(this);
+ if (!m_wheelEventTargets)
+ return 0;
+
+ unsigned count = 0;
+ for (auto& handler : *m_wheelEventTargets)
+ count += handler.value;
+
+ return count;
}
-void Document::didAddTouchEventHandler(Node* handler)
+void Document::didAddTouchEventHandler(Node& handler)
{
#if ENABLE(TOUCH_EVENTS)
- if (!m_touchEventTargets.get())
- m_touchEventTargets = adoptPtr(new TouchEventTargetSet);
- m_touchEventTargets->add(handler);
+ if (!m_touchEventTargets)
+ m_touchEventTargets = std::make_unique<EventTargetSet>();
+
+ m_touchEventTargets->add(&handler);
+
if (Document* parent = parentDocument()) {
- parent->didAddTouchEventHandler(this);
+ 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)
+void Document::didRemoveTouchEventHandler(Node& handler, EventHandlerRemoval removal)
{
#if ENABLE(TOUCH_EVENTS)
- if (!m_touchEventTargets.get())
- return;
- ASSERT(m_touchEventTargets->contains(handler));
- m_touchEventTargets->remove(handler);
- if (Document* parent = parentDocument()) {
- parent->didRemoveTouchEventHandler(this);
+ if (!m_touchEventTargets)
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);
+ removeHandlerFromSet(*m_touchEventTargets, handler, removal);
+
+ if (Document* parent = parentDocument())
+ parent->didRemoveTouchEventHandler(*this);
#else
UNUSED_PARAM(handler);
+ UNUSED_PARAM(removal);
#endif
}
-#if ENABLE(TOUCH_EVENTS)
-void Document::didRemoveEventTargetNode(Node* handler)
+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);
+ 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
+}
-void Document::resetLastHandledUserGestureTimestamp()
+LayoutRect Document::absoluteEventHandlerBounds(bool& includesFixedPositionElements)
{
- m_lastHandledUserGestureTimestamp = monotonicallyIncreasingTime();
+ includesFixedPositionElements = false;
+ if (RenderView* renderView = this->renderView())
+ return renderView->documentRect();
+
+ return LayoutRect();
}
-HTMLIFrameElement* Document::seamlessParentIFrame() const
+Document::RegionFixedPair Document::absoluteRegionForEventTargets(const EventTargetSet* targets)
{
- if (!shouldDisplaySeamlesslyWithParent())
- return nullptr;
+ if (!targets)
+ return RegionFixedPair(Region(), false);
+
+ Region targetRegion;
+ bool insideFixedPosition = false;
+
+ for (auto& keyValuePair : *targets) {
+ LayoutRect rootRelativeBounds;
- return toHTMLIFrameElement(ownerElement());
+ 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);
+ if (is<HTMLBodyElement>(element)) {
+ // For the body, just use the document bounds.
+ // The body may not cover this whole area, but it's OK for this region to be an overestimate.
+ rootRelativeBounds = absoluteEventHandlerBounds(insideFixedPosition);
+ } else
+ rootRelativeBounds = element->absoluteEventHandlerBounds(insideFixedPosition);
+ }
+
+ if (!rootRelativeBounds.isEmpty())
+ targetRegion.unite(Region(enclosingIntRect(rootRelativeBounds)));
+ }
+
+ return RegionFixedPair(targetRegion, insideFixedPosition);
}
-bool Document::shouldDisplaySeamlesslyWithParent() const
+void Document::updateLastHandledUserGestureTimestamp()
{
-#if ENABLE(IFRAME_SEAMLESS)
- if (!RuntimeEnabledFeatures::sharedFeatures().seamlessIFramesEnabled())
- return false;
- HTMLFrameOwnerElement* ownerElement = this->ownerElement();
- if (!ownerElement)
- return false;
- return m_mayDisplaySeamlesslyWithParent && ownerElement->hasTagName(iframeTag) && ownerElement->fastHasAttribute(seamlessAttr);
-#else
- return false;
-#endif
+ m_lastHandledUserGestureTimestamp = monotonicallyIncreasingTime();
+ ResourceLoadObserver::sharedObserver().logUserInteractionWithReducedTimeResolution(*this);
+}
+
+void Document::startTrackingStyleRecalcs()
+{
+ m_styleRecalcCount = 0;
+}
+
+unsigned Document::styleRecalcCount() const
+{
+ return m_styleRecalcCount;
}
DocumentLoader* Document::loader() const
@@ -5778,27 +6424,27 @@ DocumentLoader* Document::loader() const
}
#if ENABLE(CSS_DEVICE_ADAPTATION)
+
IntSize Document::initialViewportSize() const
{
if (!view())
return IntSize();
return view()->initialViewportSize();
}
+
#endif
-Element* eventTargetElementForDocument(Document* doc)
+Element* eventTargetElementForDocument(Document* document)
{
- if (!doc)
+ if (!document)
return nullptr;
- Element* element = doc->focusedElement();
- if (!element && doc->isPluginDocument()) {
- PluginDocument* pluginDocument = toPluginDocument(doc);
- element = pluginDocument->pluginElement();
- }
- if (!element && doc->isHTMLDocument())
- element = doc->body();
+ 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 = doc->documentElement();
+ element = document->documentElement();
return element;
}
@@ -5813,12 +6459,12 @@ void Document::adjustFloatQuadsForScrollAndAbsoluteZoomAndFrameScale(Vector<Floa
inverseFrameScale = 1 / frame()->frameScaleFactor();
LayoutRect visibleContentRect = view()->visibleContentRect();
- for (size_t i = 0; i < quads.size(); ++i) {
- quads[i].move(-visibleContentRect.x(), -visibleContentRect.y());
+ for (auto& quad : quads) {
+ quad.move(-visibleContentRect.x(), -visibleContentRect.y());
if (zoom != 1)
- quads[i].scale(1 / zoom, 1 / zoom);
+ quad.scale(1 / zoom);
if (inverseFrameScale != 1)
- quads[i].scale(inverseFrameScale, inverseFrameScale);
+ quad.scale(inverseFrameScale);
}
}
@@ -5872,27 +6518,24 @@ static RenderElement* nearestCommonHoverAncestor(RenderElement* obj1, RenderElem
return nullptr;
}
-void Document::updateHoverActiveState(const HitTestRequest& request, Element* innerElement, const PlatformMouseEvent* event, StyleResolverUpdateFlag updateFlag)
+void Document::updateHoverActiveState(const HitTestRequest& request, Element* innerElement)
{
ASSERT(!request.readOnly());
Element* innerElementInDocument = innerElement;
while (innerElementInDocument && &innerElementInDocument->document() != this) {
- innerElementInDocument->document().updateHoverActiveState(request, innerElementInDocument, event);
+ innerElementInDocument->document().updateHoverActiveState(request, innerElementInDocument);
innerElementInDocument = innerElementInDocument->document().ownerElement();
}
Element* oldActiveElement = m_activeElement.get();
if (oldActiveElement && !request.active()) {
// We are clearing the :active chain because the mouse has been released.
- for (RenderElement* curr = oldActiveElement->renderer(); curr; curr = curr->parent()) {
- Element* element = curr->element();
- if (!element)
- continue;
- element->setActive(false);
- m_userActionElements.setInActiveChain(element, false);
+ for (Element* currentElement = oldActiveElement; currentElement; currentElement = currentElement->parentElementInComposedTree()) {
+ currentElement->setActive(false);
+ m_userActionElements.setInActiveChain(currentElement, false);
}
- m_activeElement.clear();
+ m_activeElement = nullptr;
} else {
Element* newActiveElement = innerElementInDocument;
if (!oldActiveElement && newActiveElement && request.active() && !request.touchMove()) {
@@ -5917,7 +6560,7 @@ void Document::updateHoverActiveState(const HitTestRequest& request, Element* in
// at the time the mouse went down.
bool mustBeInActiveChain = request.active() && request.move();
- RefPtr<Element> oldHoveredElement = m_hoveredElement.release();
+ RefPtr<Element> oldHoveredElement = WTFMove(m_hoveredElement);
// A touch release does not set a new hover target; clearing the element we're working with
// will clear the chain of hovered elements all the way to the top of the tree.
@@ -5928,7 +6571,7 @@ void Document::updateHoverActiveState(const HitTestRequest& request, Element* in
// If it hasn't, we do not need to do anything.
Element* newHoveredElement = innerElementInDocument;
while (newHoveredElement && !newHoveredElement->renderer())
- newHoveredElement = newHoveredElement->parentOrShadowHostElement();
+ newHoveredElement = newHoveredElement->parentElementInComposedTree();
m_hoveredElement = newHoveredElement;
@@ -5942,32 +6585,12 @@ void Document::updateHoverActiveState(const HitTestRequest& request, Element* in
Vector<RefPtr<Element>, 32> elementsToRemoveFromChain;
Vector<RefPtr<Element>, 32> elementsToAddToChain;
- // mouseenter and mouseleave events are only dispatched if there is a capturing eventhandler on an ancestor
- // or a normal eventhandler on the element itself (they don't bubble).
- // This optimization is necessary since these events can cause O(n²) capturing event-handler checks.
- bool hasCapturingMouseEnterListener = false;
- bool hasCapturingMouseLeaveListener = false;
- if (event && newHoveredElement != oldHoveredElement.get()) {
- for (ContainerNode* curr = newHoveredElement; curr; curr = curr->parentOrShadowHostNode()) {
- if (curr->hasCapturingEventListeners(eventNames().mouseenterEvent)) {
- hasCapturingMouseEnterListener = true;
- break;
- }
- }
- for (ContainerNode* curr = oldHoveredElement.get(); curr; curr = curr->parentOrShadowHostNode()) {
- if (curr->hasCapturingEventListeners(eventNames().mouseleaveEvent)) {
- hasCapturingMouseLeaveListener = true;
- break;
- }
- }
- }
-
if (oldHoverObj != newHoverObj) {
// If the old hovered element is not nil but it's renderer is, it was probably detached as part of the :hover style
// (for instance by setting display:none in the :hover pseudo-class). In this case, the old hovered element (and its ancestors)
// must be updated, to ensure it's normal style is re-applied.
if (oldHoveredElement && !oldHoverObj) {
- for (Element* element = oldHoveredElement.get(); element; element = element->parentElement()) {
+ for (Element* element = oldHoveredElement.get(); element; element = element->parentElementInComposedTree()) {
if (!mustBeInActiveChain || element->inActiveChain())
elementsToRemoveFromChain.append(element);
}
@@ -5982,9 +6605,9 @@ void Document::updateHoverActiveState(const HitTestRequest& request, Element* in
elementsToRemoveFromChain.append(element);
}
// Unset hovered nodes in sub frame documents if the old hovered node was a frame owner.
- if (oldHoveredElement && oldHoveredElement->isFrameOwnerElement()) {
- if (Document* contentDocument = toHTMLFrameOwnerElement(*oldHoveredElement).contentDocument())
- contentDocument->updateHoverActiveState(request, nullptr, event);
+ if (is<HTMLFrameOwnerElement>(oldHoveredElement.get())) {
+ if (Document* contentDocument = downcast<HTMLFrameOwnerElement>(*oldHoveredElement).contentDocument())
+ contentDocument->updateHoverActiveState(request, nullptr);
}
}
@@ -5997,41 +6620,31 @@ void Document::updateHoverActiveState(const HitTestRequest& request, Element* in
elementsToAddToChain.append(element);
}
- size_t removeCount = elementsToRemoveFromChain.size();
- for (size_t i = 0; i < removeCount; ++i) {
- elementsToRemoveFromChain[i]->setHovered(false);
- if (event && (hasCapturingMouseLeaveListener || elementsToRemoveFromChain[i]->hasEventListeners(eventNames().mouseleaveEvent)))
- elementsToRemoveFromChain[i]->dispatchMouseEvent(*event, eventNames().mouseleaveEvent, 0, newHoveredElement);
- }
+ for (auto& element : elementsToRemoveFromChain)
+ element->setHovered(false);
bool sawCommonAncestor = false;
- for (size_t i = 0, size = elementsToAddToChain.size(); i < size; ++i) {
+ for (auto& element : elementsToAddToChain) {
if (allowActiveChanges)
- elementsToAddToChain[i]->setActive(true);
- if (ancestor && elementsToAddToChain[i] == ancestor->element())
+ element->setActive(true);
+ if (ancestor && element == ancestor->element())
sawCommonAncestor = true;
if (!sawCommonAncestor) {
// Elements after the common hover ancestor does not change hover state, but are iterated over because they may change active state.
- elementsToAddToChain[i]->setHovered(true);
- if (event && (hasCapturingMouseEnterListener || elementsToAddToChain[i]->hasEventListeners(eventNames().mouseenterEvent)))
- elementsToAddToChain[i]->dispatchMouseEvent(*event, eventNames().mouseenterEvent, 0, oldHoveredElement.get());
+ element->setHovered(true);
}
}
-
- ASSERT(updateFlag == RecalcStyleIfNeeded || updateFlag == DeferRecalcStyleIfNeeded);
- if (updateFlag == RecalcStyleIfNeeded)
- updateStyleIfNeeded();
}
bool Document::haveStylesheetsLoaded() const
{
- return !m_styleSheetCollection.hasPendingSheets() || m_ignorePendingStylesheets;
+ return !styleScope().hasPendingSheets() || m_ignorePendingStylesheets;
}
Locale& Document::getCachedLocale(const AtomicString& locale)
{
AtomicString localeKey = locale;
- if (locale.isEmpty() || !RuntimeEnabledFeatures::sharedFeatures().langAttributeAwareFormControlUIEnabled())
+ if (locale.isEmpty() || !settings().langAttributeAwareFormControlUIEnabled())
localeKey = defaultLanguage();
LocaleIdentifierToLocaleMap::AddResult result = m_localeCache.add(localeKey, nullptr);
if (result.isNewEntry)
@@ -6039,31 +6652,27 @@ Locale& Document::getCachedLocale(const AtomicString& locale)
return *(result.iterator->value);
}
-#if ENABLE(TEMPLATE_ELEMENT)
-Document* Document::ensureTemplateDocument()
+Document& Document::ensureTemplateDocument()
{
if (const Document* document = templateDocument())
- return const_cast<Document*>(document);
+ return const_cast<Document&>(*document);
if (isHTMLDocument())
m_templateDocument = HTMLDocument::create(nullptr, blankURL());
else
m_templateDocument = Document::create(nullptr, blankURL());
+ m_templateDocument->setContextDocument(contextDocument());
m_templateDocument->setTemplateDocumentHost(this); // balanced in dtor.
- return m_templateDocument.get();
+ return *m_templateDocument;
}
-#endif
-#if ENABLE(FONT_LOAD_EVENTS)
-PassRefPtr<FontLoader> Document::fontloader()
+Ref<FontFaceSet> Document::fonts()
{
- if (!m_fontloader)
- m_fontloader = FontLoader::create(this);
- return m_fontloader;
+ updateStyleIfNeeded();
+ return fontSelector().fontFaceSet();
}
-#endif
float Document::deviceScaleFactor() const
{
@@ -6081,10 +6690,8 @@ void Document::didAssociateFormControl(Element* element)
m_didAssociateFormControlsTimer.startOneShot(0);
}
-void Document::didAssociateFormControlsTimerFired(Timer<Document>& timer)
+void Document::didAssociateFormControlsTimerFired()
{
- ASSERT_UNUSED(timer, &timer == &m_didAssociateFormControlsTimer);
-
if (!frame() || !frame()->page())
return;
@@ -6095,6 +6702,27 @@ void Document::didAssociateFormControlsTimerFired(Timer<Document>& timer)
m_associatedFormControls.clear();
}
+void Document::setCachedDOMCookies(const String& cookies)
+{
+ ASSERT(!isDOMCookieCacheValid());
+ m_cachedDOMCookies = cookies;
+ // The cookie cache is valid at most until we go back to the event loop.
+ m_cookieCacheExpiryTimer.startOneShot(0);
+}
+
+void Document::invalidateDOMCookieCache()
+{
+ m_cookieCacheExpiryTimer.stop();
+ m_cachedDOMCookies = String();
+}
+
+void Document::didLoadResourceSynchronously()
+{
+ // Synchronous resources loading can set cookies so we invalidate the cookies cache
+ // in this case, to be safe.
+ invalidateDOMCookieCache();
+}
+
void Document::ensurePlugInsInjectedScript(DOMWrapperWorld& world)
{
if (m_hasInjectedPlugInsScript)
@@ -6103,11 +6731,273 @@ void Document::ensurePlugInsInjectedScript(DOMWrapperWorld& world)
// Use the JS file provided by the Chrome client, or fallback to the default one.
String jsString = page()->chrome().client().plugInExtraScript();
if (!jsString)
- jsString = plugInsJavaScript;
+ jsString = String(plugInsJavaScript, sizeof(plugInsJavaScript));
- m_frame->mainFrame().script().evaluateInWorld(ScriptSourceCode(jsString), world);
+ 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().focusedElementInScope())
+ 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)
+
+JSC::InputCursor& Document::inputCursor()
+{
+ return m_inputCursor;
+}
+
+void Document::setInputCursor(Ref<InputCursor>&& cursor)
+{
+ m_inputCursor = WTFMove(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;
+
+ // FIXME: change this back to an ASSERT once https://webkit.org/b/144970 is fixed.
+ 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 = MediaSession::create(*scriptExecutionContext());
+ return *m_defaultMediaSession;
+}
+
+#endif
+
+ShouldOpenExternalURLsPolicy Document::shouldOpenExternalURLsPolicyToPropagate() const
+{
+ if (DocumentLoader* documentLoader = loader())
+ return documentLoader->shouldOpenExternalURLsPolicyToPropagate();
+
+ return ShouldOpenExternalURLsPolicy::ShouldNotAllow;
+}
+
+bool Document::shouldEnforceHTTP09Sandbox() const
+{
+ if (m_isSynthesized || !m_frame)
+ return false;
+ DocumentLoader* documentLoader = m_frame->loader().activeDocumentLoader();
+ return documentLoader && documentLoader->response().isHTTP09();
+}
+
+#if USE(QUICK_LOOK)
+bool Document::shouldEnforceQuickLookSandbox() const
+{
+ if (m_isSynthesized || !m_frame)
+ return false;
+ DocumentLoader* documentLoader = m_frame->loader().activeDocumentLoader();
+ return documentLoader && documentLoader->response().isQuickLook();
+}
+
+void Document::applyQuickLookSandbox()
+{
+ static NeverDestroyed<String> quickLookCSP = makeString("default-src ", QLPreviewProtocol(), ": 'unsafe-inline'; base-uri 'none'; sandbox allow-scripts");
+ ASSERT_WITH_SECURITY_IMPLICATION(contentSecurityPolicy());
+ // The sandbox directive is only allowed if the policy is from an HTTP header.
+ contentSecurityPolicy()->didReceiveHeader(quickLookCSP, ContentSecurityPolicyHeaderType::Enforce, ContentSecurityPolicy::PolicyFrom::HTTPHeader);
+
+ setReferrerPolicy(ReferrerPolicy::Never);
+}
+#endif
+
+bool Document::shouldEnforceContentDispositionAttachmentSandbox() const
+{
+ if (m_isSynthesized)
+ return false;
+
+ bool contentDispositionAttachmentSandboxEnabled = 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(ReferrerPolicy::Never);
+ 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);
+}
+
+const AtomicString& Document::dir() const
+{
+ auto* documentElement = this->documentElement();
+ if (!is<HTMLHtmlElement>(documentElement))
+ return nullAtom;
+ return downcast<HTMLHtmlElement>(*documentElement).dir();
+}
+
+void Document::setDir(const AtomicString& value)
+{
+ auto* documentElement = this->documentElement();
+ if (is<HTMLHtmlElement>(documentElement))
+ downcast<HTMLHtmlElement>(*documentElement).setDir(value);
+}
+
+DOMSelection* Document::getSelection()
+{
+ return m_domWindow ? m_domWindow->getSelection() : nullptr;
+}
+
+void Document::didInsertInDocumentShadowRoot(ShadowRoot& shadowRoot)
+{
+ ASSERT(shadowRoot.isConnected());
+ ASSERT(!m_inDocumentShadowRoots.contains(&shadowRoot));
+ m_inDocumentShadowRoots.add(&shadowRoot);
+}
+
+void Document::didRemoveInDocumentShadowRoot(ShadowRoot& shadowRoot)
+{
+ ASSERT(m_inDocumentShadowRoots.contains(&shadowRoot));
+ m_inDocumentShadowRoots.remove(&shadowRoot);
+}
+
} // namespace WebCore